1. Overview
Securing passwords in Docker is a critical aspect of preserving the security of a containerized application. External services and databases often require password authentication. A lack of proper protection could compromise data security.
In this tutorial, we’ll explain different ways to secure passwords in Docker.
2. Importance of Password Security
Passwords are a common way to authenticate external services or databases. Cloud services, databases, and other third-party services constantly require authentication. However, if these passwords aren’t properly secured, they can be compromised by unauthorized users. Consequently, sensitive data can be exposed to unauthorized individuals.
Containerized environments typically last for a short time and are ephemeral, which makes securing passwords even more critical.
3. Using the Environment Variables
The easiest way to pass passwords to a container is to use ENV variables. Environment variables can be set at runtime using the -e flag when running a container. To illustrate, we can use the following command to pass the password as an ENV variable:
$ docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql
In this mysql image, the password is retrieved from the environment variable MYSQL_ROOT_PASSWORD. To set the root account’s password, we passed the env variable to the docker run command.
Environment variables aren’t stored in the image itself, so they can’t be accidentally committed to a source code repo. We don’t need to modify the image to change the ENV. Environment variables are unreliable in multi-tenant environments, where multiple containers share a host and environment. Any process running within the container can access it. So, we shouldn’t use it for sensitive data storage.
4. Using a Secret Management System
We can securely manage and store passwords using a secret management system like Vault. These systems typically provide a secure API for storing and retrieving secrets. Moreover, we can integrate it with our containerized environment to provide these secrets to the container at runtime.
4.1. Using the Vault
We can store our passwords as secrets in the vault and then use the vault’s API to retrieve the password when the container starts. In our application, we can either use the vault’s command-line client or client library.
Let’s run vault in a Docker environment to store a password and retrieve it in another Docker container:
$ docker run -itd --cap-add=IPC_LOCK -e 'VAULT_DEV_ROOT_TOKEN_ID=myroot' -e 'VAULT_DEV_LISTEN_ADDRESS=0.0.0.0:8200'
-p 8200:8200 --name vault vault
In the above command, VAULT_DEV_ROOT_TOKEN_ID is set to myroot and VAULT_DEV_LISTEN_ADDRESS to 0.0.0.0:8200, so all available network interfaces will be listening at port 8200. The -p option of the docker run command exposes the vault server’s port 8200 externally on the host’s port 8200. Let’s take a look at how to create a secret password in the vault:
$ export VAULT_ADDR='http://127.0.0.1:8200'
The above command tells the vault command-line tool to communicate with the server running on 127.0.0.1 on port 8200. Let’s log into the vault server and create a password secret:
$ vault login myroot
$ vault kv put secret/test password=mysecretpasswordtest
Vault login tries to log in to the server with the token myroot. This token handles authentication and authorization for users. The vault kv put command creates the secret at the path secret/test with a key-value pair. This allows us to store sensitive information on the server and retrieve it later.
4.2. Retrieving the Secrets
Let’s run another container that retrieves the password from the above vault server:
$ docker run --rm --network=host -e 'VAULT_ADDR=http://127.0.0.1:8200' -e 'VAULT_TOKEN=myroot'
vault sh -c 'apk add --update curl jq && vault login -method=token token=${VAULT_TOKEN}
&& PASSWORD=$(vault kv get -field=password secret/test) && echo ${PASSWORD}'
By running the above command, a new container with the vault image is created, and the environment variables VAULT_ADDR and VAULT_TOKEN are set. Also, it connects to localhost’s vault server running on port 8200. Afterward, it authenticates to the vault server, gets the password key value from secret/test, then prints it out. Authorized parties can only access secrets that provide an additional layer of security. But secret management systems are complex to set up and manage. Also, it adds a cost to our application.
5. Using the Docker Secrets
Docker secrets is a feature in Docker Swarm mode that allows us to securely manage sensitive information, such as passwords, in our Docker environment. Using Docker secrets, we can keep configuration files, sensitive information, and command line arguments out of images. This overall reduces the risk of exposure.
5.1. Creating Docker Secrets
First, we need to initialize the Docker swarm to use the Docker secrets:
$ docker swarm init
As a next step, we’ll use echo to pipe the password into docker secret create:
$ echo "testpassword" | docker secret create mysql_external_secret -
The above command creates a unique secret called “mysql_external_secret” with the value “testpassword”. With a docker-compose.yml, we can install a MySQL server in a Docker service with the environment variable MYSQL_ROOT_PASSWORD_FILE passed via Docker secrets:
version: '3.1'
services:
mysql:
image: mysql
secrets: # secrets block only for 'mysql' service
- mysql_external_secret
environment:
- MYSQL_ROOT_PASSWORD_FILE=/run/secrets/mysql_external_secret
secrets: # top level secrets block
mysql_external_secret:
external: true
The secrets block indicates that mysql_external_secret is an external secret already present on our host machine.
5.2. Deploying the Stack
Docker secrets create the secrets on the host machine and pass them to the container at runtime. This mechanism eventually reduces the risk of exposure. To run the service, let’s deploy the stack:
$ docker stack deploy --compose-file=docker-compose.yml mysql_secret_test
The above command deploys the stack defined in the docker-compose.yml file and creates the service mysql_secret_test. Using this method, we can securely manage sensitive information, such as passwords, in our Docker environment.
6. Best Practices
Password security in Docker is a crucial task that requires comprehensive planning and implementation. Securing passwords in Docker requires a multi-layered approach that includes using environment variables, encryption, Docker secrets, and regularly updating passwords.
6.1. Encrypt Sensitive Data
Encryption is a vital aspect of protecting passwords in Docker containers. We should encrypt the data before storing or passing it to the container. This provides an additional layer of security that makes unauthorized access more difficult. AES, RSA, and Blowfish are among the available encryption algorithms. The algorithm choice depends on resource availability and security requirements.
6.2. Limited Access to Sensitive Information
It is important to limit access to sensitive information, such as passwords, to only those who need it. Using RBAC, we can grant access to certain users. We can grant access to only those users who need it and revoke it when they no longer need it. Consequently, unauthorized users can’t access sensitive data, making data breaches less likely.
6.3. Regularly Update Passwords
Regularly updating passwords is a very simple and useful step in maintaining container security. We should update our password at least every three to six months. This ensures that even if a password is compromised, the risk of sensitive information is limited. Additionally, we should use strong passwords that are hard to guess.
7. Conclusion
In this article, we discussed several methods to secure passwords in Docker, including using environment variables, a secret management system, and a key management system. The best method to secure passwords in Docker depends on our specific use cases and requirements.
Environment variables can be used to secure a small number of passwords easily. However, if we need to store many passwords, a secret or key management system may be a better option.