使用Docker机密管理敏感数据

预计阅读时间:34分钟

关于秘密

就Docker Swarm服务而言,秘密是数据的一大块,例如密码,SSH私钥,SSL证书或其他不应通过网络传输或未经加密存储在Dockerfile或应用程序的数据中的数据源代码。您可以使用Docker机密来集中管理此数据,并将其安全地传输到仅需要访问它们的那些容器。机密信息在传输过程中和静止时都在Docker群中进行了加密。给定的秘密仅对那些已被授予显式访问权限的服务可访问,并且仅在那些服务任务正在运行时才可访问。

您可以使用机密来管理容器在运行时需要的任何敏感数据,但是您不想将其存储在映像或源代码管理中,例如:

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

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

使用机密的另一个用例是在容器和一组凭据之间提供抽象层。考虑一种方案,其中您的应用程序具有单独的开发,测试和生产环境。这些环境中的每一个都可以具有不同的凭据,并以相同的秘密名称存储在开发,测试和生产群中。您的容器只需要知道密钥的名称即可在所有三种环境中运行。

您还可以使用机密来管理非敏感数据,例如配置文件。但是,Docker支持使用配置 来存储非敏感数据。配置文件直接安装到容器的文件系统中,而无需使用RAM磁盘。

Windows支援

Docker包括对Windows容器上机密的支持。在实现上有差异的地方,在下面的示例中将其声明出来。请记住以下明显差异:

  • Microsoft Windows没有用于管理RAM磁盘的内置驱动程序,因此在运行的Windows容器中,秘密以明文形式保存在容器的根磁盘中。但是,当容器停止时,机密将被显式删除。此外,Windows不支持使用docker commit或类似命令将正在运行的容器作为映像持久保存。

  • 在Windows上,建议 在主机上包含Docker根目录的卷上启用 BitLocker,以确保在运行时对运行容器的机密进行加密。

  • 具有自定义目标的机密文件不会直接绑定安装到Windows容器中,因为Windows不支持非目录文件绑定安装。取而代之的是,将容器的机密全部安装在容器内 C:\ProgramData\Docker\internal\secrets(应用程序不应依赖的实现细节)。使用符号链接从那里指向容器内秘密的所需目标。默认目标是C:\ProgramData\Docker\secrets

  • 创建使用Windows容器的服务时,机密不支持用于指定UID,GID和模式的选项。当前,只有system容器中具有访问权限的管理员和用户才能访问机密。

Docker如何管理机密

将机密添加到群集时,Docker会通过双向TLS连接将机密发送到群集管理器。机密存储在Raft日志中,该日志已加密。整个Raft日志将在其他管理器之间复制,从而确保对机密的高可用性保证与对其他群集管理数据的保证一样。

当您授予对机密的新创建或正在运行的服务访问权限时,解密后的机密将安装到内存文件系统中的容器中。容器中安装点的位置默认为 /run/secrets/<secret_name>Linux容器或 C:\ProgramData\Docker\secretsWindows容器中。您还可以指定自定义位置。

您可以随时更新服务以授予其访问其他机密的权限,或撤消其对给定机密的访问权限。

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

如果节点在运行可访问机密的任务容器时失去与群集的连接,则任务容器仍可访问其机密,但在节点重新连接到群集之前无法接收更新。

您可以随时添加或检查单个机密,也可以列出所有机密。您无法删除正在运行的服务正在使用的机密。有关不中断正在运行的服务的情况下删除机密的方法,请参阅旋转机密

要更轻松地更新或回滚机密,请考虑在机密名称上添加版本号或日期。通过控制给定容器中秘密的安装点的能力,这变得更加容易。

阅读有关docker secret命令的更多信息

使用这些链接可以阅读有关特定命令的信息,或者继续阅读有关对服务使用机密示例

例子

本节包括三个分级的示例,这些示例说明了如何使用Docker机密。这些示例中使用的映像已更新,以更轻松地使用Docker机密。要了解如何以类似方式修改自己的映像,请参阅在映像中 构建对Docker Secrets的支持

注意:为了简单起见,这些示例使用单引擎群集和未缩放的服务。这些示例使用Linux容器,但是Windows容器也支持秘密。请参阅Windows支持

在撰写文件中定义和使用机密

无论是docker-composedocker stack命令支持在撰写文件中定义的秘密。有关详细信息,请参见 撰写文件参考

简单示例:开始使用机密

这个简单的示例说明了秘密如何在几个命令中起作用。对于真实示例,请继续执行 中级示例:对Nginx服务使用机密

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

    $ printf "This is a secret" | docker secret create my_secret_data -
    
  2. 创建redis服务并授予其对机密的访问权限。默认情况下,容器可以通过访问秘密/run/secrets/<secret_name>,但是您可以使用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. 使用获取redis服务任务容器的ID docker ps,以便您可以docker container exec用来连接到容器并读取机密数据文件的内容,该文件默认为所有人可读,并且与机密名称相同。下面的第一个命令说明了如何查找容器ID,第二个和第三个命令使用Shell补全功能自动执行此操作。

    $ 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. 如果提交容器,请验证该机密不可用。

    $ 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. 尝试删除秘密。删除失败,因为该redis服务正在运行并且可以访问该机密。

    
    $ 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通过更新服务,从运行的服务中删除对机密的访问。

    $ docker service update --secret-rm my_secret_data redis
    
  8. 再次重复步骤3和4,验证该服务不再有权访问该机密。容器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
    

简单示例:在Windows服务中使用机密

这是一个非常简单的示例,显示了如何将秘密与在运行于Docker的Windows上运行的Windows容器的Windows Docker上运行的Microsoft IIS服务一起使用。这是一个朴素的示例,将网页存储在秘密中。

本示例假定您已安装PowerShell。

  1. 将以下内容保存到新文件中index.html

    <html lang="en">
      <head><title>Hello Docker</title></head>
      <body>
        <p>Hello Docker! You have deployed a HTML page.</p>
      </body>
    </html>
    
  2. 如果您尚未这样做,请初始化或加入集群。

    docker swarm init
    
  3. index.html文件另存为名为的群秘密homepage

    docker secret create homepage index.html
    
  4. 创建一个IIS服务并授予其对homepage机密的访问权限。

    docker service create
        --name my-iis
        --publish published=8000,target=8000
        --secret src=homepage,target="\inetpub\wwwroot\index.html"
        microsoft/iis:nanoserver  
    

    注意:在本示例中,从技术上讲,没有理由使用机密信息。配置更合适。本示例仅用于说明。

  5. 在访问IIS服务http://localhost:8000/。它应该从第一步开始提供HTML内容。

  6. 删除服务和机密。

    docker service rm my-iis
    docker secret rm homepage
    docker image remove secret-test
    

中级示例:对Nginx服务使用机密

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

生成站点证书

为您的站点生成根CA和TLS证书以及密钥。对于生产站点,您可能需要使用诸如Let’s Encrypt生成TLS证书和密钥之类的服务,但是本示例使用命令行工具。此步骤有点复杂,但仅是设置步骤,因此您可以将某些内容存储为Docker机密。如果要跳过这些子步骤,可以使用Let's Encrypt生成站点密钥和证书,命名文件site.keysite.crt,然后跳至 Configure Nginx容器

  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文件。

配置Nginx容器

  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. 创建三个密钥,分别代表密钥,证书和 site.conf。只要文件小于500 KB,就可以将其存储为机密文件。这使您可以将密钥,证书和配置与使用它们的服务分离。在每个命令中,最后一个参数表示从主机文件系统上读取机密的文件路径。在这些示例中,秘密名称和文件名相同。

    $ 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并有权访问这三个机密的服务。该docker service create命令的最后一部分创建一个从site.conf秘密位置到的符号链接/etc/nginx.conf.d/,Nginx在此处寻找额外的配置文件。此步骤发生在Nginx实际启动之前,因此,如果更改Nginx配置,则无需重建映像。

    注意:通常,您将创建一个Dockerfile,将其复制site.conf 到适当位置,构建映像,并使用您的自定义映像运行容器。本示例不需要自定义图像。它放置site.conf 到位并一步一步运行容器。

    /run/secrets/默认情况下,机密位于容器的目录内,这可能需要在容器中执行其他步骤才能使机密在其他路径中可用。下面的示例创建一个指向site.conf文件真实位置的符号链接,以便Nginx可以读取它:

    $ 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;'"
    

    机密允许您使用target选项指定自定义位置,而不是创建符号链接。下面的示例说明了如何在不使用符号链接的情况下在容器内部site.conf 提供机密/etc/nginx/conf.d/site.conf

    $ 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;'"
    

    site.keysite.crt秘密使用短手语法,没有自定义target设置位置。简短的语法将秘密信息以与秘密相同的名称挂载在`/ run / secrets /中。在正在运行的容器中,现在存在以下三个文件:

    • /run/secrets/site.key
    • /run/secrets/site.crt
    • /etc/nginx/conf.d/site.conf
  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. 验证服务是否正常运行:您可以访问Nginx服务器,并且使用了正确的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="https://nginx.org">nginx.org</a>.<br/>
    Commercial support is available at
    <a href="https://www.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服务和存储的机密。

    $ docker service rm nginx
    
    $ docker secret rm site.crt site.key site.conf
    

进阶范例:在WordPress服务中使用机密

在此示例中,您将使用自定义根密码创建一个单节点MySQL服务,将凭据添加为机密,并创建一个使用这些凭据连接到MySQL的单节点WordPress服务。在 下面的例子建立在这一个,你展示了如何旋转的MySQL密码和更新服务,使WordPress的服务仍然可以连接到MySQL。

此示例说明了一些使用Docker机密的技术,可避免在映像中保存敏感凭据或将其直接传递给命令行。

注意:为了简单起见,该示例使用单引擎集群,并使用单节点MySQL服务,因为单个MySQL服务器实例无法仅通过使用复制服务来扩展,并且设置MySQL集群不在本示例的范围之内。 。

另外,更改MySQL根密码并不像更改磁盘上的文件那样简单。您必须使用查询或mysqladmin命令来更改MySQL中的密码。

  1. 为MySQL生成一个随机的字母数字密码,并mysql_password使用该docker secret create 命令将其存储为Docker秘密,其名称为。要使密码变短或变长,请调整openssl命令的最后一个参数。这只是创建相对随机密码的一种方法。您可以选择使用另一个命令来生成密码。

    注意:创建机密后,将无法更新它。您只能删除并重新创建它,也不能删除服务正在使用的机密。但是,您可以使用授予或撤消正在运行的服务对机密的访问权限docker service update。如果需要更新机密的功能,请考虑在机密名称中添加版本组件,以便以后可以添加新版本,更新服务以使用它,然后删除旧版本。

    最后一个参数设置为-,表示从标准输入中读取输入。

    $ openssl rand -base64 20 | docker secret create mysql_password -
    
    l1vinzevzhj4goakjap5ya409
    

    返回的值不是密码,而是密码的ID。在本教程的其余部分中,省略了ID输出。

    为MySQLroot用户生成第二个机密。此秘密不会与以后创建的WordPress服务共享。只需引导mysql服务即可。

    $ openssl rand -base64 20 | docker secret create mysql_root_password -
    

    列出Docker管理的秘密docker secret ls

    $ 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
    

    机密存储在群集的加密Raft日志中。

  2. 创建一个用户定义的覆盖网络,该网络用于MySQL和WordPress服务之间的通信。无需将MySQL服务公开给任何外部主机或容器。

    $ docker network create -d overlay mysql_private
    
  3. 创建MySQL服务。MySQL服务具有以下特征:

    • 由于小数位数设置为1,因此仅运行一个MySQL任务。负载平衡MySQL作为练习留给读者,它不仅涉及扩展服务。
    • 仅可由mysql_private网络上的其他容器访问。
    • 使用该卷mydata存储MySQL数据,以便在重新启动该mysql服务后仍可保留该数据。
    • 秘密分别安装在一个tmpfs文件系统在 /run/secrets/mysql_password/run/secrets/mysql_root_password。它们永远不会作为环境变量公开,如果docker commit运行该命令,它们也不会提交给映像。mysql_password 秘密是非特权WordPress容器用来连接MySQL的秘密。
    • 设置环境变量MYSQL_PASSWORD_FILEMYSQL_ROOT_PASSWORD_FILE指向文件/run/secrets/mysql_password/run/secrets/mysql_root_passwordmysql首次初始化系统数据库时,映像从这些文件中读取密码字符串。之后,密码将存储在MySQL系统数据库本身中。
    • 设置环境变量MYSQL_USERMYSQL_DATABASEwordpress当容器启动时,将创建一个名为的新数据库,并且 wordpress用户仅对此数据库具有完全权限。该用户无法创建或删除数据库或更改MySQL配置。

      $ 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. mysql使用docker service ls命令验证容器是否正在运行。

    $ docker service ls
    
    ID            NAME   MODE        REPLICAS  IMAGE
    wvnh0siktqr3  mysql  replicated  1/1       mysql:latest
    

    此时,您实际上可以撤消该mysql服务对mysql_passwordmysql_root_password秘密的访问, 因为密码已保存在MySQL系统数据库中。现在不要这样做,因为稍后我们将使用它们来方便旋转MySQL密码。

  5. 现在已经设置了MySQL,创建一个连接到MySQL服务的WordPress服务。WordPress服务具有以下特征:

    • 由于比例设置为1,因此仅运行一个WordPress任务。由于将WordPress会话数据存储在容器文件系统上的局限性,负载平衡WordPress留给读者练习。
    • 在主机的端口30000上公开WordPress,以便您可以从外部主机访问它。如果您没有在主机的端口80上运行Web服务器,则可以公开端口80。
    • 连接到mysql_private网络,以便它可以与mysql容器通信 ,并在所有群集节点上将端口80发布到端口30000。
    • 可以访问mysql_password机密,但是在容器内指定了不同的目标文件名。WordPress容器使用挂载点/run/secrets/wp_db_password。通过将模式设置为,还指定该密码不可读组或世界 0400
    • 将环境变量设置WORDPRESS_DB_PASSWORD_FILE为安装机密的文件路径。WordPress服务从该文件中读取MySQL密码字符串,并将其添加到wp-config.php 配置文件中。
    • 使用中的用户名wordpress和密码连接到MySQL容器,/run/secrets/wp_db_password并创建wordpress 数据库(如果尚不存在)。
    • 将其数据(例如主题和插件)存储在一个名为的卷中,wpdata 以便在服务重新启动时这些文件保持不变。
    $ docker service create \
         --name wordpress \
         --replicas 1 \
         --network mysql_private \
         --publish published=30000,target=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. 使用docker service lsdocker service ps命令验证服务是否正在运行。

    $ docker service ls
    
    ID            NAME       MODE        REPLICAS  IMAGE
    wvnh0siktqr3  mysql      replicated  1/1       mysql:latest
    nzt5xzae4n62  wordpress  replicated  1/1       wordpress:latest
    
    $ 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   
    

    此时,您实际上可以撤消WordPress服务对mysql_password机密的访问权限,因为WordPress已将机密复制到其配置文件中wp-config.php。现在不要这样做,因为我们稍后会使用它来方便旋转MySQL密码。

  7. http://localhost:30000/从任何群节点访问并使用基于Web的向导设置WordPress。所有这些设置都存储在MySQL wordpress数据库中。WordPress会自动为您的WordPress用户生成一个密码,该密码与WordPress用于访问MySQL的密码完全不同。安全地存储此密码,例如在密码管理器中。旋转秘密后,您需要它才能登录WordPress 。

    继续并撰写一两篇博客文章,并安装WordPress插件或主题以验证WordPress是否可以完全正常运行,并在重新启动服务后保存其状态。

  8. 如果打算继续进行下一个示例,该示例演示了如何旋转MySQL根密码,请不要清除任何服务或机密。

示例:旋转秘密

这个例子建立在前一个例子的基础上。在这种情况下,您将使用新的MySQL密码创建一个新机密,更新mysqlwordpress服务以使用它,然后删除旧机密。

注意:更改MySQL数据库的密码涉及运行额外的查询或命令,而不是仅更改单个环境变量或文件,因为映像仅在数据库不存在时设置MySQL密码,并且MySQL存储该密码。默认情况下,MySQL数据库中的密码。旋转密码或其他机密可能会涉及Docker之外的其他步骤。

  1. 创建新密码并将其存储为名为的秘密mysql_password_v2

    $ openssl rand -base64 20 | docker secret create mysql_password_v2 -
    
  2. 更新MySQL服务,使其可以同时访问新旧机密。请记住,您无法更新或重命名机密,但是您可以撤消机密并使用新的目标文件名授予对其的访问权限。

    $ 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
    

    更新服务会使它重新启动,而当MySQL服务第二次重新启动时,它可以访问下的旧密钥/run/secrets/old_mysql_password和下 的新密钥 /run/secrets/mysql_password

    即使MySQL服务现在可以访问新旧密码,但WordPress用户的MySQL密码尚未更改。

    注意:此示例不会旋转MySQLroot密码。

  3. 现在,wordpress使用mysqladminCLI更改用户 的MySQL密码。该命令从文件中读取新旧密码,/run/secrets但不会在命令行中公开它们或将它们保存在Shell历史记录中。

    快速执行此操作并继续下一步,因为WordPress失去了连接到MySQL的能力。

    首先,找到mysql容器任务的ID 。

    $ docker ps --filter name=mysql -q
    
    c7705cf6176f
    

    在下面的命令中替换ID,或者使用第二个变体,该变体使用shell扩展在一个步骤中完成所有操作。

    $ docker container exec <CONTAINER_ID> \
        bash -c 'mysqladmin --user=wordpress --password="$(< /run/secrets/old_mysql_password)" password "$(< /run/secrets/mysql_password)"'
    

    $ docker container exec $(docker ps --filter name=mysql -q) \
        bash -c 'mysqladmin --user=wordpress --password="$(< /run/secrets/old_mysql_password)" password "$(< /run/secrets/mysql_password)"'
    
  4. 更新wordpress服务以使用新密码,将目标路径/run/secrets/wp_db_secret保留在,并将文件许可权保留在 0400。这将触发WordPress服务的滚动重启,并使用新的机密。

    $ docker service update \
         --secret-rm mysql_password \
         --secret-add source=mysql_password_v2,target=wp_db_password,mode=0400 \
         wordpress    
    
  5. 通过再次浏览到任何群集节点上的http:// localhost:30000 /来验证WordPress是否正常工作。在上一个任务中通过WordPress向导运行时,请使用WordPress用户名和密码。

    验证您撰写的博客帖子仍然存在,并且如果您更改了任何配置值,请确认它们仍然被更改。

  6. 从MySQL服务撤消对旧机密的访问,并从Docker中删除旧机密。

    
    $ docker service update \
         --secret-rm mysql_password \
         mysql
    
    $ docker secret rm mysql_password
    
  7. 如果要尝试再次运行所有这些示例,或者只是想在运行它们之后进行清理,请使用以下命令删除WordPress服务,MySQL容器,mydatawpdata卷以及Docker机密。

    $ docker service rm wordpress mysql
    
    $ docker volume rm mydata wpdata
    
    $ docker secret rm mysql_password_v2 mysql_root_password
    

在映像中构建对Docker Secrets的支持

如果您开发了一个可以作为服务部署的容器,并且需要诸如凭据之类的敏感数据作为环境变量,请考虑调整映像以利用Docker机密。一种方法是确保创建容器时传递给图像的每个参数也可以从文件中读取。

Docker库中的许多正式映像 ,例如 以上示例中使用的 wordpress映像,都已通过这种方式进行了更新。

启动WordPress容器时,可以通过将其设置为环境变量来为其提供所需的参数。WordPress映像已更新,因此包含WordPress等重要数据的环境变量WORDPRESS_DB_PASSWORD也具有可以从文件(WORDPRESS_DB_PASSWORD_FILE)读取其值的变体。此策略可确保保留向后兼容性,同时允许您的容器从Docker管理的机密中读取信息,而不必直接传递。

笔记

Docker机密不会直接设置环境变量。这是一个明智的决定,因为环境变量可能会在容器之间无意间泄漏(例如,如果使用--link)。

在撰写中使用机密

version: "3.9"

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:

本示例使用撰写文件中的两个机密创建一个简单的WordPress网站。

关键字secrets:定义了两个秘密db_password:db_root_password:

部署时,Docker会创建这两个秘密,并使用撰写文件中指定文件中的内容填充它们。

db服务使用这两个秘密,而wordpress使用一个秘密。

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

每个服务都使用环境变量来指定服务应在哪里寻找该秘密数据。

可在Compose文件版本3参考中找到有关机密的长语法和长语法的更多信息 。

机密凭证敏感字符串敏感数据安全性加密静态加密