14、如何使用 Secret
我们经常要想容器传递敏感信息,最常见的就是密码。比如:
docker run -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql
在启动 Mysql 容器时,我们通过环境变量 MYSQL_ROOT_PASSWORD 设置 mysql 的管理员密码。不过密码是以明文的形式写在了 docker run 命令中,有潜在的安全隐患。
为了解决这个问题, docker swarm 提供了secret 机制,允许将敏感信息加密后保存到secret 中,用户可以指定哪些容器可以使用此 secret。
如果使用 secret 启动 Mysql 容器,方法是:
1、在 swarm manager 中创建 secret my_secret_data
,将密码保存其中
[root@swarm-manager ~]# echo "my-secret-pw" | docker secret create my_secret_data -
t3u62kw5ekw415cnbccldd8tz
[root@swarm-manager ~]# docker secret ls
ID NAME DRIVER CREATED UPDATED
t3u62kw5ekw415cnbccldd8tz my_secret_data 14 seconds ago 14 seconds ago
[root@swarm-manager ~]# docker secret inspect my_secret_data
[
{
"ID": "t3u62kw5ekw415cnbccldd8tz",
"Version": {
"Index": 648
},
"CreatedAt": "2020-05-18T04:25:32.703720205Z",
"UpdatedAt": "2020-05-18T04:25:32.703720205Z",
"Spec": {
"Name": "my_secret_data",
"Labels": {}
}
}
]
[root@swarm-manager ~]#
2、启动 MySQL service,并指定使用 secret my_secret_data
。
[root@swarm-manager ~]# docker service create --name mysql --secret source=my_secret_data,target=mysql_root_password -e MYSQL_ROOT_PASSWORD_FILE="/run/secrets/mysql_root_password" mysql
ipvi0cepi4jug7cb1x68ve165
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
[root@swarm-manager ~]#
[root@swarm-manager ~]# docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
hypso3i8hv55 mysql.1 mysql:latest swarm-worker1 Running Running 2 minutes ago
[root@swarm-manager ~]#
[root@swarm-worker1 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f805a9495da8 mysql:latest "docker-entrypoint.s…" 4 minutes ago Up 4 minutes 3306/tcp, 33060/tcp mysql.1.hypso3i8hv55yaxeqwie10ru4
[root@swarm-worker1 ~]# docker exec mysql.1.hypso3i8hv55yaxeqwie10ru4 cat /run/secrets/mysql_root_password
my-secret-pw
[root@swarm-worker1 ~]#
① source 指定容器使用 secret 后,secret 会被解密并存放到容器的文件系统中,默认位置为 /run/secrets/<secret_name>。--secret source=my_secret_data,target=mysql_root_password 的作用就是指定使用 secret my_secret_data,然后把器解密后的内容保存到容器 /run/secrets/mysql_root_password 文件中,文件名称 mysql_root_password 由 target 指定。
② 环境变量 MYSQL_ROOT_PASSWORD_FILE 指定从 /run/secrets/mysql_root_password 中读取并设置 MySQL 的管理员密码。
这里可能会有两个问题:
1、在第一步创建secret时,不是也使用明文了吗?这跟在环境变量中直接指定密码有什么不同?
在我们的例子中创建secret和使用secret是分开完成的,其好处是将密码和容器解耦。secret可以有专人(比如管理员)负责,而运行容器的用户只需要使用secret但并不需要知道secret的内容。也就是说例子中的两个步骤可以由不同的人在不同的时间完成。
2、secret是以文件形式mount到容器中,容器怎么知道去哪里读取呢?
这需要image的支持,如果image希望他部署出来的容器能够从secret中读取数据,那么此image就应该提供一种方式,让用户能够制定secret的位置。最常用的方法就是通过环境变量,Docker的很多官方image都采用这种方式,比如Mysql镜像同事提供了MYSQL_ROOT_PASSWORD 和 MYSQL_ROOT_PASSWORD_FILE 两个环境变量
15、Secret 的使用场景
我们可以用 secret 管理任何敏感数据。这些敏感数据是容器在运行时需要的,同时我们不又想将这些数据保存到镜像中。
secret 可用于管理:
1、用户名和密码。
2、TLS 证书。
3、SSH 秘钥。
4、其他小于 500 KB 的数据。
secret 只能在 swarm service 中使用。普通容器想使用 secret,可以将其包装成副本数为 1 的 service。
我们这里在举一个使用secret的典型场景。
数据中心有三套swarm环境,分别用于研发、测试和生产。对于同一个应用,在不同的环境中使用不同的用户名和密码。我们可以在三个环境中分别创建secret,不过使用相同的名字,比如 username 和 password。应用部署的时候三套环境指定同样的secret名字即可。
除了敏感数据,secret当然也可以用于非敏感数据,比如配置文件,不过目前新版本的Docker 提供了config子命令来管理不需要加密的数据。config 与 secret 命令的使用方法完全一致。
secret的安全性
当在swarm中创建secret时,Docker 通过 TLS 连接将加密后的secret 发送给所有的manager节点。
secret 创建后,即使是 swarm manager 也无法查看secret的明文数据,只能通过 docker secret inspect 查看secret的一般信息。
只有当secret 被指定的service 使用时,Docker才会将解密后的secret以文件的形式mount到容器中,默认的路径为 /run/secret/<secret_name>。
当容器停止运行是,Docker会unmount secret 文件,并从节点上清除。
16、通过案例学习 Secret
在下面的例子中,我们会部署一个 WordPress 应用,WordPress 是流行的开源博客系统。
我们将创建一个 Mysql Service ,将密码保存到secret 中。我们还会创建一个 WordPress service ,他将使用 secret 连接Mysql 。这个例子将展示如何使用secret避免在image中存放敏感信息,或者在命令行中直接传递敏感信息。
1、创建 secret
创建 secret 存放 MySQL 的管理员密码。
[root@swarm-manager ~]# openssl rand -base64 20 | docker secret create mysql_root_password -
ukxt024w2tdkpvmjg539mlk4u
[root@swarm-manager ~]# docker secret inspect mysql_root_password
[
{
"ID": "ukxt024w2tdkpvmjg539mlk4u",
"Version": {
"Index": 684
},
"CreatedAt": "2020-05-18T04:47:29.347052257Z",
"UpdatedAt": "2020-05-18T04:47:29.347052257Z",
"Spec": {
"Name": "mysql_root_password",
"Labels": {}
}
}
]
ukxt024w2tdkpvmjg539mlk4u是新创建的 service 的 ID,而非 service 的内容。
(上面这种方式是从标准输入读取 secret 的内容,也可以指定从文件中读取,例如:
[root@swarm-manager ~]# openssl rand -base64 20 > password.txt
[root@swarm-manager ~]# docker secret create mysql_root_password_file ./password.txt
897sfdj27t0k65ni2bhenyywr
[root@swarm-manager ~]# docker secret inspect mysql_root_password_file
[
{
"ID": "897sfdj27t0k65ni2bhenyywr",
"Version": {
"Index": 691
},
"CreatedAt": "2020-05-18T04:48:05.168409971Z",
"UpdatedAt": "2020-05-18T04:48:05.168409971Z",
"Spec": {
"Name": "mysql_root_password_file",
"Labels": {}
}
}
]
)
一般情况下,应用不会直接用 root 密码访问 MySQL。我们会创建一个单独的用户 workpress
,密码存放到 secret mysql_password
中。
[root@swarm-manager ~]# openssl rand -base64 20 | docker secret create mysql_password -
hx0lg2nbgbyz2k38uz4is3a83
[root@swarm-manager ~]# docker secret inspect mysql_password
[
{
"ID": "hx0lg2nbgbyz2k38uz4is3a83",
"Version": {
"Index": 692
},
"CreatedAt": "2020-05-18T04:58:55.066156483Z",
"UpdatedAt": "2020-05-18T04:58:55.066156483Z",
"Spec": {
"Name": "mysql_password",
"Labels": {}
}
}
]
[root@swarm-manager ~]# docker secret ls
ID NAME DRIVER CREATED UPDATED
hx0lg2nbgbyz2k38uz4is3a83 mysql_password 47 seconds ago 47 seconds ago
ukxt024w2tdkpvmjg539mlk4u mysql_root_password 12 minutes ago 12 minutes ago
2、创建自定义 overlay 网络
[root@swarm-manager ~]# docker network create --driver overlay mysql_private
laz9xbf44gtoq4314975tnc3b
[root@swarm-manager ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
344d54ebb898 bridge bridge local
d2ae471ab291 docker_gwbridge bridge local
6b5fc34b76ca host host local
oo9lrb47fmw2 ingress overlay swarm
laz9xbf44gto mysql_private overlay swarm
68be162550c8 none null local
[root@swarm-manager ~]#
3、创建mysql service (有关 mysql 镜像环境变量更详细的使用方法可参考 https://hub.docker.com/_/mysql/)
[root@swarm-manager ~]# docker service create --name mysql --network mysql_private --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
ucasufycqxrq729oztqd8y9r2
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
[root@swarm-manager ~]#
[root@swarm-manager ~]# docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
j6gwo0q4ko7a mysql.1 mysql:5.7 swarm-worker1 Running Running 4 minutes ago
[root@swarm-manager ~]#
MYSQL_DATABASE
指明创建数据库 wordpress
。
MYSQL_USER
和 MYSQL_PASSWORD_FILE
指明创建数据库用户 workpress
,密码从 secret mysql_password
中读取。
4、创建 WordPress service(有关 wordpress 镜像环境变量更详细的使用方法可参考 https://hub.docker.com/_/wordpress/)
[root@swarm-manager ~]# docker service create --name wordpress --network mysql_private --publish 80:80 --secret source=mysql_password,target=wp_db_password -e WORDPRESS_DB_HOST='mysql:3306' -e WORDPRESS_DB_NAME='wordpress' -e WORDPRESS_DB_USER='wordpress' -e WORDPRESS_DB_PASSWORD_FILE='/run/secrets/wp_db_password' wordpress
n6nzptn3mz6t9lbs575z7ec3y
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
[root@swarm-manager ~]#
[root@swarm-manager ~]# docker service ps wordpress
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
s0za6lq6qyyo wordpress.1 wordpress:latest swarm-worker2 Running Running 5 minutes ago
[root@swarm-manager ~]#
WORDPRESS_DB_HOST 指明 MySQL service 地址 mysql:3306,这里用到了 DNS。
WORDPRESS_DB_NAME 指明 WordPress 的数据库为 wordpress,与前面 MYSQL_DATABASE 一致。
WORDPRESS_DB_USER 指明连接 WordPress 数据库的用户为 wordpress,与前面 MYSQL_USER 一致。
WORDPRESS_DB_PASSWORD_FILE 指明数据库的用户 wordpress 的密码,从 secret mysql_password 中获取
5、验证 WordPress
访问 WordPress页面 http://swarm-manager-ip
设置密码后登录界面如下