How to add Credentials to Docker ADD command?

Introduction

Security has always been a great threat to mankind. In IT the security of credentials is a tedious task. Here, we are going to discuss various methods to add the credentials to the docker container. Also, the most useful and secure methods are mentioned.

Methods

Adding credentials can be done in a lot of different ways. Some of the types are mentioned below. Each method has its place in the industry. Some are just rejected by the developers due to security issues and some works very well in the case of credential security.

  • Using the Build arguments in Dockerfile.

  • Using Environment variables in Dockerfile.

  • Using Docker Secrets.

Using build arguments

Passing credentials using build arguments is not preferred. This could be a security issue. One of the use cases of the build argument is mentioned below with an example.

Step 1 : Create a Dockerfile and a python program file.

This Dockerfile is going to create a python runtime on the ubuntu base image.

Example

<div class="code-mirror  language-docker" contenteditable="plaintext-only" spellcheck="false" style="outline: none; overflow-wrap: break-word; overflow-y: auto; white-space: pre-wrap;"><span class="token comment">#declare the build arguments that can be used before FROM in the # Dockerfile.</span>
<span class="token instruction"><span class="token keyword">ARG</span> UBUNTU_VERSION=latest</span>

<span class="token comment">#use ubuntu as the base image.</span>
<span class="token instruction"><span class="token keyword">FROM</span> ubuntu:<span class="token variable">$UBUNTU_VERSION</span></span>

<span class="token comment">#declare the build arguments that will be used after FROM command in #Dockerfile.</span>
<span class="token instruction"><span class="token keyword">ARG</span> PYTHON_VERSION=2</span>

<span class="token comment">#install the python on the ubuntu image</span>
<span class="token instruction"><span class="token keyword">RUN</span> apt-get update -y && apt-get upgrade -y && apt-get install python<span class="token variable">$PYTHON_VERSION</span> -y</span>

<span class="token comment">#set the working directory.</span>
<span class="token instruction"><span class="token keyword">WORKDIR</span> /python/</span>

<span class="token comment">#copy the python program to the Docker image.</span>
<span class="token instruction"><span class="token keyword">COPY</span> program.py .</span>

<span class="token comment">#run this python program whenever this image is used to create a container.</span>
<span class="token instruction"><span class="token keyword">CMD</span> python3 program.py</span>
</div>

Now create a python file named "program.py".

Output

print("********************Hello from TUTORIALSPOINT*********************")

Step 2 : Build the image

Example

<div class="code-mirror  language-docker" contenteditable="plaintext-only" spellcheck="false" style="outline: none; overflow-wrap: break-word; overflow-y: auto; white-space: pre-wrap;">$ docker build --build-arg PYTHON_VERSION=3 -t busy_python .
</div>

Output

Sending build context to Docker daemon 3.584kB
Step 1/7 : ARG UBUNTU_VERSION=latest
Step 2/7 : FROM ubuntu:${UBUNTU_VERSION}
---> 6b7dfa7e8fdb
Step 3/7 : ARG PYTHON_VERSION=2
---> Running in be6541523070
Removing intermediate container be6541523070
---> e3bef06439e8
Step 4/7 : RUN apt-get update -y && apt-get upgrade -y && apt-get install
python$PYTHON_VERSION -y
---> Running in e3ff50442993
Step 5/7 : WORKDIR /python/
---> Running in a147f39ec056
Removing intermediate container a147f39ec056
---> 166cfe1d9514
Step 6/7 : COPY program.py .
---> b09acbeb8f38
Step 7/7 : CMD python3 program.py
---> Running in eec7ec3982de
Removing intermediate container eec7ec3982de
---> 47dbde8eca00
Successfully built 47dbde8eca00
Successfully tagged busy_python:latest

Step 3 : Run the image

Now we will run this image and see if python says hello.

Example

<div class="code-mirror  language-docker" contenteditable="plaintext-only" spellcheck="false" style="outline: none; overflow-wrap: break-word; overflow-y: auto; white-space: pre-wrap;">$ docker run busy_python
</div>

Output

********************Hello from TUTORIALSPOINT*********************

Hence the build arguments worked.

The disadvantages of Build Arguments

This has security-related issues. The argument?s values can be seen by anyone who has access to the docker history.

<div class="code-mirror  language-docker" contenteditable="plaintext-only" spellcheck="false" style="outline: none; overflow-wrap: break-word; overflow-y: auto; white-space: pre-wrap;">$ docker history busy_python:latest

IMAGE CREATED CREATED BY SIZE COMMENT

47dbde8eca00 12 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "pyth? 0B

b09acbeb8f38 12 minutes ago /bin/sh -c #(nop) COPY
file:a268373fa65eae71? 75B

166cfe1d9514 12 minutes ago /bin/sh -c #(nop) WORKDIR /python/0B

1bdd202b9d86 12 minutes ago |1 PYTHON_VERSION=3 /bin/sh -c apt-getupdat? 70.1MB

e3bef06439e8 13 minutes ago /bin/sh -c #(nop) ARG PYTHON_VERSION=20B

6b7dfa7e8fdb 11 days ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 11 days ago /bin/sh -c #(nop) ADD file:481dd2da6de715252? 77.8MB 
</div>

Hence this is not a secure method for credentials.

Using Environment Variables

The build arguments are only available till the build of the image. The environment variables are also available to the image and the container after the build. That is the main difference between build arguments and environment variables.

Here we will use environment variables to pass the credentials to the MySQL database.

Step 1 : Create a Dockerfile

<div class="code-mirror  language-docker" contenteditable="plaintext-only" spellcheck="false" style="outline: none; overflow-wrap: break-word; overflow-y: auto; white-space: pre-wrap;"><span class="token comment">#use the mysql as the base image</span>
<span class="token instruction"><span class="token keyword">FROM</span> mysql</span>

<span class="token comment">#set the required root password</span>
<span class="token instruction"><span class="token keyword">ENV</span> MYSQL_ROOT_PASSWORD mypassword@root</span>
</div>

Step 2 : Build the image

Example

<div class="code-mirror  language-docker" contenteditable="plaintext-only" spellcheck="false" style="outline: none; overflow-wrap: break-word; overflow-y: auto; white-space: pre-wrap;">$docker build -t mysql_test .
</div>

Output

Sending build context to Docker daemon 2.048kB
Step 1/2 : FROM mysql
---> 7484689f290f
Step 2/2 : ENV MYSQL_ROOT_PASSWORD mypassword
---> Running in 80d7ad7561d4
Removing intermediate container 80d7ad7561d4
---> a5168465919b
Successfully built a5168465919b
Successfully tagged mysql_test:latest

Step 3 : Run the image without passing the credentials as that has been already set in the Dockerfile.

<div class="code-mirror  language-docker" contenteditable="plaintext-only" spellcheck="false" style="outline: none; overflow-wrap: break-word; overflow-y: auto; white-space: pre-wrap;">$docker run -d --name cont mysql_test
943d02de21c555618ae9eb4b416faccf00d989020c565a1336afb4743cb6b7b1
</div>

Step 4 : Get inside the MySQL container and use the credentials to start the database.

Example

<div class="code-mirror  language-docker" contenteditable="plaintext-only" spellcheck="false" style="outline: none; overflow-wrap: break-word; overflow-y: auto; white-space: pre-wrap;">$ docker exec -it cont /bin/bash
</div>

Now start accessing the database.

<div class="code-mirror  language-docker" contenteditable="plaintext-only" spellcheck="false" style="outline: none; overflow-wrap: break-word; overflow-y: auto; white-space: pre-wrap;"><span class="token comment"># mysql -h 172.17.0.2 -P 3306 -u root -pmypassword</span>
</div>

Output

mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.31 MySQL Community Server - GPL
Copyright (c) 2000, 2022, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>

Disadvantages

Here MYSQL also gave us a warning about using the password on the command line. This is unsafe to use the password directly on the command line. Also, we should not use the password in the Dockerfile which could be seen using the inspect command in the docker.

Example

<div class="code-mirror  language-docker" contenteditable="plaintext-only" spellcheck="false" style="outline: none; overflow-wrap: break-word; overflow-y: auto; white-space: pre-wrap;">$ docker inspect -f "{{ .Config.Env }}" cont
</div>

Output

[PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin GOSU_VERSION=1.14 MYSQL_MAJOR=8.0 MYSQL_VERSION=8.0.31-1.el8 MYSQL_SHELL_VERSION=8.0.31-1.el8 MYSQL_ROOT_PASSWORD=mypassword]

Hence the password is visible.

Using Docker Secrets

If an image is created on your local system and shared with someone else or even published on the Docker Hub. The main aim of Docker Secrets is that the credentials used while creating the image should not be accessible to the user of the image.

In this example, we will create a service container and provide a secret file. Then, commit the container to form an image for later use. Now, run a container using this image and check if the secret message is present or not.

Step 1 : Create a Docker Secret

First, we need to initialize the Docker Swarm manager.

<div class="code-mirror  language-docker" contenteditable="plaintext-only" spellcheck="false" style="outline: none; overflow-wrap: break-word; overflow-y: auto; white-space: pre-wrap;">$docker swarm init
</div>

If your Docker Swarm does not start due to IP resolution use --advertiseaddr=ip_of_the_machine and --listen-addr=0.0.0.0

<div class="code-mirror  language-docker" contenteditable="plaintext-only" spellcheck="false" style="outline: none; overflow-wrap: break-word; overflow-y: auto; white-space: pre-wrap;">$ docker swarm init --advertise-addr <ip_of_machine> --listen-addr 0.0.0.0
</div>

Output

Swarm initialized: current node (eksm5jqv8sn8jlr8fwq31n6ht) is now a manager.
To add a worker to this swarm, run the following command:
   docker swarm join --token SWMTKN-1- 1okpgh4spk3nab0mjjzk3c2nx3a68p3l1ww06bx8fu20nvpr0j90vxfk3dsyqvw3s1edzr5k4ou 192.168.43.97:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

Now, create Docker Secret using the below command.

Example

<div class="code-mirror  language-docker" contenteditable="plaintext-only" spellcheck="false" style="outline: none; overflow-wrap: break-word; overflow-y: auto; white-space: pre-wrap;">$echo 'secret credentials for TUTORIALSPOINT database' | docker secret create ttrlpnt_secret_file -
</div>

Output

qmry8v6wsihjuizgtg292ozau

To get complete details about the Docker secret file, use the below command.

Example

<div class="code-mirror  language-docker" contenteditable="plaintext-only" spellcheck="false" style="outline: none; overflow-wrap: break-word; overflow-y: auto; white-space: pre-wrap;">$ docker secret ls
</div>

Output

ID NAME DRIVER CREATED UPDATED qmry8v6wsihjuizgtg292ozau ttrlpnt_secret_file 2 hours ago 2 hours ago

This means our Docker Secret is ready to be used.

Step 2 : Create a service

We will create a mysql service that will take the above-created Docker Secret as the root password.

Example

<div class="code-mirror  language-docker" contenteditable="plaintext-only" spellcheck="false" style="outline: none; overflow-wrap: break-word; overflow-y: auto; white-space: pre-wrap;">$ docker service create --name=ttrlpnt_mysql --secret source=ttrlpnt_secret_file,target=ttrlpnt_secret_file -e MYSQL_ROOT_PASSWORD_FILE="/run/secrets/ttrlpnt_secret_file" mysql:latest
</div>

Output

image mysql:latest could not be accessed on a registry to record its digest. Each node will access mysql:latest independently, possibly leading to different nodes running different versions of the image.
n7651xa5porbsf4l948vwa33c
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged

Check if the service is running.

Example

<div class="code-mirror  language-docker" contenteditable="plaintext-only" spellcheck="false" style="outline: none; overflow-wrap: break-word; overflow-y: auto; white-space: pre-wrap;">$ docker ps
</div>

Output

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
27fcefc610c8 mysql:latest "docker-entrypoint.s?" 35 seconds ago Up 31 seconds 3306/tcp, 33060/tcp ttrlpnt_mysql.1.bzkxffaovta8mj5q33ap7z1tl

Step 3: Exec inside the container

Now, we will get inside the container and get the content of the secret file stored in it i.e. the root password.

Example

<div class="code-mirror  language-docker" contenteditable="plaintext-only" spellcheck="false" style="outline: none; overflow-wrap: break-word; overflow-y: auto; white-space: pre-wrap;">$ docker exec ttrlpnt_mysql.1.bzkxffaovta8mj5q33ap7z1tl bash -c 'cat /run/secrets/ttrlpnt_secret_file'
</div>

Output

secret credentials for TUTORIALSPOINT database

Hence the secret file is present.

Step 4 : Check if the committed image file has the secret data

Create an image from this container.

Example

<div class="code-mirror  language-docker" contenteditable="plaintext-only" spellcheck="false" style="outline: none; overflow-wrap: break-word; overflow-y: auto; white-space: pre-wrap;">$ docker commit ttrlpnt_mysql.1.bzkxffaovta8mj5q33ap7z1tl secret_mysql:v1
</div>

Output

sha256:d5fcdc6c3b093485146dfd8e89b2f8be133090bc4ecf3995f4ce409dec30c523

Now inspect the image and check if we get the password for the root.

Example

<div class="code-mirror  language-docker" contenteditable="plaintext-only" spellcheck="false" style="outline: none; overflow-wrap: break-word; overflow-y: auto; white-space: pre-wrap;">$ docker inspect -f "{{ .Config.Env }}" secret_mysql:v1
</div>

Output

[MYSQL_ROOT_PASSWORD_FILE=/run/secrets/ttrlpnt_secret_file
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
GOSU_VERSION=1.14 MYSQL_MAJOR=8.0 MYSQL_VERSION=8.0.31-1.el8
MYSQL_SHELL_VERSION=8.0.31-1.el8]

Hence only the path is mentioned and the credentials are secure.

Conclusion

In this article. We learned various ways to pass the credentials to the Docker containers. The security issues related to them are also discussed and solutions are also provided. Using Docker Secrets is one of the most useful and secure ways to give your logins.

Updated on: 2023-01-05T15:40:00+05:30

5K+ Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements