SSL 配置

可用的 SSL 配置任务

Omnibus 提供几种 SSL 配置的常见用例。

  1. 允许使用 https 连接到极狐GitLab 实例服务。
  2. 为外部资源连接配置公共证书捆绑包。

Host Services

管理员可以通过任何极狐GitLab 服务支持的方法,启用安全的 http 服务。

服务 手动 SSL Let’s Encrypt
Primary GitLab Instance Domain Yes Yes
Container Registry Yes Yes
Mattermost Yes Yes
GitLab Pages Yes No

集成 Let’s Encrypt

极狐GitLab 可以与 Let’s Encrypt 集成。

主 GitLab 实例

  • 如果 external_url 设置为 https 协议且未配置证书时,默认启用。
note为了让 Let’s Encrypt 验证自动工作,运行验证检查的公共 Let’s Encrypt 服务器需要可以访问端口 80 和 443。验证不适用于非标准端口。 如果环境是私有的或隔离的,certbot 为自定义安装实例生成证书提供手动方法
caution管理员不打算使用 Let’s Encrypt,应该在 /etc/gitlab/gitlab.rb 中设置 letsencrypt['enable'] = false 以禁用。

将以下内容添加到 /etc/gitlab/gitlab.rb 以启用对主域名的 Let’s Encrypt 支持:

letsencrypt['enable'] = true                      # GitLab 10.5 and 10.6 require this option
external_url "https://gitlab.example.com"         # Must use https protocol
letsencrypt['contact_emails'] = ['foo@email.com'] # Optional
noteLet’s Encrypt 颁发的证书每九十天过期一次。可选的 contact_emails设置会在到期日期临近时将到期警报发送到配置的地址。

GitLab 组件

按照步骤启用基本的 Let’s Encrypt 集成并使用以下任何适用的内容修改 /etc/gitlab/gitlab.rb

registry_external_url "https://registry.example.com"     # container registry, must use https protocol
mattermost_external_url "https://mattermost.example.com" # mattermost, must use https protocol
#registry_nginx['ssl_certificate'] = "path/to/cert"      # Must be absent or commented out

Let’s Encrypt 证书是使用 GitLab 主实例作为证书上的主名称创建的。其他服务(例如 registry)作为备用名称添加到同一证书中。请注意,在上面的示例中,主域是名 gitlab.example.com,registry 域名时registry.example.com。 管理员无需担心设置通配符证书。

Let’s Encrypt 自动更新

caution管理员计划使用他们自己的 Let’s Encrypt 证书,应该在 /etc/gitlab/gitlab.rb 中设置 letsencrypt['enable'] = false 以禁用自动更新。 否则,gitlab-ctl reconfigure 可能会尝试更新证书,从而覆盖它们。

默认计划在每月 4 日午夜之后更新。分钟由 external_url 中的值决定,以帮助分配上游 Let's Encrypt 服务器上的负载。

通过将以下内容添加到 /etc/gitlab/gitlab.rb 来显式设置更新时间:

# This example renews every 7th day at 12:30
letsencrypt['auto_renew_hour'] = "12"
letsencrypt['auto_renew_minute'] = "30"
letsencrypt['auto_renew_day_of_month'] = "*/7"
note仅当证书将在 30 天后到期时才会更新。例如,如果您将其设置为每月 1 日 00:00 更新,并且证书在 31 日到期,则证书将在更新之前到期。

/etc/gitlab/gitlab.rb 中使用以下内容禁用自动更新:

letsencrypt['auto_renew'] = false

Let’s Encrypt 手动更新

使用以下命令中的 一个 手动更新 Let’s Encrypt 证书:

sudo gitlab-ctl reconfigure
sudo gitlab-ctl renew-le-certs
caution系统将尝试更新任何 Let’s Encrypt 证书。如果您打算使用自己的 Let’s Encrypt 证书,则必须在 /etc/gitlab/gitlab.rb 中设置 letsencrypt['enable'] = false 以禁用集成。 否则证书可能会因续订而被覆盖。

使用 Let’s Encrypt 以外的 ACME 服务器

您可以使用 Let’s Encrypt 以外的 ACME 服务器,并将极狐GitLab 配置为使用它来获取证书。提供自己的 ACME 服务器的服务有:

要将极狐GitLab 配置为使用自定义 ACME 服务器:

  1. 编辑 /etc/gitlab/gitlab.rb 并设置 ACME 端点:

    external_url 'https://example.com'
    letsencrypt['acme_staging_endpoint'] = 'https://ca.internal/acme/acme/directory'
    letsencrypt['acme_production_endpoint'] = 'https://ca.internal/acme/acme/directory'
    

    如果自定义 ACME 服务器提供了它,那么也可以使用暂存端点。在将请求提交给 ACME 生产之前,首先检查暂存端点确保 ACME 配置正确。这样做是为了在处理配置时避免 ACME 速率限制。

    默认值:

    https://acme-staging-v02.api.letsencrypt.org/directory
    https://acme-v02.api.letsencrypt.org/directory
    
  2. 重新配置极狐GitLab:

    sudo gitlab-ctl reconfigure
    

连接外部资源

某些环境连接到外部资源以执行各种任务。 Omnibus 允许这些连接使用安全的 http (https)。

默认配置

Omnibus 附带了官方的 CAcert.org 可信根证书颁发机构集合,用于验证证书的真实性。

其它证书颁发机构

Omnibus 支持使用自签名证书连接到外部服务。

note对于使用自签名证书的安装实例,Omnibus 提供了一种管理这些证书的方法。有关其工作原理的更多技术细节,请参阅本页底部的详细信息

安装自定义公共证书

notec_rehash 依赖需要一个 perl 解释器来正确地符号链接证书。Perl 目前未捆绑在 Omnibus GitLab 中。
  1. 从您的私钥证书生成 PEMDER 编码的公共证书。
  2. 仅将公共证书文件复制到/etc/gitlab/trusted-certs 目录中。默认情况下,系统期望找到一个以您的 GitLab URL 为标题的证书,扩展名为 .crt。 例如,如果您的服务器地址是 https://gitlab.example.com,则证书应命名为 gitlab.example.com.crt。要指定不同的路径和文件名,您可以更改默认 SSL 证书位置
  3. 重新配置极狐GitLab:

    sudo gitlab-ctl reconfigure
    

默认情况下,极狐GitLab 期望找到以您的 GitLab URL 命名的证书。例如,您的服务器地址是 https://gitlab.example.com,则证书应命名为 gitlab.example.com.crt

要指定不同的路径和文件名,您可以更改默认 SSL 证书位置。

caution如果使用自定义证书链,由于 c_rehash 仅为第一个证书创建哈希,则必须将根证书和/或中间证书放入/etc/gitlab/trusted-certs 中的单独文件中。

故障排查

有用的 OpenSSL 调试命令

有时,通过直接在源中查看 SSL 证书链,可以更好地了解它是有帮助的。这些命令是用于诊断和调试的标准 OpenSSL 工具库的一部分。

noteGitLab 包括所有 GitLab 库都链接到的自定义编译版本的 OpenSSL,使用此 OpenSSL 版本运行以下命令很重要。
  • 通过 HTTPS 执行与主机的测试连接。将 HOSTNAME 替换为您的 GitLab URL(不包括 HTTPS),并将 port 替换为提供 HTTPS 连接的端口(通常为 443):

    echo | /opt/gitlab/embedded/bin/openssl s_client -connect HOSTNAME:port
    

    echo 命令向服务器发送一个空请求,导致它关闭连接而不是等待额外的输入。您可以使用相同的命令来测试远程主机(例如,托管外部仓库的服务器),方法是将 HOSTNAME:port 替换为远程主机的域名和端口号。

    此命令的输出显示证书链、服务器提供的任何公共证书,以及验证或连接可能发生的错误。这样可以快速检查 SSL 设置的任何直接问题。

  • 使用x509以文本形式查看证书的详细信息。 请务必将 /path/to/certificate.crt 替换为证书的路径:

    /opt/gitlab/embedded/bin/openssl x509 -in /path/to/certificate.crt -text -noout
    

    例如,GitLab 会自动从 Let’s Encrypt 获取证书并将其放置在/etc/gitlab/ssl/hostname.crt。您可以使用带有该路径的 x509 命令来快速显示证书的信息(例如,主机名、颁发者、有效期等)。

    如果证书存在问题,将会出现错误

  • 从服务器获取证书并对其进行解码。这结合了上述两个命令来获取服务器的 SSL 证书并将其解码为文本:

    echo | /opt/gitlab/embedded/bin/openssl s_client -connect HOSTNAME:port | /opt/gitlab/embedded/bin/openssl x509 -text -noout
    

常见的 SSL 错误

  1. SSL certificate problem: unable to get local issuer certificate

    此错误表示客户端无法获取根 CA。要解决此问题,您可以尝试新人在客户端上连接的服务器的根 CA,或修改证书 使得在您尝试连接的服务器上显示完整的链式证书。

    note 建议使用完整的证书链,以防止客户端连接时出现 SSL 错误。完整的证书链顺序应首先包含服务器证书,然后是所有中间证书,最后是根 CA。
  2. unable to verify the first certificate

    此错误表明服务器提供了不完整的证书链。要修复此错误,您需要用完整链式证书替换服务器的证书。 完整的证书链顺序应首先包含服务器证书,然后是所有中间证书,最后是根 CA。

  3. certificate signed by unknown authority

    此错误表明客户端不信任证书或 CA。要修复此错误,连接到服务器的客户端需要信任证书或 CA

  4. SSL certificate problem: self signed certificate in certificate chain

    此错误表明客户端不信任证书或 CA。要修复此错误,连接到服务器的客户端需要信任证书或 CA

golang 编写的 Git-LFS 等嵌入式服务报告未知机构签名的自定义证书

gitlab-workhorse 和其他用 golang 编写的服务使用来自 golangcrypto/tls 库而不是 OpenSSL

/etc/gitlab/gitlab.rb 中添加以下条目以解决问题:

gitlab_workhorse['env'] = {
  'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/'
}
note如果您已将 GitLab 安装到除 /opt/gitlab/ 之外的路径,请在您的操作环境中使用正确的路径修改上面的条目。

证书导致重新配置失败

ERROR: Not a certificate: /opt/gitlab/embedded/ssl/certs/FILE. Move it from /opt/gitlab/embedded/ssl/certs to a different location and reconfigure again.

检查 /opt/gitlab/embedded/ssl/certs 并删除除 README.md 之外的任何不是有效 X.509 证书的文件。

note运行 gitlab-ctl reconfigure 构造从自定义公共证书的 subject 哈希命名的链接,并将它们放在 /opt/gitlab/embedded/ssl/certs/ 中。/opt/gitlab/embedded/ssl/certs/ 中损坏的链接将被自动删除。存储在 /opt/gitlab/embedded/ssl/certs/ 中的 cacert.pemREADME.md 以外的文件将被移动到 /etc/gitlab/trusted-certs/ 中。

丢失或跳过自定义证书

GitLab versions 8.9.0, 8.9.1, and 8.9.2 all mistakenly used the /etc/gitlab/ssl/trusted-certs/ directory. This directory is safe to remove if it is empty. If it still contains custom certificates then move them to /etc/gitlab/trusted-certs/ and run gitlab-ctl reconfigure.

如果没有在 /opt/gitlab/embedded/ssl/certs/ 中创建链接,你会看到运行gitlab-ctl reconfigure后出现 “Skipping cert.pem” 消息,这意味着可能存在以下四个问题之一:

  1. /etc/gitlab/trusted-certs/ 中的文件是链接
  2. 该文件不是有效的 PEM 或 DER 编码的证书
  3. Perl 未安装在 c_rehash 正确链接证书所需的操作系统上
  4. 证书包含字符串 TRUSTED

使用以下命令测试证书的有效性:

/opt/gitlab/embedded/bin/openssl x509 -in /etc/gitlab/trusted-certs/example.pem -text -noout
/opt/gitlab/embedded/bin/openssl x509 -inform DER -in /etc/gitlab/trusted-certs/example.der -text -noout

无效的证书文件会产生以下输出:

unable to load certificate
140663131141784:error:0906D06C:PEM routines:PEM_read_bio:no start line:pem_lib.c:701:Expecting: TRUSTED CERTIFICATE

测试 c_rehash 是否由于缺少 perl 解释器而没有链接证书:

$ /opt/gitlab/embedded/bin/c_rehash /etc/gitlab/trusted-certs
bash: /opt/gitlab/embedded/bin/c_rehash: /usr/bin/perl: bad interpreter: No such file or directory

如果您看到此消息,则需要使用发行版的包管理器安装 perl。

如果您检查证书本身,请查找字符串 TRUSTED

-----BEGIN TRUSTED CERTIFICATE-----
...
-----END TRUSTED CERTIFICATE-----

如果像上面的例子一样,然后尝试删除字符串 TRUSTED 并再次运行 gitlab-ctl reconfigure

未检测到自定义证书

如果在执行 gitlab-ctl reconfigure 命令后:

  1. /opt/gitlab/embedded/ssl/certs/ 中没有创建链接;
  2. 您已将自定义证书放在 /etc/gitlab/trusted-certs/ 中;并且
  3. 您未看到任何跳过的或链接的自定义证书消息

您可能会遇到 Omnibus 认为已添加自定义证书的问题。

要解决,请删除受信任的证书目录哈希:

rm /var/opt/gitlab/trusted-certs-directory-hash

然后再次运行gitlab-ctl reconfigure。重新配置应现在检测并链接您的自定义证书。

Let’s Encrypt 证书由未知机构签署

Let’s Encrypt 集成的初始实现仅使用证书,而不是完整的证书链。

现在使用完整的证书链。对于已经在使用证书的安装,在续订逻辑指示证书即将到期之前不会发生切换。要尽快强制执行,请运行以下命令:

rm /etc/gitlab/ssl/HOSTNAME*
gitlab-ctl reconfigure

其中 HOSTNAME 是证书的主机名。

重新配置时 Let’s Encrypt 失败

重新配置时,Let’s Encrypt 可能会在以下常见情况下失败:

  1. 如果您的服务器无法访问 Let’s Encrypt 验证服务器,Let’s Encrypt 可能会失败,反之亦然:

    letsencrypt_certificate[gitlab.domain.com] (letsencrypt::http_authorization line 3) had an error: RuntimeError: acme_certificate[staging]  (/opt/gitlab/embedded/cookbooks/cache/cookbooks/letsencrypt/resources/certificate.rb line 20) had an error: RuntimeError: [gitlab.domain.com] Validation failed for domain gitlab.domain.com
    

    如果您由于 Let’s Encrypt 而在重新配置 GitLab 时遇到问题确保您的端口 80 和 443 已打开并可访问

  2. 您的域名的证书颁发机构授权 (CAA) 记录不允许 Let’s Encrypt 为您的域名颁发证书。在重新配置输出中查找以下错误:

    letsencrypt_certificate[gitlab.domain.net] (letsencrypt::http_authorization line 5) had an error: RuntimeError: acme_certificate[staging]   (/opt/gitlab/embedded/cookbooks/cache/cookbooks/letsencrypt/resources/certificate.rb line 25) had an error: RuntimeError: ruby_block[create certificate for gitlab.domain.net] (/opt/gitlab/embedded/cookbooks/cache/cookbooks/acme/resources/certificate.rb line 108) had an error: RuntimeError: [gitlab.domain.com] Validation failed, unable to request certificate
    
  3. 如果您使用的是例如 gitlab.example.com 之类的测试域名,但没有证书,您将看到上面显示的 unable to request certificate。在这种情况下,通过在 /etc/gitlab/gitlab.rb 中设置 letsencrypt['enable'] = false 来禁用 Let’s Encrypt。

您可以使用 Let’s Debug 诊断工具测试您的域名。它可以帮助您找出无法颁发 Let’s Encrypt 证书的原因。

GitLab 和 SSL 的工作实现细节

Omnibus 包含自己的 OpenSSL 库,并将所有已编译的程序(例如 Ruby、PostgreSQL 等)链接到该库。该库编译为在 /opt/gitlab/embedded/ssl/certs 中查找证书。

Omnibus 通过使用 c_rehash 将添加到 /etc/gitlab/trusted-certs/ 的任何证书链接到 /opt/gitlab/embedded/ssl/certs 来管理自定义证书。例如,假设我们将 customcacert.pem 添加到 /etc/gitlab/trusted-certs/

$ sudo ls -al /opt/gitlab/embedded/ssl/certs
total 272
drwxr-xr-x 2 root root   4096 Jul 12 04:19 .
drwxr-xr-x 4 root root   4096 Jul  6 04:00 ..
lrwxrwxrwx 1 root root     42 Jul 12 04:19 7f279c95.0 -> /etc/gitlab/trusted-certs/customcacert.pem
-rw-r--r-- 1 root root 263781 Jul  5 17:52 cacert.pem
-rw-r--r-- 1 root root    147 Feb  6 20:48 README

证书的指纹为 7f279c95,它链接到自定义证书。

当我们发出 HTTPS 请求时会发生什么?让我们以一个简单的 Ruby 程序为例:

#!/opt/gitlab/embedded/bin/ruby
require 'openssl'
require 'net/http'

Net::HTTP.get(URI('https://www.google.com'))

以下是屏幕后面发生的事情:

  1. “require openssl”行导致解释器加载 /opt/gitlab/embedded/lib/ruby/2.3.0/x86_64-linux/openssl.so
  2. Net::HTTP 调用然后尝试读取 /opt/gitlab/embedded/ssl/certs/cacert.pem 中的默认证书包。
  3. SSL 协商。
  4. 服务器发送其 SSL 证书。
  5. 如果发送的证书包含在捆绑包中,则 SSL 将成功完成。
  6. 否则,OpenSSL 可能会通过在预定义的证书目录中,搜索与其指纹匹配的文件来验证其他证书。 例如,如果证书的指纹为“7f279c95”,OpenSSL 将尝试读取“/opt/gitlab/embedded/ssl/certs/7f279c95.0”。

请注意,OpenSSL 库支持 SSL_CERT_FILESSL_CERT_DIR 环境变量的定义。前者定义了要加载的默认证书包,而后者定义了一个用于搜索更多证书的目录。如果你已将证书添加到“trusted-certs”目录中,这些变量不是必需的。但是,如果由于某种原因需要设置它们,它们可以定义为环境变量。例如:

gitlab_rails['env'] = {"SSL_CERT_FILE" => "/usr/lib/ssl/private/customcacert.pem"}