[分享] 极狐GitLab SSL证书管理

JH GitLab SSL证书管理

官方原文:

在极狐GitLab中,目前支持两种配置证书的方式:

  1. 通过Let’s Encrypt工具自动为你免费生成域名证书;

  2. 使用自己的证书手动配置,一般有两种:

    • 购买的商业证书
    • 私有CA自签名的免费证书

同时GitLab里有几个服务是需要单独配置域名,但是上面两种配置方式对不同的服务支持范围不一样,如下表所示:

服务 手动配置 Let’s Encrypt自动配置
GitLab主应用 yes yes
Container Registry yes yes
Mattermost yes yes
GitLab Pages yes no

Let’s Encrypt 自动配置

在Omnibus GitLab安装的时候,只要你通过变量EXTERNAL_URL="https://gitlab.example.com"指定一个https开头的域名,那么它就会触发组件安装流程,同时会自动开启Let's Encrypt服务,为你指定的域名自动生成一个免费的证书。这个过程需要下面的条件:

  • 你配置的这个域名,在公网上可以解析到安装GitLab实例的服务器的IP地址,且Let's Encrypt服务端可以访问到这个IP地址进行验证;
  • 只能通过标准的80443端口进行访问,非标准端口不支持;

如果你在安装的时候没有通过变量EXTERNAL_URL指定域名,那么通过yum安装或者rpm安装时,不会触发GitLab组件安装流程,也不会触发Let's Encrypt服务。需要修改配置文件/etc/gitlab/gitlab.rb里的配置,然后执行gitlab-ctl reconfigure来重新触发。需要修改的配置如下:

## GitLab 实例
external_url  "https://gitlab.example.com"    # 域名必须是https开头
letsencrypt['contact_email'] = ['foo@example.com']   # 可选配置,拥有接收证书相关的通知

# Container Registry
registry_external_url "https://registry.example.com"    #必须是https开头
#registry_nginx['ssl_certificate'] = "path/to/cert"     # 这个配置必须注释或者删除

## Mattermost
mattermost_external_url "https://mattermost.example.com"  # 必须是https开头

修改好配置后,执行命令sudo gitlab-ctl reconfigure让配置生效。然后尝试使用你配置域名来访问安装好的实例。

默认情况下registrymattermost使用external_url域名对应的证书和私钥。而不是registry.exmaple.com.crtregistry.exmaple.com.key。原因是因为Let's Encrypt可以一次签发包含多个子域名的证书。

根据上面表格中的内容知道GitLab Pages只能使用手动来配置对应的证书。

手动配置HTTPS

商业证书

如果不想使用Let's Encrypt来为你生成证书,特别是对于一些无法联网的私有化部署客户,这个时候就需要使用自己购买的商业证书,安装配置流程如下:

  1. 直接使用rpm包进行安装,不要配置EXTERNAL_URL变量

  2. 安装完成后,打开/etc/gitlab/gitlab.rb文件,添加下面的配置:

    external_url "https://gitlab.example.com"    # 必须是https开头的域名
    letsencrypt['enable'] = false
    
  3. 创建保存证书的目录,并将证书文件和私钥文件存放到该目录下:

    mkdir -p /etc/gitlab/ssl
    chmod 755 /etc/gitlab/ssl
    cp gitlab.exmaple.com.crt gitlab.example.com.key /etc/gitlab/ssl/
    

    默认情况下,证书和私钥文件的名称分别是域名.crt域名.key,例如上面示例里的gitlab.example.com.crtgitlab.example.com.key

    这里需要注意的点是,如果你的私钥文件有密码保护,需要先去除,否则安装过程不会报错,但是安装成功以后服务无法正常启动

    最后执行命令sudo gitlab-ctl reconfigure命令让配置生效。

这个时候开启一个组件就要配置一个证书,例如开启Container Registry功能,就需要配置registry.exmaple.com这个域名对应的证书和私钥文件。商业证书和Let’s Encrypt的证书都是公共CA签发的,这些组件对应的配置分别是:

# registry
registry_external_url "https://registry.example.com"
registry_nginx['ssl_certificate'] = "/path/to/registry/certificate"
registry_nginx['ssl_certificate_key'] "/path/to/registry/private/key"

# mattermost
mattermost_external_url "https://mattermost.example.com"
mattermost_nginx['ssl_certificate'] = "/path/to/mattermost/certificate"
mattermost_nginx['ssl_certificate_key'] "/path/to/mattermost/private/key"


# pages
pages_external_url "https://pages.example.com"
pages_nginx['ssl_certificate'] = "/path/to/pages/certificate"
pages_nginx['ssl_certificate_key'] "/path/to/pages/private/key"

自签名证书

如果不想使用购买的证书,需要使用私有CA签发的证书,那么基本配置流程如下:

  1. 创建私有CA

    # 生成私有CA秘钥
    openssl genrsa -out cakey.pem 2048
    
    # 生成私有CA证书,有效期3650天
    openssl req -new -x509 -key cakey.pem -out cacert.pem \
        -subj '/C=CN/ST=Hubei/L=Wuhan/O=CS/CN=Jihu GitLab .Inc' -days 3650
    
  2. 签发所需域名证书

    # 生成域名私钥和域名证书签发请求(csr)
    openssl req -newkey rsa:2048 -nodes -keyout gitlab5.myhuihui.com.key \
        -subj '/C=CN/ST=Hubei/L=Wuhan/O=GitLab/OU=CS_PS/CN=gitlab5.myhuihui.com' \
        -out gitlab5.myhuihui.com.csr
    
    # 使用CA私钥和证书来签发域名证书
    openssl x509 -req \
        -extfile <(printf "subjectAltName=DNS:gitlab5.myhuihui.com,DNS:registry.myhuihui.com,DNS:mattermost.myhuihui.com,DNS:pages.myhuihui.com") \
        -days 365 -in gitlab5.csr -CAkey cakey.pem \
        -CA cacert.pem -CAcreateserial -out gitlab5.myhuihui.com.crt
    

    这里也是签发了单个文件里包含多个域名的证书。

  3. 修改配置文件

    这一步的配置和上面使用商业证书的一样,配置完成后,执行命令gitlabctl reconfigure来重新加载配置。

校验

上面使用Let’s Encrypt和商业证书时,都是使用互联网上的权威CA签发的证书,因此服务端组件一般不会有问题。但是使用私有CA签发的证书时,部分组件的连接会有问题,因此需要来连接进行验证,目前验证的功能主要有:

  • 页面访问
  • 安装GitLab Runner后,注册不同类型的runner
  • Git客户端推送和拉取代码
  • 执行CI任务并推送到gitlab package registry
  • 本地容器镜像推送到gitlab container registry
  • 执行pipelines时从仓库拉取代码
  • 其他场景

页面访问

打开web浏览器,输入测试域名https://gitlab5.myhuihui.com,可以看到浏览器提示证书不安全。无法打开网页,需要手动在系统或者浏览器里添加私有CA证书信任后才能正常打开。

安装Runner并测试

linux类型runner安装

直接下载gitlab-runner的rpm包进行安装,安装完成后注册时不能直接注册,会因为https自签名证书无法验证导致报错。报错如下:

x509: certificate signed by unknown authority

需要添加--tls-ca-file=/path/to/ca-certificate选项,来帮助验证自签名证书。一般来说gitlab-runner需要的私有证书都放在/etc/gitlab-runner/certs目录下,因此先创建目录并将证书复制过去:

mkdir /etc/gitlab-runner/certs
cp cacert.pem /etc/gitlab-runner/certs/

然后注册时传递私有CA证书路径,示例命令如下:

gitlab-runner register --tls-ca-file=/etc/gitlab-runner/certs/cacert.pem

后面的流程和正常注册流程一样。

docker类型runner安装

使用docker类型gitlab-runner直接注册也会报一样的错误,如下所示:

x509: certificate signed by unknown authority

原因和linux类型基本一致,docker类型的gitlab-runner是通过挂载主机上的目录来保持配置文件,例如官网文档里的启动方式是:

docker run -d --name gitlab-runner --restart always \
    -v /srv/gitlab-runner/config:/etc/gitlab-runner \
    -v /var/run/docker.sock:/var/run/docker.sock \
    gitlab/gitlab-runner:v15.2.1

挂载的主机目录是/srv/gitlab-runner/config,挂载到容器内的目录是/etc/gitlab-runner。而执行register命令时,是在容器内部执行的,因此我们需要把私有CA证书在容器内部的路径当做参数传递。对于gitlab-runner而言,私有CA证书存放在/etc/gitlab-runner/certs目录,因此我们需要先创建这个目录,并将私有CA证书复制进去:

mkdir /srv/gitlab-runner/config/certs
cp cacert.pem /srv/gitlab-runner/config/certs/

然后再执行注册命令:

docker run --rm -d \
    -v /srv/gitlab-runner/config:/etc/gitlab-runner \
    gitlab/gitlab-runner register \
    --tls-ca-file=/etc/gitlab-runner/certs/cacert.pem

后面的流程和商业证书一致。

Helm chart类型runner安装

使用Helm chart安装gitlab-runner时,也需要将私有CA证书传递到Pod内部,一般是通过Secret方式,因此我们需要先生成一个包含私有CA证书的Secret,命令是:

kubectl create secret generic <secret-name> \
    --namespace <namespace> \
    --from-file=<target_filename>=<certificate_filename>

参数说明:

  • secret-name,生成的Secret名称,例如gitlab-example-com-cert
  • namespaceSecret所在名称空间
  • certificate_filename,证书文件名称,格式必须是gitlab.example.com.crt,如果默认文件名不是这种格式,那么需要使用target_filename来指定。

那么我们的创建命令就是:

kubectl create secret generic gitlab5-myhuihui-com-cert \
    --namespace gitlab-runner \
    --from-file=gitlab5.myhuihui.com.crt=cacert.pem

Secret创建好以后,我们在helm chartvalues.yml文件中添加下面的配置:

certsSecretName: "gitlab5-myhuihui-com-cert"

helm chart安装后,会将证书挂在到gitlab-runner默认使用目录/home/gitlab-runner/.gitlab-runner/certs下,如果我们登录到Pod里面,可以在这个目录下看到文件gitlab5.myhuihui.com.crt

后面的流程和正常安装基本一致。

Kubernete Agent注册

按照官方文档中基本流程来安装,可以看到对应的agent pod中也会报上面一样的错误,如下所示:

{"level":"error","time":"2022-09-01T06:19:17.283Z","msg":"Error handling a connection",
"mod_name":"reverse_tunnel","error":"Connect(): rpc error: code = Unavailable desc = connection 
error: desc = \"transport: Error while dialing failed to WebSocket dial: failed to send handshake 
request: Get \\\"https://gitlab5.myhuihui.com/-/kubernetes-agent/\\\": 
x509: certificate signed by unknown authority\""}

根据官方issue里的内容,在使用helm chart安装时,可以通过config.caCert选项来进行配置,这个选项会自动读取本地的私有CA证书内容并创建一个ConfigMap,挂载到Pod的/etc/ssl/certs/目录下,解决认证问题。示例命令如下:

helm install -n gitlab-agent gitlab-agent-1521 gitlab/gitlab-agent \
    --set config.kasAddress=wss://gitlab5.myhuihui.com/-/kubernetes-agent/ \
    --set config.token=qKXh9N5vszLwQHKv2yxxtRkDmt3ECTbz2pWyXqKc4rFJZH-mog \
    --set config.caCert="$(cat ~/cacert.pem)"

如果是通过仓库里的yaml配置文件来手动创建agent,那么就需要自己来完成两项工作:

  1. 手动在kubernetes集群上,使用私有CA证书文件创建一个ConfigMap
  2. 在yaml文件里,将这个ConfigMap挂载到agent的Pod里
配置ConfigMap
kubectl -n gitlab-agent create configmap ca-private --from-file=myCA.pem
配置挂载
        volumeMounts:
        - name: token-volume
          mountPath: /config
        # 下面是新增的内容
        - name: ca-pemstore-volume
          mountPath: /etc/ssl/certs/myCA.pem
          subPath: myCA.pem
      volumes:
      - name: token-volume
        secret:
          secretName: gitlab-agent-token
      # 下面是新增的内容
      - name: ca-private
        configMap:
          name: ca-private
          items:
          - key: myCA.pem
            path: myCA.pem

Git客户端推送和拉取代码

当使用https开头的地址来拉取代码时,会出现以下报错:

git clone https://gitlab5.myhuihui.com/root/python-demo.git
正克隆到 'python-demo'...
fatal: 无法访问 'https://gitlab5.myhuihui.com/root/python-demo.git/':SSL certificate problem: unable to get local issuer certificate

经查询确认这就是自签名证书无法验证的问题,需要使用下面的配置关闭SSL验证后才能正常拉取和推送代码:

git config --global http.sslVerify false

如果使用gitssh开头的地址来拉取或者推送代码时,则无影响,因为不走https协议。

运行CI任务并推送package到package registry,推送镜像到container registry

运行CI流水线测试,在实际的测试过程中发现多种和私有证书相关的问题,例如:

  • curl、git等命令连接GitLab 服务端时,都会报证书签名错误,每个命令都需要添加额外的参数来去掉https证书验证。
  • 使用docker连接container registry时报证书错误,需要为dind里的docker添加额外的配置来解决证书验证的问题。
  • 上传cacheartifacts时有https验证问题

经过上面的验证可知,实际生产中非常非常不推荐使用私有CA证书来配置GitLab实例,可能出现的问题有上面列出的项目。除此之外,根据上面的经验可以知道,只要你的配置或者CI流程中,有需要通过https协议和GitLab服务端进行交互的,那么一定会出现https证书认证的问题,都需要自己来手动配置相关选项来解决,会浪费较多的时间和精力。因此在实际生产中,推荐直接使用商业证书或者极狐GitLab自带的Let's Encrypt组件来生成商业证书,避免这些问题。

1 个赞