Secrets in Docker Compose

how do you manage secret values with docker-compose v3.1?

You can read the corresponding section from the official documentation.

To use secrets you need to add two things into your docker-compose.yml file. First, a top-level secrets: block that defines all of the secrets. Then, another secrets: block under each service that specifies which secrets the service should receive.

As an example, create the two types of secrets that Docker will understand: external secrets and file secrets.

1. Create an 'external' secret using docker secret create

First thing: to use secrets with Docker, the node you are on must be part of a swarm.

$ docker swarm init

Next, create an 'external' secret:

$ echo "This is an external secret" | docker secret create my_external_secret -

(Make sure to include the final dash, -. It's easy to miss.)

2. Write another secret into a file

$ echo "This is a file secret." > my_file_secret.txt

3. Create a docker-compose.yml file that uses both secrets

Now that both types of secrets are created, here is the docker-compose.yml file that will read both of those and write them to the web service:

version: '3.1'

services:
web:
image: nginxdemos/hello
secrets: # secrets block only for 'web' service
- my_external_secret
- my_file_secret

secrets: # top level secrets block
my_external_secret:
external: true
my_file_secret:
file: my_file_secret.txt

Docker can read secrets either from its own database (e.g. secrets made with docker secret create) or from a file. The above shows both examples.

4. Deploy your test stack

Deploy the stack using:

$ docker stack deploy --compose-file=docker-compose.yml secret_test

This will create one instance of the web service, named secret_test_web.

5. Verify that the container created by the service has both secrets

Use docker exec -ti [container] /bin/sh to verify that the secrets exist.

(Note: in the below docker exec command, the m2jgac... portion will be different on your machine. Run docker ps to find your container name.)

$ docker exec -ti secret_test_web.1.m2jgacogzsiaqhgq1z0yrwekd /bin/sh

# Now inside secret_test_web; secrets are contained in /run/secrets/
root@secret_test_web:~$ cd /run/secrets/

root@secret_test_web:/run/secrets$ ls
my_external_secret my_file_secret

root@secret_test_web:/run/secrets$ cat my_external_secret
This is an external secret

root@secret_test_web:/run/secrets$ cat my_file_secret
This is a file secret.

If all is well, the two secrets we created in steps 1 and 2 should be inside the web container that was created when we deployed our stack.

How to use secrets when building docker compose locally

Support for this was recently implemented in v2. See the below pull requests.

  • https://github.com/docker/compose/pull/9386
  • https://github.com/compose-spec/compose-spec/pull/238

The provided example looks like this:

services:
frontend:
build:
context: .
secrets:
- server-certificate
secrets:
server-certificate:
file: ./server.cert

So you are close, but you have to add the secret key under the build key.

Also keep in mind that you have to use docker compose instead of docker-compose, in order to use v2 which is built into the docker client.

Pass docker-compose secret to Dockerfile

I've gotten this working with slight changes to your docker compose:

version: '3.8'

services:
worksnow:
build:
context: .
secrets:
- mysecret

entrypoint: cat /run/secrets/mysecret
secrets:
- mysecret

secrets:
mysecret:
file: ./secret
$ docker compose up
[+] Running 1/1
⠿ Container docker-compose-secrets-worksnow-1 Recreated 0.1s
Attaching to docker-compose-secrets-worksnow-1
docker-compose-secrets-worksnow-1 | cool
docker-compose-secrets-worksnow-1 exited with code 0

It seems like the trouble is that the secret is needed during the build in order for Docker to successfully interpret the RUN statement. Once you actually run the container, of course, it also needs the secret to be available then in order to access it.

RUN is a container build step, so (confusingly) it's not going to be executed when the container is actually run. That's why I needed to add an entrypoint to get the output to show up.

In case you're wondering if including the secrets in the build step is somehow storing the secret in the image, it's not. We can test this using Google's container-diff.

$ container-diff diff --type=file daemon://busybox daemon://docker-compose-worksnow

-----File-----

These entries have been added to busybox:
FILE SIZE
/proc 0
/run 0
/run/secrets 0
/sys 0

These entries have been deleted from busybox: None

These entries have been changed between busybox and docker-compose-notworking: None

How do I configure a secret in docker compose?

Setting a secret only exposes that value at a filesystem location under /run/secrets. If you want to get that value into a variable, you would need to do that yourself as part of your container startup.

For example, an ENTRYPOINT script like that this would make /run/secrets/username available as DB_USERNAME:

#!/bin/sh

if [ -f /run/secrets/username ]; then
export DB_USERNAME=$(cat /run/secrets/username)
fi

exec "$@"

Secrets in docker compose

Apparently this is not supported for "docker compose", only for "docker swarm". The docs are misleading.

Docker Compose doesn't support real (swarmkit) secrets, and imitates them by bind-mounting the file directly into the container (which means that permissions on the host are the same as in the container).

You can change the ownership of the file on the host to match the uid/gid of the user in the container, but otherwise I don't think there's much that can be done unfortunately

UPDATE 2022

If you want this functionality, please upvote this PR, and/or add some comments, so the developers know how badly we want this feature. That PR was supposed to add this feature, but was not completed.

How to use 'mode' with docker secrets mounted with files?

I believe this is just a limitation for how docker compose implements the spec. The fields were originally added for Swarm Mode which loads the secret into the database, and pushes it to nodes in the swarm on a need to know basis. Those nodes load the file into a tmpfs file that gets mounted into the container, and in that tmpfs file, they are adjusting the permissions and ownership.

With compose, I expect that to be implemented with a bind mount since there's no swarm manager and database to store the secrets in. And with a bind mount, there's no option to change the permissions and ownership between the source and target of the bind, the Linux kernel will pass those directly through.



Related Topics



Leave a reply



Submit