Docker Login 登录凭证安全存储方式
作者:Ramboooooooo
一、凭证存储场景描述
Docker
利用 docker login
命令来校验用户镜像仓库的登录凭证,实际并不是真正意义上的登录。
仅仅是一种登录凭证的试探校验。如果用户名密码都正确的情况下,Docker
则会已仓库登录的地址为 key
值,用户名、密码以 base64
的编码格式保存在 Docker
配置文件中。
在 Linux
中的路径是 $HOME/.docker/config.json
- 在从未登录
Docker
仓库时,该配置文件不存在 - 首次登录
Docker
仓库后,登录信息和配置文件存储信息如下
[root@node103 /]# docker login 192.169.5.207:8004 Username: admin Password: WARNING! Your password will be stored unencrypted in /root/.docker/config.json. Configure a credential helper to remove this warning. See https://docs.docker.com/engine/reference/commandline/login/#credentials-store Login Succeeded [root@node103 /]# cat /root/.docker/config.json { "auths": { "192.169.5.207:8004": { "auth": "Y1W2R3t4a5W64768T9W99u0d0G85lgdfHdNdAsMjAyMA==" } }, "HttpHeaders": { "User-Agent": "Docker-Client/19.03.12 (linux)" } }[root@node103 /]#
通过命令行可以将 base64
加密后的用户名密码解码
[root@node103 /]# echo "Y1W2R3t4a5W64768T9W99u0d0G85lgdfHdNdAsMjAyMA==" | base64 --decode username:password [root@node103 /]#
从 config.json
数据结构可以了解到,Docker
针对每一个镜像仓库,只会保存最近一次有效的用户名和密码,之后执行 docker login $domain
会直接使用 config.json
中对应域名的用户名和密码登录,当处理完毕以后,可以执行 docker logout $domain
将指定仓库的用户登录凭证从 config.json
中删除。
[root@node103 /]# docker logout 192.169.5.207:8004 Removing login credentials for 192.169.5.207:8004 [root@node103 /]# cat /root/.docker/config.json { "auths": {}, "HttpHeaders": { "User-Agent": "Docker-Client/19.03.12 (linux)" } } [root@node103 /]#
通过以上的试验可以发现,将用户登录仓库的凭据信息默认保存在 Docker
的 config.json
文件中,是及其不安全并且容易泄露的。除非每个用户每次在与镜像仓库交付完成以后,手动执行 docker logout
删除。
Dokcer
也考虑到了这一点,针对不同的平台,其提供了不同的辅助工具将仓库的登录凭证保存到其它的安全系数高的存储产品中。所以我们需要采用别的保存密码的产品来保存 docker login
的密码信息。
二、存储凭证产品选型
点击此处 查看 Docker
提供的产品选型。
本示例以 pass
为例,在 CentOS
操作系统上将 Docker
的 Credetial store
切换到 pass
存储,不再写入 config.json
文件中。
三、切换存储凭证产品
1.安装 pass
所需的依赖组件
yum install -y gpg rng-tools
2.安装 pass
存储程序
由于 pass
不支持 yum
直接下载安装,我们点击此处寻找合适的源码包方式进行安装
# 进入源码包下载自定义规划目录 cd /usr/local/src # 下载指定版本的源码包 wget https://git.zx2c4.com/password-store/snapshot/password-store-1.7.3.tar.xz # 将源码包解压缩到自定义程序安装目录 tar Jxf password-store-1.7.3.tar.xz -C /usr/local/ # 进入源码解压后的目录 cd /usr/local/password-store-1.7.3 # 编译安装 make install # 验证安装结果 [root@node103 /]# pass version ============================================ = pass: the standard unix password manager = = = = v1.7.3 = = = = Jason A. Donenfeld = = Jason@zx2c4.com = = = = http://www.passwordstore.org/ = ============================================ [root@node103 /]#
3.使用 GPG
生成 KEY
[root@node103 ~]# gpg --gen-key gpg (GnuPG) 2.0.22; Copyright (C) 2013 Free Software Foundation, Inc. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Please select what kind of key you want: (1) RSA and RSA (default) (2) DSA and Elgamal (3) DSA (sign only) (4) RSA (sign only) Your selection? 1 RSA keys may be between 1024 and 4096 bits long. What keysize do you want? (2048) 4096 Requested keysize is 4096 bits Please specify how long the key should be valid. 0 = key does not expire <n> = key expires in n days <n>w = key expires in n weeks <n>m = key expires in n months <n>y = key expires in n years Key is valid for? (0) 0 Key does not expire at all Is this correct? (y/N) y GnuPG needs to construct a user ID to identify your key. Real name: Rambo Email address: rambo1203@sina.com Comment: blog.rambo123.com You selected this USER-ID: "Rambo (blog.rambo123.com) <rambo1203@sina.com>" Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o You need a Passphrase to protect your secret key. ------------------------------------------------------- | Enter passphrase | | | | | | Passphrase ******__________________________________ | | | | <OK> <Cancel> | ------------------------------------------------------- ---------------------------------------------------------------------- | Warning: You have entered an insecure passphrase. | | A passphrase should be at least 8 characters long. | | | | <Take this one anyway> <Enter new passphrase> | ---------------------------------------------------------------------- ------------------------------------------------------- | Please re-enter this passphrase | | | | Passphrase ******__________________________________ | | | | <OK> <Cancel> | ------------------------------------------------------- We need to generate a lot of random bytes. It is a good idea to perform some other action (type on the keyboard, move the mouse, utilize the disks) during the prime generation; this gives the random number generator a better chance to gain enough entropy. We need to generate a lot of random bytes. It is a good idea to perform some other action (type on the keyboard, move the mouse, utilize the disks) during the prime generation; this gives the random number generator a better chance to gain enough entropy. gpg: /root/.gnupg/trustdb.gpg: trustdb created gpg: key 93B4B164 marked as ultimately trusted public and secret key created and signed. gpg: checking the trustdb gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u pub 4096R/93B4B164 2020-08-29 Key fingerprint = 769F 06F1 EA11 7BBB 1725 9BCE 20A6 9A00 93B4 B164 uid Rambo (blog.rambo123.com) <rambo1203@sina.com> sub 4096R/4981C1FF 2020-08-29 [root@node103 ~]#
4.查看 GPG
生成的 KEY
[root@node103 ~]# gpg --list-keys /root/.gnupg/pubring.gpg ------------------------ pub 4096R/93B4B164 2020-08-29 uid Rambo (blog.rambo123.com) <rambo1203@sina.com> sub 4096R/4981C1FF 2020-08-29 [root@node103 ~]#
5.通过上一步骤得到的 pub
4096R/
后面的 id
来初始化 pass
[root@node103 ~]# pass init Usage: pass init [--path=subfolder,-p subfolder] gpg-id... [root@node103 ~]# pass init "93B4B164" mkdir: created directory ‘/root/.password-store/' Password store initialized for 93B4B164 [root@node103 ~]#
6.验证 pass
的密码本
# 为 key 设置密码并保存在 pass 密码本中 [root@node103 ~]# pass insert admin # 设置 key 为 admin 的密码 Enter password for admin: Retype password for admin: # 显示 key 为 admin 的密码 [root@node103 ~]# pass show admin ------------------------------------------------------- | Please re-enter this passphrase | | | | Passphrase ******__________________________________ | | | | <OK> <Cancel> | ------------------------------------------------------- # 保存 admin 的密码为 123456 123456 [root@node103 ~]# # 更多操作请参考 pass help [root@node103 /]# pass help ============================================ = pass: the standard unix password manager = = = = v1.7.3 = = = = Jason A. Donenfeld = = Jason@zx2c4.com = = = = http://www.passwordstore.org/ = ============================================ Usage: pass init [--path=subfolder,-p subfolder] gpg-id... Initialize new password storage and use gpg-id for encryption. Selectively reencrypt existing passwords using new gpg-id. pass [ls] [subfolder] List passwords. pass find pass-names... List passwords that match pass-names. pass [show] [--clip[=line-number],-c[line-number]] pass-name Show existing password and optionally put it on the clipboard. If put on the clipboard, it will be cleared in 45 seconds. pass grep [GREPOPTIONS] search-string Search for password files containing search-string when decrypted. pass insert [--echo,-e | --multiline,-m] [--force,-f] pass-name Insert new password. Optionally, echo the password back to the console during entry. Or, optionally, the entry may be multiline. Prompt before overwriting existing password unless forced. pass edit pass-name Insert a new password or edit an existing password using vi. pass generate [--no-symbols,-n] [--clip,-c] [--in-place,-i | --force,-f] pass-name [pass-length] Generate a new password of pass-length (or 25 if unspecified) with optionally no symbols. Optionally put it on the clipboard and clear board after 45 seconds. Prompt before overwriting existing password unless forced. Optionally replace only the first line of an existing file with a new password. pass rm [--recursive,-r] [--force,-f] pass-name Remove existing password or directory, optionally forcefully. pass mv [--force,-f] old-path new-path Renames or moves old-path to new-path, optionally forcefully, selectively reencrypting. pass cp [--force,-f] old-path new-path Copies old-path to new-path, optionally forcefully, selectively reencrypting. pass git git-command-args... If the password store is a git repository, execute a git command specified by git-command-args. pass help Show this text. pass version Show version information. More information may be found in the pass(1) man page. [root@node103 /]#
7.安装 Docker Credential
辅助工具,具体最新版本请点击此处 获取
# 进入自定义资源下载目录 cd /usr/local/src # 下载 Docker Credetial wget https://github.com/docker/docker-credential-helpers/archive/refs/tags/v0.6.3.tar.gz # 解压 Docker Credential tar zxvf docker-credential-pass-v0.6.3-amd64.tar.gz # 为 Docker Credential 赋予可执行权限 chmod +x docker-credential-pass # 将 Docker Credential 移动到环境变量中 mv docker-credential-pass /usr/local/bin/ # 查看 Docker Credentail 的版本 [root@node103 /]# docker-credential-pass version 0.6.3 [root@node103 /]#
8.修改 Docker
配置
# 清空 .docker/config.json 文件内容,然后将下面配置写入 config.json 文件中,注意 credsStore 是各辅助安装包名字的尾缀 [root@node103 /]# cat /root/.docker/config.json { "credsStore": "pass" } [root@node103 /]#
9.初始化 docker password store
[root@node103 /]# pass insert docker-credential-helpers/docker-pass-initialized-check # 密码本保存文件目录 mkdir: created directory ‘/root/.password-store/docker-credential-helpers' # 密码本访问密码 Enter password for docker-credential-helpers/docker-pass-initialized-check: Retype password for docker-credential-helpers/docker-pass-initialized-check: # 密码本中保存的密码信息,验证初始化结果 [root@node103 /]# docker-credential-pass list {} [root@node103 /]# # 也可以通过 show 来查看刚刚创建密码本的密码(执行的过程中无需输入密码) [root@node103 /]# pass show docker-credential-helpers/docker-pass-initialized-check 123456 [root@node103 /]#
10.再次执行 docker login
登录镜像仓库,同时查看 $HOME/.docker/config.json
文件内容
# 登录之前查看 config.json 文件中的内容 [root@node103 /]# cat /root/.docker/config.json { "credsStore": "pass" } # 采用用户名密码登录镜像仓库 [root@node103 /]# docker login 192.169.5.207:8004 Username: admin Password: # 登录成功也没有警告提示了 Login Succeeded # 再次查看 config.json 文件内容,发现用户名密码也没有保存在该文件中,而是保存到了加密文件中去了 [root@node103 /]# cat /root/.docker/config.json { "auths": { "192.169.5.207:8004": {} }, "HttpHeaders": { "User-Agent": "Docker-Client/19.03.12 (linux)" }, "credsStore": "pass" } # 重启 Docker 以免下次登录出现以下情况 [root@node103 /]# docker login 192.169.5.207:8004 Authenticating with existing credentials... Login did not succeed, error: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running? Username (admin): ^C [root@node103 /]# systemctl restart docker [root@node103 /]# docker login 192.169.5.207:8004 Authenticating with existing credentials... Login Succeeded [root@node103 /]#
11.验证以上登录镜像仓库的用户名密码是否保存在 pass
中
- 由于需要使用
tree
命令,这里需要安装
yum install tree -y
- 查看密码本中的密码原文
[root@node103 /]# docker-credential-pass list {"192.169.5.207:8004":"admin"} [root@node103 /]# pass Password Store ├── admin └── docker-credential-helpers ├── docker-pass-initialized-check └── MTkyLjE2OS41LjIwNzo4MDA0 └── admin [root@node103 /]# pass show docker-credential-helpers/MTkyLjE2OS41LjIwNzo4MDA0/admin 这里将显示设置的密码明文 [root@node103 /]#
12.保存密码文件路径
[root@node103 MTkyLjE2OS41LjIwNzo4MDA0]# cd /root/.password-store/docker-credential-helpers/MTkyLjE2OS41LjIwNzo4MDA0 [root@node103 MTkyLjE2OS41LjIwNzo4MDA0]# ll total 4 -rw------- 1 root root 591 Aug 29 14:36 admin.gpg [root@node103 MTkyLjE2OS41LjIwNzo4MDA0]#
四、凭据存储使用总结
Docker
默认采用config.json
文件保存docker login
的用户名密码- 并且这些用户名密码都是通过
base64
加密存储的很容易被泄露 - 我们应该切换用户名密码保存源,如
pass
- 在需要保存
Docker
用户名密码的操作客户端都安装pass
并根据以上配置即可 - 配置完成通过
docker login
进行一次登录验证 - 重启
Docker
在进行一次登录验证(此次是不需要输入登录密码的) dokcer login
的操作脚本都不需要进行相应的变化
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。