本文翻译自极狐GitLab的官方博客,详细介绍了gitlab-sshd方案实现的背景以及该组件和OpenSSH相比较之下所具备的优势,供大家参考。正文内容如下
为什么要转移到我们自己的SSHD,这背后的故事也非常有趣。GitLab提供了大量通过SSH连接执行的特性,最流行的一个就是Git-over-SSH,让我们可以通过SSH和一个Git服务端进行通信。因为一些历史原因,我们通过OpenSSH和一个单独的组件,一个叫做GitLab Shell的二进制文件的组合来实现这些特性。GitLab Shell处理每个OpenSSH服务端建立的连接,以在 SSH客户端和 Git 服务端之间来回传递数据。这个方案是久经历练且依赖一个新人的组件,例如OpenSSH。下面是我们为什么决定实现我们自己的SSHD。
社区贡献
每个人都可以给GitLab贡献!而gitlab-sshd就是一个来自lorenz的社区贡献,作为我们已有安装里的一个轻量级替代方案。一个只需要极少外部依赖的独立二进制文件对于容器化部署来说非常有优势,一个GitLab支持的代替方案也可以打开新的机会:
- PROXY协议支持让通过SSH的群组IP地址限制成为可能:群组IP地址限制在
GitLab Shell + OpenSSH的方案里无法实现,因为OpenSSH不支持PROXY协议。因此,GitLab Shell无法看到真实用户的IP地址。我们不得不使用一个打补丁的OpenSSH版本或者实现我们自己的方案来支持PROXY协议。使用我们自己的方案,我们可以提供PROXY协议,接收到用户的真实IP地址,并提供群组IP地址限制功能。 Kubernetes里平滑关闭、存活性和就绪性探测等特性的兼容:使用OpenSSH,我们无法控制已建立的连接,当Kubernetes Pods轮替时,所有进行中的连接都会被立即丢弃,会导致长时间运行的git clone操作被打断。有了专用的服务端以后,连接现在是可管理的且可以被平滑关闭:服务端监听中断信号,当接收到信号时,停止接收新的连接并在完全关闭之前等待一个平滑周期。这个平滑周期让正在进行的连接可以正常结束。Prometheus指标和分析成为可能:在我们之前的部署方式里,GitLab Shell只是一个二进制文件,用来创建和SSH连接存活时间一样长的进程。这种方式无法提供一种直接的方式来运行指标服务器。而使用专用的服务器,我们现在可以收集指标并实现详情记录,用于监控和调试。- 在一些场景下,性能有了明显的提升,资源使用率有了明显的下降:轻量级的协程相对于每个SSH连接都创建一个独立的进程来说资源消耗更低。在基础场景里,创建一个独立的进程表现的更好。但是,使用
gitlab-sshd可以引入一个Go分析器来解决性能问题,从操作方面来说,是一个明显的提升。 - 仅使用一个限制的SSH实现特性集来降低攻击问题:使用之前的方式,我们允许建立一个到
OpenSSH服务端的连接,但是将它限制到一个特定的特性集。使用gitlab-sshd,对一个我们还未支持的OpenSSH特性的任意未预料的调用再也不可能了,戏剧性的降低了攻击问题。 - 简单的架构更容易理解。
之前的架构
当前的架构
风险和挑战,
但是,修改一个广泛使用,且负责安全的组件,会带来巨大的风险。我们同时经历了挑战和风险:
- 安全方面:一个
SSH服务端是一个关键组件,它在用户和服务端之间建立一个安全连接,并允许他们之间私下通信。任何错误都可能打开安全漏洞,允许从认证绕过到远程代码执行等各种操作。要减轻风险,我们执行多轮安全审查 - 开发之前(检查使用的组件)、开发中、以及在工作版本代码部署到预发布环境之后。 - 操作方面:这个组件是广泛使用的,任何错误会影响大量的用户。要减轻这个风险,我们渐进式地增加变更流量的比例,从1%、到5%递增。并且在遇到问题的时候立刻回滚。在8次尝试之后,我们让这个服务端成功接收处理了100%的流量。
我们遇到的问题
使用范围如此广泛的组件会有大范围的问题,我们遇到并解决了下面的问题:
- 和其他进行中的特性不兼容:我们第一次
gitlab-sshd部署消耗了大量的内存,它和其他正在开发中的特性交互产生了负面影响。我们在引入一个通用组件时必须时刻记住和其他组件的交互。 - 在
golang.org/x/crypto库里有限的特性集:这个库用来建立SSH连接,并且有限支持在OpenSSH里可用的算法和特性。我们创建了我们自己的分支来提供缺失的特性。OpenSSH客户端在8.2版本里因为一些安全原因废弃了主机证书里基于SHA-1的签名。但是通过server-sig-algs扩展提供了向后兼容。但是golang.org/x/crypto不支持它。我们开始支持这个扩展;- 一些
MACs和密钥交换算法不支持:hmac-sha2-512和hmac-sha2-256是最显眼的,我们也开始支持这些算法; - 有问题的SSH客户端,例如在
Ubuntu 18.04里附带的gpg-agent v2.2.4和OpenSSH v7.6,可能会发送ssh-rsa-512作为公钥算法,但是实际包含一个rsa-sha签名。我们不得不放松RSA签名检查来解决这个问题。 - 重新实现
OpenSSH里可用的选项:熟悉的OpenSSH选项像LoginGraceTime和ClientAliveInternal不可用,所以我们实现了多个替代项来保留我们需要的特性。
学习到的东西
不幸的是,在生产环境上问题变得可见.感谢压力测试以及各种可能的OpenSSH配置,即使在我们的预发布环境上已经捕获到了一些bug,预测所有类型的问题还是不可能。但是,下面这些动作帮助我们解决了这些问题:
- 增量部署:这个部署方案被证明及其有效,让我们可以在迭代的同时不会中断大部分用户的服务。
- 寻求多视角:我们征求了大量团队的意见,例如安全、架构、质量和扩展团队。帮助我们从多个方面评估这个项目,降低风险并阻止大量问题的发生。