在 Swarm 集群中管理敏感数据

在 Docker Swarm 服务中, Secret 是一种 BLOB(二进制大对象) 数据, 就像密码、SSH 私钥、 SSL 证书或那些不应该未加密就直接存储在 Dockerfile 或应用程序代码中的数据。在 Docker 1.13 及更高版本中,可以使用 Docker Secrets 集中管理这些数据,并将其安全地传输给需要访问的容器。 一个给定的 Secret 只能被那些已被授予明确访问权限的服务使用,并且只能在这些服务任务正在运行的情况下。

不想在镜像或代码中管理的任何敏感数据,都可以使用 Secret 来管理,比如:

  • 用户名和密码
  • TLS certificates and keys
  • SSH keys
  • 其他重要数据:比如数据库名、内部服务器信息
  • 通用的字符串或二进制内容 (最大可达 500 Kb)

注意: Docker Secret 仅对 Swarm service有效,对独立的容器是无用的。如需使用,可以启动 1 个副本的service来使用 Docker Secret。

另一个使用 Secert 的场景是在容器和一组证书之间提供一个抽象层。考虑一种场景:有开发、测试和生产三套环境,同一个应用程序在不同的环境中都可以拥有不同的凭证,并且以相同的 Secret Name 存储在开发、测试和生产集群中。这样容器只需要知道 Secret Name,就可以在不同的环境中正常工作。

当然,你也可以使用 Docker Secret 管理非敏感数据,比如配置文件。但是,非敏感数据还是建议使用 Docker Config 来管理。

通过案例学习 Secret

我们将创建一个 Mysql Service ,将密码保存到secret 中。我们还会创建一个 WordPress service ,他将使用 secret 连接Mysql 。这个例子将展示如何使用secret避免在image中存放敏感信息,或者在命令行中直接传递敏感信息。

1 、创建 secret

我们使用 docker secret create 命令以管道符的形式创建 secret 存放 MySQL 的密码。

一般情况下,应用不会直接用 root 密码访问 MySQL。我们会创建一个单独的用户 wordpress,密码存放到 secret mysql_password中。

[root@node1 ~]$ openssl rand -base64 20 | docker secret create mysql_password -
lmlpd2qxi7lzvdf993udgqn6b

返回的是新创建的 secret的 ID,不是密码

再创建 secret 存放 MySQL 的root密码

[root@node1 ~]$ openssl rand -base64 20 | docker secret create mysql_root_password -
5f1i7uj8b8f6gxac54z5blsyo

上面这种方式是从标准输入读取 secret 的内容,也可以指定从文件中读取,例如:

[root@node1 ~]$ openssl rand -base64 20 > password.txt
[root@node1 ~]$ docker secret create mysql_root_password_file ./password.txt
jqwde9g4nlxdzbydmg91gy6k2

2、查看 secret

使用 docker secret ls 命令来查看 secret

[root@node1 ~]$ docker secret ls
ID                          NAME                       DRIVER              CREATED             UPDATED
lmlpd2qxi7lzvdf993udgqn6b   mysql_password                                 12 minutes ago      12 minutes ago
5f1i7uj8b8f6gxac54z5blsyo   mysql_root_password                            11 minutes ago      11 minutes ago
jqwde9g4nlxdzbydmg91gy6k2   mysql_root_password_file                       7 minutes ago       7 minutes ago

3、创建自定义 overlay 网络

用于mysql和WordPress service的网络通信,不需要把mysql service暴露出去

[root@node1 ~]$  docker network create -d overlay mysql_private
b5htbtr7dyy7o0g2i13n2415r
[root@node1 ~]$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
655033d70654        bridge              bridge              local
45b3b6c6bb0b        docker_gwbridge     bridge              local
6b1a153f6fb9        host                host                local
dmmsdyma59i1        ingress             overlay             swarm
b5htbtr7dyy7        mysql_private       overlay             swarm
8417dc8e3faf        none                null                local

4、创建 MySQL service

创建服务相关命令已经在前边章节进行了介绍,这里直接列出命令。

$ 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:5.7

确认mysql 容器已经正常启动

[root@node1 ~]$ docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
m4g2uncfb0xo        mysql               replicated          1/1                 mysql:5.7

5、创建 WordPress service

secret 默认通过 tmpfs 文件系统挂载到容器的 /run/secrets 目录中。

$ 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

查看服务,确保2个service 都正常启动

$ docker service ls

ID            NAME   MODE        REPLICAS  IMAGE
wvnh0siktqr3  mysql      replicated  1/1       mysql:latest
nzt5xzae4n62  wordpress  replicated  1/1       wordpress:latest

现在浏览器访问 IP:30000,即可开始 WordPress 的安装与使用。

通过以上方法,我们没有像以前通过设置环境变量来设置 MySQL 密码, 而是采用 docker secret 来设置密码,防范了密码泄露的风险。

轮换rotate

这里用修改mysql密码来示例

1、创建新的mysql密码并用secret 存储

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

2、修改mysql service

修改mysql service ,让它能同时使用新旧2个secret,因为update service 会导致容器重启,这时WordPress 可以访问旧的secret 持续提供服务

$ 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

3、修改WordPress 用户密码

通过mysqladmin 修改wordpress 的mysql密码

$ 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 service 使用新密码

$ docker service update \
     --secret-rm mysql_password \
     --secret-add source=mysql_password_v2,target=wp_db_password,mode=0400 \
     wordpress

5、确认WordPress是否正常

访问WordPress验证网站是否正常

6、移除旧的secret

$ docker service update \
     --secret-rm mysql_password \
     mysql

$ docker secret rm mysql_password

在 docker stack 中的使用

version: '3.1'

services:
   db:
     image: mysql:5.7
     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:

results matching ""

    No results matching ""