Secrets in Compose: Environment variable has file path as value instead of its content

163 Views Asked by At

I'm trying to use Secrets with Docker Compose

Here is my Docker Compose (only the relevant parts):

version: "3.8"
services:
  My-Service:
    build:
      context: ./..
      dockerfile: ./docker/Dockerfile
    image: my-app/api
    container_name: my-app-api
    environment:
      ASPNETCORE_ENVIRONMENT: Docker
      ASPNETCORE_URLS: http://+:8080
      Swagger__Authentication__ClientSecret_FILE: /run/secrets/my_secret
    secrets:
      - my_secret
    ports:
      - "50002:8080"

secrets:
  my_secret:
    file: ./my_secret.txt

I have a my_secret.txt file containing a secret.

Then I create the image and run it

docker compose -f ./docker/compose.yaml -p my-app up -d --build

Inside the container, checking the /run/secrets/my_secret file is here and has the correct value

sh-5.1$ cat /run/secrets/my_secret
My super secret

The only problem is that my Swagger__Authentication__ClientSecret_FILE environment variable has the value of my file path instead of its content

sh-5.1$ echo $Swagger__Authentication__ClientSecret_FILE
/run/secrets/my_secret

So what am I doing wrong here? I think I'm doing exactly like the Docker documentation says, yet it doesn't work as intended. I honestly can't wrap my head around this.

Thanks for your answer(s).

2

There are 2 best solutions below

0
Billuc On

Let's examine your first example (the one that works). You execute this command : cat /run/secrets/my_secret and you get result My super secret.

The cat command reads a file at the provided path and print its content. Thus, your command reads the file at /run/secrets/my_secret and print its content which is My super secret.

Now, let's examine the second one. You execute this command echo $Swagger_Authentication_ClientSecret and get this result /run/secrets/my_secret.

$Swagger_Authentication_ClientSecret is the value saved in the variable Swagger_Authentication_ClientSecret. You did set this variable in the environment of your container to be /run/secrets/my_secret. You execute echo with this value as parameter. echo simply prints the value given as parameter. Your command then print the value stored in your variable.

What you want is to print the content of the file whose path is the value stored in your variable. To do so, just replace echo with cat in your second command

0
Jérôme MEVEL On

Ok I finally got something working even if it's not as clean as I'd like.

My mistake was assuming that my environment variable Swagger__Authentication__ClientSecret_FILE would "magically" have its value set to the content of my /run/secrets/my_secret file but there's no such kind of automatic magic, it has to be manually set.

Here is my new Docker Compose file

version: "3.8"
services:
  My-Service:
    build:
      context: ./..
      dockerfile: ./docker/Dockerfile
    image: my-app/api
    container_name: my-app-api
    environment:
      ASPNETCORE_ENVIRONMENT: Docker
      ASPNETCORE_URLS: http://+:8080
      Swagger__Authentication__ClientSecret_FILE: /run/secrets/my_secret
    secrets:
      - my_secret
    ports:
      - "50002:8080"
    entrypoint: ./compose-entrypoint.sh
    volumes:
      # $FINAL_WORKDIR is an environment variable set in the .env file with the path of the final WORKDIR set in the Dockerfile before executing the ENTRYPOINT
      - "./compose-entrypoint.sh:$FINAL_WORKDIR/compose-entrypoint.sh:ro"

secrets:
  my_secret:
    file: ./my_secret.txt

The changes are:

  • It now overrides the ENTRYPOINT directive from the Dockerfile and execute a compose-entrypoint.sh file instead
  • It mounts the compose-entrypoint.sh file in a volume (as read-only with :ro) so it can be executed as the entrypoint (make sure the file is executable on your host machine so it will be executable as well in your container)

The logic to set the Swagger__Authentication__ClientSecret environement variable I need happens in this compose-entrypoint.sh file:

#!/bin/sh

export Swagger__Authentication__ClientSecret=$(cat $(echo $Swagger__Authentication__ClientSecret_FILE))
source ./entrypoint.sh

Note: I kept this file as simple as possible to better demonstrate the basis principle. However a better way is simply to copy how MySQL is doing (thanks to @xerx593 who shared the link in a comment)

This script is:

  1. Getting the value of the Swagger__Authentication__ClientSecret_FILE environment variable which is actually a file path
  2. Read the content of this file
  3. Set a Swagger__Authentication__ClientSecret environment variable with the content read from the file
  4. Call the entrypoint.sh file that is the original file called in the Dockerfile

At the end of my Dockerfile I just had to copy the entrypoint-sh bash file and make it executable

COPY ./docker/entrypoint.sh ./entrypoint.sh
RUN chmod +x ./entrypoint.sh

ENTRYPOINT ["./entrypoint.sh"]

Note that by default it is executing the entrypoint.sh file which is NOT setting any additional environment variable. The compose-entrypoint.sh file is executed only when using Docker Compose.

Hope this could clarify some obscure parts.