Docker Compose: Mount secret file content as environment variable content

Hi,

currently it is possible to mount a secret file. But only as a file. If the content of the file has to become the content of a environment variable, then you have to do extra work. And this is only possible, if the docker image supports a shell. But some minimalist docker images don’t have even a shell. Then you can’t use docker secret files at all.

So it would be great, to add a feature, that the content of a docker secret is mounted as the content of a environment variable. K8s has a similar feature. And it would be very helpful also in context of docker compose.

You mean, the env_file property?

services:
  someservice:
    ...
    env_file: container.env

container.env

VAR1=foo
VAR2=bar

The values defined will become the container’s environment

Note that environnement variables are not secret at all. Having access to a shell it’s then easy to do a printenv (Linux cli cimmand) and hop everything is visible.

Plain text mounted files are also no “secrets” at all. I agree, that the _FILE environment postfix name approach is better, then having the secret values as environment variable directly, but also not all services support the _FILE environment postfix name approach. And not all docker images have a shell, to create an own work around

You mean, the env_file property?

services:
  someservice:
    ...
    env_file: container.env

container.env

VAR1=foo
VAR2=bar

The values defined will become the container’s environment

That would be a nice extra feature.

I meant

secret.json

{ `a`: `complex secret` }
services:
  someservice:
    ...
    MY_ENV_VARIABLE: secret.json

becomes

MY_ENV_VARIABLE="{ `a`: `complex secret` }"

Something similar to k8s

       - name: MYSQL_ROOT_PASSWORD
         valueFrom:
           secretKeyRef:
             name: mysqlpwd
             key: password

As secrets are there YAML file, it is more complex, as you have to define the key in the secret yaml. That would not be the case in docker compose.

The env_file property I described already exists

As for secrets, look into Docker Secrets

Sorry, I don’t see your example in that documentation.

Also that documentation is about docker swarm. I’m talking about docker compose.

The env_file property

1 Like

Secrets available for Swarm. The one that compsoe supports is just mounting files to where real secrets would be mounted. You can use an external tool to manage ecrets and run the containers in the environment where the secrets are decrypted.

I wrote about sops here:

When you encrypt a secret using sops and the file called secret.yml containing

password: ENC[AES256_GCM,data:L6KCZA==,iv:94uAN6y0iW5yG+2xdSnWcfUZYUBa5bcRKeQ54Ymq30E=,tag:LGjJ192V3La7ANykNmPJ1A==,type:str]

where the decrypted password is “test”, youc an run

sops exec-env secret.yml 'docker run --rm -it -e password=$password bash -c "echo $password"'

You could also try storing the decrypted password on tmpfs if you run a sidecar container that uses a shared memory with your main container and the sidecar can decrypt the secret file into the shared folder. I can write about that later.

I just use the official term of Secrets in Compose | Docker Docs
I’m fully aware, that these secrets are not encrypted. Just handled in a different way. Similar to k8s.

I should have read your previous posts more carefully :slight_smile: I just wrote quickly before lunch. Then you indeed got the anwswer from @deanayalon

1 Like

The disadvantage of the ENV files is, that you have to define the environment variable in the shared env file.
But if different services require different environment variable names, then you have to duplicate the files and their content, just using different environment variable names.
Hence you have no chance to maintain only one value and reuse it in multiple use cases.

You can have a common env file and separate env files for values that are different in different services. You can even have a default values and override some of the values in a second env file. Wouldn’t that help?

Let’s say, you have to share a technical user credential between the db and two different service
the service a requires the environment variable names DB_USER and DB_PASSWORD and the service b requires the environment variable names spring.datasource.username and spring.datasource.password

With the env file approach I’ve to create either one big env file with multiple entries like

DB_USER=abc
spring.datasource.username=ABC
ANOTHER_SERVER_USERNAME=ABC
...
DB_PASSWORD=MY_PWD
spring.datasource.password=MY_PWD
ANOTHER_SERVER_USERNAME=MY_PWD

or you have to create several env files

DB_USER=abc
DB_PASSWORD=MY_PWD
spring.datasource.username=ABC
spring.datasource.password=MY_PWD
ANOTHER_SERVER_USERNAME=ABC
ANOTHER_SERVER_USERNAME=MY_PWD

One way or the other, you have to maintain a lot of entries.

I would expect a k8s alike approach:

Defining two files db-username.txt & db-password.txt.

And then mounting these as different environment variables. Similar to k8s

       - name: MYSQL_ROOT_PASSWORD
         valueFrom:
           secretKeyRef:
             name: mysqlpwd
             key: password

perhaps something like

services:
   myFirstService:
     environment:
       MY_ENV: HARDCODED
     secrets:
       - my_first_secret
     environment_secrets:
       MY_USERNAME: db-username
       MY_PASSWORD: db-password
   mySecondService:
     environment:
       MY_ENV: HARDCODED
     secrets:
       - my_first_secret
     environment_secrets:
       spring.datasource.username: db-username
       spring.datasource.password: db-password
secrets:
   db-password:
     file: db-password.txt
   db-username:
     file: db-username.txt

Then how about creating a single .env file for Docker Compose:

SECRET_USER=ABC
SECRET_PASSWORD=MY_PWD

and using a compose like:

services:
  myFirstService:
    environment:
      MY_ENV: HARDCODED
      MY_USERNAME: $SECRET_USER
      MY_PASSWORD: $SECRET_PASSWORD
 mySecondService:
   environment:
     MY_ENV: HARDCODED
     spring.datasource.username: $SECRET_USER
     spring.datasource.password: $SECRET_PASSWORD

A file called .env is automatically read by Docker Compose and you can use the values in the compose file and pass to different variables in different services.

You suggestion seems to, that the whole secret feature at all is not necessary.
But the advantage of the secret feature is

  1. You can mount files directly
  2. You can separate critical information like credentials from not so critical information like log levels

And now image two different services: one is supporting the _FILE postfile approach and another does not.

services:
   myFirstService:
     secrets:
       - db-username
       - db-password
     environment:
       MY_USERNAME_FILE: /run/secrets/db-username
       MY_PASSWORD_FILE: /run/secrets/db-password
   mySecondService:
     environment_secrets:
       spring.datasource.username: db-username
       spring.datasource.password: db-password
secrets:
   db-password:
     file: db-password.txt
   db-username:
     file: db-username.txt

I totally agree with you, that the _FILE approach is the best, but why I should I make it more insecure for all services, if only one service does not support that nice feature?

So you would like to use a single secret file that allows you to mount some of the values as a file and use some of those as environment variables?

You could ask for it in the roadmap

Until then, you could only write some workarounds using scripts and templates. I’m not sure I would do it either.