Docker官方提供了用于搭建私有registry的镜像,并配有详细文档。
官方Registry镜像:https://hub.docker.com/_/registry
官方文档:https://docs.docker.com/registry
根据文档快速搭建的私有registry,只支持http。但是目前docker客户端的pull、push等命令,默认使用https的方式和registry进行交互,这将导致我们搭建的私有registry无法正常使用。
本文将从两个方面入手解决此问题:
- 让客户端以http方式和私有registry交互。
- 让私有registry支持https。
当然,第二种方式是本文的重点。此外,本文还介绍了如何使用用户名密码的方式,验证客户端身份。
这里假设我们的服务端的ip为:192.168.0.1,没有专用域名。大家在配置时,内网ip外网ip均可。如果有专用域名,处理方式也类似,只是要在生成证书时,注意命令的配置。
客户端使用Http的方式和私有Registry进行交互
这是常用的一种处理方式,优点是方便快捷,分分钟搞定。只要修改客户端docker守护进程的配置文件,将私有registry配置为一个不安全的registry即可 。此后,客户端都将使用http的方式和这个registry交互。主要步骤如下:
在服务端启动私有registry:
docker run -d -p 5000:5000 --name docker-registry registry:2
这个命令只是作为演示,真实环境下肯定还会配置volume。
配置客户端,和私有registry交互:
客户端的daemon.json配置内容如下:
{
"insecure-registries": ["192.168.0.1:5000"]
}
daemon.json的一般路径为:/etc/docker/daemon.json
配置参考官方文档:https://docs.docker.com/engine/reference/commandline/dockerd
启动(或重启)客户端docker,使用以下命令制作一个自己的busybox镜像,并推送至私有registry:
# 制作自己的busybox镜像
docker tag busybox 192.168.0.1:5000/busybox
# 将镜像推送至私有registry
docker push 192.168.0.1:5000/busybox
docker会使用http的方式和registry进行交互,将镜像推送至私有registry。
配置私有Registry,支持Https
下面介绍下如何配置私有Registry,使其支持https访问。
准备证书
跟常见的https站点一样,我们先要弄到一份证书,才能让我们自己的registry支持https。证书都是由CA(数字证书管理机构)签发的,我们可以向CA购买证书。根据CA的不同,证书类型的不同,证书的价格相差悬殊,大家可以自己网上搜搜看。如果有兴趣的话,大家还可以通过浏览器的调试工具,看看各大支持https网站的证书,分别是哪些CA签发的。
各个CA都有一份自己的证书(根证书),CA颁发给我们的证书都是用这份根证书签发出来的,操作系统会内置部分CA的根证书(比如受信任的根证书颁发机构下的证书)。这些内置CA签发出来的证书都将会被操作系统认为是安全的。比如某个网站使用了这些CA颁发的证书,那么你在访问这个网站时,浏览器地址栏就会变成绿色,提示你网站是安全的,否则,浏览器会用明显的红色提醒你正在访问不安全的网站。
举个例子,12306的网站:https://kyfw.12306.cn/otn,你在访问时会提示不安全。通过浏览器调试工具查看其证书,发现是一个叫 SRCA
的CA颁发证书给它的。继续百度,得知 SRCA
的全称为:Sinorail Certification Authority(可翻译为中铁数字证书认证中心)。相当于是中铁自己作为CA,给自己旗下的一个网站签发了一份证书。SRCA
的CA资质是肯定不会被国际认可的,道理你懂的,所以 SRCA
也不会被操作系统收录为受信任的CA。结果就是你在访问12306的网站时,浏览器会提示你正在访问不安全的网站。
这个问题的一种处理方式(也是12306网站目前在用的方式),就是把 SRCA
这个CA的根证书安装到系统中,操作系统就会认为由这个CA签发出的证书都是可信的,这样,访问12306的网站就不会再提示证书错误了。12306网站首页上有安装根证书的操作说明,大家可以自行查看。
向知名CA购买证书无疑是最好的选择,这里我们处于学习和个人使用的目的,将采用类似12306的方式,自己作为CA,为自己签发一份证书。
首先,我们自己把自己视为CA,生成一份根证书
# 生成CA私钥
openssl genrsa -out ca.key 2048
# 生成CA证书
openssl req -new -x509 -days 365 -key ca.key -out ca.crt -subj "/C=CN/CN=name"
参数说明:
CN=name
指证书的颁发者和使用者均为name(自己给自己签发根证书,颁发者和使用者当然都是自己,没毛病)。建议更改为你的姓名或公司名称。尝试了下中文,出现了乱码,所以建议使用英文。
第二个命令生成的证书(ca.crt文件),就是所谓的CA根证书,到时候要交给客户端安装,否则客户端默认会认为,由这个CA签发出来的证书是不安全的。
再用这份根证书,为我们的registry生成一份证书
# 生成私钥文件
openssl genrsa -out server.key 2048
# 生成证书签名请求文件
openssl req -new -key server.key -out server.csr -subj "/C=CN/CN=192.168.0.1"
# 生成证书
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -extfile <(printf "subjectAltName=IP:192.168.0.1") -out server.crt
参数说明:
CN=192.168.0.1
指证书的使用者为192.168.0.1,可更改为任意字符串。在私有registry没有域名的情况下,建议更改为私有registry的ip;有域名的情况下,建议更改为域名。此配置不影响证书的正常使用。subjectAltName=IP:192.168.0.1
指明证书使用者的ip必须为192.168.0.1。如果证书颁发给的是域名而非ip,则使用subjectAltName=DNS:你的域名
作为配置。当然也可以两者使用,比如subjectAltName=IP:192.168.0.1,DNS:你的域名
,这种配置应该是同时限制使用者的ip和域名的作用,没有深究。注意,此配置是X509 Version 3的新特性,配置后一份证书可供一个或多个ip(或域名)使用。此配置是必须有,并且是配置正确的,否则https请求会报错。
启动私有registry
命令如下:
docker run -d -p 5000:5000 --name docker-registry \
-v /home/docker-registry:/home/docker-registry \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/home/docker-registry/server.crt \
-e REGISTRY_HTTP_TLS_KEY=/home/docker-registry/server.key \
registry:2
参数说明如下:
REGISTRY_HTTP_TLS_CERTIFICATE
证书文件路径REGISTRY_HTTP_TLS_KEY
私钥路径
参考官方文档中的http章节:https://docs.docker.com/registry/configuration/#http
注意,文档中有说明,如果一个配置A是可选的,但它又存在必选的子配置B和C,你可以整个A及其子配置B、C都不配置,否则B和C都必须配置。原文如下:
In some instances a configuration option is optional but it contains child options marked as required. In these cases, you can omit the parent with all its children. However, if the parent is included, you must also include all the children marked required.
配置客户端,安装CA根证书
以下指令用于将证书安装至客户端:
# 备份
cp /etc/pki/tls/certs/ca-bundle.crt{,.backup}
# 导入CA根证书
cat ca.crt >> /etc/pki/tls/certs/ca-bundle.crt
如果使用的是window系统,直接双击证书,导入至受信任的根证书颁发机构下的证书目录下即可。运行certmgr.msc命令可打开证书管理器,删除本证书。
客户端和私有Registry交互
注意,如果之前在daemon.json文件中配置过insecure-registries,则必须去掉此配置并重启docker,否则客户端会使用http方式和registry交互,导致交互失败。
推送镜像至私有registry:
# 制作自己的busybox镜像
docker tag busybox 192.168.0.1:5000/busybox
# 将镜像推送至私有registry
docker push 192.168.0.1:5000/busybox
docker会使用https的方式和registry进行交互,将镜像推送至私有registry。
配置Registry,使用用户名密码的方式,验证客户端身份
私有registry支持通过使用htpasswd工具生成的用户名密码配置文件,来验证客户端身份。
不同linux发行版的中的htpasswd工具安装方式可能存在不同,这里以CentOS为例:
yum install -y httpd-tools
生成用户名密码配置文件
# 创建用户名密码配置文件.passwd,并新增用户user1,密码手工输入
htpasswd -cB .passwd user1
htpasswd工具的参考文档:https://httpd.apache.org/docs/2.4/programs/htpasswd.html
注意命令中的-B参数,配置后,htpasswd将以bcrypt加密方式加密密码,安全性更高。bcrypt也是docker指定的唯一一种加密方式,其他加密方式均不被支持,详见官方文档中的htpasswd章节:https://docs.docker.com/registry/configuration/#htpasswd。
这里再列几个命令用于维护用户:
# 新增用户user2:
htpasswd -B .passwd user2
# 删除用户user2:
htpasswd -D .passwd user2
注意:htpasswd工具并未提供修改密码功能,如需修改密码,可以先删除用户,再新增用户实现。
启动私有registry:
docker run -d -p 5000:5000 --name docker-registry \
-v /home/docker-registry:/home/docker-registry \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/home/docker-registry/server.crt \
-e REGISTRY_HTTP_TLS_KEY=/home/docker-registry/server.key \
-e REGISTRY_AUTH=htpasswd \
-e REGISTRY_AUTH_HTPASSWD_REALM=basic-realm \
-e REGISTRY_AUTH_HTPASSWD_PATH=/home/docker-registry/.passwd \
registry:2
文档中没有对REGISTRY_AUTH_HTPASSWD_REALM这个必选配置有比较详细的说明,自己百度了些资料,大概是用于和silly、token这两种身份验证方式配合使用,没做深究。因为我们这里只用到了htpasswd这种身份验证方式,所以就用文档里的默认配置了。哪位如果比较了解这部分内容,希望不吝赐教。
此后,我们就可以使用用户名密码登录私有registry了,没有登录的客户端将被拒绝访问:
# 登录私有registry
docker login 192.168.0.1:5000 -u user1 -p 你的密码
# 将镜像推送至私有registry
docker push 192.168.0.1:5000/busybox
注意:用户名密码的验证方式只建议在https的方式下使用,因为用户名密码是通过http请求头发送的,如果使用http的方式会很不安全,原文如下:
Only use the htpasswd authentication scheme with TLS configured, since basic authentication sends passwords as part of the HTTP header.
户名密码的验证方式还有个缺点,私有registry只在启动的时候读取htpasswd生成的用户名密码配置文件,所以任何对该配置文件的修改,必须重启registry才能生效,原文如下:
The htpasswd file is loaded once, at startup.
其他私有Registry搭建方式
网上也有很多资料介绍其他私有registry搭建方式,其中一种是在registry前端架设nginx服务器。这种方式也是很值得推荐的,很多公共的registry提供商应该也是扩展这种方式提供服务。
这种方式的其优点有:
- nginx会处理https相关内容。
- 可以使用nginx配置负载均衡,搭建registry集群,提高registry服务的可靠性。
当然缺点也有:
- 配置较繁琐,需要对nginx和registry有比较深入的了解,特别是在涉及到负载均衡和集群时。
- 如果涉及到负载均衡和集群,registry的运维工作量也不可小觑。