一、Docker Hub公共仓库
顾名思义,仓库是用来存储东西的地方,就像现实世界中一样,生产制造需要原材料库、半成品库、成品库。而在软件工程中,也是一样。例如存储代码的代码库,依赖库。那么以容器为中心的软件工程中,容器镜像也被称为制品,它必须经历一个构建过程而产生。而镜像仓库就是用来存储和管理容器镜像的。
在我们应用容器的过程中,势必会产生很多个容器镜像。一个应用拥有一个镜像,而一个镜像可能又有多个版本,同时对应多个版本的应用。这些镜像应该被统一存储和管理。如果任由其进行分发复制,这会带来一定的混乱。
一个良好的管理模式是,镜像构建由独立的节点完成,而镜像存储交由镜像仓库来完成,将镜像统一存储在一个地方并通过镜像仓库服务来提供对外的访问,而负责运行容器的节点则统一从镜像仓库拉取所需的镜像来完成容器的部署。
在解决了基本的存储与分发问题后,镜像仓库这一专门用于管理镜像的服务,应该可以做更多的工作,例如:镜像漏洞扫描、日志审计、安全的访问控制、垃圾镜像回收等。于是后面就出现了许多开源的镜像仓库项目,来着重解决镜像管理方面问题。
Docker Hub官网:https://hub.docker.com/
# docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: gaoyufu
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
# docker push centos:test
二、Registry私有仓库
registry v2版本是由Docker公司开发的,该产品遵循OCI标准。实际上在OCI规范中包含两部分,分别是运行时规范与镜像规范,而registry v2所拥有的一些概念,经过提炼而并入了镜像规范中。
registry v2版本大部分功能均以API接口方式提供,方便二次开发与扩展。适用于开发与小型部署。项目提供一个registry镜像,用于创建容器,然后提供registry服务。我们可以通过该镜像快速部署一个registry服务。
1、部署registry私有仓库服务
# docker pull registry:2
2: Pulling from library/registry
Digest: sha256:8be26f81ffea54106bae012c6f349df70f4d5e7e2ec01b143c46e2c03b9e551d
Status: Downloaded newer image for registry:2
1、运行私有仓库服务
# docker run -itd --name registry --restart=always -p 5000:5000 -v /data/registry:/var/lib/registry registry:2
b94d47e57026c239509e3dea3b4b107332c450f0bc775095660f2154275f94e1
参数 | 说明 |
---|---|
-p | 端口映射。宿主机端口:容器暴露的端口 |
-p 80 | 如果-p选项后边只写了一个端口,那么这个端口是指容器暴露的端口号随机映射到宿主机(32768开始...) |
-P | 后边没有接任何的端口参数。那么它会把容器暴露的端口,全部随机的映射到宿主机 |
-v | 挂载目录。 宿主机的目录:容器内的目录 |
如果我们需要现在就推送一个镜像(nginx:1.19.6),那么我们需要对它打标签,加入我们的registry服务地址,如下所示。
# docker tag nginx:1.19.6 192.168.1.10:5000/nginx:1.19.6
2、编辑docker配置文件
当我们使用docker pull在拉取镜像时,默认会访问镜像服务地址的443端口,也就是说docker客户端默认是使用https协议与镜像仓库服务通信的。如果我们部署的私有仓库,比如使用registry v2搭建的镜像服务,默认没有使用https协议,并且端口也不是默认的80端口,那么我们就需要显示指明,并且配置docker守护进程使其接收不安全的http协议。
其docker客户端守护进程接受https协议,需要更改配置文件(/etc/docker/daemon.json),在其中加入insecure-registries配置,如下所示。
{
"insecure-registries" : ["192.168.1.10:5000"]
}
或者更改/usr/lib/systemd/system/docker.service配置
# vim /usr/lib/systemd/system/docker.service
#ExecStart=/usr/bin/dockerd -H unix:// //修改成为以下内容
ExecStart=/usr/bin/dockerd --insecure-registry 192.168.1.10:5000
# systemctl daemon-reload
# systemctl restart docker
这里假设您镜像仓库地址为192.168.1.10,而端口为5000。那么您需要做以上配置更改,然后重启docker守护进程使配置生效。
3、上传私有仓库
# docker push 192.168.1.10:5000/web
The push refers to repository [192.168.1.10:5000/web]
latest: digest: sha256:caf5eb847cc83a90e93d6490e1e0b513c0af78e269272d993a56d90830ad4c0c size: 2409
# curl -XGET http://192.168.1.10:5000/v2/_catalog # 查看上传的镜像
{"repositories":["web"]}
# curl -XGET http://192.168.1.10:5000//v2/busybox/tags/list
2、配置https
既然使用https,那么我们需要生成证书,本文讲解的是使用openssl自签名证书,当然也可以使用诸如 Let’s Encrypt 等生成证书,当然如果你在公有云中部署,那么也可以从云厂商配套提供的SSL证书服务获取。
我们需要把docker配置文件中的接受http连接的配置去除,然后重启docker守护进程使其生效。
接下来,创建一个工作目录,之后的工作都在此目录进行
mkdir ~/ssl && cd ~/ssl
1、生成所需证书
使用openssl生成CA私钥
openssl genrsa -out ca.key 4096
生成CA证书
这里假设宿主机,域名为myregistry.local,那么命令如下。在下面的命令中调整-subj选项中的值以反映您的组织。如果使用FQDN连接registry主机,则必须将其指定为通用名称(CN)属性(注意:如果您在实验中请将CN值替换为实际的域名或IP地址)
openssl req -x509 -new -nodes -sha512 -days 3650 \
-subj "/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=registry.local" \
-key ca.key \
-out ca.crt
对--subj设置参数含义解释如下
字段 | 字段含义 | 示例 |
---|---|---|
C | Country国家 | CN |
ST | State or Province | 省 |
L | Location or City 城市 | Beijing |
O | Organization 组织或企业 | example |
OU | Organization Unit 部门 | Personal |
CN | Common Name 域名或IP | yourdomain.com |
这里需要说明的是CN应该填写您的主机名或能够解析为IP的DNS域名,或者是实际的IP地址,最好不要使用localhost、127.0.0.1这类信息。
那么由于上述配置命令中使用的主机名称,那么就必须保证该名称可被解析为具体的IP地址访问,您可以使用自己的DNS服务来提供解析,那么在这里采用另外一种方式,使用主机的hosts来进行解析,需要编辑/etc/hosts文件添加如下内容。
192.168.1.1 myregistry.local
生成服务器证书
证书包含一个.crt文件和一个.key文件,首先生成私钥。
openssl genrsa -out myregistry.local.key 4096
生成证书签名请求(CSR)
注意:如果您在实验过程中,请将以下命令中CN值替换为实际域名或IP地址。
openssl req -sha512 -new \
-subj "/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=registry.local" \
-key registry.local.key \
-out registry.local.csr
生成一个x509 v3扩展文件
无论您使用FQDN还是IP地址连接到Harbor主机,都必须创建此文件,以便可以为您的registry主机生成符合主题备用名称(SAN)和x509 v3的证书扩展要求。替换DNS条目以反映您的域。
cat > v3.ext <<-EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1=registry.local
DNS.2=registry
DNS.3=registry
EOF
使用该v3.ext文件为registry主机生成证书。
将CRS和CRT文件名中的替换为registry主机名
openssl x509 -req -sha512 -days 3650 \
-extfile v3.ext \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-in registry.local.csr \
-out registry.local.crt
由于Docker使用的证书是cert格式,因此需要转换crt至cert供docker客户端使用,转换命令如下
openssl x509 -inform PEM -in registry.local.crt -out registry.local.cert
2、使用证书
由于自签名证书是不受信任的,因此我们需要将证书与私钥复制到docker证书目录中,默认路径是/etc/docker/certs.d/<domain.com>。例如我们的域为myregistry.local,那么路径为/etc/docker/certs.d/myregistry.local。
新建目录如下
mkdir -p /etc/docker/certs.d/myregistry.local
拷贝文件至docker证书存储默认路径
cp registry.local.cert registry.local.key ca.crt /etc/docker/certs.d/registry.local
然后重启docker守护进程
systemctl restart docker
新建certs目录专门存储registry证书,该目录将以数据卷挂载至registry内部。
mkdir ~/certs
cp registry.local.crt registry.local.key ~/certs && cd ~/certs
重新启动registry,将其定向为使用TLS证书。此命令将certs/目录绑定安装到容器中的/certs/,并设置环境变量,该变量告诉容器在何处找到myregistry.local.crt与myregistry.local.key文件。并设置registry在端口443上运行。
docker run -d --restart=always --name registry \
-v "$(pwd)":/certs \
-e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/myregistry.local.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/myregistry.local.key \
-p 443:443 \
registry:2
3、用户访问限制
如果需要基于用户访问限制。实现访问限制的最简单方法是通过基本身份验证(这与其他Web服务器的基本身份验证机制非常相似)。可以使用本机基本身份验证htpasswd来存储机密,这只是一个简单的方案。
创建一个密码文件,为用户提供一个条目testuser,密码为 testpassword,可以使用httpd镜像来完成这个操作。
mkdir auth
sudo docker run \
--entrypoint htpasswd \
httpd:2 -Bbn testuser testpassword > auth/htpasswd
然后重新创建registry容器,并加入auth参数
docker stop registry && sudo docker rm registry
docker run -d --restart=always --name registry \
-v "$(pwd)":/certs \
-v "$(pwd)":/auth \
-e "REGISTRY_AUTH=htpasswd" \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
-e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/myregistry.local.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/myregistry.local.key \
-p 443:443 \
registry:2
此时在推送镜像前,需要先登录registry,可以尝试登录验证
docker login myregistry.local
输入用户名和密码。
如果登录成功,那么表示认证是正常的,那么接下来您就可以向registry中推送镜像了。
3、常用API操作
需要说的是,如果访问开启用户认证的registry,那么需要生成认证信息,这通常由用户+密码组成,并且由base64加密。生成认证信息命令
# echo -n "testuser:testpassword" |base64
dGVzdHVzZXI6dGVzdHBhc3N3b3Jk
1、查询镜像是否存在以及标签列表
方法:GET /v2/name/tags/list
其中name为镜像名
这里使用curl来完成
如果是访问带用户认证的registry,那么需要在curl设置请求头,加入认证信息
curl -H "Authorization: Basic dGVzdHVzZXI6dGVzdHBhc3N3b3Jk" -X GET https://myregistry.local/v2/nginx/tags/list
而对于没有用户认证,但开启了https的registry,在使用curl访问时,需要指定使用的证书,命令如下。
# curl --cacert ca.crt -X GET https://myregistry.local/v2/nginx/tags/list
{"name":"nginx","tags":["1.19.6"]}
2、获取镜像信息
方法:GET /v2/name/manifests/reference
其中name为镜像名,reference可以是"tag"或"digest"
curl --cacert ca.crt -X GET https://myregistry.local/v2/nginx/manifests/1.19.6
3、删除镜像
方法:DELETE /v2/name/manifests/reference
其中name为镜像名,reference只能是"digest"
查看本地镜像的digest方法,命令如下
docker images --digests
三、Harbor私有仓库
1、部署
1、配置要求
硬件
资源 | 最低限度 | 推荐 |
---|---|---|
CPU | 2个CPU | 4个CPU |
内存 | 4GB | 8 GB |
存储 | 40 GB | 160 GB |
软件
软件 | 版本 | 描述 |
---|---|---|
docker | 17.06.0-ce +版或更高版本 | 有关安装说明,请参阅Docker Engine文档 |
docker-compose | 版本1.18.0或v2 | 用户编排部署harbor各组件 |
openssl | 首选最新版本 | 用于生成Harbor的证书和密钥 |
网络端口
端口 | 协议 | 描述 |
---|---|---|
443 | HTTPS | Harbor门户和核心API在此端口上接受HTTPS请求。您可以在配置文件中更改此端口。 |
4443 | HTTPS | 与Harbor的Docker内容信任服务的连接。仅在启用公证人的情况下才需要。您可以在配置文件中更改此端口。 |
80 | HTTP | Harbor门户和核心API在此端口上接受HTTP请求。您可以在配置文件中更改此端口。 |
2、下载安装介质
如果您没有安装docker-compose,那么您应该首先下载并安装至操作系统中,通过这里的下载地址:https://github.com/docker/compose/releases
在本文中使用的docker-compose版本为1.29.2,操作系统为centos7.9。
你可以先下载然后传入操作系统中,也可以在宿主机中直接使用wget命令下载并安装docker-compose,命令如下
# curl -L "https://mirror.ghproxy.com/https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# chmod +x /usr/local/bin/docker-compose
# docker-compose -v
docker-compose version 1.29.2, build 5becea4c
接下来下载harbor安装介质,托管在GitHub上的harbor项目提供了离线与在线两种安装包,那么这里以离线安装包为例。
下载地址:https://github.com/goharbor/harbor/releases
# 仅配置文件
# wget https://github.com/goharbor/harbor/releases/download/v2.10.1/harbor-online-installer-v2.10.1.tgz
# 含镜像包
# wget https://mirror.ghproxy.com/https://github.com/goharbor/harbor/releases/download/v2.10.1/harbor-offline-installer-v2.10.1.tgz
# tar zxf harbor-offline-installer-v2.10.1.tgz -C /usr/local/
# cd /usr/local/harbor/
# docker load -i harbor.v2.10.1.tar.gz # 导入harbor镜像
3、更改harbor配置
在harbor安装包解压后的harbor目录中,包含了一个配置模板harbor.tmpl,该模板文件预置了一些默认配置,因此我们将其拷贝一份命名为harbor.yml,然后在此基础之上修改。
# cd /usr/local/harbor/
# cp harbor.yml.tmpl harbor.yml
# vim harbor.yml
hostname: registry.local #harbor服务器主机IP或域名
http:
# port for http, default is 80. If https enabled, this port will redirect to https port
port: 80
# https related config
https:
# https port for harbor, default is 443
port: 443
# The path of cert and key files for nginx
certificate: /data/certs/registry.local.crt
private_key: /data/certs/registry.local.key
在certificate与private_key中填写正确的服务器证书及私钥文件路径,这里是宿主机中持久化目录中的路径,该路径最终会被挂载至harbor容器中,如果不使用https证书请将整块注释。
下面是访问harbor默认的管理用户初始密码,默认的管理用户为admin,database是用于设置harbor使用的postgreSQL数据库的,data_volume是设置用于挂载至harbor容器内部持久化harbor数据的,挂载目录。
harbor_admin_password: Harbor12345
# Harbor DB configuration
database:
password: root123
max_idle_conns: 50
max_open_conns: 1000
# The default data volume
data_volume: /data
4、执行安装脚本
在harbor默认安装,是不包含trivy、notary、chartmuseum这些服务支持的。但安装脚本支持这些服务可以与harbor一起安装并完成配置。
如果需要harbor支持trivy,那么可以在运行脚本时使用--with参数。例如支持trivy,命令如下
./install.sh --with-trivy
请注意:在2.10.1版本中,notary和chartmuseum服务已被弃用
当完成安装后,请使用以下命令检查harbor各组件运行是否正常
# docker-compose ps
NAME COMMAND SERVICE STATUS PORTS
harbor-core "/harbor/entrypoint.…" core running (healthy)
harbor-db "/docker-entrypoint.…" postgresql running (healthy)
harbor-jobservice "/harbor/entrypoint.…" jobservice running (healthy)
harbor-log "/bin/sh -c /usr/loc…" log running (healthy) 127.0.0.1:1514->10514/tcp
harbor-portal "nginx -g 'daemon of…" portal running (healthy)
nginx "nginx -g 'daemon of…" proxy running (healthy) 0.0.0.0:80->8080/tcp, :::80->8080/tcp, 0.0.0.0:443->8443/tcp, :::443->8443/tcp
redis "redis-server /etc/r…" redis running (healthy)
registry "/home/harbor/entryp…" registry running (healthy)
registryctl "/home/harbor/start.…" registryctl running (healthy)
trivy-adapter "/home/scanner/entry…" trivy-adapter running (healthy)
可以看到所有组件的容器运行状态为UP并且healthy,那表示运行正常。
docker-compose这个命令可以管理harbor服务,不过需要注意的是想使用以下命令,必须是和 docker-compose.yml 这个文件在同一个目录下
# docker-compose start | stop | restart # 启动|停止|重启
5、登录harbor
可以使用docker login登录harbor验证
docker login registry.local
输入用户名admin,密码Harbor12345
验证没有问题后,可以使用浏览器访问https://registry.local,或https://<YOUR_HOST_IP>
同样输入用户admin,密码Harbor12345,点击登录即可。
6、推送镜像
推送镜像前首先需要在harbor中创建指定的项目,来存储镜像。项目可以是公有各私有的,公有可以允许任何可访问者pull,而私有的只允许该项目成员pull镜像。
创建项目,登录harbor,选择“项目” -> “新建项目”
在此页面填写项目名称,例如test,访问级别,不勾选公开则为私有,harbor是支持为项目进行配置额的,存储容量就是其中一个指标,-1表示无限制。
而镜像代理,代理缓存允许您使用Harbor代理和缓存来自目标公共或私有仓库的镜像。当对代理缓存项目的提取请求到来时,如果未缓存该映像,Harbor从目标仓库中提取该映像,并提供该提取命令,就好像它是来自代理缓存项目的本地映像一样
当建立好项目后,那么镜像访问路径应该有如下三部分组成。
<domain name>/<project name>/<image name>:<tag>
按照以上格式,如果需要将镜像推送至harbor仓库,那么对本地镜像重新打tag,使其符合以上格式,例如本地的一个nginx:1.19.6镜像,那么生成tag如下
docker tag nginx:1.19.6 registry.local/test/web:v1
然后将其推送至harbor
docker push registry.local/test/web:v1
当完成推送后,可在harbor具体的项目中查看到该镜像。
同样的如果需要从harbor拉取镜像至本地,也如同推送时书写镜像路径,命令如下
docker pull registry.local/test/web:v1
7、配置https证书
在配置之前,我们首先需要生成支持harbor提供https访问的ssl密钥及证书。
您可以使用由受信任的第三方CA签名的证书,也可以使用自签名证书。本文将介绍如何使用 OpenSSL创建CA,以及如何使用CA对服务器证书和客户端证书进行签名。您也可以使用其他CA提供程序,例如 Let's Encrypt、阿里云免费证书等。
为了统一文件的管理,这里在解压后的harbor文件中创建证书,首先创建一个工作目录,之后的证书生成工作都在此进行。
cd ~/harbor && mkdir certs && cd certs
使用openssl生成CA私钥(注意一定要进到证书目录)
openssl genrsa -out ca.key 4096
生成CA证书
在下面的命令中调整-subj选项中的值以反映您的组织。如果使用FQDN连接Harbor主机,则必须将其指定为通用名称(CN)属性。
openssl req -x509 -new -nodes -sha512 -days 3650 \
-subj "/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=yourdomain.com" \
-key ca.key \
-out ca.crt
对--subj设置参数含义解释如下
字段 | 字段含义 | 示例 |
---|---|---|
C | Country国家 | CN |
ST | State or Province | 省 |
L | Location or City 城市 | Beijing |
O | Organization 组织或企业 | example |
OU | Organization Unit 部门 | Personal |
CN | Common Name 域名或IP | yourdomain.com |
这里需要说明的是CN应该填写您的主机名或能够解析为IP的DNS域名,或者是实际的IP地址,最好不要使用localhost、127.0.0.1这类信息。
那么这里假设宿主机的主机名称为registry.local,那么IP地址为192.168.1.10,那以应该修改以上命令如下:
openssl req -x509 -new -nodes -sha512 -days 3650 \
-subj "/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=registry.local" \
-key ca.key \
-out ca.crt
那么由于上述配置命令中使用的主机名称,那么就必须保证该名称可被解析为具体的IP地址访问,您可以使用自己的DNS服务来提供解析,那么在这里采用另外一种方式,使用主机的hosts来进行解析,编辑/etc/hosts文件添加如下内容即可。
192.168.1.10 registry.local
生成服务器证书
证书通常包含一个.crt文件和一个.key文件。
生成私钥
openssl genrsa -out registry.local.key 4096
生成证书签名请求(CSR)
调整-subj选项中的值以反映您的组织。如果使用FQDN连接Harbor主机,则必须将其指定为通用名称(CN)属性,并在密钥和CSR文件名中使用它。
openssl req -sha512 -new \
-subj "/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=registry.local" \
-key registry.local.key \
-out registry.local.csr
生成一个x509 v3扩展文件
无论您使用FQDN还是IP地址连接到Harbor主机,都必须创建此文件,以便可以为您的Harbor主机生成符合主题备用名称(SAN)和x509 v3的证书扩展要求。替换DNS条目以反映您的域。
cat > v3.ext <<-EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1=registry.local
DNS.2=registry
DNS.3=registry
EOF
使用该v3.ext文件为您的Harbor主机生成证书。将CRS和CRT文件名中的替换为Harbor主机名
openssl x509 -req -sha512 -days 3650 \
-extfile v3.ext \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-in registry.local.csr \
-out registry.local.crt
8、部署证书
生成后ca.crt,registry.local.crt和registry.local.key文件,必须将它们提供给Docker和harbor,和重新配置港使用它们。
由于https通信是建立在Docker客户端与harbor服务器之间,那么因此我们同样也要将证书文件提供给docker,甚至在操作系统层面也需要提供这类凭证。
harbor采用容器部署方式,使用数据卷来持久化数据,其默认会挂载宿主机文件系统中/data路径至容器内部,证书文件同样也是如此,那么为了让容器内的harbor应用进程方便加载证书文件,我们在默认路径下创建一个目录用来存储证书文件。
mkdir -p /data/certs
然后证书文件挂载至此
cp registry.local.{crt,key} /data/certs/
由于Docker使用的证书是cert格式,因此需要转换crt至cert供docker客户端使用,转换命令如下
openssl x509 -inform PEM -in registry.local.crt -out registry.local.cert
将服务器证书,密钥和CA文件复制到Harbor主机上的Docker证书文件夹中。如果路径不存在您必须首先创建适当的文件夹。
mkdir -p /etc/docker/certs.d/registry.local
cp registry.local.{cert,key} ca.crt /etc/docker/certs.d/registry.local/
然后重启docker进程
systemctl restart docker
注意:如果将用于代理harbor服务的nginx端口443映射到了其他端口,请创建文件夹格式,如/etc/docker/certs.d/yourdomain.com:port或/etc/docker/certs.d/harbor_IP:port。
过期替换证书需要重新执行prepare, 否则只重启替换证书不生效,客户端仍然报证书过期。
./prepare
docker-compose up -d
2、镜像扫描
这里的镜像扫描是指扫描镜像中所隐藏的危及容器安全的漏洞信息。harbor的镜像扫描是基于trivy来进行的实际是是调用trivy对harbor镜像仓库内的镜像进行安全扫描。
trivy是一个专门用于检查容器镜像中是否包含漏信息的扫描器,同杀毒引擎一样,该扫描器的对漏洞的鉴别依赖于其数据库内数据的丰富程度而决定的。因此trivy在每次扫描时都必须读取该数据库,并且要保证数据库的及时更新,只有拥有较新数据的数据库才能对漏洞的识别做到准确无误。
trivy支持独立安装,但在harbor项目中以容器方式运行,trivy运行在一个名为trivy-adapter的容器中,其数据库使用数据卷来持久化存储。但刚安装完成的harbor,其trivy容器内是没有任何数据的,需要联网下载,但访问境外网络有时会非常缓慢。值得一提的是有一个trivydb项目,在维护一个可以离线使用的trivy数据库,需要注意的是即使是离线使用为了安全性也应该及时的将最新的trivydb更新到环境中。
在GitHub上可以找到trivydb项目,该项目提供一个离线包,trivy-offline.tar.gz,可以下载回来解压得到数据库文件,项目地址如下:
https://github.com/aquasecurity/trivy-db
因此我们可以将该离线文件下载回来,放入至trivy-adapter容器的数据卷中,这样来节省更新数据库所需要的时间。其实trivy是支持在扫描镜像时,跳过下载更新的,使用参数“--skip-update”,而当不显示指定该参数时,默认是进行更新检查并下载更新的。而在harbor安装时的配置文件(harbor.yml)中是支持设置trivy跳过更新检查参数。
或者尝试执行以下命令
# docker exec -it trivy-adapter /bin/bash
trivy image --download-db-only --debug
或者
cd /home/scanner/.cache/trivy
trivy --cache-dir ./db image --download-db-only
# cd /data/trivy-adapter/trivy/db
# ls
db fanal metadata.json trivy.db
# cp fanal/fanal.db ../fanal/
# cp db/* .
登录harbor,选择对应的项目,例如这里是demo,然后点击需要扫描的容器镜像,选中该镜像对应的版本,然后点击“扫描”即可。
扫描完成后,可查看扫描结果,如下图所示,证明该镜像是安全的。
如果出现危险漏洞,也同样显示信息摘要,例如下图中的nginx镜像
点击进入可查看具体的漏洞描述信息。
如果由于因为修复漏洞所带来的风险因素考虑,有些时候我们可能并不想立刻修复某些漏洞,那么可以将该漏洞加入特赦名单,使trivy忽略该漏洞。你需要记住漏洞对应的缺陷码如“CVE-2018-12886”类似这种,然后将其加入特赦名单。
项目 -> 配置管理 -> CVE特赦名单
如上图所示,这里有“系统”与“项目”之分,系统是指全局配置,在系统的全局配置中也同样设有特赦名单选项。可以根据需要进行配置。当然也可以勾选“自动扫描镜像”在镜像上传后自动启动扫描器。除此之外还可以设置,阻止含有潜在漏洞的镜像上传,并且可以对漏洞级别进行配置。
3、分发与复制
镜像仓库存在的意义,就是为了方便镜像制品的分发与复制,因此该部分功能也算是一个仓库服务的核心。Harbor是支持多种分发方式的
仓库至仓库:该模式允许从当前Harbor仓库传输至另一个仓库,而对端仓库可以是Harbor也可以是其他的仓库服务,但有一个条件就是必须符合OCI规范。例如有(docker hub、国内的云厂商镜像服务、GitLab、docker distrubtion、helm chart)。具体可参考Harbor的官方文档。
可以为镜像传输设置策略,来过滤哪些镜像需要被发送至对端仓库,策略是可以根据镜像库、tag和标签,而何时传输也是可以根据时间、定时或手动触发等多种方式进行配置。
传输是按照“初次全量,之后增量”的原则,也就是说第一次传输时,是一个全量的复制,而在这之后则是增量的传输。避免每次都使用一个完整的复本进行覆盖操作,这样就提高的传输效率。
该模式一个较为典型的使用场景是,不同环境之间进行镜像传输,例如你有一个本地环境与一个线上环境,那么在本地完成调试后,就可以利用该模式将镜像同步至线上环境的Harbor仓库中。
如果需要配置仓库间复制,需要先harbor的仓库管理中新建仓库,如下所示,你也可以点击“测试连接”来检查你的配置是否正确
然后需要在harbor的复制管理中添加复制规则,通过规匹配哪些资源需要同步复制,如下所示。
主从模式:该模式也可以说是上一中模式的复制版,不同的是Harbor至Harbor之间的传输,也是一对多的传输。可以通过一个主库将镜像制品分发至多个从库中,而多个从库则可以分别分担镜像拉取的压力,提高整体的负载能力。当然也可以使多个从库分散至不同的网络位置,让各网络区域的客户端进行就近拉取,这相当于是中以+边缘的一种组合。
代理模式:该模式是将harbor充当一个代理者,相当于镜像中间人,它不是镜像最终的供给源,而是从镜像最终的供给源中获取镜像然后转交至客户端,并且在本地缓存以便再下次拉取时直接提供给客户端。当你在harbor中创建一个项目并将设定为代理模式时,那么您首先需要指定一个镜像源,代理项目会从该处下载镜像并转交给拉取者,同时完成本地缓存。
配置harbor代理,是在项目中进行配置,在建立项目时选中“镜像代理”选项,那么此时也需要一个代理目标,代理目标来自在仓库管理中的配置,你需要先在仓库管理中配置一个你需要代理的仓库,然后在创建项目中添加为代理。如下所示
P2P预热:使用p2p网络来进行镜像加速,我们应该都使用过云厂商的提供的镜像加速服务,其实这是一个道理。使用docker pull 拉取镜像时,如果配置了镜像加速,那么这里的镜像加速地址其实是一个p2p网络入口,那么在拉取镜像前会首先查询p2p网络中是否有该镜像的缓存,如果没有再去实际的仓库中进行拉取。
harbor并不包含一个p2p网络,而利用p2p网络。harbor可以提前通知p2p网络需要缓存的镜像,然后由p2p网络将镜像缓存至网络中,而等到docker pull的时候,这时由于p2p网络中已经含有,因此可以直接从p2p网络中获取。分担了harbor压力同时提高了分发效率,不同的客户端可以配置不同p2p入口,来提升整体效率。
值得一提的是在CNCF孵化的众多项目中,有和p2p网络相关的应用项目,例如dragonfly与kraken。dragonfly项目是由阿里团队研发后贡献给CNCF。因此我们也可以使用dragonfly来构建自己的p2p网络,然后来对接harbor做p2p预热。
在harbor中配置p2p预热,需要先在“分发复制”中配置p2p实例,如下所示
前提是你必须先部署p2p网络,可以使用dragonfly来构建。
配置完实例后,就可以在项目的p2p预热中添加预热规则,通过规则匹配来选定需要预热的镜像,如下所示。
4、安全签名
Harbor对镜像的安全签名支持,是在体现在安全策略上的,其本身并不具备签名管理能力,而是使用api与第三方进行集成。例如Notation和Cosign。
notary会以容器方式运行,面通过harbor的proxy(nginx)来对外暴露服务入口,而notary与harbor之间的是供给关系,harbor需要通过notary来验证镜像是否签名,并根据这个来进行安全性策略的实施,例如可以配置拒绝未签名的镜像进行pull或push。
镜像的签名并不在harbor操作,而由notary cli来完成,实际上在开启安全签名后,将镜像存储到仓库的步骤分为两种:一是先使用notary cli来对镜像签名,然后使用docker cli将镜像push至仓库。
单从签名来看,其主要目的有两个:确定属主、防止篡改。就像我们给自己的物品签上自己的名字,来证明该物品属于我们。那镜像也需要被证明,该镜像出自哪里,除此之外更重要的是防止篡改。镜像做为一个可被分发的制品,在分发过程中可能被经过多人之手,那么如何保证在这个过程中不被恶意的篡改?
在安全签名中利用了类似https通信中加密原理,使用hash算法对镜像进行hash运算,生成摘要,然后再使用非对称加密进行加密。hash算法可以接收一个不定长的输入,然后给出一个定长输出,例如md5类似,一但文件被修改,再次运行得到的字符串将发生变更。而非对称加密,是公/私 钥模式(私钥加密,公钥解密)。镜像签名的应用过程如下:
1、镜像的拥有者在上传镜像前使用hash算法对镜像进行hash生成摘要,然后使用私钥对这份摘要加密
2、然后将镜像上传至镜像仓库,将公钥与包含摘要的签名一同上传到认证服务器(notary)保存。
3、而当使用者需要该镜像时,会先将镜像拉取然后对镜像再次hash运算,然后从认证服务器(notary)获得该镜像的签名与公钥,然后使用公钥解密签名得到原始摘要,最后对比两份摘要来确定该镜像是否被修改。
你可以使用notary cli来对镜像的tag进行签名,要想在环境中启动镜像签名,配置步骤如下:
首先在harbor对应用的项目中,启用“内容信任”项目,例如这里使用默认的library项目
然后在docker的宿主机中安装notary客户端,可以从以下地址下载最新版本的客户端工具。
notary下载:https://github.com/theupdateframework/notary/releases
然后将其拷贝至宿主机中,然后配置安装,假设上传路径为/home/ops
cd /home/ops/ && chmod +x notary-Linux-amd64 && mv notary-Linux-amd64 /usr/local/bin/notary
注意:对notary的操作建议使用root用户,在root用户环境下,或者将当前用户加入root组,这样做的目的是避免使用sudo命令,sudo命令会因为环境、权限问题导致一些配置失效问题产生。
设置环境变量开启docker签名功能
cat > /etc/profile.d/notary << EOF
export DOCKER_CONTENT_TRUST=1
export DOCKER_CONTENT_TRUST_SERVER=https://registry-1.local:4443
export NOTARY_SNAPSHOT_PASSPHRASE=Harbor12345
export NOTARY_ROOT_PASSPHRASE=Harbor12345
export NOTARY_TARGETS_PASSPHRASE=Harbor12345
EOF
说明:DOCKER_CONTENT_TRUST变量是启用签名,DOCKER_CONTENT_TRUST_SERVER变量设置notary服务的地址,在harbor启用notary后默认地址为harbor主机端口为4443,NOTARY_ROOT_PASSPHRASE是根密钥密码,在使用签名功能初始化一个信任集合时初次需要配置根密钥,NOTARY_SNAPSHOT_PASSPHRASE变量是快照密钥,NOTARY_TARGETS_PASSPHRASE是目标密钥,当使用notary cli 来操作签名时,会提示输入快照密码与目标密钥,使用变量存储密码值可避免重复输入。
在当前用户下创建tls目录,然后将harbor的ca证书复制在此目录下,因为notary服务器同样需要验证。
mkdir -p ~/.docker/tls/registry-1.local:4443
cp /etc/docker/certs.d/registry-1.local/ca.crt ~/.docker/tls/registry-1.local:4443/
配置完成后可以使用notary来为镜像tag生成签名,这里需要说明的是签名的作用目标为镜像的tag,而多的tag同属于一个镜像,也就是一个信任集合。例如有镜像registry-1.local/library/nginx:1.19.6 和registry-1.local/library/nginx:latest,那么1.19.6 与 latest同属于registry-1.local/library/nginx镜像,那这个也称之为一个信任集合。
在对镜像的tag添加签名前需要先初始化一个集合
notary -s https://registry-1.local:4443 -d ~/.docker/trust --tlscacert /etc/docker/certs.d/registry-1.local/ca.crt init registry-1.local/library/nginx
说明: -s参数是指定notary的服务器,-d是指定签名密钥以及先前下载的信任元数据的缓存目录,默认为~/.docker/trust,init是要进行操作,registry-1.local/library/nginx是待初始的信息集合。你可以对以上命令进行简化,使用别名功能
alias notary="notary -s https://registry-1.local:4443 -d ~/.docker/trust --tlscacert /etc/docker/certs.d/registry-1.local/ca.crt"
简化后的命令格式如下({cmd}代表要执行的操作,如init):
notary {cmd} registry-1.local/library/nginx
执行以上命令后,默认会提示输入root密钥口令,快照密钥口令,目标密钥口令,而我们在上一步设置的对应的变量(NOTARY_SNAPSHOT_PASSPHRASE,NOTARY_TARGETS_PASSPHRASE,NOTARY_ROOT_PASSPHRASE),因此可以被忽略,只提示访问notary服务器的用户名与密码,那么这个用户就是habor管理下的用户,例如admin/Harbor12345。
完成之后可以使用以下命令可查看当前用户的拥有的key信息。
notary key list
假设这里你有一个registry-1.local/library/nginx镜像,该镜像有一个latest的tag,那么需要为此tag进行签名。那么你需要执行以下命令
notary add registry-1.local/library/nginx latest file.txt
这会将目标(latest)名称下的本地文件file.txt(它在当前工作目录中必须存在)添加到我们设置的信任集合(registry-1.local/library/nginx)中。file.txt的内容实际上并未添加到信任集合(registry-1.local/library/nginx)的目标(latest)中, 一个“目标”由文件路径和一个或多个内容的校验和组成。
此时可以查看该信任集合的状态
notary status registry-1.local/library/nginx
Unpublished changes for registry-1.local/library/busybox:
# ACTION SCOPE TYPE PATH
- ------ ----- ---- ----
0 create targets target latest
可以看到有未发布的改变信息。
那么此时需要将该信任集合的改变推送至notary服务端,命令如下,会提示输入用户名密码,输入harbor用户的认证信息即可
notary publish registry-1.local/library/nginx
那么最后需要将该镜像推送至镜像仓库,同样会提示输入用户名密码,输入harbor用户的认证信息即可。
docker push registry-1.local/library/nginx:latest
那么此时登录harbor可以查看到推送的镜像签名状态为“已签名”,由于勾选了内容信任策略,当从harbor中拉取镜像时,如果该镜像在harbor中的签名状态为“未签名”那么将禁止拉取。
5、资源清理与垃圾回收
资源清理是一种逻辑上的删除,而垃圾回收是物理的删除。资源清理在项目管理中进行配置,通过定义tag保留策略,选定需要保留的部分,而未被选中的则会被清理。该清理是从元数据中删除,因此是一种逻辑上的删除操作,tag保留策略可以根据镜像的tag、仓库、以及通过设定保留最近多少天之内(push/pull)的、或者最近(push/pull)的多少个镜像。
而在这里还有一个不可变的tag规则配置,所谓不可变,是指通过策略来匹配一组镜像,而被匹配的镜像,其本身将不再接受变更,类似只读状态。不可变tag规则同样支持根据仓库、镜像tag来进行匹配。
垃圾清理,是将不再有任何引用的层文件进行物理删除,垃圾清理策略支持手动或定时来执行,也就是说可以手动的触发,也可以配置定时任务来定期执行。harbor的垃圾回收是非阻塞式的。非阻塞是指在进行垃圾回收时,仍然可接受push任务,而在这之前是不接受push任务的,整个仓库在垃圾回收时会进入只读状态。
只所以要变为只读状态,是因为在计算层文件引用计数的过程中,如果此时用户正在上传镜像,则垃圾回收可能会删除正在上传的层文件,从而破坏镜像。因此,在垃圾回收任务执行时需要阻塞镜像的推送。
harbor实现这种垃圾回收机制,利益于数据库的应用,由于一个镜像包含多个层文件,要删除一个镜像首先要保证所有层文件在存储中的引用都为0,这代表都该镜像此时没有任何的引用指向,删除是安全的。
而在这之前的docker distribution对层文件的遍历是来自文件系统,这是非常低效的操作,而随着镜像层的增加所带来的效率上的衰减是递增的。而harbor从2.0开始使用数据库来存储层文件的引用关系,使用数据库来查询将效率大大提高。在层文件的数据库表中加入了版本和状态列,层文件的每一次状态改变都会增加版本,通过版本来实现一种乐观锁。当非阻塞垃圾回收任务执行删除时,会尝试将待删除的层文件标记为“deleting”状态。如果该待标记的层文件刚好被Docker 客户端正在推送的镜像引用,则非阻塞垃圾回收任务的“deleting”标记将会失败。原因是 Docker 客户端在推送过程中发起的 HEAD Blob 请求被 Harbor 中间件拦截,中间件会增加层文件的版本。而非阻塞垃圾回收任务在更新层文件状态为“deleting”时,层文件的版本已经不符合数据库里的最新版本信息,导致更新失败。
删除的具体执行由Registryctl控制器来完成,Registryctl控制器引用distribution的代码实现了层文件与清单文件删除API,然后将其暴露给harbor core的非阻塞垃圾回收任务调用。
时间窗口的应用,在docker push镜像时,分先推送层文件,而此时的层文件引用计数是为0的,harbor在层文件推送完成,然后等最后的清单文件完成推送之后,才标记层文件的引用。因此在harbor执行垃圾回收时,避免删除此时推送上来的镜像层,设定了时间窗口,harbor会删除从垃圾回收任务执行开始时间算,向后2小时之前的层文件。
6、升级Harbor和迁移数据
1、升级
将现有 Harbor 实例升级到更新版本时,您可能需要迁移harbor.yml
. 由于迁移可能会改变数据库架构和 的设置harbor.yml
,因此您应该始终在进行任何迁移之前备份您的数据。
重要升级说明
- 同样,您必须在任何数据迁移之前备份您的数据。
- 在Harbor v2.5中,如果使用外部数据库,请确保PostgreSQL版本>=10。
1、停止服务
登录到 Harbor 主机,如果它仍在运行,请停止并删除现有的 Harbor 实例。
cd harbor
docker-compose down
2、备份harbor配置
备份 Harbor 的当前文件,以便您可以在必要时回滚到当前版本。
mv harbor /my_backup_dir/harbor
3、备份数据库,默认情况下在目录中/data/database
。
cp -r /data/database /my_backup_dir/
4、从https://github.com/goharbor/harbor/releases获取最新的 Harbor 发布包 并解压。
5、在升级 Harbor 之前,请执行迁移
迁移工具在作为 docker 镜像交付的 harbor-prepare 工具中。你可以从 docker hub 拉取镜像。在以下命令中:
docker pull goharbor/prepare:[tag]
或者,如果您使用的是脱机安装程序包,则可以从脱机安装程序包中包含的镜像包中加载它。在以下命令中将 [tag] 替换为新的 Harbor 版本,例如 v2.10
# tar zxf harbor-offline-installer-v2.10.0.tgz -C /usr/local/
# cd /usr/local/harbor/
# docker docker image load -i harbor.v2.10.0.tar.gz # 导入harbor镜像
复制/path/to/old/harbor.yml
到harbor.yml
并升级它
docker run -it --rm -v /:/hostfs goharbor/prepare:v2.10.0 migrate -i /usr/local/harbor/harbor.yml
数据库的schema升级和数据迁移是在Harbor启动时由core执行的。如果迁移失败,查看核心日志进行调试。
7、在该./harbor
目录中,运行./install.sh
脚本以安装新的 Harbor 实例。
2、回滚
目录版本仅作为区分不同版本
cd /usr/local/harbor/ # 进入当前版本目录
docker-compose down # 关闭harbor
rm -rf /usr/local/harbor-v2.10.0 # 删除当前版本harbor
cp /my_backup_dir/harbor /usr/local/ # 恢复旧版本harbor
cp /my_backup_dir/database /data/database # 恢复数据
cd /usr/local/harbor-v2.6.2 && docker-compose up -d # 重启harbor服务
评论区