docker swarm英文文档学习-10-使用Docker密钥管理敏感数据

Manage sensitive data with Docker secrets使用Docker secrets管理敏感数据

About secrets

对于Docker Swarm服务来说,secret是一组数据,比如密码、SSH私钥、SSL证书或其他不应该在网络上传输或存储在Dockerfile或应用程序源代码中未加密的数据。在Docker 1.13及更高版本中,可以使用Docker secrets集中管理该数据,并将其安全地传输到需要访问它的容器。Secrets在传输过程中和在Docker集群中被加密。给定的secret只对那些已被授予显式访问权限的服务可访问,并且仅在这些服务任务运行时可访问。

你可以使用secret来管理容器运行时需要但不希望存储在镜像或源代码控制中的任何敏感数据,例如:

  • 用户名和密码
  • TLS证书和密钥
  • SSH密钥
  • 其他重要数据,如数据库或内部服务器的名称
  • 通用字符串或二进制内容(大小不超过500 kb)

注意:Docker的secrets只适用于集群服务,而不适用于独立容器。要使用此特性,请考虑将容器调整为作为服务运行。有状态容器通常可以在不更改容器代码的情况下以1的比例运行。

使用secrets的另一个用例是在容器和一组凭证之间提供一个抽象层。考虑这样一个场景:你的应用程序有独立的开发、测试和生产环境。这些环境中的每一个都可以具有不同的凭证,这些凭证存储在具有相同secret名称的开发、测试和生产集群中。容器只需要知道在所有三个环境中运行的secret的名称。

你还可以使用secret来管理非敏感数据,比如配置文件。但是,Docker 17.06和更高版本支持使用configs存储非敏感数据。配置直接挂载到容器的文件系统中,不需要使用RAM磁盘。

 

How Docker manages secrets  Docker如何管理secrets

当你向群中添加一个secret时,Docker通过一个相互的TLS连接将该secret发送给群管理器。这个secret存储在raft子日志中,日志是加密的。整个raft日志被复制到其他管理器中,确保机密的高可用性保证与集群管理数据的其他部分相同。

警告:raft数据在Docker 1.13或更高版本中加密。如果你的任何集群管理器运行较早的版本,并且其中一个管理器成为集群的管理器,则secret将未加密地存储在该节点的raft日志中。在添加任何secret之前,请将所有管理器节点更新到Docker 1.13或更高版本,以防止将秘密写入纯文本raft日志。


当你授予新创建或正在运行的服务对secret的访问权限时,解密的secret将挂载到内存文件系统中的容器中。在Linux容器中,挂载点的位置默认为/run/secrets/<secret_name>,或者在Windows容器中默认为C: ProgramData\Docker\secrets。你可以在Docker 17.06或更高版本中指定自定义位置。

你可以更新服务以授予其对其他secret的访问权,或者随时撤销其对给定secret的访问权。

只有当节点是集群管理器或正在运行已被授予secret访问权的服务任务时,节点才能访问(加密的)secret。当容器任务停止运行时,共享给它的解密secret将从该容器的内存文件系统中卸载,并从节点的内存中刷新。

如果节点在运行具有secret访问权的任务容器时失去了与集群的连接,那么任务容器仍然可以访问其secret,但是在节点重新连接到集群之前不能接收更新。

你可以在任何时候添加或检查单个secret,或者列出所有secret。你无法删除正在运行的服务正在使用的secret。有关在不中断正在运行的服务的情况下删除secret的方法,请参见Rotate a secret

为了更容易地更新或回滚secret,可以考虑在secret名称中添加版本号或日期。通过在给定容器中控制secret的挂载点,可以更容易地做到这一点。

 

Read more about docker secret commands更多有关docker secret命令的信息

使用这些链接可以阅读有关特定命令的信息,或者继续阅读example about using secrets with a service

 

Examples

本节包括三个分级示例,演示如何使用Docker secrets。这些示例中使用的镜像已经进行了更新,以便更容易地使用Docker secrets。要了解如何以类似的方式修改自己的镜像,请参见Build support for Docker Secrets into your images

注意:为了简单起见,这些示例使用单引擎集群和未扩展的服务。示例使用Linux容器,但Windows容器也支持Docker 17.06及更高版本中的secrets。看Windows support.

 

Defining and using secrets in compose files在compose文件中定义并使用secrets

docker-composedocker stack命令支持在compose文件中定义secrets。详情看the Compose file reference

Simple example: Get started with secrets 开始

这个简单的例子展示了secrets是如何在几个命令中工作的。对于真实的示例,请继续Intermediate example: Use secrets with a Nginx service.。

1.向Docker添加一个secrets。docker secret create命令读取标准输入,因为最后一个参数,表示从中读取秘密的文件被设置为-。

$ printf "This is a secret" | docker secret create my_secret_data -

2.创建一个redis服务,并授予它访问secret的权限。默认情况下,容器可以访问/run/secrets/上的secret,但是可以使用target选项定制容器上的文件名。

$ docker service  create --name redis --secret my_secret_data redis:alpine

3.使用docker service ps检查任务是否正常运行。如果一切正常,输出如下:

$ 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  

如果出现错误,并且任务失败并多次重新启动,你将看到如下内容:

$ 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.使用docker ps命令得到redis的服务任务容器的ID,这样你可以使用docker container exec去连接到容器和阅读的secret数据文件的内容,其默认为所有人可读,并且有着和secret的名称相同的名称。下面的第一个命令演示了如何查找容器ID,第二个和第三个命令使用shell补全来自动查找容器ID。

$ docker ps --filter name=redis -q

5cb1c2348a59

$ docker container 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 container exec $(docker ps --filter name=redis -q) cat /run/secrets/my_secret_data

This is a secret

5.如果提交容器,请验证该secret不可用。

$ 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.移除这个secret。删除失败是因为redis服务正在运行,并且可以访问该secret。

$ 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.通过更新服务,从正在运行的redis服务中删除对该secret的访问。

$ docker service update --secret-rm my_secret_data redis

8.再次重复步骤3和步骤4,验证服务不再具有访问secret的权限。容器ID是不同的,因为service update命令重新部署服务。

$ docker container 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

9.停止并删除服务,并从Docker中删除秘密。

$ docker service rm redis

$ docker secret rm my_secret_data

 

Intermediate example: Use secrets with a Nginx service

本例分为两部分。第一部分是关于生成站点证书的,完全不直接涉及Docker secret,但是它设置了第二部分,在其中存储和使用站点证书和Nginx配置作为secret。

GENERATE THE SITE CERTIFICATE生成站点证书

为你的站点生成根CA和TLS证书和密钥。对于生产站点,你可能希望使用Let’s Encrypt之类的服务来生成TLS证书和密钥,但是本示例使用命令行工具。这一步有点复杂,但只是一个设置步骤,以便你可以将某些内容存储为Docker secret。如果你想跳过这些子步骤,可以使用Let 's Encrypt生成站点密钥和证书,命名文件site.keysite.crt,然后跳到 Configure the Nginx container

1.生成一个根密钥:

$ openssl genrsa -out "root-ca.key" 4096

2.使用根密钥生成一个CSR:

$ 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.配置根CA.编辑一个名为root-ca.cnf的新文件,并将以下内容粘贴到其中。这限制根CA只签名叶证书,而不签名中间CA。

[root_ca]
basicConstraints = critical,CA:TRUE,pathlen:1
keyUsage = critical, nonRepudiation, cRLSign, keyCertSign
subjectKeyIdentifier=hash

4.签名证书

$ 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.生成站点密钥

$ openssl genrsa -out "site.key" 4096

6.生成站点证书并使用站点密钥签名

$ openssl req -new -key "site.key" -out "site.csr" -sha256 \
          -subj '/C=US/ST=CA/L=San Francisco/O=Docker/CN=localhost'

7.配置站点证书。编辑一个名为site.cnf的新文件,并将以下内容粘贴到其中。这限制了站点证书,因此它只能用于对服务器进行身份验证,而不能用于对证书进行签名。

[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.对站点证书签名

$ 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.site.csrsite.cnf文件不被Nginx服务需要,但是如果你想生成一个新的站点证书,则需要它们。保护root-ca.key文件。

 

 

CONFIGURE THE NGINX CONTAINER 配置Ngnix容器

1.生成一个非常基本的Nginx配置,它通过HTTPS提供静态文件。TLS证书和密钥存储为Docker密钥,这样可以方便地轮转它们。

在当前目录中,创建一个带着下面内容的名为site.conf的新文件

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.创建两个秘密,分别表示密钥和证书。你可以将任何文件作为秘密存储,只要它小于500kb。这允许你将密钥和证书与使用它们的服务解耦。在这些示例中,密钥名称和文件名是相同的。

$ docker secret create site.key site.key

$ docker secret create site.crt site.crt

$ docker secret create site.conf site.conf

 

$ 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

3.创建一个运行Nginx并可以访问这三个secret的服务。docker service create命令的最后一部分从site.conf secret的位置创建一个符号链接到/etc/nginx.conf,其中Nginx查找额外的配置文件。这个步骤发生在Nginx实际启动之前,所以如果你更改了Nginx配置,就不需要重新构建镜像。

注意:通常你会创建一个复制site.conf到位置的Dockerfile,构建镜像,并使用自定义镜像运行容器。本例不需要自定义镜像。它把site.conf放到位置上,并在一步内运行容器。
在Docker 17.05和更早的版本中,secret总是位于/run/secrets/目录中。Docker 17.06及更高版本允许你为容器中的secret指定自定义位置。下面的两个例子说明了不同之处。这个命令的旧版本要求你创建指向site.conf真实位置的符号链接,以便Nginx可以读取它,但新版本不需要这样做。保留了较老的示例,以便你可以看到差异。

Docker 17.06 and higher:

$ docker service create \
     --name nginx \
     --secret site.key \
     --secret site.crt \
     --secret source=site.conf,target=/etc/nginx/conf.d/site.conf \
     --publish published=3000,target=443 \
     nginx:latest \
     sh -c "exec nginx -g 'daemon off;'"

Docker 17.05 and earlier:

$ docker service create \
     --name nginx \
     --secret site.key \
     --secret site.crt \
     --secret site.conf \
     --publish published=3000,target=443 \
     nginx:latest \
     sh -c "ln -s /run/secrets/site.conf /etc/nginx/conf.d/site.conf && exec nginx -g 'daemon off;'"

第一个示例同时显示了secret的短语法和长语法,第二个示例只显示了短语法。这个简短的语法在/run/secrets/中创建与secret同名的文件。在运行的容器中,现在存在以下三个文件:

  • /run/secrets/site.key
  • /run/secrets/site.crt
  • /etc/nginx/conf.d/site.conf (or /run/secrets/site.conf if you used the second example)

4.查看Nginx服务是否正在运行

$ 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

5.核查服务是可操作的:你可以到达Ngnix服务端,并且正确的TLS证书正在被使用

$ curl --cacert root-ca.crt https://localhost: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. 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>

 

$ openssl s_client -connect localhost: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)

6.在运行此示例之后,请通过删除nginx服务以及存储的secrets来清理。

 

 

Build support for Docker Secrets into your images

如果你开发的容器可以部署为服务,并且需要敏感数据(例如凭据)作为环境变量,那么可以考虑调整镜像以利用Docker的secrets。一种方法是确保在创建容器时传递给镜像的每个参数也可以从文件中读取。

Docker库中的许多官方镜像,比如上面例子中使用的wordpress镜像,都是用这种方式更新的。

启动WordPress容器时,通过将其设置为环境变量,为其提供所需的参数。WordPress镜像已经更新,因此包含WordPress重要数据的环境变量,如WORDPRESS_DB_PASSWORD,也有可以从文件(WORDPRESS_DB_PASSWORD_FILE)读取其值的变量。这种策略确保了向后兼容性,同时允许容器从docker管理的secret中读取信息,而不是直接传递。

注意:Docker secrets不直接设置环境变量。这是一个有意识的决定,因为环境变量可能在容器之间无意地泄漏(例如,如果使用--link)。

 

Use Secrets in Compose

version: '3.1'

services:
   db:
     image: mysql:latest
     volumes:
       - db_data:/var/lib/mysql
     environment:
       MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD_FILE: /run/secrets/db_password
     secrets:
       - db_root_password
       - db_password

   wordpress:
     depends_on:
       - db
     image: wordpress:latest
     ports:
       - "8000:80"
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD_FILE: /run/secrets/db_password
     secrets:
       - db_password


secrets:
   db_password:
     file: db_password.txt
   db_root_password:
     file: db_root_password.txt

volumes:
    db_data:

这个例子使用一个compose文件中的两个secrets创建了一个简单的WordPress站点。

关键字secrets:定义两个secret db_password:和db_root_password:。

在部署时,Docker将创建这两个secrets,并用组成文件中指定的文件中的内容填充它们。

db服务同时使用这两个secrets,wordpress也在使用一个。

在部署时,Docker在服务中挂载/run/secrets/<secret_name>下的文件。</secret_name>这些文件从不保存在磁盘中,而是在内存中管理。

每个服务使用环境变量来指定服务应该在何处查找该secret数据。

有关secrets的短语法和长语法的更多信息,请参阅Compose file version 3 reference

posted @ 2019-01-10 15:56  慢行厚积  阅读(938)  评论(0编辑  收藏  举报