Document Docker secrets

Fixes #529

Signed-off-by: Misty Stanley-Jones <misty@docker.com>
This commit is contained in:
Misty Stanley-Jones 2016-11-14 17:18:37 -08:00
parent 5b2842922c
commit f4a28b31ec
3 changed files with 846 additions and 1 deletions

View File

@ -289,6 +289,8 @@ toc:
title: Manage nodes in a swarm
- path: /engine/swarm/services/
title: Deploy services to a swarm
- path: /engine/swarm/secrets/
title: Manage sensitive data with Docker secrets
- path: /engine/swarm/networking/
title: Attach services to an overlay network
- path: /engine/swarm/admin_guide/

837
engine/swarm/secrets.md Normal file
View File

@ -0,0 +1,837 @@
---
title: Manage sensitive data with Docker secrets
description: How to securely store, retrieve, and use sensitive data with Docker services
keywords: swarm, secrets, credentials, sensitive strings, sensitive data, security, encryption, encryption at rest
---
## About secrets
In terms of Docker Swarm services, a _secret_ is a blob of data, such as a
password, SSH private key, SSL certificate, or another piece of data that should
not be transmitted over a network or stored unencrypted in a Dockerfile or in
your application's source code. In Docker 1.13 and higher, you can use Docker
_secrets_ to centrally manage this data and securely transmit it to only those
containers that need access to it. Secrets are encrypted during transit and at
rest in a Docker swarm. A given secret is only accessible to those services
which have been granted explicit access to it, and only while those service
tasks are running.
You can use secrets to manage any sensitive data which a container needs at
runtime but you don't want to store in the image or in source control, such as:
- Usernames and passwords
- TLS certificates and keys
- SSH keys
- Other important data such as the name of a database or internal server
- Generic strings or binary content (up to 500 kb in size)
> **Note**: Docker secrets are only available to swarm services, not to
> standalone containers. To use this feature, consider adapting your container to
> run as a service with a scale of 1.
Another use case for using secrets is to provide a layer of abstraction between
the container and a set of credentials. Consider a scenario where you have
separate development, test, and production environments for your application.
Each of these environments can have different credentials, stored in the
development, test, and production swarms with the same secret name. Your
containers only need to know the name of the secret in order to function in all
three environments.
## How Docker manages secrets
When you add a secret to the swarm, Docker sends the secret to the swarm manager
over a mutual TLS connection. The secret is stored in the Raft log, which is
encrypted. The entire Raft log is replicated across the other managers, ensuring
the same high availability guarantees for secrets as for the rest of the swarm
management data.
>**Warning**: Raft data is encrypted in Docker 1.13 and higher. If any of your
Swarm managers run an earlier version, and one of those managers becomes the
manager of the swarm, the secrets will be stored unencrypted in that node's Raft
logs. Before adding any secrets, update all of your manager nodes to Docker 1.13
to prevent secrets from being written to plain-text Raft logs.
When you grant a newly-created or running service access to a secret, the
decrypted secret is mounted into the container in an in-memory filesystem at
`/run/secrets/<secret_name>`. You can update a service to grant it access to
additional secrets or revoke its access to a given secret at any time.
A node only has access to (encrypted) secrets if the node is a swarm manager or
if it is running service tasks which have been granted access to the secret.
When a container task stops running, the decrypted secrets shared to it are
unmounted from the in-memory filesystem for that container and flushed from the
node's memory.
If a node loses connectivity to the swarm while it is running a task container
with access to a secret, the task container still has access to its secrets, but
cannot receive updates until the node reconnects to the swarm.
You can add or inspect an individual secret at any time, or list all
secrets. You cannot remove a secret that a running service is
using. See [Rotate a secret](secrets.md#example-rotate-a-secret) for a way to
remove a secret without disrupting running services.
In order to update or roll back secrets more easily, consider adding a version
number or date to the secret name. This is made easier by the ability to control
the mount point of the secret within a given container.
## Read more about `docker secret` commands
Use these links to read about specific commands, or continue to the
[example about using secrets with a service](secrets.md#example-use-secrets-with-a-service).
- [`docker secret create`](../reference/commandline/secret_create.md)
- [`docker secret inspect`](../reference/commandline/secret_inspect.md)
- [`docker service ls`](../reference/commandline/secret_ls.md)
- [`docker secret rm`](../reference/commandline/secret_rm.md)
- [`--secret`](../reference/commandline/service_create.md#create-a-service-with-secrets) flag for `docker service create`
- [`--secret-add` and `--secret-rm`](../reference/commandline/service_update.md#adding-and-removing-secrets) flags for `docker service update`
## Examples
This section includes three graduated examples which illustrate how to use
Docker secrets. The images used in these examples have been updated to make it
easier to use Docker secrets. To find out how to modify your own images in
a similar way, see
[Build support for Docker Secrets into your images](#build-support-for-docker-secrets-into-your-images).
> **Note**: These examples use a single-Engine swarm and unscaled services for
> simplicity.
### Simple example: Get started with secrets
This simple example shows how secrets work in just a few commands. For a
real-world example, continue to
[Intermediate example: Use secrets with a Nginx service](#intermediate-example-use-secrets-with-a-nginx-service).
1. Add a secret to Docker. The `docker secret create` command reads standard
input.
```bash
$ echo "This is a secret" | docker secret create my_secret_data
```
2. Create a `redis` service and grant it access to the secret. By default,
the container can access the secret at `/run/secrets/<secret_name>`, but
you can customize the file name on the container using the `target` option.
```bash
$ docker service create --name="redis" --secret="my_secret_data" redis:alpine
```
3. Verify that the task is running without issues using `docker service ps`. If
everything is working, the output looks similar to this:
```bash
$ docker service ps redis
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
bkna6bpn8r1a redis.1 redis:alpine ip-172-31-46-109 Running Running 8 seconds ago
```
If there were an error, and the task were failing and repeatedly restarting,
you would see something like this:
```bash
$ docker service ps redis
NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
redis.1.siftice35gla redis:alpine moby Running Running 4 seconds ago
\_ redis.1.whum5b7gu13e redis:alpine moby Shutdown Failed 20 seconds ago "task: non-zero exit (1)"
\_ redis.1.2s6yorvd9zow redis:alpine moby Shutdown Failed 56 seconds ago "task: non-zero exit (1)"
\_ redis.1.ulfzrcyaf6pg redis:alpine moby Shutdown Failed about a minute ago "task: non-zero exit (1)"
\_ redis.1.wrny5v4xyps6 redis:alpine moby Shutdown Failed 2 minutes ago "task: non-zero exit (1)"
```
4. Get the ID of the `redis` service task container using `docker ps` , so that
you can use `docker exec` to connect to the container and read the contents
of the secret data file, which defaults to being readable by all and has the
same name as the name of the secret. The first command below illustrates
how to find the container ID, and the second and third commands use shell
completion to do this automatically.
```bash
$ docker ps --filter name=redis -q
5cb1c2348a59
$ docker exec $(docker ps --filter name=redis -q) ls -l /run/secrets
total 4
-r--r--r-- 1 root root 17 Dec 13 22:48 my_secret_data
$ docker exec $(docker ps --filter name=redis -q) cat /run/secrets/my_secret_data
This is a secret
```
5. Verify that the secret is **not** available if you commit the container.
```bash
$ docker commit $(docker ps --filter name=redis -q) committed_redis
$ docker run --rm -it committed_redis cat /run/secrets/my_secret_data
cat: can't open '/run/secrets/my_secret_data': No such file or directory
```
6. Try removing the secret. The removal fails because the `redis` is running
and has access to the secret.
```bash
$ docker secret ls
ID NAME CREATED UPDATED
wwwrxza8sxy025bas86593fqs my_secret_data 4 hours ago 4 hours ago
$ docker secret rm my_secret_data
Error response from daemon: rpc error: code = 3 desc = secret 'my_secret_data' is in use by the following service: redis
```
7. Remove access to the secret from the running `redis` service by updating the
service.
```bash
$ docker service update --secret-rm="my_secret_data" redis
```
8. Repeat steps 3 and 4 again, verifying that the service no longer has access
to the secret. The container ID will be different, because the
`service update` command redeploys the service.
```bash
$ docker exec -it $(docker ps --filter name=redis -q) cat /run/secrets/my_secret_data
cat: can't open '/run/secrets/my_secret_data': No such file or directory
```
7. Stop and remove the service, and remove the secret from Docker.
```bash
$ docker service rm redis
$ docker secret rm my_secret_data
```
### Intermediate example: Use secrets with a Nginx service
This example is divided into two parts.
[The first part](#generate-the-site-certificate) is all about generating
the site certificate and does not directly involve Docker secrets at all, but
it sets up [the second part](#configure-the-nginx-container), where you store
and use the site certificate and Nginx configuration as secrets.
#### Generate the site certificate
Generate a root CA and TLS certificate and key for your site. For production
sites, you may want to use a service such as `Lets Encrypt` to generate the
TLS certificate and key, but this example uses command-line tools. This step
is a little complicated, but is only a set-up step so that you have
something to store as a Docker secret. If you want to skip these sub-steps,
you can [use Let's Encrypt](https://letsencrypt.org/getting-started/) to
generate the site key and certificate, name the files `site.key` and
`site.crt`, and skip to
[Configure the Nginx container](#configure-the-nginx-container).
1. Generate a root key.
```bash
$ openssl genrsa -out "root-ca.key" 4096
```
2. Generate a CSR using the root key.
```bash
$ openssl req \
-new -key "root-ca.key" \
-out "root-ca.csr" -sha256 \
-subj '/C=US/ST=CA/L=San Francisco/O=Docker/CN=Swarm Secret Example CA'
```
3. Configure the root CA. Edit a new file called `root-ca.cnf` and paste
the following contents into it. This constrains the root CA to only be
able to sign leaf certificates and not intermediate CAs.
```none
[root_ca]
basicConstraints = critical,CA:TRUE,pathlen:1
keyUsage = critical, nonRepudiation, cRLSign, keyCertSign
subjectKeyIdentifier=hash
```
4. Sign the certificate.
```bash
$ openssl x509 -req -days 3650 -in "root-ca.csr" \
-signkey "root-ca.key" -sha256 -out "root-ca.crt" \
-extfile "root-ca.cnf" -extensions \
root_ca
```
5. Generate the site key.
```bash
$ openssl genrsa -out "site.key" 4096
```
6. Generate the site certificate and sign it with the site key.
```bash
$ openssl req -new -key "site.key" -out "site.csr" -sha256 \
-subj '/C=US/ST=CA/L=San Francisco/O=Docker/CN=localhost'
```
7. Configure the site certificate. Edit a new file called `site.cnf` and
paste the following contents into it. This constrains the site
certificate so that it can only be used to authenticate a server and
can't be used to sign certificates.
```none
[server]
authorityKeyIdentifier=keyid,issuer
basicConstraints = critical,CA:FALSE
extendedKeyUsage=serverAuth
keyUsage = critical, digitalSignature, keyEncipherment
subjectAltName = DNS:localhost, IP:127.0.0.1
subjectKeyIdentifier=hash
```
8. Sign the site certificate.
```bash
$ openssl x509 -req -days 750 -in "site.csr" -sha256 \
-CA "root-ca.crt" -CAkey "root-ca.key" -CAcreateserial \
-out "site.crt" -extfile "site.cnf" -extensions server
```
9. The `site.csr` and `site.cnf` files are not needed by the Nginx service, but
you will need them if you want to generate a new site certificate. Protect
the `root-ca.key` file.
#### Configure the Nginx container
1. Produce a very basic Nginx configuration that serves static files over HTTPS.
The TLS certificate and key will be stored as Docker secrets so that they
can be rotated easily.
In the current directory, create a new file called `site.conf` with the
following contents:
```none
server {
listen 443 ssl;
server_name localhost;
ssl_certificate /run/secrets/site.crt;
ssl_certificate_key /run/secrets/site.key;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
```
2. Create three secrets, representing the key, the certificate, and the
`site.conf`. You can store any file as a secret as long as it is smaller
than 500 KB. This allows you to decouple the key, certificate, and
configuration from the services that will use them.
```bash
$ cat site.key | docker secret create site.key
$ cat site.crt | docker secret create site.crt
$ cat site.conf | docker create site.conf
```
```bash
$ docker secret ls
ID NAME CREATED UPDATED
2hvoi9mnnaof7olr3z5g3g7fp site.key 58 seconds ago 58 seconds ago
aya1dh363719pkiuoldpter4b site.crt 24 seconds ago 24 seconds ago
zoa5df26f7vpcoz42qf2csth8 site.conf 11 seconds ago 11 seconds ago
```
4. Create a service that runs Nginx and has access to the three secrets. The
last part of the `docker service create` command creates a symbolic link
from the location of the `site.conf` secret to `/etc/nginx.conf.d/`, where
Nginx looks for extra configuration files. This step happens before Nginx
actually starts, so you don't need to rebuild your image if you change the
Nginx configuration.
> **Note**: Normally you would create a Dockerfile which copies the `site.conf`
> into place, build the image, and run a container using your custom image.
> This example does not require a custom image. It puts the `site.conf`
> into place and runs the container all in one step.
```bash
$ docker service create \
--name nginx \
--secret site.key \
--secret site.crt \
--secret site.conf \
--publish 3000:443 \
nginx:latest \
sh -c "ln -s /run/secrets/site.conf /etc/nginx/conf.d/site.conf && exec nginx -g 'daemon off;'"
```
This uses the short syntax for the `--secret` flag, which creates files in
`/run/secrets/` with the same name as the secret. Within the running
containers, the following three files now exist:
- `/run/secrets/site.key`
- `/run/secrets/site.crt`
- `/run/secrets/site.conf`
5. Verify that the Nginx service is running.
```bash
$ docker service ls
ID NAME MODE REPLICAS IMAGE
zeskcec62q24 nginx replicated 1/1 nginx:latest
$ docker service ps nginx
NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
nginx.1.9ls3yo9ugcls nginx:latest moby Running Running 3 minutes ago
```
6. Verify that the service is operational: you can reach the Nginx
server, and that the correct TLS certificate is being used.
```bash
$ curl --cacert root-ca.crt https://0.0.0.0:3000
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
```
```bash
$ openssl s_client -connect 0.0.0.0:3000 -CAfile root-ca.crt
CONNECTED(00000003)
depth=1 /C=US/ST=CA/L=San Francisco/O=Docker/CN=Swarm Secret Example CA
verify return:1
depth=0 /C=US/ST=CA/L=San Francisco/O=Docker/CN=localhost
verify return:1
---
Certificate chain
0 s:/C=US/ST=CA/L=San Francisco/O=Docker/CN=localhost
i:/C=US/ST=CA/L=San Francisco/O=Docker/CN=Swarm Secret Example CA
---
Server certificate
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
subject=/C=US/ST=CA/L=San Francisco/O=Docker/CN=localhost
issuer=/C=US/ST=CA/L=San Francisco/O=Docker/CN=Swarm Secret Example CA
---
No client certificate CA names sent
---
SSL handshake has read 1663 bytes and written 712 bytes
---
New, TLSv1/SSLv3, Cipher is AES256-SHA
Server public key is 4096 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : TLSv1
Cipher : AES256-SHA
Session-ID: A1A8BF35549C5715648A12FD7B7E3D861539316B03440187D9DA6C2E48822853
Session-ID-ctx:
Master-Key: F39D1B12274BA16D3A906F390A61438221E381952E9E1E05D3DD784F0135FB81353DA38C6D5C021CB926E844DFC49FC4
Key-Arg : None
Start Time: 1481685096
Timeout : 300 (sec)
Verify return code: 0 (ok)
```
7. To clean up after running this example, remove the `nginx` service and the
stored secrets.
```bash
$ docker service rm nginx
$ docker secret rm site.crt site.key nginx.conf
```
### Advanced example: Use secrets with a WordPress service
In this example, you create a single-node MySQL service with a custom root
password, add the credentials as secrets, and create a single-node WordPress
service which uses these credentials to connect to MySQL. The
[next example](#example-rotate-a-secret) builds on this one and shows you how to
rotate the MySQL password and update the services so that the WordPress service
can still connect to MySQL.
This example illustrates some techniques to use Docker secrets to avoid saving
sensitive credentials within your image or passing them directly on the command
line.
> **Note**: This example uses a single-Engine swarm for simplicity, and uses a
> single-node MySQL service because a single MySQL server instance cannot be
> scaled by simply using a replicated service, and setting up a MySQL cluster is
> beyond the scope of this example.
>
> Also, changing a MySQL root passphrase isnt as simple as changing
> a file on disk. You must use a query or a `mysqladmin` command to change the
> password in MySQL.
1. Generate a random alphanumeric password for MySQL and store it as a Docker
secret with the name `mysql_password` using the `docker secret create`
command. To make the password shorter or longer, adjust the last argument of
the `openssl` command. This is just one way to create a relatively random
password. You can use another command to generate the password if you
choose.
> **Note**: After you create a secret, you cannot update it. You can only
> remove and re-create it, and you cannot remove a secret that a service is
> using. However, you can grant or revoke a running service's access to
> secrets using `docker service update`. If you need the ability to update a
> secret, consider adding a version component to the secret name, so that you
> can later add a new version, update the service to use it, then remove the
> old version.
```bash
$ openssl rand -base64 20 | docker secret create mysql_password
l1vinzevzhj4goakjap5ya409
```
The value returned is not the password, but the ID of the secret. In the
remainder of this tutorial, the ID output is omitted.
Generate a second secret for the MySQL `root` user. This secret won't be
shared with the WordPress service created later. It's only needed to
bootstrap the `mysql` service.
```bash
$ openssl rand -base64 20 | docker secret create mysql_root_password
```
List the secrets managed by Docker using `docker secret ls`:
```bash
$ docker secret ls
ID NAME CREATED UPDATED
l1vinzevzhj4goakjap5ya409 mysql_password 41 seconds ago 41 seconds ago
yvsczlx9votfw3l0nz5rlidig mysql_root_password 12 seconds ago 12 seconds ago
```
The secrets are stored in the encrypted Raft logs for the swarm.
2. Create a user-defined overlay network which will be used for communication
between the MySQL and WordPress services. There is no need to expose the
MySQL service to any external host or container.
```bash
$ docker network create -d overlay mysql_private
```
3. Create the MySQL service. The MySQL service will have the following
characteristics:
- Because the scale is set to `1`, only a single MySQL task runs.
Load-balancing MySQL is left as an exercise to the reader and involves
more than just scaling the service.
- Only reachable by other containers on the `mysql_private` network.
- Uses the volume `mydata` to store the MySQL data, so that it persists
across restarts to the `mysql` service.
- The secrets are each mounted in a `tmpfs` filesystem at
`/run/secrets/mysql_password` and `/run/secrets/mysql_root_password`.
They are never exposed as environment variables, nor can they be committed
to an image if the `docker commit` command is run.
- Sets the environment variables `MYSQL_PASSWORD_FILE` and
`MYSQL_ROOT_PASSWORD_FILE` to point to the
files `/run/secrets/mysql_password` and `/run/secrets/mysql_root_password`.
The `mysql` image reads the password strings from those files when
initializing the system database for the first time. Afterward, the
passwords are stored in the MySQL system database itself.
- Sets environment variables `MYSQL_USER` and `MYSQL_DATABASE`. A new
database called `wordpress` is created when the container starts, and the
`wordpress` user will have full permissions for this database only. This
user will not be able to create or drop databases or change the MySQL
configuration.
```bash
$ docker service create \
--name mysql \
--replicas 1 \
--network mysql_private \
--mount type=volume,source=mydata,destination=/var/lib/mysql \
--secret source=mysql_root_password,target=mysql_root_password \
--secret source=mysql_password,target=mysql_password \
-e MYSQL_ROOT_PASSWORD_FILE="/run/secrets/mysql_root_password" \
-e MYSQL_PASSWORD_FILE="/run/secrets/mysql_password" \
-e MYSQL_USER="wordpress" \
-e MYSQL_DATABASE="wordpress" \
mysql:latest
```
4. Verify that the `mysql` container is running using the `docker service ls` command.
```bash
$ docker service ls
ID NAME MODE REPLICAS IMAGE
wvnh0siktqr3 mysql replicated 1/1 mysql:latest
```
At this point, you could actually revoke the `mysql` service's access to the
`mysql_password` and `mysql_root_password` secrets because the passwords
have been saved in the MySQL system database. Don't do that for now, because
we will use them later to facilitate rotating the MySQL password.
5. Now that MySQL is set up, create a WordPress service that connects to the
MySQL service. The WordPress service has the following characteristics:
- Because the scale is set to `1`, only a single WordPress task runs.
Load-balancing WordPress is left as an exercise to the reader, because of
limitations with storing WordPress session data on the container
filesystem.
- Exposes WordPress on port 30000 of the host machine, so that you can access
it from external hosts. You can expose port 80 instead if you do not have
a web server running on port 80 of the host machine.
- Connects to the `mysql_private` network so it can communicate with the
`mysql` container, and also publishes port 80 to port 30000 on all swarm
nodes.
- Has access to the `mysql_password` secret, but specifies a different
target file name within the container. The WordPress container will use
the mount point `/run/secrets/wp_db_password`. Also specifies that the
secret is not group-or-world-readable, by setting the mode to
`0400`.
- Sets the environment variable `WORDPRESS_DB_PASSWORD_FILE` to the file
path where the secret is mounted. The WordPress service will read the
MySQL password string from that file and add it to the `wp-config.php`
configuration file.
- Connects to the MySQL container using the username `wordpress` and the
password in `/run/secrets/wp_db_password` and creates the `wordpress`
database if it does not yet exist.
- Stores its data, such as themes and plugins, in a volume called `wpdata`
so these files persist when the service restarts.
```bash
$ docker service create \
--name wordpress \
--replicas 1 \
--network mysql_private \
--publish 30000:80 \
--mount type=volume,source=wpdata,destination=/var/www/html \
--secret source=mysql_password,target=wp_db_password,mode=0400 \
-e WORDPRESS_DB_USER="wordpress" \
-e WORDPRESS_DB_PASSWORD_FILE="/run/secrets/wp_db_password" \
-e WORDPRESS_DB_HOST="mysql:3306" \
-e WORDPRESS_DB_NAME="wordpress" \
wordpress:latest
```
6. Verify the service is running using `docker service ls` and
`docker service ps` commands.
```bash
$ docker service ls
ID NAME MODE REPLICAS IMAGE
wvnh0siktqr3 mysql replicated 1/1 mysql:latest
nzt5xzae4n62 wordpress replicated 1/1 wordpress:latest
```
```bash
$ docker service ps wordpress
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
aukx6hgs9gwc wordpress.1 wordpress:latest moby Running Running 52 seconds ago
```
At this point, you could actually revoke the WordPress service's access to
the `mysql_password` secret, because WordPress has copied the secret to its
configuration file `wp-config.php`. Don't do that for now, because we will
use it later to facilitate rotating the MySQL password.
7. Access `http://localhost:30000/` from any swarm node and set up WordPress
using the web-based wizard. All of these settings are stored in the MySQL
`wordpress` database. WordPress automatically generates a password for your
WordPress user, which is completely different from the password WordPress
uses to access MySQL. Store this password securely, such as in a password
manager. You will need it to log into WordPress after
[rotating the secret](#example-rotate-a-secret).
Go ahead and write a blog post or two and install a WordPress plugin or
theme to verify that WordPress is fully operational and its state is saved
across service restarts.
8. Do not clean up any services or secrets if you intend to proceed to the next
example, which demonstrates how to rotate the MySQL root password.
### Example: Rotate a secret
This example builds upon the previous one. In this scenario, you create a new
secret with a new MySQL password, update the `mysql` and `wordpress` services to
use it, then remove the old secret.
**Note**: Changing the password on a MySQL database involves running extra
queries or commands, as opposed to just changing a single environment variable
or a file, since the image only sets the MySQL password if the database doesnt
already exist, and MySQL stores the password within a MySQL database by default.
Rotating passwords or other secrets will often involve additional steps outside
of Docker.
1. Create the new password and store it as a secret named `mysql_password_v2`.
```bash
$ openssl rand -base64 20 | docker secret create mysql_password_v2
```
2. Update the MySQL service to give it access to both the old and new secrets.
Remember that you cannot update or rename a secret, but you can revoke a
secret and grant access to it using a new target filename.
```bash
$ docker service update \
--secret-rm mysql_password mysql
$ docker service update \
--secret-add source=mysql_password,target=old_mysql_password \
--secret-add source=mysql_password_v2,target=mysql_password \
mysql
```
Updating a service causes it to restart, and when the MySQL service restarts
the second time, it has access to the old secret under
`/run/secrets/old_mysql_password` and the new secret under
`/run/secrets/mysql_password`.
Even though the MySQL service has access to both the old and new secrets
now, the MySQL root password has not yet been changed.
3. Now, change the MySQL password for the `wordpress` user using the `mysql`
CLI. This command reads the old and new password from the files in
`/run/secrets` but does not expose them on the command line or save them in
the shell history.
Do this quickly and move on to the next step, because WordPress will lose
the ability to connect to MySQL.
First, find the ID of the `mysql` container task.
```bash
$ docker ps --filter --name=mysql -q
c7705cf6176f
```
Substitute the ID in the command below, or use the second variant which
uses shell expansion to do it all in a single step.
```bash
$ docker exec <CONTAINER_ID> \
bash -c 'mysqladmin --user=wordpress --password="$(< /run/secrets/old_mysql_password)" password "$(< /run/secrets/mysql_password)"'
```
**or**:
```bash
$ docker exec $(docker ps --filter --name=mysql -q) \
bash -c 'mysqladmin --user=wordpress --password="$(< /run/secrets/old_mysql_password)" password "$(< /run/secrets/mysql_password)"'
```
4. Update the `wordpress` service to use the new password, keeping the target
path at `/run/secrets/wp_db_secret` and keeping the file permissions at
`0400`. This will trigger a rolling restart of the WordPress service and
the new secret will be used.
```bash
$ docker service update \
--secret-rm mysql_password \
--secret-add source=mysql_password_v2,target=wp_db_password,mode=0400 \
wordpress
```
5. Verify that WordPress works by browsing to http://localhost:30000/ on any
swarm node again. You'll need to use the WordPress username and password
from when you ran through the WordPress wizard in the previous task.
Verify that the blog post you wrote still exists, and if you changed any
configuration values, verify that they are still changed.
6. Revoke access to the old secret from the MySQL service and
remove the old secret from Docker.
```bash
$ docker service update \
--secret-rm mysql_password \
mysql
$ docker secret rm mysql_password
```
7. If you want to try the running all of these examples again or just want to
clean up after running through them, use these commands to remove the
WordPress service, the MySQL container, the `mydata` and `wpdata` volumes,
and the Docker secrets.
```bash
$ docker service rm wordpress mysql
$ docker volume rm mydata wpdata
$ docker secret rm mysql_password_v2 mysql_root_password
```
## Build support for Docker Secrets into your images
If you develop a container that can be deployed as a service and requires
sensitive data, such as a credential, as an environment variable, consider
adapting your image to take advantage of Docker secrets. One way to do this is
to ensure that each parameter you pass to the image when creating the container
can also be read from a file.
Many of the official images in the
[Docker library](https://github.com/docker-library/), such as the
[wordpress](https://github.com/docker-library/wordpress/)
image used in the above examples, have been updated in this way.
When you start a WordPress container, you provide it with the parameters it
needs by setting them as environment variables. The WordPress image has been
updated so that the environment variables which contain important data for
WordPress, such as `WORDPRESS_DB_PASSWORD`, also have variants which can read
their values from a file (`WORDPRESS_DB_PASSWORD_FILE`). This strategy ensures
that backward compatibility is preserved, while allowing your container to read
the information from a Docker-managed secret instead of being passed directly.
>**Note**: Docker secrets do not set environment variables directly. This was a
conscious decision, because environment variables can unintentionally be leaked
between containers (for instance, if you use `--link`).

View File

@ -66,7 +66,7 @@ $ docker service create --name helloworld alpine ping docker.com
9uk4639qpg7npwf3fn2aasksr
```
## Configuring services
## Configure services
When you create a service, you can specify many different configuration options
and constraints. See the output of `docker service create --help` for a full
@ -99,6 +99,12 @@ $ docker service create --name helloworld \
9uk4639qpg7npwf3fn2aasksr
```
### Grant a service access to secrets
To create a service with access to Docker-managed secrets, use the `--secret`
flag. For more information, see
[Manage sensitive strings (secrets) for Docker services](secrets.md)
### Specify the image version the service should use
When you create a service without specifying any details about the version of