DEV Community

Kurt Feeley for AWS Community Builders

Posted on • Updated on

Securing .NET App Secrets with AWS Secrets Manager

AWS Systems Manager Parameter Store is a great all around addition to your configuration and secrets management story. Parameter Store can be a cost effective solution as there isn't a charge for standard parameters. Parameter Store supports the storage of common configuration data like a URL (String Type) and data that's a bit more complex like a list of OAuth2 scopes (StringList Type) but, AWS Systems Manager Parameter Store also supports more sensitive configuration data like secrets, passwords and tokens (SecureString Type).

So, why use AWS Secrets Manager? AWS Secrets Manager features automated secret rotation and direct integration with services like RDS, Redshift, and DocumentDB. So, if you need to automatically rotate secrets or need integration with data storage technologies like RDS, Redshift, and DocumentDB, AWS Secrets Manager may be the right choice for you.

In this post we’ll focus on AWS Secrets Manager, but if AWS Systems Manager Parameter Store sounds more like your thing, check out this post on Using AWS Systems Manager Parameter Store as a .NET Configuration Provider.

Image description

Photo by saeed karimi on Unsplash

The Solution

In this article, we’ll take a look at using AWS Secrets Manager to store and retrieve confidential data. We’ll create a .NET API as the reference application and we'll use the AWS CLI and a .NET library developed by AWS that makes this process simple.

Prerequisites

To complete this solution, you will need the .NET CLI which is included in the .NET 6 SDK. In addition, you will need to download the AWS CLI and configure your environment for the AWS CLI. You will also need to create an AWS IAM user with programmatic access with the appropriate permissions to create and read secrets in AWS Secrets Manager.

Warning: some AWS services may have fees associated with them.

The Dev Environment

This tutorial was developed using Ubuntu 20.04, AWS CLI v2, .NET 6 SDK and Visual Studio Code 1.66.2. Some commands/constructs may very across systems.

Store Secrets with the AWS CLI

Using the AWS CLI, we’ll first create a random password.

$ aws secretsmanager get-random-password
Enter fullscreen mode Exit fullscreen mode

The response will look something like the following:

{
   “RandomPassword”: “txRxQ6#[Muq_%oVVg,0vLrDJ;7{GG^Gy”
}
Enter fullscreen mode Exit fullscreen mode

Let’s now store that password in AWS Secrets Manager. Take note of the name of the secret.

$ aws secretsmanager create-secret  \
    ––name test-secret \
    ––secret-string 'txRxQ6#[Muq_%oVVg,0vLrDJ;7{GG^Gy'
Enter fullscreen mode Exit fullscreen mode

On completion, you will get a response with the ARN, Name and VersionId.

Create the .NET Test Application

With the secret created, let’s create a test application that integrates with AWS Secrets Manager which will allow us to retrieve the stored secret. For this example, we will use a .NET API.

$ dotnet new webapi ––name Api
Enter fullscreen mode Exit fullscreen mode

Now that we have a reference application, let’s pull in the Nuget that contains the AWS Secrets Manager Caching library with the following command.

$ dotnet add Api/ package AWSSDK.SecretsManager.Caching
Enter fullscreen mode Exit fullscreen mode

Let’s go into the Program.cs file within the new "Api" application and add a few lines directly below the builder variable declaration:

builder.Services.AddScoped<IAmazonSecretsManager>(a =>
      new AmazonSecretsManagerClient(RegionEndpoint.USEast1)
);
Enter fullscreen mode Exit fullscreen mode

These lines will setup the dependency injection so that when a class requires an IAmazonSecretsManager based class, an AmazonSecretsManagerClient will be supplied.

The Program.cs file should now look like:

using Amazon;
using Amazon.SecretsManager;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddScoped<IAmazonSecretsManager>(a =>
      new AmazonSecretsManagerClient(RegionEndpoint.USEast1)
);

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();
Enter fullscreen mode Exit fullscreen mode

To demonstrate the use of Secrets Manager, let’s create a Controller named SecretController.cs. In the SecretController class, let's create a GetSecret method with the following logic.

This exercise is obviously for demonstration purposes only and not based on a true use case.

[HttpGet]
public async Task<IActionResult> GetSecret()
{
    GetSecretValueRequest request = new GetSecretValueRequest
    {
        SecretId = "test-secret",
        VersionStage = "AWSCURRENT"
    };

    GetSecretValueResponse response = await _secretsManager.GetSecretValueAsync(request);
    return Ok(new { Secret = response.SecretString });
}
Enter fullscreen mode Exit fullscreen mode

Here, we instantiate the GetSecretValueRequest object assigning the SecretId for the secret that we are trying to fetch and also ask for the latest version of the secret by setting the VersionStage to AWSCURRENT. The last step is to send the request and parse the response.

Let’s take a look at the complete SecretController class.

using Amazon.SecretsManager;
using Amazon.SecretsManager.Model;
using Microsoft.AspNetCore.Mvc;

namespace Api.Controllers;

[ApiController]
[Route("[controller]")]
public class SecretController : ControllerBase
{
    private readonly IAmazonSecretsManager _secretsManager;

    public SecretController(IAmazonSecretsManager secretsManager)
    {
        _secretsManager = secretsManager;
    }

    [HttpGet]
    public async Task<IActionResult> GetSecret()
    {
        GetSecretValueRequest request = new GetSecretValueRequest
        {
            SecretId = "test-secret",
            VersionStage = "AWSCURRENT"
        };

        GetSecretValueResponse response = await _secretsManager.GetSecretValueAsync(request);
        return Ok(new { Secret = response.SecretString });
    }
}


Enter fullscreen mode Exit fullscreen mode

Testing the Application

First, let’s get the "Api" application up and running with the following .NET CLI command:

$ dotnet run ––project Api/
Enter fullscreen mode Exit fullscreen mode

*Note, here we have configured the app to run on port 5000. Your app port may very.

In your favorite browser, let’s navigate to http://localhost:5000/secret. You should see something like the following:

{
   "secret": "txRxQ6#[Muq_%oVVg,0vLrDJ;7{GG^Gy"
}
Enter fullscreen mode Exit fullscreen mode

Keep the browser open for a test after we practice a couple more commands.

Let’s go back to the CLI and create a new password to store.

$ aws secretsmanager get-random-password
Enter fullscreen mode Exit fullscreen mode

Again, you should see something like the following:

{
   “RandomPassword”: “0)r}|iRvK2H,%<R9tAJNDu<M@gw*OUD-”
}
Enter fullscreen mode Exit fullscreen mode

Copy the generated password and then let’s update the secret like so:

$ aws secretsmanager put-secret-value \
    ––secret-id test-secret \ 
    ––secret-string ‘0)r}|iRvK2H,%<R9tAJNDu<M@gw*OUD-’
Enter fullscreen mode Exit fullscreen mode

On completion, you will see a response with values for ARN, Name, VersionId, and VersionStages.

Let’s return to the browser and give it a refresh. If you closed the browser, just reopen the browser and browse to: http://localhost:5000/secret. You should now see the value you just entered for the secret and the response should look something like this:

{
   "secret": "0)r}|iRvK2H,%<R9tAJNDu<M@gw*OUD-"
}
Enter fullscreen mode Exit fullscreen mode

With our tests complete, let’s delete that secret.

$ aws secretsmanager delete-secret ––secret-id test-secret
Enter fullscreen mode Exit fullscreen mode

Summary

That’s it! We have concluded this post where we went over reading AWS Secrets Manager secrets from within a .NET application as well as creating, updating and deleting secrets in AWS Secrets Manager via the AWS CLI.

Latest comments (0)