A couple weeks ago I posted a blog on configuring a Storage Account as a SFTP server. I did that because I wanted to test the Azure Function that I was working on to connect to the SFTP using SSH keys stored in Key Vault. The client I was working have very hard security controls that didn’t allowed me to connect directly when debugging to their on-premises SFTP server.
In this post I’ll show how I did that, what were the complications and how I solved them.
Project Structure
For this example I’m using Azure Functions v4 with .Net 6, Yes, I’m using the latest .Net version and latest Functions SDK :). To be able to communicate with these other Azure resources I’m using the following packages:
- Azure.Identity (1.5.0)
- Azure.Security.KeyVault.Secrets (4.2.0)
- Microsoft.Azure.Functions.Extensions (1.1.0)
- Microsoft.Azure.WebJobs.Extensions.Storage (5.0.0)
- Microsoft.Extensions.DependencyInjection (6.0.0)
- Microsoft.NET.Sdk.Functions (4.0.1)
- SSH.NET (2020.0.1)
Instead of creating just snippets of code to show what I want to do, I’m creating a production ready code to be used right away using the Repository-Service Pattern with DI for Azure Functions. I found this blog post that I think describes the idea of this pattern well.
Storing the SSH Private Key in Key Vault
We need to connect to the SFTP server using a SSH key and for that, I looked at how could I use the Azure Key Vault keys to store and retrieve this SSH key, but there’s no way to retrieve the private key from Azure Key Vault using the SDK. This behavior needs to be embedded in the consumer of the key in Key Vault and this was not the case for the SSH.Net library.
So the other option was to use a secret to store the SSH private key. The best way to achieve this is to encode this private key with a base64 encoding and retrieve and convert it back in the code.
So, the first thing you need to do is to convert the SSH private to base64 and store that in Key Vault as a secret as the picture below shows:
Reading the SSH Private Key from Key Vault
Once the key is stored in Key Vault, you are going to be able to retrieve it as below. Please be aware that the credentials that will have permission to Key Vault needs to be configured before trying this, otherwise the connection will fail.
Here’s how I’m doing this in the code:
1 2 3 4 5 6 7 8 9 |
// Retrieve the secret from key vault var secretClient = new SecretClient(new Uri(keyVaultUrl), new DefaultAzureCredential()); var secretValue = await secretClient.GetSecretAsync(secretName); // Save the secret in a Stream var secretBytes = Convert.FromBase64String(secretValue.Value); MemoryStream ms = new MemoryStream(); ms.Write(secretBytes, 0, secretBytes.Length); ms.Position = 0; |
I’m converting the secret back to a Stream, because the SSH.Net library requires the private key to be in this way to be able to use it.
Connecting to the SFTP server
Now that I have the SSH private key, I’m able to create the authentication credential to access the SFTP server. To do that, I’m using the following code:
1 2 3 4 5 6 7 8 9 |
// Create authentication method as SSH PrivateKeyFile pkf = new PrivateKeyFile(keyFile); PrivateKeyAuthenticationMethod pkam = new PrivateKeyAuthenticationMethod(sftpUsername, pkf); AuthenticationMethod[] am = {pkam}; // Connect to the SFTP server ConnectionInfo ci = new ConnectionInfo(sftpAddress, sftpUsername, am); SftpClient sftpClient = new SftpClient(ci); sftpClient.Connect(); |
As you can see these are just snippets of the full code that I did for this function to show the points that I want to make across, but you can get the complete code here.
Uploading the file to the SFTP server
Once the connection is successful, you can upload (or download) your files to the SFTP server using the code below:
1 2 3 4 |
// Upload file to SFTP server await Task.Factory.FromAsync( sftpClient.BeginUploadFile(fileToCopy, $"{sftpFolderName}/{fileName}"), sftpClient.EndUploadFile); |
As you can see I’m using Task to have an asynchronous behavior on the upload of the file to follow the async pattern that I implemented in my Azure Function. Again, you can see the complete code here.
Configuration Settings
Below you will be able to see the configuration settings that I created to be retrieved by the Azure Function. As mentioned in the previous post, I’m using a Storage Account with SFTP enabled, which makes my life easier to have a SFTP server available for my tests. If you are using the same approach, be aware of the parameters you used to create the user as they are going to impact if the connection will work or not.
Summary
So, in this post I spent quite some time researching the best way to connect to SFTP using SSH keys stored in Key Vault and this is what I could accomplish with this SSH.Net Library. If there’s any other solution out there I would like to know, so please comment on the post.
Hopefully this solution I did will be useful to others.
Cheers!!!