Security Top

I just announced the new Learn Spring Security course, including the full material focused on the new OAuth2 stack in Spring Security 5:

>> CHECK OUT THE COURSE

1. Introduction

One of the many philosophies of 12-factor apps is that configuration should be stored in the environment. In practical terms, this means storing configuration separate from our code.

In this tutorial, we'll look at how Docker secrets can help us achieve this goal. We'll look at how we can create and manage Docker secrets. Then, we'll look at how we can utilize Docker secrets as part of our application deployments.

2. What Are Secrets?

In general, secrets provide a mechanism to securely store data that can be read by applications at runtime. Secrets play a critical role in storing sensitive data separately from application code. This includes data such as passwords, hostnames, SSH keys, and more.

For example, let's say our application requires a database connection. To do this, it needs a hostname, username, and password. Furthermore, there's a different database server for development, testing, and production.

With secrets, each environment can provide its own database information to the applications. The application code does not need to know which environment it runs in. It simply needs a consistent way to look up the values.

While Docker secrets are a relatively new feature, most cloud platforms have provided some form of secrets for many years.

For example, both Amazon Web Services and Google Cloud both have a Secrets Manager tool, while Azure provides a Key Vault service. Kubernetes also provides first-class support for secrets.

Docker's implementation of secrets uses many of the same features as the previously mentioned systems:

  • Secrets are created and managed separately from applications
  • Follows principles of least privileged and need-to-know access
  • Flexibility to store a variety of different data types

With a basic understanding of Docker secrets, let's take a look at how to manage them.

3. Managing Docker Secrets

In this section, we'll look at how to manage Docker secrets from creation to removal.

3.1. Docker Requirements

Currently, Docker secrets are only available to swarm services. This means stand-alone containers cannot access secrets.

Therefore, to use these secrets, we must configure our cluster for swarm using the command:

docker swarm init --advertise-addr <MANAGER-IP>

Where <MANAGER-IP> is the IP address assigned by Docker to the manager node.

On Docker Desktop for Windows and Mac, we can simplify the command:

docker swarm init

With our cluster configured for swarm, we can now create secrets.

3.2. Creating Docker Secrets

Docker secrets can store just about any type of data that can be represented as a string or in binary:

  • Usernames and passwords
  • Hostnames and ports
  • SSH keys
  • TLS Certificates

The only real limit is the data must be under 500KB in size.

When creating a secret, the command accepts input from the command line:

docker secret create my_secret -

In this form, the command allows us to type in the value of the secret and even supports multi-line data. To finish entering data, we must give it an EOF signal (Ctrl+D on Unix-based systems).

However, manually typing input with a keyboard is both error-prone and not practical when combined with automated flows. Therefore, we can also use the contents of a file to create a secret:

docker secret create my_secret /path/to/secret/file

3.3. Displaying Docker Secrets

Once we've created a secret, we can confirm that it was successful:

docker secret ls
ID                          NAME        DRIVER    CREATED          UPDATED
2g9z0nabsi6v7hsfra32unb1o   my_secret             30 minutes ago   30 minutes ago

This shows all of our secrets, along with the unique IDs that have been assigned to them. We can inspect individual secrets as well:

docker secret inspect my_secret
[
    {
        "ID": "2g9z0nabsi6v7hsfra32unb1o",
        "Version": {
            "Index": 15
        },
        "CreatedAt": "2022-05-13T00:34:41.2802246Z",
        "UpdatedAt": "2022-05-13T00:34:41.2802246Z",
        "Spec": {
            "Name": "my_secret",
            "Labels": {}
        }
    }
]

3.4. Removing Docker Secrets

It's considered a best practice to remove a secret once it's no longer needed. For example, we can permanently remove a secret from the command line:

docker secret rm my_secret

Note that this command does not work if the secret is being used by any service.

4. Using Docker Secrets

With an understanding of how to manage secrets, we can now look at how to actually use them in our applications. As with most things in the Docker ecosystem, there are multiple ways to do this.

4.1. Docker Service

Because Docker secrets require our cluster to be in swarm mode, we cannot access secrets from the normal Docker run command. Instead, we have to create services, and we can specify one or more secrets with the command line:

docker service create --name my_app --secret my_secret openjdk:19-jdk-alpine

4.2. Docker Compose

With Docker Compose version 3 and later, we have two options for using secrets. Below is a simple example of defining a service and secrets:

version: '3.1'
services:
  my_app:
    image: my_app:latest
    secrets:
     - my_external_secret
     - my_file_secret
secrets:
  my_external_secret:
    external: true
  my_file_secret:
    file: /path/to/secret/file.txt

In this example, we define two secrets. The first one is external, meaning it refers to a secret created using the Docker secret command. The second one refers to a file and doesn't require any initial setup with Docker.

Keep in mind that using the file approach bypasses most of the benefits of using Docker secrets. Also, the file must be available to all hosts where the service may run, and we must take care to protect its contents using mechanisms other than Docker.

4.3. Accessing Secrets

Docker makes secrets available to our applications as files. The default behavior is to make each secret its own file in the directory /run/secrets. Using our earlier example, the contents of my_secret would be available in the file /run/secrets/my_secret.

We can change the location of the file by specifying it with our service:

docker service create
  --name my_app
  --secret source=my_secret,target=/different/path/to/secret/file,mode=0400

This is useful if the secret contains information that our application expects in a specific location. For example, we can use a secret to store an Nginx configuration file and then make it available in the standard location (/etc/nginx/conf.d/site.conf).

5. Conclusion

Secrets are an important tool for any container-based architecture because they help us achieve the goal of keeping code and configuration separate. In addition, Docker secrets provide a way to securely store sensitive data and make it available to applications that need it.

Security bottom

I just announced the new Learn Spring Security course, including the full material focused on the new OAuth2 stack in Spring Security 5:

>> CHECK OUT THE COURSE
Security footer banner
guest
0 Comments
Inline Feedbacks
View all comments