Docker Private Registry 是私有的 Docker Image 存储池。其[Registry v2 源码][12]是公开的。
互联网上有很多关于使用安全的 TLS 搭建 Docker Private Registry 失败的问题,有一些 work around 的建议是使用--insecure-registry
选项,也就是通过不使用安全的 HTTPS-TLS 方式来暂时绕过这个问题。
使用
--insecure-registry
的 work aroud : 运行命令:sudo vi /etc/default/docker
添加:DOCKER_OPTS="$DOCKER_OPTS --insecure-registry=www.example.com:8080"
然后执行:sudo service docker restart
而搭建 Private Registry 的目的往往是因为要建立自有的 Docker Image 池,否则干嘛不使用公开的 Docker Hub 上那么多成熟的并且很多是由官方维护的 Docker Images 呢?出于此目的,对于 Private Registry 的安全机制就要格外关注,决不能简单使用上述的 work around 来绕过。
安全有两个方面:
接下来就这两个方面的安全,阐述如何搭建 Docker Private Registry 。
如果不了解公开秘钥加密体系原理,请 Google 相关内容,或参考[OpenSSL 与 SSL 数字证书概念贴][6]和[SSL/TLS 原理详解][7]。这里仅阐述互联网如何应用该原理打造一套可信任体系的。
首先早期互联网基础架构人先建立了 Root CA ,用来对证书进行签名,随着互联网规模越来越大,仅几个 root CA 已经不足以应付需求,于是采用了类似 DNS 逐层授权的模式,建立多个 Intermediate CA ,这些 CA 本质上都可以为站点证书进行签名。
当 CA 用自己的私钥为证书进行了签名之后,得让客户端方便地获取 CA 的公钥。现实操作中,各个操作系统、浏览器等客户端采取将可信 Root CA 以及部分 Intermediate CA 公钥内置的办法,当安装这些软件的时候,这些公钥就已经就绪了,还有一些客户端采取的办法是从操作系统中获取 CA 公钥。 CA 公钥也以证书的形式提供。(证书是对密钥进行签名 /Sign 和散列 /Hash 之后的结果)
在人们使用客户端访问一个带有 SSL TLS 证书站点的时候,为了证明这些站点的证书就是真正的,客户端就用内置的 CA 证书对这些经过 CA 私钥签名过的证书进行校验。
因此,当站点安装了可信证书但客户端却报安全错误的时候,需要检查 CA 的证书是否已经下载到本地,可否被客户端正确读取。
根据[Docker 官方文档][1],获取安全证书有两个办法:一是从互联网上的认证 CA 处获取,二是自己建 CA 自己给证书签名。
公网 IP 是为了申请[Let's Encrypt][2]证书。在获得证书之后,这个公网 IP 其实不是必须的。因此可以在某些拥有公网 IP 的机器上获取证书,再将证书转移到使用私网 IP 的 Registry 服务器上。若采用这样的方式, Docker 客户端需要将私网 IP 和 dockie.mydomain.com 对应写入 hosts 文件或将该解析写到私网 DNS 服务器里。但证书到期 renew 的时候还需要同样的公网域名(公网 IP 可以不同)。
$ git clone https://github.com/letsencrypt/letsencrypt.git
$ cd letsencrypt
$ sudo ./letsencrypt-auto
根据该向导,选用standalone
模式,最后获取到的证书文件放在/etc/letsencrypt/archive/dockie.mydomain.com/
。首先备份这些证书到自己电脑上去。
$ sudo mkdir /opt/data
$ sudo mkdir /opt/certs
$ sudo cp /etc/letsencrypt/archive/dockie.mydomain.com/cert*.pem /opt/certs/dockie.mydomain.com.crt
$ sudo cp /etc/letsencrypt/archive/dockie.mydomain.com/*key*.pem /opt/certs/dockie.mydomain.com.key
$ sudo apt-get install docker
$ sudo yum install docker
此时执行$ sudo docker ps
和$ sudo docker images
命令可以看到本机里没有任何 image 和运行的 container 。$ sudo docker pull registry:2
待 pull 执行完毕再执行$ sudo docker image
可以看到本服务器已经有了 image ,执行$ sudo docker ps
可确认该 image 并未运行。$ sudo docker run -d -p 5000:5000 --restart=always --name registry \
-v /opt/data:/var/lib/registry -v /opt/certs:/certs \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/dockie.mydomain.com.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/dockie.mydomain.com.key registry:2
docker-compose.yml
文件,其内容如下:registry:
restart: always
image: registry:2
ports:
- 5000:5000
environment:
REGISTRY_HTTP_TLS_CERTIFICATE: /certs/dockie.mydomain.com.crt
REGISTRY_HTTP_TLS_KEY: /certs/dockie.mydomain.com.key
volumes:
- /path/data:/var/lib/registry
- /path/certs:/certs
然后再执行$ sudo docker-compose up -d
此时服务器上安装并运行一个 Private Registry 的工作就完成了。通过执行$ sudo docker ps
可以查看运行 Registry Image 的容器。接下来介绍在 Docker 客户端上如何与该 Private Registry 通信。
Docker 客户端可以是服务器本身,也可以是另一个安装了 Docker Engine 的计算机( Linux/Windows/Mac OS ),以另一个计算机为例。
在 Docker 客户端上,确认执行ping dockie.mydomain.com
可以获得正确 IP ,telnet dockie.mydomain.com 5000
能够获得响应,屏幕显示如下:
Trying xx.xx.xx.xx (服务器 IP )...
Connected to dockie.mydomain.com.
Escape character is '^]'.
查看站点证书,执行命令:openssl s_client -showcerts -verify 32 -connect dockie.mydomain.com:5000
使用 curl 来测试 TLS 是否工作正常。执行命令:
$ curl -i -k -v https://dockie.mydomain.com:5000
若正常,则有以下返回提示:
* Rebuilt URL to: https://dockie.mydomain.com:5000/
* Trying xx.xx.xx.xx (服务器 IP )...
* Connected to dockie.mydomain.com (xx.xx.xx.xx) port 5000 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* skipping SSL peer certificate verification
* SSL connection using TLS_RSA_WITH_AES_128_CBC_SHA
* Server certificate:
* subject: CN=dockie.mydomain.com
* start date: May 05 00:48:00 2016 GMT
* expire date: Aug 03 00:48:00 2016 GMT
* common name: dockie.mydomain.com
* issuer: CN=Let's Encrypt Authority X3,O=Let's Encrypt,C=US
> GET / HTTP/1.1
> User-Agent: curl/7.40.0
> Host: dockie.mydomain.com:5000
> Accept: */*
>
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Cache-Control: no-cache
Cache-Control: no-cache
< Date: Sat, 07 May 2016 06:28:17 GMT
Date: Sat, 07 May 2016 06:28:17 GMT
< Content-Length: 0
Content-Length: 0
< Content-Type: text/plain; charset=utf-8
Content-Type: text/plain; charset=utf-8
若没有正常响应,先检查 Docker 客户端与服务器是否使用同样的时区并用 ntp 进行过校准。分别在两台机器上执行$ date
查看当前时区与时间。若不同,则 Google 搜索相关文档并参考校准。
需要说明的是:即使 curl 测试通过了, Docker 客户端仍然有可能不工作。这其实是因为 Docker 对 Let's Encrypt 证书支持并不到位,所以必须手工安装 root CA 和 Intermediate CA 证书。 某些版本 Docker 使用自己的目录 /etc/docker/certs.d/存储 CA 证书,某些版本使用操作系统的 CA 证书路径。这造成了文档方面极大的混乱。 在使用操作系统 CA 证书的时候, Docker 会按照以下方法进行: 按照源码 crypto/x509/root_unix.go , Go 语言 (Docker 所使用的语言) 将会在以下文件中检查 CA 证书。 "/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc. "/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL "/etc/ssl/ca-bundle.pem", // OpenSUSE "/etc/ssl/cert.pem", // OpenBSD "/usr/local/share/certs/ca-root-nss.crt", // FreeBSD/DragonFly "/etc/pki/tls/cacert.pem", // OpenELEC "/etc/certs/ca-certificates.crt", // Solaris 11.2+ 建议在安装证书的时候,首先采用安装到操作系统路径,若 Docker 还是报 unknow authority 错误再安装到 Docker 自己定义的路径中。具体安装方法见下文。
如果响应中有unknow authority
,那么需要安装[Let's Encrypt][2]的授权机构证书。在[Let's Encrypt Certificates][3]页面可以找到对其授权的 root CA 证书以及其本身的证书,将 pem 格式证书下载到 Docker 客户端机器上。具体选择哪个,可使用 Chrome 浏览器访问 https://dockie.mydomain.com:5000 ,点击链接旁变绿色的小锁子,再点“详细信息”,如下图:
然后在右侧控制台点击“ View Certificates ”按钮,如下图:
最后在弹出的窗口中点击“证书路径”,如下图:
此时即可查看该证书对应的上级 CA 证书都是哪些类型。接下来下载 pem 格式证书,本例中,下载的证书分别为: ISRG Root X1 和 Let ’ s Encrypt Authority X3 (IdenTrust cross-signed)。前者是 root CA 证书,后者是 Intermediate CA 证书。如下图:
接下来就很关键,必须将两个 CA 证书都安装,否则执行$ sudo docker push
的时候就可能还会有错误提示:
x509: certificate signed by unknown authority
下载证书:
$ sudo wget https://letsencrypt.org/certs/isrgrootx1.pem
$ sudo wget https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem
将证书安装到 Docker 客户端机器的操作系统中。
$ sudo mkdir /usr/local/share/ca-certificates/dockie.mydomain.com
$ sudo cat lets-encrypt-x3-cross-signed.pem >> /usr/local/share/ca-certificates/dockie.mydomain.com/ca.crt
$ sudo cat isrgrootx1.pem >> /usr/local/share/ca-certificates/dockie.mydomain.com/ca.crt
$ sudo update-ca-certificates
$ sudo yum install ca-certificates
$ sudo update-ca-trust force-enable
$ sudo cat lets-encrypt-x3-cross-signed.pem >> /etc/pki/ca-trust/source/anchors/dockie.mydomain.com.crt
$ sudo cat isrgrootx1.pem >> /etc/pki/ca-trust/source/anchors/dockie.mydomain.com.crt
$ sudo update-ca-trust extract
$ sudo cat lets-encrypt-x3-cross-signed.pem >> /etc/pki/tls/certs/ca-bundle.crt
$ sudo cat isrgrootx1.pem >> /etc/pki/tls/certs/ca-bundle.crt
接下来,就可以愉快地在 Docker 客户端上向 Docker Private Registry push 一个 Image :
首先先下载一个 Ubuntu :$ sudo docker pull ubuntu
然后打 tag :$ sudo docker tag ubuntu dockie.mydomain.com:5000/ubuntu
最后传给 Private Registry :sudo docker push dockie.mydomain.com:5000/ubuntu
大功告成!
小技巧:若无法方便地将 Base Images 从公共 Registry 中 pull 回来,可以先在一台可以 pull 的服务器上 pull image ,然后使用命令
sudo docker save -o 镜像名.tar 镜像 ID
予以导出成文件,将文件通过 scp 或 ftp 传输到 Private Registry 上,再使用命令sudo docker load < 镜像名.tar
进行导入。
docker export 导出的是 Container , docker save 导出的是 Image ,若导入 docker export 导出的文件,需要使用 docker import 命令。
自己建立 CA 最常见的用法是用于测试,通常是在一台服务器上,既做 CA 也做站点,然后把 CA 证书拷贝到客户端(很多时候客户端也在同一服务器)。所以这种方式和使用 Let's Encrypt 证书有异曲同工之处,都需要让客户端正确使用 CA 证书。具体 CA 的建立方法以及站点证书的获取可参考[基于 OpenSSL 自建 CA 和颁发 SSL 证书][8]。获得站点证书和 CA 证书后,参考以上设置 Let's Encrypt 证书的方法进行设置。
如本文开头所述,客户端与 Docker Private Registry 进行安全交互可以使用密码或双向 SSL 。双路 SSL 的基本原理就是把客户端访问可信站点的方法路径反方向也实施一次,这样 Registry 就可以对有授权的客户端开放服务。由于此方法比较复杂,除非极度机密的通信采用该模式,一般通信均不用。
这里仅介绍使用密码的方式。
参考[Docker 官方文档][10],将docker-compose.yml
文件修改为:
registry:
restart: always
image: registry:2
ports:
- 5000:5000
environment:
REGISTRY_HTTP_TLS_CERTIFICATE: /certs/dockie.mydomain.com.crt
REGISTRY_HTTP_TLS_KEY: /certs/dockie.mydomain.com.key
REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
volumes:
- /opt/data:/var/lib/registry
- /opt/certs:/certs
- /opt/auth:/auth
使用 Apache 的 htpasswd ,生成用户名和密码,放在 /opt/auth 目录下即可。参考[Digital Ocean 的介绍文章][13]
[1]: ( https://docs.docker.com/registry/deploying/) [2]: ( https://letsencrypt.org) [3]: ( https://letsencrypt.org/certificates/) [4]: ( https://community.letsencrypt.org/t/which-browsers-and-operating-systems-support-lets-encrypt/4394) [5]: ( http://kb.kerio.com/product/kerio-connect/server-configuration/ssl-certificates/adding-trusted-root-certificates-to-the-server-1605.html) [6]: ( http://seanlook.com/2015/01/15/openssl-certificate-encryption/) [7]: ( http://seanlook.com/2015/01/07/tls-ssl/) [8]: ( http://seanlook.com/2015/01/18/openssl-self-sign-ca/) [9]: ( http://stackoverflow.com/questions/29286307/x509-certificate-signed-by-unknown-authority-both-with-docker-and-with-github) [10]: ( https://docs.docker.com/registry/deploying/) [11]: ( https://github.com/docker/distribution/blob/master/docs/deploying.md) [12]: ( https://github.com/docker/distribution) [13]: ( https://www.digitalocean.com/community/tutorials/how-to-set-up-a-private-docker-registry-on-ubuntu-14-04)
1
metrue 2016-07-10 10:19:15 +08:00 via Android
写的好.
|