17 Apr 2024 · Software Engineering

    A Complete Guide to Docker Secrets Management

    12 min read
    Contents

    Before now, the concept of containerization was left solely for the DevOps engineers. However today, the average developer is expected to have a basic understanding of how to containerize their applications. With the most popular containerization tool being Docker.

    With this increasing demand for containers, there are some concepts in Docker that most engineers don’t pay atttention to. One of such concepts involves the management of sensitive data in containers. You can observe this in the way most developers hardcode sensitive data like API keys, database credentials, and other secrets directly into their Dockerfiles or environment variables.

    This is bad practice and can lead to security vulnerabilities. This article will guide you through the best practices for managing sensitive data in Docker containers. It will teach you Docker’s built-in secrets management feature and how to use it to secure your applications. By the end of this article, you’ll know how to securely pass your sensitive data to your containers.

    Prerequisites

    This article will contain a lot of hands-on examples and practical demonstrations. To follow along, you should have the following:

    • Basic understanding of containerization especially Docker.
    • A computer with Docker installed. Use the official Docker installation guide to install Docker on your machine.
    • Basic understanding of container orchestration concepts (optional, for the Docker Swarm section).
    • Docker Compose installed on your machine. You can use the official Docker compose installation guide to install Docker compose.

    What are Docker Secrets?

    Docker secrets is a feature for Docker containers, that allows you to use and store sensitive data in a safe and secure way. These sensitive data could be SSH keys, API keys, database credentials, or any other piece of information that should not be shared with everyone who has access to the container.

    By default, data transmitted between Docker containers is not encrypted or protected, so it can potentially be intercepted and read by someone with access to the network. This is why Docker secrets comes into the mix, to provide the security needed for your data.

    Why Do you Need Docker Secrets?

    In a situation where you have one of your secrets, say your TLS certificates stored in plain text within your Dockerfile or in your Docker image. This means that anyone who has access to your Dockerfile or Docker image can easily access this secret. This is why you need Docker secrets.

    Coupled with its main function of providing security, there are other reasons why you need Docker secrets. Some of these reasons include:

    • Superior Security Over Environment Variables: Docker secrets are generally considered more secure for storing sensitive data compared to environment variables. Docker secrets are stored in a virtual filesystem managed by Docker, and they are only accessible to the containers that have been granted access to them. Environment variables, on the other hand, are visible to anyone with access to the container, including processes running within the container.
    • Single Storage Location: Docker secrets are stored in a single location on the Docker host, and they are accessible to all containers running on the host. This makes it easy to manage secrets across multiple containers which is usually the case when using Docker Swarm or Docker Compose.
    • Ease Of Management: Although not utilized as often, using Docker secrets isn’t rocket science. It’s quite easy to manage and use Docker secret via the Docker CLI.
    • Abstraction Between Containers and Secrets: In a case where you have multiple environments, say, test, local and production. By using Docker secrets, your containers don’t need to be aware of the specific credentials used in each environment. Instead, they rely on a consistent secret name, abstracting away the underlying details. This flexibility allows you to deploy the same container image across multiple environments without modification.

    How do Docker Secrets Work?

    You can only use Docker secrets when working with a collection of containers such as in Docker Swarm or Docker compose. When you add some secrets to a Docker Swarm, Docker will use a mutual TLS connection to securely pass these stored secrets to the Swarm manager. These secrets are stored in something called a Raft log, which is like an encrypted record-keeping system.

    The Raft log along with the secrets are then replicated across all the managers in the Swarm. This ensures that even if one manager fails, the secret and other management data remain available.

    Figure 1. Flow diagram for Docker secrets architecture

    When you want a service to use a secret, Docker takes the encrypted secret from the Raft log, decrypts it, and mounts it into the container’s memory as a special kind of file system. This file system is only accessible to the service’s containers, and it’s mounted as a read-only file system.

    By default, in Linux containers, this mounted secret is placed in /run/secrets/<secret_name>, while in Windows containers, it’s put in C:\ProgramData\Docker\secrets. However, you can choose a different location if needed.

    Nodes in the Swarm (the machines running Docker containers) only have access to encrypted secrets if they are Swarm managers or if they are running tasks for services that have been granted access to those secrets.

    When a container stops running, any secrets it had access to are removed from the in-memory filesystem of that container and wiped from the node’s memory.

    In the case where a node loses connection to the Swarm while running a task with access to a secret, the task can still access the secrets it had. However, it won’t receive updates until it reconnects to the Swarm.

    How to Create a Docker Secret

    Docker provides a set of commands that make it easy to create and manage secrets. By running the docker secret createcommand, you can create a new secret and store it in the Docker host. However, there are two ways you can use this command.

    • Using standard input: This is the simplest way to create a secret. The syntax for this is:
    echo "mysecretpassword" | docker secret create my_secret -
    kotjiwncv3kp8uyyj8d7kzesn
    • You’re creating a secret named my_secret and passing the secret data mysecretpassword through a pipe | from the echo command. Docker will then read the secret data from standard input.
    • Using a file: You can also create a secret from a file. The syntax for this is:
    docker secret create my_secret_file /path/to/my_secret_file
    l95k6p8h7q6xgji9lxh164ibs

    In this case, you’re passing the my_secret_file to the docker secret create command. Docker will then read the secret data from the file and create a secret named my_secret_file.

    The kotjiwncv3kp8uyyj8d7kzesn and l95k6p8h7q6xgji9lxh164ibs are the IDs of the secrets you just created. You can use these IDs to reference the secrets in other Docker commands.

    To view your newly created secret, run the docker secret ls command. You should have a similar output:

    ID                          NAME             DRIVER    CREATED              UPDATED
    kotjiwncv3kp8uyyj8d7kzesn   my_secret                  3 minutes ago        3 minutes ago
    l95k6p8h7q6xgji9lxh164ibs   my_secret_file             About a minute ago   About a minute ago
    

    How to Manage Docker Secrets

    The docker secret command provides a set of subcommands that allow you to manage secrets. Below are some of the most commonly used subcommands:

    • docker secret ls: As seen above, this command lists all the secrets stored on the Docker host.
    • docker secret inspect: This command provides detailed information about a specific secret. You can use it to view the metadata of a secret, such as its ID, name, and creation date.
    docker secret inspect my_secret
    
      [
          {
              "ID": "kotjiwncv3kp8uyyj8d7kzesn",
              "Version": {
                  "Index": 11
              },
              "CreatedAt": "2024-03-19T12:07:44.93980171Z",
              "UpdatedAt": "2024-03-19T12:07:44.93980171Z",
              "Spec": {
                  "Name": "my_secret",
                  "Labels": {}
              }
          }
      ]
    • docker secret rm: This command is used to remove a secret from the Docker host. You can use it to delete a secret by passing the secret ID or name as an argument.
    docker secret rm my_secret
    my_secret
    • After running this command, the secret with the name my_secret will be removed from the Docker host.

    Integrating Docker Secrets into Docker Swarm

    Docker secrets are only available to services that are running on the same Docker Swarm. This means that you can only use Docker secrets with services that are part of a Docker Swarm.

    If you’re not familiar with Docker Swarm, it is a container orchestration tool that allows you to manage a cluster of Docker hosts.

    To use Docker secrets with Docker Swarm, you need to create a secret and then grant a service access to that secret. You can do this by adding the --secret flag to the docker service create command.

    The following demo will create a redis service with a secret on Docker Swarm.

    • Initialize a Docker Swarm cluster. If you haven’t already done this, run the following command:
    docker swarm init
    • Create a secret using the docker secret create command:
    echo "mysecretpassword" | docker secret create redis_password -
    
    qg8cpym1q5v96jg67qkj4q5rc
    • Create the redis service and grant it access to the secret using the --secret flag:
    docker service create --name redis --secret redis_password redis:alpine
    
    p3rmff7x5ta4gi4804cp9suj3
    overall progress: 1 out of 1 tasks
    1/1: running   [==================================================>]
    verify: Service converged

    In this command, you’re creating a service named redis and granting it access to the redis_password secret. The redis:alpine image is used to create the service.After running this command, the redis service will be created and granted access to the redis_password secret. The secret will be mounted into the service’s containers and made available to the application.

    • Confirm that the redis service is running:
     docker service ps redis
    • You can see if the secret is mounted on the redis container using the command below:
    docker container exec $(docker ps --filter name=redis -q) ls -l /run/secrets
    
    total 4
    -r--r--r--    1 root     root            16 Mar 19 12:51 redis_password
    • To stop the redis service from having access to the secret, use the following command:
    docker service update --secret-rm redis_password redis
    
    redis
    overall progress: 1 out of 1 tasks
    1/1: running   [==================================================>]
    verify: Service converged
    

    After running this command, the redis service will no longer have access to the redis_password secret.

    • Confirm the service no longer has access to the secret by running the command in step 5.
    docker container exec $(docker ps --filter name=redis -q) ls -l /run/secrets
    
    ls: /run/secrets: No such file or directory
    • Once you’re done with the demo, you can remove the service using the following command:
    docker service rm redis
    
    redis

    Integrating Docker Secrets into Docker Compose

    As mentioned earlier, Docker secrets can only work on multiple containers and Docker compose allows you to run multiple containers at once. This means that you can use Docker secrets with Docker Compose.

    There are two syntaxes for using Docker secrets with Docker Compose. They are the long and short syntax. The long syntax gives you room to specify several attributes for the secret, while the short syntax allows you to specify just the name of the secret.

    The following demo will use the short syntax and apply a secret to a redis service using Docker compose.

    • Create the file(s) to store your secret value. You can call this file my_secret.txt and store the secret value in it.
    echo "mysecretpassword" > my_secret.txt
    • Create a docker-compose.yml file with the following content:
    version: "3.9"
    
    services:
    redis:
      image: redis:alpine
      secrets:
        - my_secret
    
    secrets:
    my_secret:
      file: ./my_secret.txt

    In this file, you’re creating a service named redis and granting it access to the my_secret secret. The redis:alpineimage is used to create the service. The my_secret secret is created using the file attribute and the value is read from the my_secret.txt file.

    • Run the following command to start the redis service:
    docker-compose up -d

    After running this command, the redis service will be created and granted access to the my_secret secret. The secret will be mounted into the service’s containers and made available to the application.

    • Confirm that redis is running:
    docker-compose ps
    
    Name                   Command               State   Ports
    ---------------------------------------------------------------
    ubuntu_redis_1   docker-entrypoint.sh redis ...   Up
    • You can see if the secret is mounted on the redis container using the same command from the Docker Swarm section.
    docker container exec $(docker ps --filter name=redis -q) ls -l /run/secrets
    
    total 4
    -rw-rw-r--    1 1000     redis           17 Mar 19 18:12 my_secret

    The redis secret is now mounted into the redis container and made available to the application.

    Conclusion

    The main takeaway from this article is that secrets management is a critical aspect of container security. By using Docker secrets, you can securely store and manage sensitive data in your containers.

    This article goes in-depth to explain the inner workings of Docker secrets, how to manage them, and how you can integrate secrets into Docker Swarm and Docker Compose. By following the examples provided, you’ll be able to store your secrets securely and use them in your applications.

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    Avatar
    Writen by:
    As a mentor at AltSchool Africa, Prince Onyeanuna combines his passion for cloud engineering and education to support the learning and development of aspiring cloud engineers. He leverages his expertise in DevOps and technical writing to provide guidance, feedback, and resources to the students, helping them master cloud technologies and best practices.
    Avatar
    Reviewed by:
    I picked up most of my soft/hardware troubleshooting skills in the US Army. A decade of Java development drove me to operations, scaling infrastructure to cope with the thundering herd. Engineering coach and CTO of Teleclinic.