dockerdocker-composedocker-secrets

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


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).


Solution

  • 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:

    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.