在公网搭建的 GitLab 频频遇到安全挑战,然而其实只需要做一两个简单的动作,维护成本就能够大大降低,并且还能避免未被许可的内容,被搜索引擎爬虫暴露的到处都是。

本篇文章,我们就来聊聊公网搭建的 GitLab 代码仓库的安全小细节。

写在前面

公网搭建 GitLab ,常见的攻击面主要有:

  • 运行宿主机系统部分
  • 运行宿主机网络部分
  • 应用 Web 程序漏洞
  • 应用 SSH 漏洞

前两点可以通过 SLB + VPC 进行网络隔离,来降低被攻击风险。后两点除了保证最快跟进系统安全补丁,升级应用版本外,其实还有更好的解决方案,毕竟存放着数据的程序,每次升级都有未知的风险:

  • 解决 Web 漏洞可以通过加一层 Basic Auth 来解决。
  • 解决 SSH 攻击风险,可以通过加一个简单的日志监控程序来解决:参考之前文章中 监控 GitLab SSH 端口 小节。

但是加一层 Basic Auth 其实会对 GitLab 使用造成一些麻烦。

为 GitLab 添加请求验证

GitLab 程序本身并不支持 Basic Auth,这里需要使用一个 Web 前端软件来完成这部分的工作,比如:Nginx、Traefik。

我这里选择使用 Traefik,因为配置更简单,具体配置可以参考之前文章的“ 添加网络请求验证 ”小节。

在配置声明里添加一句话就够了,比如这样:

这样处理之后,所有的 HTTP 请求就都会被验证是否是合法访问啦。而其他的端口和协议则不受影响,比如开在22端口的 SSH 服务等。

Basic Auth 到底是什么

当访问页面的时候,会展示类似下面的对话框,要求用户登陆,否则会提示 401 Unauthorized

访问页面的时候会显示一个对话框

当爬虫/安全检测工具请求页面的时候,如果没有提交用户名和密码,获得的结果也是一样。

如果你输入了正确的用户名以及用户密码,你会发现在你的请求参数中会出现一个额外的请求参数 authorization

访问页面的时候会显示一个对话框

参数数值一般由两部分构成,第一部分表示加密方式(加密协议名称),第二部分则是你的身份信息(更多信息详见 RFC 7617)。

现代浏览器一般会很智能的在你第一次正确输入之后,将身份信息记录下来,携带在后续的每一次请求中,如果是使用程序或者工具的话,则需要手动将 authorization 信息加入到每一个 HTTP 的请求头中。

然而 Basic Auth 挡住了外来者随意访问的同时,还挡住了 GitLab CI Runner。

项目关联 Runner 离线

这是怎么回事呢,我们继续往下看。

解救被拦住的 CI Runner

在解释为什么 CI Runner 会被 Basic Auth 拦住时,我们需要先了解另外一个协议规范 RFC1738 中对于 HTTP 协议的定义:

当我们使用客户端(浏览器、curl等工具)请求这类格式的地址时,部分客户端会将 user:password 部分转换为标准的 HTTP Authorization 请求头。

默认 CI Runner 行为

在不做任何修改时,CI 直接执行会报错,日志输出类似下面:

结合文章前面的内容,我们知道这里是缺少了 Authorization 请求头,那么我们尝试给请求补上这个请求头,在 CI 和 GitLab 中间搭建一台 Proxy ,让 CI 请求 GitLab 数据的时候,自动完成“认证”。

请求自动添加验证信息

如果使用 Nginx 搭建一个支持 GET/POST 请求的代理,核心配置如下:

再次使用 curl 对 GitLab 进行请求,发现没有出现之前的 401 非验证提示:

打开项目配置,会发现 Runner 已经上线。

项目关联 Runner 上线

CI 构建依旧是失败的

继续在 GitLab Runner 运行 CI 流水线,会看到还是报错无法通过构建。

还记得前面提到过的 Authorization 请求头和 HTTP RFC规范吗?GitLab Runner 在处理 CI 任务的时候,使用的是https://gitlab-ci-token:[MASKED]@gitlab.domain/repo.git/ 这样的 HTTP 协议,请求中的用户名和密码和 Nginx ProxyPass 中的字段“八字不合”。

那么不使用 HTTP 协议,使用 SSH 协议或许能解决问题。

尝试使用 SSH 协议

可惜的是,官方并不支持 GitLab Runner 使用 SSH 协议进行仓库下载,有类似需求的用户还真不少,如果你愿意找,类似下面的 issue 还有不少:

虽然官方没有直接提供这个功能,但是从官方文档中看到有一个 clone_url,查找代码发现实现很简单,只需要稍微改造就能够满足我们的需求:

改动后的代码如下:

如果你不想在浪费时间在折腾构建环境上,可以参考我之前写的一篇文章: 源码编译 GitLab Runner 

执行 CI 成功

再次执行 CI 任务,会发现已经能够顺利的进行啦。

最后

GitLab 先折腾到这里,或许后面有时间的会写一篇更加全面的折腾攻略。如果你对 GitLab 感兴趣,可以浏览我之前写的相关文章

—EOF