Skip to content

Using docker swarm secrets in PSS®X production

If you want to use Docker containers in production, chances are you’ll want to store your credentials in a secure way.

Docker also includes a more extensive secrets solution, accessed via the docker secret command group, but it only works with services deployed to a Swarm cluster.

Swarm secrets are managed by your cluster and will be securely replicated to each node within it. Secrets are guaranteed to be highly available, like the other cluster-level data created by the swarm.

Secrets are encrypted during transit and storage. They’re held in memory, so they’re not persisted on individual nodes or container filesystems. Additionally, nodes only receive the secrets required by the containers they’re running, further minimizing the risk of exposure.

Creating a Swarm

Because docker secrets only work in Swarm mode, you’ll need access to a swarm before you can try it. Run the following command to create a new single-node swarm on your current machine:

docker swarm init

Creating Secrets

Once you’ve created a swarm, you can use docker secrets to start managing your secrets. First create a local file that holds your secret value:

"yourStrong(!)Password" | Set-Content -Path mssql_sa.pwd -Encoding Ascii -Force -NoNewline
echo "yourStrong(!)Password" > mssql_sa.pwd

Next, create the Docker secret object. The command takes two arguments: the secret’s name, and the path to the file that contains its value:

docker secret create MSSQL_SA_PASSWORD mssql_sa.pwd -
docker secret create MSSQL_SA_PASSWORD mssql_sa.pwd -

One another possible way to store secrets with Vault.

To store a secret;

"yourStrong(!)Password" | vault write secret/mssql_sa -
echo "yourStrong(!)Password"  | vault write secret/mssql_sa -

To retrieve a secret and put it into your Docker swarm:

vault read -field=value secret/mssql_sa | docker secret create MSSQL_SA_PASSWORD -
echo "yourStrong(!)Password"  | vault write secret/mssql_sa -

Referencing docker swarm secrets in compose files

You can reuse Swarm secrets in services managed by Docker Compose. Create the secret using docker secret create, then reference it within the services section of your docker-compose.yml file by setting the external field to true:

services:
  sqlserver:
    ...
    environment:
      - MSSQL_SA_PASSWORD_FILE=/run/secrets/MSSQL_SA_PASSWORD
    secrets:
      - MSSQL_SA_PASSWORD

secrets:
  MSSQL_SA_PASSWORD:
    external: true

When you set external: true in your Compose file, it tells Docker that the secret doesn’t need to be created by the Compose file itself. Instead, it should already exist and be available when the Compose is run. If the secret isn’t there, it will result in an error.

The long syntax provides more granularity in how the secret is created within the service's containers.

  • source: The name of the secret as it exists on the platform.
  • target: The name of the file to be mounted in /run/secrets/ in the service's task container, or absolute path of the file if an alternate location is required. Defaults to source if not specified.
  • uid and gid: The numeric UID or GID that owns the file within /run/secrets/ in the service's task containers. Default value is USER running container.
  • mode: The permissions for the file to be mounted in /run/secrets/ in the service's task containers, in octal notation. The default value is world-readable permissions (mode 0444). The writable bit must be ignored if set. The executable bit may be set.

The following example sets the name of the Default__Ssl__CertPassphrase secret file to Redis__Connections__Ssl__CertPassphrase within the container, sets the mode to 0440 (group-readable), and sets the user and group to 103.

services:
  sqlserver:
    ...
    environment:
    ...
    secrets:
      - source: Default__Ssl__CertPassphrase
        target: Redis__Connections__Ssl__CertPassphrase
        uid: "103"
        gid: "103"
        mode: "0440"

secrets:
  Default__Ssl__CertPassphrase:
    external: true

Using docker secrets in PSS®X backing services

Now, you will probably want to reference secrets from your environment variables (e.g: MSSQL_SA_PASSWORD), but that is unfortunately NOT supported yet.

Secrets are accessible from the containers that have access to them by using the file path /run/secrets/MSSQL_SA_PASSWORD, so what you can do, is add another environment variable to your docker-compose, having a custom name (appending _FILE for example)

services:
  sqlserver:
    ...
    environment:
      - MSSQL_SA_PASSWORD_FILE=/run/secrets/MSSQL_SA_PASSWORD # This will be directed to => `MSSQL_SA_PASSWORD` environment variable with help of bash script.

And in your container entrypoint, call the following function for each environment variable you have set up

This will export the value stored in the secret to the correct environment variable. In this case MSSQL_SA_PASSWORD exported as environment variable.

#!/usr/bin/env bash

# By setting an environment variable matching *_FILE to a file path, the prefixed environment
# variable will be overridden with the value specified in that file

echo "Configure environment variables of the mssql..."
mssql_env_vars=(
    MSSQL_SA_PASSWORD
)
for env_var in "${mssql_env_vars[@]}"; do
    file_env_var="${env_var}_FILE"
    if [[ -n "${!file_env_var:-}" ]]; then
        if [[ -r "${!file_env_var:-}" ]]; then
            export "${env_var}=$(< "${!file_env_var}")"
            unset "${file_env_var}"
        else
            warn "Skipping export of '${env_var}'. '${!file_env_var:-}' is not readable."
        fi
    fi
done
unset mssql_env_vars

echo "Starting sql server..."
/opt/mssql/bin/sqlservr

All PSS®X backing services automatically map to environment variables with the _FILE suffix.

Using docker secrets in PSS®X .net core application configuration files

In ASP.NET Core application the configuration is based on key-value pairs established by configuration provider. Configuration providers read configuration data into key-value pairs from a variety of configuration source like settings files, environment variables, etc.

This is how a configuration file is added to an application:

...
builder.Host
   .ConfigureAppConfiguration((context, config) =>
   {
      config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
      config.AddEnvironmentVariables();
   };
...

and this is how a configuration may look like for a connection string looks like:

"ConnectionStrings": {
  "Default": "pssx-sql-server;User Id=sa;Password=myPassword;Initial Catalog=pssx-app;MultipleActiveResultSets=true;TrustServerCertificate=True",
  "Hangfire": "pssx-sql-server;User Id=sa;Password=myPassword;Initial Catalog=pssx-hangfire;MultipleActiveResultSets=true;TrustServerCertificate=True"
}

In the above example the password is stored in plain text and unencrypted and this is not safe.

Now we need to read docker secrets files and pull them into the .net core configuration. This can be easily achieved with the Docker Secrets.NET Core configuration provider for PSSX.