本文使用「署名 4.0 国际 (CC BY 4.0)」许可协议,欢迎转载、或从新修改使用,但须要注明来源。 署名 4.0 国际 (CC BY 4.0)html
本文做者: 苏洋前端
建立时间: 2019年08月05日 统计字数: 7023字 阅读时间: 15分钟阅读 本文连接: soulteary.com/2019/08/05/…nginx
在公网搭建的 GitLab 频频遇到安全挑战,然而其实只须要作一两个简单的动做,维护成本就可以大大下降,而且还能避免未被许可的内容,被搜索引擎爬虫暴露的处处都是。git
本篇文章,咱们就来聊聊公网搭建的 GitLab 代码仓库的安全小细节。docker
公网搭建 GitLab ,常见的攻击面主要有:编程
前两点能够经过 SLB
+ VPC
进行网络隔离,来下降被攻击风险。后两点除了保证最快跟进系统安全补丁,升级应用版本外,其实还有更好的解决方案,毕竟存放着数据的程序,每次升级都有未知的风险:浏览器
Basic Auth
来解决。可是加一层 Basic Auth
其实会对 GitLab 使用形成一些麻烦。安全
GitLab 程序自己并不支持 Basic Auth
,这里须要使用一个 Web 前端软件来完成这部分的工做,好比:Nginx、Traefik。bash
我这里选择使用 Traefik,由于配置更简单,具体配置能够参考以前文章的“ 添加网络请求验证 ”小节。网络
在配置声明里添加一句话就够了,好比这样:
- "traefik.gitlab.frontend.auth.basic=soulteary:$apr1$E86fARwM$tXmggGAtCEDKqsBCSvDA3/ 复制代码
这样处理以后,全部的 HTTP 请求就都会被验证是不是合法访问啦。而其余的端口和协议则不受影响,好比开在22端口的 SSH 服务等。
当访问页面的时候,会展现相似下面的对话框,要求用户登录,不然会提示 401 Unauthorized
。
当爬虫/安全检测工具请求页面的时候,若是没有提交用户名和密码,得到的结果也是同样。
curl -I https://gitlab.domain/
HTTP/2 401
content-type: text/plain
vary: Accept-Encoding
www-authenticate: Basic realm="traefik"
content-length: 17
date: Sat, 03 Aug 2019 18:22:17 GMT
复制代码
若是你输入了正确的用户名以及用户密码,你会发如今你的请求参数中会出现一个额外的请求参数 authorization
。
参数数值通常由两部分构成,第一部分表示加密方式(加密协议名称),第二部分则是你的身份信息(更多信息详见 RFC 7617)。
现代浏览器通常会很智能的在你第一次正确输入以后,将身份信息记录下来,携带在后续的每一次请求中,若是是使用程序或者工具的话,则须要手动将 authorization
信息加入到每个 HTTP 的请求头中。
然而 Basic Auth 挡住了外来者随意访问的同时,还挡住了 GitLab CI Runner。
这是怎么回事呢,咱们继续往下看。
在解释为何 CI Runner 会被 Basic Auth
拦住时,咱们须要先了解另一个协议规范 RFC1738 中对于 HTTP 协议的定义:
//<user>:<password>@<host>:<port>/<url-path>
复制代码
当咱们使用客户端(浏览器、curl等工具)请求这类格式的地址时,部分客户端会将 user:password
部分转换为标准的 HTTP Authorization
请求头。
在不作任何修改时,CI 直接执行会报错,日志输出相似下面:
Running with gitlab-runner 12.0.2 (d0b76032)
on ci-runner sGNUJnq6
Using Shell executor...
Running on ci-runner...
Fetching changes with git depth set to 50...
Initialized empty Git repository in /data/runner/builds/sGNUJnq6/0/config/project/.git/
Created fresh repository.
remote: 401 Unauthorized
fatal: Authentication failed for 'https://gitlab-ci-token:[MASKED]@gitlab.domain/repo.git/'
ERROR: Job failed: exit status 1
复制代码
结合文章前面的内容,咱们知道这里是缺乏了 Authorization
请求头,那么咱们尝试给请求补上这个请求头,在 CI 和 GitLab 中间搭建一台 Proxy ,让 CI 请求 GitLab 数据的时候,自动完成“认证”。
若是使用 Nginx 搭建一个支持 GET/POST 请求的代理,核心配置以下:
location / {
forward-http-post-requests-via-rewrite
proxy_pass https://192.168.0.123;
proxy_redirect https://192.168.0.123/ /;
proxy_read_timeout 10s;
proxy_set_header Host 'gitlab.domain';
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Authorization 'Basic YmFhaABCD';
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto 'https';
proxy_http_version 1.1;
}
复制代码
再次使用 curl 对 GitLab 进行请求,发现没有出现以前的 401
非验证提示:
curl -I https://gitlab.domain/
HTTP/2 302
cache-control: no-cache
content-type: text/html; charset=utf-8
date: Sat, 03 Aug 2019 18:45:39 GMT
location: https://gitlab.domain/users/sign_in
referrer-policy: strict-origin-when-cross-origin
referrer-policy: strict-origin-when-cross-origin
server: nginx
strict-transport-security: max-age=315360000
vary: Accept-Encoding
x-content-type-options: nosniff
x-download-options: noopen
x-frame-options: DENY
x-permitted-cross-domain-policies: none
x-request-id: Z8uahE5gN39
x-runtime: 0.012164
x-ua-compatible: IE=edge
x-xss-protection: 1; mode=block
复制代码
打开项目配置,会发现 Runner 已经上线。
继续在 GitLab Runner 运行 CI 流水线,会看到仍是报错没法经过构建。
Running with gitlab-runner 12.2.0~beta.1803.g41d5c6ad (41d5c6ad)
on ci-runner S8sY8o6A
Using Shell executor...
Running on ci-runner...
Fetching changes with git depth set to 50...
Reinitialized existing Git repository in /data/runner/builds/S8sY8o6A/0/project/.git/
> GitLab: The project you were looking for could not be found.
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
ERROR: Job failed: exit status 1
复制代码
还记得前面提到过的 Authorization
请求头和 HTTP RFC规范吗?GitLab Runner 在处理 CI 任务的时候,使用的是https://gitlab-ci-token:[MASKED]@gitlab.domain/repo.git/
这样的 HTTP 协议,请求中的用户名和密码和 Nginx ProxyPass 中的字段“八字不合”。
那么不使用 HTTP 协议,使用 SSH 协议或许能解决问题。
惋惜的是,官方并不支持 GitLab Runner 使用 SSH 协议进行仓库下载,有相似需求的用户还真很多,若是你愿意找,相似下面的 issue 还有很多:
虽然官方没有直接提供这个功能,可是从官方文档中看到有一个 clone_url
,查找代码发现实现很简单,只须要稍微改造就可以知足咱们的需求:
// GetRemoteURL checks if the default clone URL is overwritten by the runner
// configuration option: 'CloneURL'. If it is, we use that to create the clone
// URL.
func (b *Build) GetRemoteURL() string {
cloneURL := strings.TrimRight(b.Runner.CloneURL, "/")
if !strings.HasPrefix(cloneURL, "http") {
return b.GitInfo.RepoURL
}
variables := b.GetAllVariables()
ciJobToken := variables.Get("CI_JOB_TOKEN")
ciProjectPath := variables.Get("CI_PROJECT_PATH")
splits := strings.SplitAfterN(cloneURL, "://", 2)
return fmt.Sprintf("%sgitlab-ci-token:%s@%s/%s.git", splits[0], ciJobToken, splits[1], ciProjectPath)
}
复制代码
改动后的代码以下:
// GetRemoteURL checks if the default clone URL is overwritten by the runner
// configuration option: 'CloneURL'. If it is, we use that to create the clone
// URL.
func (b *Build) GetRemoteURL() string {
cloneURL := strings.TrimRight(b.Runner.CloneURL, "/")
if !strings.HasPrefix(cloneURL, "http") && !strings.HasPrefix(cloneURL, "ssh") {
return b.GitInfo.RepoURL
}
variables := b.GetAllVariables()
ciJobToken := variables.Get("CI_JOB_TOKEN")
ciProjectPath := variables.Get("CI_PROJECT_PATH")
splits := strings.SplitAfterN(cloneURL, "://", 2)
if strings.HasPrefix(cloneURL, "ssh") {
ciProjectPath = strings.TrimLeft(ciProjectPath, "/")
return fmt.Sprintf("git@%s:/%s.git", splits[1], ciProjectPath)
}
return fmt.Sprintf("%sgitlab-ci-token:%s@%s/%s.git", splits[0], ciJobToken, splits[1], ciProjectPath)
}
复制代码
若是你不想在浪费时间在折腾构建环境上,能够参考我以前写的一篇文章: 源码编译 GitLab Runner 。
再次执行 CI 任务,会发现已经可以顺利的进行啦。
Running on ci-runner...
Fetching changes with git depth set to 50...
Reinitialized existing Git repository in /data/runner/builds/S8sY8o6A/0/project.git/
Checking out 860587f6 as master...
Skipping Git submodules setup
Warning: Permanently added '192.168.x.x' (ECDSA) to the list of known hosts.
From gitlab.domain:project
3957006..860587f master -> origin/master
Updating 3957006..860587f
Fast-forward
.gitlab-ci.yml | 6 +++---
README.md | 1 +
2 files changed, 4 insertions(+), 3 deletions(-)
Stopping service ...
Stopping service ... done
Removing service ...
Removing service ... done
Network service is external, skipping
Creating service ...
Creating service ... done
Job succeeded
复制代码
GitLab 先折腾到这里,或许后面有时间的会写一篇更加全面的折腾攻略。若是你对 GitLab 感兴趣,能够浏览我以前写的相关文章。
—EOF
我如今有一个小小的折腾群,里面汇集了一些喜欢折腾的小伙伴。
在不发广告的状况下,咱们在里面会一块儿聊聊软件、HomeLab、编程上的一些问题,也会在群里不按期的分享一些技术沙龙的资料。
喜欢折腾的小伙伴欢迎扫码添加好友。(请注明来源和目的,不然不会经过审核)