Skip to content

Instantly share code, notes, and snippets.

@alexlarsson
Last active June 2, 2020 00:41
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save alexlarsson/c8e3277d2678c1061319 to your computer and use it in GitHub Desktop.
Save alexlarsson/c8e3277d2678c1061319 to your computer and use it in GitHub Desktop.
Private files in docker container

Private files in docker container

It is sometimes necessary to have files in a container that shouldn't ever end up in an image. These files are generally some form of private key or password that aren't allowed to be distributed. This document details a few usecases for such files and their requirements.

Use Cases

Private keys for packages

Not all package content is freely accessible on the network. For instance, using yum to download RHEL packages require that you provide x509 certificates and private keys to prove that you are entitled to download a particular product. In general such entitlements are tied to the computer running the program, and needs to somehow be transferred from the host to the container so that the yum instance in the container can access it.

In practice how this works is that a base image sets up a yum repository configuration file, /etc/yum.repos.d/my-corp.repo:

[my-corp-rpms]
name = Internal only RPMs for mycorp
baseurl = https://cdn.mycorp.com/content/rpms/
enabled = 1
gpgcheck = 1
gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mycom
sslverify = 1
sslcacert = /etc/secret/mycom-ca.pem
sslclientkey = /etc/secret/mycom-private-key.key
sslclientcert = /etc/secret/mycom-cert.crt

In this example we have 3 secret files: the ca root, and the private key/certificate pair. They are typically tied to the machine that will execute the container (at least that is how e.g. RHEL licenses work), and are should ideally to be supplied to all containers running on that machine automatically.

Private SSH keys

A container, like e.g. a continuous integration mechanism, might need to access e.g. a git repository that is not publicly available. To authenticate to this container it would need to supply a ssh private key.

This involves a file like $HOME/.ssh/id_rsa being available in the container. Generally this key is tied either to the user starting the container, or it is tied to the specific container instance. That means the file is read either from the user's home directory, or is manually specified when starting the container instance.

API Keys

Many web services require API keys that identify a particular user. A container that uses such an api should ideally not ship the key with the container, as that makes distributing the image problematic. It is much better to ship the image without the key and then apply the specific key (e.g. you might have a different key for testing) when starting the container.

Requirements

The private files can't be put in a base image, and in order avoid accidentally getting committed into an image they should not be stored in the normal container filesystem. They should instead work in a similar fashion to volumes.

In some cases the files come from the container host, but in other cases they come directly from the client, being uploaded via the REST api. We don't want to store these files unnecessarily long or unnecessarily make them available outside the container. Furthermore, volumes are not available during the build phase, which is required for some use cases.

This means using the existing volume feature as-is is not ideal, as they are not available during the build and require the files to be visible in the host filesystem.

Related issues and features

  • PR#3070 Add support for certificates for registry

    This is very similar to the above yum usecase, except it applies to the docker daemon itself when contacting a registry, rather than yum inside the container talking to a repo.

  • Index/registry authentication from $HOME/.dockercfg.

    This file contains login information that are automatically uploaded by the client to the docker daemon for all operations that may need to talk to the index, such as push/pull, etc. This is similar to e.g. the ssh key example above, except it applies to the daemon, and the authentication is not visible inside the container.

  • PR#5655 Always mount a /run tmpfs in the container

  • Issue#332 flatten images - merge multiple layers into a single one

Proposal

First of all, each container gets a tmpfs mounted on /run that is not visible outside the container, and that dies with the container. This is similar to how /run works on a normal distribution, and forms the basis for allowing container files that are not part of any image.

Then during container initialization any registred host-specific private files are copied into a directory in /run, say /run/docker. Additionally any private data uploaded during docker run is placed in this directory, possibly overriding the host-specific files.

Another possibility to hide files is to put them in a image layer and then remove them at the end, then flattening the image to make sure the intermediate layers are not in the final image. This is not ideal though, as it puts the responsibility for not shipping the private files on the user, which can lead to accidental leaks.

User experience

For the host-specific private files the easiest solution is to have some directory on the host, like /etc/docker/private.d that gets copied into /run/docker. This will allow use case nr 1 to work very easy. We just put the repo config in the base layer, with the key config pointing to e.g. /run/docker/yum/ and have the entitlement setup mechanism create these files. Then anyone can just run docker build . and have the dockerfile be able to do yum install.

For user space files there are two alternatives, both of which would be supported. First of all you can add references to files in the $HOME/.dockercfg file next to the current authentication tokens. These would be automaticall uploaded on every docker run or docker build call. The other alternative is to specify the files as argument to the individual docker call, like docker run --private dir/foo-key-file:foo/api.key which would upload this file to the daemon for that container only.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment