最近,咱们遇到了一个Docker私有镜像拉取的错误,用了几小时研究Docker私有仓库(Registry)凭证存储的细节才弄清楚。尽管最终修复起来很容易,但在凭据存储设计的过程当中了解了一两件事,以及如何对Docker私有仓库(Registry)凭证进行安全配置。linux
Docker私有镜像拉取失败的状况以下:nginx
Blimp有时须要从Docker私有仓库(Registry)中拉取私有镜像。这一般能够正常工做,可是不幸的是,有时拉取私有镜像时,会收到如下错误消息:git
Get https://1234.dkr.ecr.us-east-1.amazonaws.com/v2/blimp/blimp/manifests/v0.1: no basic auth credentials
要了解咱们是如何解决的,首先你须要了解Docker凭证。github
Docker的外部证书存储
通常建议,存储Docker凭证要放在外部凭证存储中。一般位于~/.docker/config.json的Docker配置文件中。docker
企业中使用私有镜像仓库,一般都须要开启认证,认证凭据多是用户在企业中通用的帐户。但docker login之后,会在~/.docker/config.json中保存base64之后的用户名、密码,这样,在一些多人使用的服务器上,就会出现帐号泄露的问题。shell
有没有解决方法呢?json
docker提供credentials store,也就是讲密码存储到外置的credentials store中。安全
目前支持以下几种:ruby
D-Bus Secret Service: https://github.com/docker/docker-credential-helpers/releasesApple macOS keychain: https://github.com/docker/docker-credential-helpers/releasesMicrosoft Windows Credential Manager: https://github.com/docker/docker-credential-helpers/releasespass: https://github.com/docker/docker-credential-helpers/releases
对于linux服务器来讲,只能选择pass,由于D-Bus须要X11支持,而Apple和Microsoft看上去就不像是给Linux准备的。服务器
所以,为了提升Docker凭据的安全性,你能够在Docker配置文件中使用两个字段来配置Docker获取凭据和存储凭据的方式:credsStore和credHelpers。
credsStore告诉Docker使用哪一个帮助程序与凭证存储(credentials store)进行交互。全部帮助程序的名称都以docker-credential-开头,credsStore的值为帮助程序名称的后缀。
若是你使用Mac笔记本电脑,则可能会使用Mac OS钥匙串。使用Mac OS钥匙串后的帮助程序的名称是docker-credential-osxkeychain。所以,你Docker配置文件config.json将包括如下内容:
{ "credsStore": "osxkeychain"}
若是要查看Docker当前为你提供的凭据是什么,可使用list命令。例如:
docker-credential-osxkeychain list
输出信息是一对服务器和用户名列表。例如:
{ "http://quay.io":"kklin", "https://index.docker.io":"kevinklin"}
credHelpers是帮助程序( helpers ),用于生成短暂的凭据。例如,若是你使用gcr,gcloud会安装一个credHelper来使用你的Google登陆名获取令牌的。采用这种方式后,Docker永远不会直接拥有你的Google凭据, docker-credential-gcloud充当Docker和Google凭据之间的中间人。
但即便咱们对Docker login密码加密保存,仍然获得以下的错误消息:
Get https://1234.dkr.ecr.us-east-1.amazonaws.com/v2/blimp/blimp/manifests/v0.1: no basic auth credentials
经过上文,咱们知道能够经过运行docker-credential-osxkeychain list和get命令来查看1234.dkr.ecr.us-east-1.amazonaws.com的凭据,分析判断那么为何会收到一个错误消息-没有任何凭据(no basic auth credentials)?
Docker1.11版本前:私有仓库密码存储在配置文件中
Docker1.11版本前,Docker配置文件config.json经过auths字段来存储凭据,直到2016年1.11版本才可以使用外部凭据存储。
每当你登陆私有仓库时,Docker都会将auths的值设置为你的密码。你的配置文件可能包含如下内容:
{ "auths": { "https://index.docker.io/v1/": { "auth": "YW11cmRhY2E6c3VwZXJzZWNyZXRwYXNzd29yZA==" }, "localhost:5001": { "auth": "aGVzdHVzZXI6dGVzdHBhc3N3b3Jk" } }}
所以在采用credsStore后,当你使用docker login进行登陆时,你的凭据还会被存储,但其中的字段auths不包含用户名或密码。结果看起来像这样:
{"auths": { "https://index.docker.io/v1/": {}}
但Docker拉取镜像时从auths 和 credsStore二者同时获取凭据。所以,Docker拉取镜像时会有两份凭据,一份具备正确的用户名和密码,而一份则彻底没有密码。
不幸的是,Docker偏心使用auths中的https://的凭证,并尝试使用空凭证拉取镜像。所以,会有no basic auth credentials错误。
经过上文,咱们肯定了问题是一个空凭证被添加到 Docker配置文件config.json 中,咱们就很容易解决该问题。咱们须要作的就是添加一条if语句以跳过空凭据:
具体参考:https://github.com/kelda/blimp/blob/master/cli/up/up.go
addCredentials := func(authConfigs map[string]clitypes.AuthConfig) { for host, cred := range authConfigs { // Don't add empty config sections. if cred.Username != "" || cred.Password != "" || cred.Auth != "" || cred.Email != "" || cred.IdentityToken != "" || cred.RegistryToken != "" { creds[host] = types.AuthConfig{ Username: cred.Username, Password: cred.Password, Auth: cred.Auth, Email: cred.Email, ServerAddress: cred.ServerAddress, IdentityToken: cred.IdentityToken, RegistryToken: cred.RegistryToken, } } }}
Docker凭证安全风险
不少使用Docker的组织,可能仍在使用传统的auths方法。若是是这样,你的~/.docker/config.json可能看起来像这样:
{ "auths": { "https://index.docker.io/v1/": { "auth": "YW11cmRhY2E6c3VwZXJzZWNyZXRwYXNzd29yZA==" }, "localhost:5001": { "auth": "aGVzdHVzZXI6dGVzdHBhc3N3b3Jk" } }}
这看起来很安全,密码彷佛是一堆乱码。大家会想,这些密码确定是加密的吧?
其实,Docker所作的只是使用base64对密码进行编码。正如David Rieger在Hacker Noon上指出的那样,
Base64乍一看可能看起来像加密,但事实并不是如此。Base64是一种编码方案,不是加密方案。你只需复制Base64字符串,而后在几秒钟内将其转换为ASCII。
那个看似安全的密码aGVzdHVzZXI6dGVzdHBhc3N3b3Jk,读取密码所须要作的只是对base64进行解码:
echo aGVzdHVzZXI6dGVzdHBhc3N3b3Jk| base64 -D hestuser:testpassword
所以,若是未对Docker配置文件中的密码加密,则Docker将以纯文本格式存储你的密码。纯文本格式的好消息是,解决问题很容易,可是也容易被泄露和攻击。
为此,Docker提供了credentials store,也就是把密码存储到外置的credentials store中。
译者:王延飞
原文连接:https://dzone.com/articles/what-a-mysterious-bug-taught-us-about-how-docker-s

本文分享自微信公众号 - K8S中文社区(k8schina)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。