How to restrict environment variables passed to linked containers - docker

We've started to use docker extensively (and we love it), but have discovered a rather nasty security issue. Linked containers have full access to the source container's environment settings.
For example, say you create a mysql container.
docker run --name db -e MYSQL_ROOT_PASSWORD=mysecretpassword -d mysql
And now you create a wordpress container
docker run --name wp --link db:db \
-e WORDPRESS_DB_USER=wp \
-e WORDPRESS_DB_PASSWORD=1234 \
-d wordpress
If you now inspect the environment in the wordpress container, you'll be able to see the mysql root password.
docker exec -i wp sh -c "env|grep ^MYSQL_MYSQL_ENV"
MYSQL_ENV_MYSQL_MAJOR=5.7
MYSQL_ENV_MYSQL_ROOT_PASSWORD=mysecretpassword
MYSQL_ENV_MYSQL_VERSION=5.7.5-m15
This is a major security hole ! Any random code or module within the wordpress container could use the mysql root password to connect and reek havoc. And if the mysql database is shared with multiple wordpress containers (and joomla containers), the havoc could be global.
My question is, is there a way to limit what environment variables are passed between linked containers?
A secondary question -- I've scrutinized the docs on linking containers https://docs.docker.com/userguide/dockerlinks/#environment-variables
But it does NOT describe this behavior. I was thinking maybe this was an unintended side effect, and perhaps I should open a bug report?

My question is, is there a way to limit what environment variables are passed between linked containers?
If this is a concern in your environment, your best bet may be to adopt a solution other than container linking for service discovery. For example, you could use one of the various etcd-backed discovery mechanisms that are out there -- either using etcd directly, or something like consul, registrator or skydns.

Related

Access files on host server from the Meteor App deployed with Meteor Up

I have a Meteor App deployed with Meteor UP to Ubuntu.
From this App I need to read a file which is located outside App container on the host server.
How can I do that?
I've tried to set up volumes in the mup.js but no luck. It seems that I'm missing how to correctly provide /host/path and /container/path
volumes: {
// passed as '-v /host/path:/container/path' to the docker run command
'/host/path': '/container/path',
'/second/host/path': '/second/container/path'
},
Read the docs for Docker mounting volumes but obviously can't understand it.
Let's say file is in /home/dirname/filename.csv.
How to correctly mount it into App to be able to access it from the Application?
Or maybe there are other possibilities to access it?
Welcome to Stack Overflow. Let me suggest another way of thinking about this...
In a scalable cluster, docker instances can be spun up and down as the load on the app changes. These may or may not be on the same host computer, so building a dependency on the file system of the host isn't a great idea.
You might be better to think of using a file storage mechanism such as S3, which will scale on its own, and disk storage limits won't apply.
Another option is to determine if the files could be stored in the database.
I hope that helps
Let's try to narrow the problem down.
Meteor UP is passing the configuration parameter volumes directly on to docker, as they also mention in the comment you included. It therefore might be easier to test it against docker directly - narrowing the components involved down as much as possible:
sudo docker run \
-it \
--rm \
-v "/host/path:/container/path" \
-v "/second/host/path:/second/container/path" \
busybox \
/bin/sh
Let me explain this:
sudo because Meteor UP uses sudo to start the container. See: https://github.com/zodern/meteor-up/blob/3c7120a75c12ea12fdd5688e33574c12e158fd07/src/plugins/meteor/assets/templates/start.sh#L63
docker run we want to start a container.
-it to access the container (think of it like SSH'ing into the container).
--rm to automatically clean up - remove the container - after we're done.
-v - here we give the volumes as you define it (I here took the two directories example you provided).
busybox - an image with some useful tools.
/bin/sh - the application to start the container with
I'd expect that you also cannot access the files here. In this case, dig deeper on why you can't make a folder accessible in Docker.
If you can, which would sound weird to me, you can start the container and try to access into the container by running the following command:
docker exec -it my-mup-container /bin/sh
You can think of this command like SSH'ing into a running container. Now you can check around if it really isn't there, if the credentials inside the container are correct, etc.
At last, I have to agree it #mikkel, that it's not a good option to mount a local directoy, but you can now start looking into how to use docker volume to mount a remote directory. He mentioned S3 by AWS, I've worked with AzureFiles on Azure, there are plenty of possibilities.

How can I reuse a Docker container as a service?

I already have a running container for both postgres and redis in use for various things. However, I started those from the command line months ago. Now I'm trying to install a new application and the recipe for this involves writing out a docker compose file which includes both postgres and redis as services.
Can the compose file be modified in such a way as to specify the already-running containers? Postgres already does a fine job of siloing any of the data, and I can't imagine that it would be a problem to reuse the running redis.
Should I even reuse them? It occurs to me that I could run multiple containers for both, and I'm not sure there would be any disadvantage to that (other than a cluttered docker ps output).
When I set container_name to the names of the existing containers, I get what I assume is a rather typical error of:
cb7cb3e78dc50b527f71b71b7842e1a1c". You have to remove (or rename) that container to be able to reuse that name.
Followed by a few that compain that the ports are already in use (5432, 6579, etc).
Other answers here on Stackoverflow suggest that if I had originally invoked these services from another compose file with the exact same details, I could do so here as well and it would reuse them. But the command I used to start them was somehow never written to my bash_history, so I'm not even sure of the details (other than name, ports, and restart always).
Are you looking for docker-compose's external_links keyword?
external_links allows you reuse already running containers.
According to docker-compose specification:
This keyword links to containers started outside this docker-compose.yml or even outside of Compose, especially for containers that provide shared or common services. external_links follow semantics similar to the legacy option links when specifying both the container name and the link alias (CONTAINER:ALIAS).
And here's the syntax:
external_links:
- redis_1
- project_db_1:mysql
- project_db_1:postgresql
You can give name for your container. If there is no container with the given name, then it is the first time to run the image. If the named container is found, restart the container.
In this way, you can reuse the container. Here is my sample script.
containerName="IamContainer"
if docker ps -a --format '{{.Names}}' | grep -Eq "^${containerName}\$"; then
docker restart ${containerName}
else
docker run --name ${containerName} -d hello-world
fi
You probably don't want to keep using a container that you don't know how to create. However, the good news is that you should be able to figure out how you can create your container again by inspecting it with the command
$ docker container inspect ID
This will display all settings, the docker-compose specific ones will be under Config.Labels. For container reuse across projects, you'd be interested in the values of com.docker.compose.project and com.docker.compose.service, so that you can pass them to docker-compose --project-name and use them as the service's name in your docker-compose.yaml.

Can one determine the hostname of the Docker host from a Container?

This is kind of a duplicate question to this one. . But ironically, in spite of that question's title, all the answers say: Use an ENV variable.
My use case: I don't know if docker is running via docker-compose or swarm. However, it will not be docker run! I am trying to kick off an upgrade script that resides on the host. Thus, from within a container I need the docker host name. Is there any programmatic way to get this WITHOUT environment variables?
Here is the feature request that has been implemented in 17.10 https://github.com/moby/moby/issues/30966
This is how you do it:
$ hostname testing123
$ docker service create \
--name foo \
--hostname "{{.Service.Name}}-{{.Task.Slot}}-{{.Node.Hostname}}" \
nginx:alpine
$ docker inspect foo.1.k51r3eclvcelsxy6jthtavkwa --format
'{{.Config.Hostname}}'
foo-1-testing123
This should also work in docker-compose
services:
nginx:
image: nginx
hostname: '{{.Node.Hostname}}'
No. The goal of docker is to isolate the host environment as much as possible. The only way you could get the hostname is if you were to pass it into the container as a variable (or bind-mount a file containing the hostname, etc).
I don't know if docker is running via docker-compose or swarm. However, it will not be docker run! I am trying to kick off an upgrade script that resides on the host.
Why do you care how the container was started? How do you plan to run a script on the host? The answers to these questions might help us provide a better answer to your original question.
If you're using Docker for Mac, you can use the following hostname:
docker.for.mac.localhost

How can you write a docker daemon that can kick off other docker containers as needed?

I've seen several options for how a docker container can communicate directly with its host system but they all seem kind of sneaky. For instance, it appears one can start a container and bind (using -v) the in-container docker executable to the host's docker executable. One can send messages to the host using a networking protocol. It also appears that the --privilege flag might help as well.
Each of these methods appears to have drawbacks and security concerns. My bigger question is if this architecture is even the best approach.
Our goal is to have a docker daemon process running, polling a database being used as a queue. (I know this is frowned upon in some ways but our traffic is very low and internal. Performance for this sort of queue is not an issue.) When the docker daemon detects that there is work to be done, it kicks off another docker container to handle that work. That container dies when it is finished. Each container belongs to a "system" and will run load on that system. Each system can only have one container running load on it.
Is this a paradigm that makes sense?
Would the daemon be better off as just a host-level process? A Python script, for instance, instead of a docker container?
Is Docker meant to be used this way? Am I just missing where, in the Docker documentation, it tells me how to do this?
Are my "sneaky" ideas above not so sneaky, after all?
I understand there is an opportunity for opinion here. I am looking for some concise best practices.
Thanks, in advance!
The preferred solution that I've seen the most is to install the docker binaries in a container, and then mount the /var/run/docker.sock into the container. The Dockerfile I have for something similar looks like:
FROM upsteam:latest
ARG DOCKER_GID=999
USER root
# install docker
RUN curl -sSL https://get.docker.com/ | sh
# app setup goes here
# configure user with access to docker
RUN groupmod -g ${DOCKER_GID} docker && \
usermod -aG docker appuser
USER appuser
And then it's run with:
docker run -d --name myapp -v /var/run/docker.sock:/var/run/docker.sock myapp
This would be the most efficient solution since you remove the network bandwidth. And it removes any network vulnerabilities, either from an open port, or from including the TLS cert inside your container which could accidentally leak with something like a lost backup.

How to dynamically set environment variables of linked containers?

I have two containers webinterface and db, while webinterface is started using the --link option (for db) which generates the environment variables
DB_PORT_1111_TCP=tcp://172.17.0.5:5432
DB_PORT_1111_TCP_PROTO=tcp
DB_PORT_1111_TCP_PORT=1111
DB_PORT_1111_TCP_ADDR=172.17.0.5
...
Now my webinterface container uses a Dockerfile where some static environment variables are defined to define the connection:
ENV DB_HOST localhost
ENV DB_PORT 2222
Knowing that there is also an -e option for docker run, the problem is that I want to use those variables in the Dockerfile (used in some scripts) but overwrite them with the values generated with the --link option, i.e. something like:
docker run -d -e DB_HOST=$DB_PORT_1111_TCP_ADDR
This would use the host's defined environment variable which doesn't work here.
Is there a way to handle this?
This is a variable expansion issue so to resolve try the following:
docker run -d -e DB_HOST="$DB_PORT"_1111_TCP_ADDR
With a Unix process that is already running, its environment variables can only be changed from inside the process, not from the outside, so their are somewhat non-dynamic by nature.
If you find Docker links limiting, you are not the only person out there. One simple solution to this would be using WeaveDNS. With WeaveDNS you can simply use default ports (as with Weave overlay network there is no need to expose/publish/remap any internal ports) and resolve each component by via DNS (i.e. your app would just need to look for db.weave.local, and doesn't need to be aware of clunky environment variable scheme that Docker links present). To get a better idea of how WeaveDNS works, checkout one of the official getting started guides. WeaveDNS effectively gives you service discovery without having to modify the application you have.

Resources