在生产环境使用harbor时,处于安全的需求harbor使用https提供服务。使用权威CA签发的TLS证书配置简单,但存在证书费用。在内网使用的harbor时使用的往往是自签发的非权威证书。使用自签发的非权威证书时涉及到harbor本身,docker甚至os层面的配置,对初次接触harbor的用户而言存在一定困难。
升级harbor为https协议时其实要考虑两个场景,harbor本身作为服务端提供镜像存取服务,或者作为client端从其它使用https协议的harbor中同步镜像。这两种场景有配置差异,本文从这两种应用场景都给予了讲解。
若考虑安全需求,harbor改用https协议,重点在于TLS证书是权威CA颁发,还是由私有CA签发。
由权威CA签发的商业证书通常不会引发使用问题,但使用经济成本高。而私有CA签发的证书虽无费用,但默认是无法通过操作系统、设备及各类应用的tls证书校验的,必须通过额外的配置,所有访问harbor的客户端必须将私有CA的公钥添加到自己的CA证书链中才能完成harbor证书的有效性校验,才能正常与harbor交互。
当客户端设备或系统数量较多时,最好是建立一个专用的私有CA服务,让操作系统和各类应用预先将私有CA的公钥整合到自身的CA证书链中,以减少配置成本。
harbor的应用场景主要分为两大类:
1、harbor作为服务端使用,为各类客户端提供镜像、chart的推拉或者API调用服务。harbor若从私有CA获得Tls证书,并以https协议提供服务,则所有访问harbor服务的客户端都必须将私有CA的公钥添加到自己的CA证书链中。
2、harbor作为客户端使用,主动访问或调用其它系统,常见于从其它registry同步数据,或者代理其它registry的项目。若harbor主动调用或访问的其它系统以https方式提供服务,且使用的Tls证书是由私有CA签发,则harbor需要把私有CA的公钥添加到自己的CA证书链中。
以最简单的通过docker向harbor推送docker镜像为例:
harbor使用http协议提供服务时,所有与其通讯的docker daemon
都必须在/etc/docker/daemon.json
中配置:
{
"insecure-registries": ["harbor_ip:harbor_port"]
}
配置完成后,重启docker daemon
即可向harbor推送镜像
若再改用https协议,且使用的Tls证书由私有CA签发,则需要在每个运行docker daemon
的服务器上进行的操作有:
1、修改/etc/docker/daemon.json
配置文件中,从"insecure-registries"
删除harbor_ip:harbor_port
记录
2、把harbor的公钥、私钥以及CA的公钥拷贝到/etc/docker/certs.d/<harbor域名或harbor_ip:harbor_port>
目录下
3、在某些版本操作系统上,还可能需要把CA的公钥更新到操作系统的CA证书链文件中
4、重启docker daemon
服务
以上还只是最常见的docker推送镜像场景,改用https协议后,配置负担明显增加。
以下从harbor分别作为服务端和客户端的使用场景,描述了使用私有CA签发的Tls证书可能会带来的问题及解决方法以供参考
harbor作为服务端使用
harbor作为服务端使用时,通常是为docker、helm提供registry服务,或者响应应用的harbor api调用。此时,docker、helm以及应用都属于client角色。
若harbor从http协议更换到https协议,且Tls证书是由私有CA签发,客户端不做任何默配置下会出现的问题:
1、原本能正常工作的主机上,使用docker进行镜像的pull和push会失败
2、原本能正常工作的主机上,使用helm的repo add、repo update、pull、push操作都会失败
3、原本能正常调用harbor api的应用会出现因Tls 不合法而调用失败的错误
4、原本能从该harbor同步数据的客户端会出现失败
要解决客户端访问harbor的问题,需要根据客户端应用类型,客户端运行环境来做针对性设置。
1、若客户端为docker daemon
, 参考【Harbor https配置】- 【Docker Tls】设置环节
2、若客户端为helm, helm使用的是操作系统的默认CA-bundle来校验服务端公钥,需要更新操作系统的/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
,更新方法:
1、获取给harbor签发证书的CA的公钥,通常是ca.crt文件
2、让操作系统信任自有CA签发的harbor证书
注意:这里需要把CA的公钥添加到操作系统中
在redhat衍生系统上:
```shell
cp ca.crt /etc/pki/ca-trust/source/anchors/ca.crt
update-ca-trust
```
在ubuntu衍生系统上:
```shell
cp ca.crt /usr/local/share/ca-certificates/ca.crt
update-ca-certificates
```
大部分c/c++、golang、python等应用都是使用操作系统的ca来校验证书,更新操作系统的ca-bundle文件即可解决
3、若客户端为java应用,java的jdk自带ca证书链,默认使用自己内置的ca而不是操作系统的ca来校验tls证书。
可以使用jdk内置的keytool
工具将私有CA的公钥导入到Jdk的ca证书链中,导入前先检测which keytool
,若找不到keytool
, 则需要把$JAVA_HOME/bin
目录加入到PATH
变量中
keytool -import -v -trustcacerts -alias harbor-private-ca -file </path/to/ca.crt> -storepass changeit -keystore "$JAVA_HOME/jre/lib/security/cacerts"
若添加过程出错,则需要先删除, 再重新导入。删除导入的证书指令:
keytool -delete -alias harbor-private-ca
导入完成后,java应用便可以校验由私有CA签发的所有Tls证书,只要harbor使用的Tls证书由同一个CA签发,java应用都能通过https协议与Harbor api通信
4、若客户端运行在容器环境下,大部分镜像中的工作负载是靠/etc/pki/tls/certs/ca-bundle.crt
文件中列出的CA来校验服务端公钥的合法性的,因此只需要把给harbor颁发证书的CA的公钥添加到镜像中/etc/pki/tls/certs/ca-bundle.crt
文件的末尾即可。可通过重新制作镜像,或者启动容器时挂载经过修改的ca-bundle.crt
文件到/etc/pki/tls/certs/ca-bundle.crt
文件
也有像java这种使用自己的cacert来校验证书合法性的,则需要将CA的公钥合并到应用信任的ca列表中。java应用可参考上述步骤3,然后重新制作镜像。
harbor作为客户端使用
harbor作为客户端使用时,通常是指harbor从其它harbor服务或registry同步数据,或者是代理其它harbor、registry上的项目。这种情况下harbor本身从http升级到https不会影响其作为客户端的行为,但若上游的harbor或docker registry从http变更到https,且是使用私有CA签发的Tls证书时,会引发的问题为:
1、harbor中原本能正常同步的任务会出现执行失败
2、harbor代理类型的项目将无法正常操作
解决上述问题的关键是让harbor中需要与上游harbor、registry服务进行通讯的镜像组件能校验上游harbor、registry的tls证书, 涉及的镜像:chartmuseum, core, jobservice, registry, trivy
,实现的方式是将给上游harbor、registry颁发证书的CA的公钥添加到harbor涉及的镜像中。
不同安装方式的harbor添加CA公钥的方式不一样
如果是使用离线安装包,以docker-compose启动的harbor, 在2.x系列可以按如下操作:
1、获取私有CA的公钥ca.crt文件
2、将CA的ca.crt文件拷贝到habor应用目录的./common/config/shared/trust-certificates
目录下
cp ca.crt /path/to/harobr/common/config/shared/trust-certificates/ca.crt
3、重新启动harbor
docker-compose down
docker-compose up -d
如果是使用1.x系列的harbor版本,自定义CA的功能尚处于技术验证阶段,想添加自定义CA证书会比较困难。有两种方式可以操作:
1、直接更换registry, core, chartmusem, clair
镜像集成的/etc/pki/tls/certs/ca-bundle.crt
文件
该方式需要在操作系统上先生成新ca-bundle.crt
文件,然后重新构建harbor涉及的镜像,将ca-bundle.crt
文件替换到镜像的/etc/pki/tls/certs/ca-bundle.crt
文件位置
2、手工修改docker-compose.yaml文件,让把签发harbor证书的私有CA的公钥ca.crt
文件挂载到harbor镜像中
1、获取私有CA的公钥ca.crt文件
2、在harbor app目录创建文件夹
mkdir -p ./common/config/shared/trust-certificates
3、拷贝ca.crt文件到./common/config/shared/trust-certificates目录
cp ca.crt /path/to/harobr/common/config/shared/trust-certificates/ca.crt
4、修改docker-compose.yaml中定义的registry、registryctl、chartmusem service, 在vlolumes配置项增加一个主机目录挂载配置。以registry service为例:
增加的配置项为:
- type: bind
source: ./common/config/shared/trust-certificates
target: /harbor_cust_cert
其它service的配置与上述registry service一致
5、然后docker-compose down 停止并删除现有harbor容器
6、执行docker-compse up启动新的harbor容器
registry、registryctl、chartmusem
容器在启动时会自动将/path/to/harobr/common/config/shared/trust-certificates/ca.crt
文件合并到容器内部的/etc/pki/tls/certs/ca-bundle.crt文件中
如果是harbor是使用helm chart安装在k8s中,添加自定义CA的操作与helm chart版本有关
若harbor chart版本为1.1.*版本,chart values.yaml文件没有提供自定义CA的入口,最好参考harbor升级文档先升级到1.3.11版本
若harbor chart版本为1.3.*版本,参考下列参考步骤:
1、获取CA的公钥ca.crt文件
2、在harbor所在的命名空间下创建一个ccse-harbor-ca-bundle
secret
kubectl creat secret generic ccse-harbor-ca-bundle --from-file=car.crt -n harbor
3、获取当前release使用的配置信息:
helm get all harbor -n harbor > all.yaml
4、获取当前harbor版本的chart文件,修改chart的values.yaml文件,保持跟all.yaml中的配置一致
5、去掉persistence.imageChartStorage.caBundleSecretName
的注释,设置其值为ccse-harbor-ca-bundle
6、执行helm upgrade harbor -n harbor -f values.yaml
升级harbor配置
若harbor chart版本为1.4及以上版本,则已经支持添加自定义CA公钥了,参考下列步骤:
1、获取CA的公钥ca.crt文件
2、在harbor所在的命名空间下创建一个ccse-harbor-ca-bundle
secret
kubectl creat secret generic ccse-harbor-ca-bundle --from-file=car.crt -n harbor
3、获取当前release使用的配置信息:
helm get all harbor -n harbor > all.yaml
4、获取当前harbor版本的chart文件,修改chart的values.yaml文件,保持跟all.yaml中的配置一致
5、去掉values.yaml文件中caBundleSecretName
的注释,设置其值为ccse-harbor-ca-bundle
6、执行helm upgrade harbor -n harbor -f values.yaml .
升级harbor配置
Harbor https配置
应该使用一个全局统一的CA来给所有Harbor实例颁发证书,以减轻信任私有CA的配置负担
生成CA证书
假定harbor使用的域名为harbor.example.com
1、生成CA的私钥
openssl genrsa -out ca.key 4096
2、生成CA的公钥
openssl req -x509 -new -nodes -sha512 -days 3650 \
-subj "/C=CN/ST=Guangdong/L=Guangzhou/O=ctyun/OU=CCSE/CN=example.com" \
-key ca.key \
-out ca.crt
-subj
参数代表与组织相关的信息:
-
/c
: 国家 -
/ST
: 省份 -
/L
:地区 -
/O
: 组织 -
/OU
: 组织单位 -
/CN
: Common Name,CA的Common Name可以使用持有机构的域名,或代表身份的字符串值
生成harbor证书
为了方便,可以生成泛域名证书,减少证书生成负担
1、生成harbor私钥
openssl genrsa -out harbor.ctyun.com.key 4096
2、生成CSR文件
openssl req -sha512 -new \
-subj "/C=CN/ST=Guangdong/L=Guangzhou/O=ctyun/OU=ccse/CN=harbor.ctyun.com" \
-key harbor.ctyun.com.key \
-out harbor.ctyun.com.csr
3、生成x509 v3 扩展文件
harbor有同过域名或ip访问的场景,在[alt_names]
中把域名和ip都写上去
如果是生成泛域名证书,则一定要在[alt_names]
中写入泛域名的记录,类似下列的*.ctyun.com
和*.*.ctyun.com
cat > v3.ext <<-EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1=harbor.ctyun.com
DNS.2=ctyun.com
DNS.3=*.ctyun.com //支持一级泛域名
DNS.4=*.*.ctyun.com //支持二级泛域名
IP.1=127.0.0.1
IP.2=<harbor_host_ip>
EOF
harbor_host_ip
是指访问harbor的ip地址,通常与harbor主机的ip地址保持一致
4、生成harbor证书
openssl x509 -req -sha512 -days 3650 \
-extfile v3.ext \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-in harbor.ctyun.com.csr \
-out harbor.ctyun.com.crt
Harbor Tls设置
harbor有两种安装方式:1、使用离线安装包安装在host主机上;2、使用helm chart安装在k8s集群中
不同的安装方式,TLS设置方式也不一样
若harbor采用离线安装包安装在host主机上,设置Tls的步骤如下:
1、修改harbor app根目录下的harbor.yaml
文件
hostname: harbor.ctyun.com
https:
# https port for harbor, default is 443
port: 443
# The path of cert and key files for nginx
certificate: /path/to/harbor.ctyun.com.key
private_key: /path/to/harbor.ctyun.com.cert
2、执行harbor 应用根目录下的prepare
脚本
sh prepare
3、停止并删除正在运行的harbor容器
docker-compose down -v
4、重新启动harbor
docker-compose up -d
若harbor采用helm安装在k8s中, 设置tls的步骤如下:
1、获取当前harbor release使用的chart文件
2、获取当前harbor release的配置信息
helm get all harbor -n harbor > all.yaml
3、确保chart的values.yaml配置信息与all.yaml保持一致,该过程无自动化工具,需手工完成配置迁移
4、修改values.yaml的配置值
expose:
tls:
enabled: true
secretName: "ccse-harbor-https-cert"
externalUrl: https://harbor.example.com //域名不变,访问协议改成https
5、获取上述步骤生成的harbor证书和ca公钥,对harbor证书重命名
cp /path/to/harbor.ctyun.com.key tls.key
cp /path/to/harbor.ctyun.com.crt tls.crt
cp /path/to/ca.crt ca.crt
6、在harbor命名空间下创建secret ccse-harbor-https-cert
kubectl create secret generic ccse-harbor-https-cert --from-file=tls.crt --from-file=tls.key --from-file=ca.crt -n harbor
6、执行helm upgrade指令
helm upgrade harbor -n harbor -f values.yaml .
Docker Tls设置
1、转换harbor证书格式, docker把.cert
结尾的证书当做registry的公钥, .crt
结尾的证书文件当做ca的公钥
openssl x509 -inform PEM -in harbor.ctyun.com.crt -out harbor.ctyun.com.cert
2、把harbor的公钥、私钥以及ca证书拷贝到/etc/docker/certs.d/harbor.ctyun.com
目录下
cp harbor.ctyun.com.cert /etc/docker/certs.d/harbor.ctyun.com/
cp harbor.ctyun.com.key /etc/docker/certs.d/harbor.ctyun.com/
cp ca.crt /etc/docker/certs.d/harbor.ctyun.com/
如果harbor的访问域名带了端口,则/etc/docker/certs.d/
下的目录名称要创建为harbor访问域名:端口
的样式
/etc/docker/certs.d/
的目录结构
/etc/docker/certs.d/
└── yourharbordomain.com:port
├── yourharbordomain.com.cert <-- Server certificate signed by CA
├── yourharbordomain.com.key <-- Server key signed by CA
└── ca.crt <-- Certificate authority that signed the registry certificate
3、 让操作系统信任自有CA签发的harbor证书,以便让通过操作系统ca-bunle.crt
文件来校验tls证书的应用(helm、curl之类的)能顺利完成私有CA签发的证书的校验
注意:这里需要把CA的公钥添加到操作系统中
在redhat衍生系统上:
cp ca.crt /etc/pki/ca-trust/source/anchors/ca.crt
update-ca-trust
在ubuntu衍生系统上:
cp ca.crt /usr/local/share/ca-certificates/ca.crt
update-ca-certificates
4、检查/etc/docker/daemon.json
,若insecure-registries
的键值包含harbor.ctyun.com
记录则必须删除,否则docker
daemon将会用http协议访问harbor
5、重新启动docker
systemctl restart docker