-
Notifications
You must be signed in to change notification settings - Fork 63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adds support for loading environment vars from file. #938
Conversation
The docker entrypoint script appears to look for secrets (along with other things) in the environment. Providing secrets via environment variables has a couple of negative security implications: 1. Anyone who can see your docker compose/swarm file can see your secrets. 2. Anyone who can inspect your docker environment can see your secrets (e.g., `docker ps`) Docker swarm supports the concept of Docker Secrets, which are defined and managed by docker swarm with their own set of access controls independent from access to to management and compose files. When one uses docker secrets, they get mounted in a `tmpfs` drive inside the container at `/run/secrets/*` (where `*` is the name of the secret defined in the compose/swarm file). These secrets can be simple one-line files (e.g., a password) or complex files (e.g., a TLS key). By having the entrypoint script load environment variables from a provided file path like this, we can securely move the secrets into the container and load them without ever exposing them outside of the container.
I did my best to follow conventions found in this file, but I could have gotten something wrong so just let me know if I need to make changes. The variable name for |
@MicahZoltu defining a path to a file that is dynamically loaded exposes a different vector of security vulnerabilities. In docker-compose you can define you env file, f.e.: https://github.com/standardnotes/server/blob/main/docker-compose.example.yml#L4 In Docker Swarm to my understanding you can define the secrets that will be passed to the service. The The Setting env vars from a file is not in the scope of standardnotes server. |
What security vulnerabilities are you thinking of here? The Docker documentation explicitly describes these secrets files as the secure way to pass runtime secrets to a container, and modern images support this in various ways (for example, MySQL supports
Docker secrets get mounted as a file inside the container. There is no way that I know of (and I looked into this before opening this issue) to pass secrets as environment variables to the root process inside the container directly. It is up to the container to turn the file in You can get more information about how Docker Swarm Secrets work at https://docs.docker.com/engine/swarm/secrets/#how-docker-manages-secrets, or if you just want some examples (including the MySQL example I referenced above) you can check out https://docs.docker.com/compose/use-secrets/
I have been trying to find an alternative workaround that is secure, but unfortunately the best I have come up with is to build my own derivative image based on your image that has this code prepended to your base image. I would much rather see this integrated upstream, as that makes it so people don't have to trust some rando on the internet to provide a docker image that allows for secure secret storage. |
Hmm correct if I'm wrong but dynamically loading the variables would not make them secret. After this part is executed: export $(cat "$ENVIRONMENT_FILE" | xargs) if I have view access to the host as you mentioned I would still be able to get the env vars of that container. I understand your problem but I think this calls for a much more complex solution, meaning something like this (this is just a pseudo solution to give the idea):
And in |
I believe that is incorrect. If you have view access to the host but do not have the ability to exec into the container, then you would not be able to see those environment variables. The key is that this is executed inside the running container, not on the host. This is what I had in mind for what the services:
myapp:
image: standardnotes/server
environment:
ENVIRONMENT_FILE: '/run/secrets/my_secret'
secrets:
- my_secret
secrets:
my_secret:
file: ./my_secret.txt The container would start up and inside of it is
I would rather only provide overrides in the secrets file, and bring everything else in via environment variables the "normal" way. However, if you have a solution in mind that is would require all configuration to come in through a single file then that would address my immediate problem. It is a tad inconvenient because it means anyone who needs to make any configuration changes must have access to the secrets as well, but in my case that person is the me in both cases since this is for personal use. 😄 That being said, it is unclear to me how you would achieve that other than the way I have proposed here (which allows for only providing what you want via secrets file). I'm certainly open to other ideas though! |
Ok I understand now what you're trying to achieve here, but the export of the entire file is a bit too generous also in terms of security vulnerability. From what I can see in MySQL they are exporting one secret per file which makes more sense from the predictability point of view: docker-library/mysql@4dd3313#diff-7b63e1c368f06fe2cc9d89949a98742384d920adb7ddcf24f7348d95972d7205R21-R42 This is also mentioned in https://docs.docker.com/compose/use-secrets/ on the
I would suggest you amend your code to that kind of approach instead. |
Sure, I can switch this over to the one-per-file strategy. Should there be a generalized mechanism for providing any environment variable as either a |
Well a generic approach would be preferred - you can take a look at the mysql one as an example |
Copied the technique used by MySQL as seen in docker-library/mysql@4dd3313#diff-7b63e1c368f06fe2cc9d89949a98742384d920adb7ddcf24f7348d95972d7205R21-R42 This makes all usernames/passwords and similar overrideable with a `_FILE` suffix, while leaving everything else as-is for setting via normal environment variables.
@karolsojko I switched to using the technique that MySQL uses, and applied it only to usernames/passwords and key IDs/secrets. Let me know if this aligns with what you had in mind. |
Thank you for the contribution @MicahZoltu 🎉 |
@MicahZoltu would you be able to provide a markdown blurb of how to use this new addition, so we could add it to our docs? |
Here is a quick writeup, could probably use some love by someone with better writing skills than I. (optional) Usernames, passwords, API keys, and API IDs can be provided as files instead of environment variables, which allows leveraging [Docker Secrets](https://docs.docker.com/engine/swarm/secrets/). To do this, set an environment variable with a name matching the normal environment variable, but with `_FILE` suffix, to a path to the file that contains the secret (as UTF-8 text with no newline).
To utilize this with Docker Swarm Secrets, create a secret with `docker secret create standardnotes_db_password Passw0rd1!` and then reference `standardnotes_db_password` in your Docker Compose file like in the example below.
```yml
services:
server:
image: standardnotes/server
container_name: server_self_hosted
restart: unless-stopped
ports: ...
volumes: ...
networks: ...
env_file: .env
environment:
- DB_PASSWORD_FILE: '/run/secrets/db_password'
secrets:
- db_password
...
...
secrets:
standardnotes_db_password:
name: db_password
external: true
``` |
The docker entrypoint script appears to look for secrets (along with other things) in the environment. Providing secrets via environment variables has a couple of negative security implications:
docker ps
)Docker swarm supports the concept of Docker Secrets, which are defined and managed by docker swarm with their own set of access controls independent from access to to management and compose files. When one uses docker secrets, they get mounted in a
tmpfs
drive inside the container at/run/secrets/*
(where*
is the name of the secret defined in the compose/swarm file). These secrets can be simple one-line files (e.g., a password) or complex files (e.g., a TLS key).By having the entrypoint script load environment variables from a provided file path like this, we can securely move the secrets into the container and load them without ever exposing them outside of the container.