使用 Docker 和 Traefik 搭建 GitLab(後篇)

蘇洋發表於2019-04-10

本文使用「署名 4.0 國際 (CC BY 4.0)」許可協議,歡迎轉載、或重新修改使用,但需要註明來源。 署名 4.0 國際 (CC BY 4.0)

本文作者: 蘇洋

建立時間: 2019年04月10日 統計字數: 7143字 閱讀時間: 15分鐘閱讀 本文連結: soulteary.com/2019/04/10/…


使用 Docker 和 Traefik 搭建 GitLab(後篇)

前篇文章提到了要介紹一些 GitLab 安全配置上的問題,本篇文章就來簡單聊聊如何加固你部署在公網上的 GitLab 程式碼倉庫。

問題背景

一般來說,我們建議在內網環境下使用 GitLab 這類包含大量敏感資訊,相對大型複雜的軟體,因為網路的隔離,天然可以減少許多“可攻擊面”。

如果需要外部使用,則推薦使用 “專用隧道” 等方式提供定向的流量訪問途徑。

前文提到過,這次遇到的訴求恰恰是:

  • 不能使用專有的流量隧道
  • 不能搭建在內網,要提供公網訪問方式

沒關係,見招拆招即可。

分析攻擊基本面

在暫時不考慮內鬼作祟的情況下,對應用進行環境加固。可以先對可以受到攻擊的場景進行簡單的分類:

  • 系統層(宿主機環境)
  • 網路層(具備流量互動的場景)
  • 應用層(軟體執行環境、相關配置)
  • 使用者層(軟體使用日常場景)

考慮篇幅、文章針對性兩個原因,本文重點介紹“應用層”安全加固。

系統加固

網上關於系統加固的資料有不少,我之前的文章中也有零碎的提過,感興趣可以自行翻看。為了方便你進行搜尋,這裡簡單提一下常規手段:

  • 針對敏感埠進行日誌審計。
  • 修改系統敏感埠,比如 SSH 埠,並考慮按需開啟。
  • 限制登入使用者和使用者登入方式(比如堡壘機、或者祕鑰認證),並建立普通使用者取代 root 進行日常操作。
  • 購買雲安全服務,並對接監控告警。

網路加固

網路加固的內容比較雜,我們從下面五個點展開。

加密流量傳輸

網路加固這裡有一個簡單原則,除了本機流量外,但凡可以使用 SSL 加密的流量,一律使用 SSL 加密模式進行傳輸,包括:

  • 跨主機之間的系統呼叫
  • 應用和資料庫之前的呼叫

雖然不進行 SSL 化配置,部署和編碼成本會有所提高,如果機器資源緊張,還可能影響一些效能,並且還可能帶來額外的費用問題:

  • 企業使用的SSL證書按年付費,價格十分昂貴。
  • 不過如果你覺得購買證書費用太過高昂,也可以使用自簽名證書來解決問題。

實際上,部署 SSL 所帶來的各種成本放到長期來看,都是可以忽略不計的一次性投入,但是安全風險問題是基礎和底線,不值得為此冒險

避免公開的 DNS 解析

提到網路服務,其中有一點經常被忽略:DNS 解析

如果你的應用只針對少數人提供服務,不妨考慮不在公網 DNS 上進行解析,僅通過繫結 Hosts 提供服務。

配合 Traefik 的服務發現功能,如果對方不知道你的服務域名,即使通過 IP 掃描到你的站點,請求後得到的結果也只有 404 Not Found

新增網路請求驗證

上一條措施,不進行公網域名暴露,已經可以解決一大部分掃描器的嗅探。但是面對有針對性的攻擊,這招就不靈光了。

此時,建議在我們的 Web 系統前新增一層基礎的使用者驗證:Baisc Auth

使用 Traefik 新增這層驗證很容易,只需要下面兩行簡單的宣告:

- "traefik.gitlab.frontend.auth.basic=${BASIC_AUTH}"
- "traefik.gitlab.frontend.auth.basic.removeHeader=true"
複製程式碼

這兩行配置的作用是:

  • 第一行告訴程式,我們要使用 Basic 認證,認證的使用者名稱密碼是什麼。
  • 第二行配置則告訴程式,這個認證僅僅在 Traefik 流量進入的時候使用,不要繼續傳遞給應用程式,避免帶來其他麻煩(比如 Confluence 這類應用會將 HTTP 請求頭中的 authorization 用作系統登入憑據)。

當然,這裡同樣需要建立一個 .env 環境配置檔案,比如:

BASIC_AUTH=soulteary:$apr1$rgGAffTk$vDZ1tL03og0nZ8XlCfdv80
複製程式碼

如果你好奇這段程式碼是如何生成的,可以在使用 Docker 搭建 Confluence 這篇文章中找到答案。

下面給出一個相對完整的配置參考:

labels:
      - "traefik.enable=true"
      # GitLab Web 服務
      - "traefik.gitlab.frontend.auth.basic.removeHeader=true"
      - "traefik.gitlab.frontend.auth.basic=${BASIC_AUTH}"
      - "traefik.gitlab.port=80"
      - "traefik.gitlab.frontend.rule=Host:gitlab.${BASEHOST}"
      - "traefik.gitlab.frontend.entryPoints=http,https"
      - "traefik.gitlab.frontend.headers.SSLProxyHeaders=X-Forwarded-For:https"
      - "traefik.gitlab.frontend.headers.STSSeconds=315360000"
      - "traefik.gitlab.frontend.headers.browserXSSFilter=true"
      - "traefik.gitlab.frontend.headers.contentTypeNosniff=true"
      - "traefik.gitlab.frontend.headers.customrequestheaders=X-Forwarded-Ssl:on"
      - "traefik.gitlab.frontend.passHostHeader=true"
      - "traefik.gitlab.frontend.passTLSCert=false"
複製程式碼

使用浮動 IP

如果對方不光使用侵入的方式進行攻擊,還想讓你暫時無法正常使用系統,比如對你進行令人髮指的 DDoS 攻擊。

作為被攻擊方,可以使用 浮動IP 的方式,在遭遇攻擊的時刻,降低切換 IP 的成本,快速金蟬脫殼,這裡配合支援動態加速的 CDN 服務效果更好。

應用層

應用層做的事情也比較雜,我們來慢慢說起。

使用者側流量加密

建議系統不提供任何 HTTP 流量,防止使用者側流量被劫持利用。

所有出公網流量一律走 HTTPS,如果你也使用前文提到的 Traefik ,那麼這個事情預設就是做好了的(參考剛剛的配置)。

對接 Prometheus 效能監控

如果你對可用性有很高的要求,可以參考官方文件,對接 Prometheus 效能監控,如果你對Prometheus沒有概念,可以先瀏覽一下官方的線上示例,這部分展開聊可以寫好幾篇,先略過。

處理 CI Runner

CI 雖然作為呼之即來、揮之即去的“附加部分”,但是實際上也可以因為“頻繁呼叫”而拒絕服務,或者因為不恰當的 CI 配置,而洩露敏感資訊,或者作為攻擊跳板,傷害到線上業務程式碼。

對於 GitLab CI Runner 執行監控,推薦使用 timoschwarzer/gitlab-monitor ,不過如果你在系統中配置好了推送訊息,專案數量比較少的時候,一個手機Push過來,或許更方便迅捷。

對於 CI Runner ,要確定儘可能少的提供 SHELL 模式的 Runner,多提供容器模式的 Runner,減少 Runner 攻擊到宿主機的可能。

另外 Runner 可被觸發的分支和倉庫要做額外的限制,儘可能避免過度頻繁的 Runner 執行,讓宿主機器“過勞死”。

最後,Runner 中使用的環境變數和配置資訊,需要使用加密環境變數的方式進行獲取,而非明文寫死在配置檔案程式碼中。GitLab 這部分做的很好,有興趣的小夥伴可以瞭解一下。

監控 GitLab SSH 埠

因為我們對使用者提供了 SSH 的方式去 ClonePush 程式碼,所以作為開放訪問的 SSH 埠就面臨被攻擊的可能。

下面是一臺長期執行在公網的程式碼倉庫的埠日誌(cat logs/sshd/current),我節選了比較有代表性的一部分日誌,並隱去了具體時間:

Invalid user admin from 179.53.182.234
Invalid user user from 183.89.94.13
Invalid user admin from 36.236.233.142
Invalid user admin from 143.255.154.219
Invalid user admin from 85.57.5.107
Invalid user ubnt from 85.57.5.107
Invalid user admin from 85.57.5.107
Invalid user admin from 156.223.73.14
Invalid user support from 200.145.6.88
Invalid user admin from 152.231.118.191
Invalid user user from 171.228.172.27
Invalid user admin from 27.66.79.45
Invalid user Admin from 117.0.57.69
Invalid user admin from 14.207.231.218
Invalid user gitlab from 121.71.20.66
Invalid user admin from 183.157.173.121
Invalid user guest from 37.214.104.206
Invalid user admin from 197.32.190.120
Invalid user Administrator from 125.34.196.43
Invalid user Administrator from 125.34.196.43
Invalid user \243\254git from 112.87.206.54
Invalid user \243\254git from 112.87.206.54
Invalid user admin from 116.118.104.96
Invalid user admin from 14.186.202.33
Invalid user admin from 113.172.217.15
....
複製程式碼

可以看到有大量掃描器在默默的替你關注者你的系統安全,毫不誇張的說,一旦你漏出破綻,你的機器、你的應用就不歸你使用了,這類掃描器的擁有者便能光明正大的隨意用你的機器、玩你的系統、欺負你的使用者…

如何避免這類惡意的掃描器呢?其實寫一段簡單的日誌檢測指令碼就能解決很大一部分問題。

比如下面這段指令碼,在參考 這篇文章 後,我結合實際情況,更新了它,讓指令碼能夠處理 GitLab 的日誌格式。

#!/bin/bash

# 允許錯誤嘗試的最大次數
LIMIT=3
# 要進行分析的日誌檔案
SCAN_LOG="/data/gitlab/logs/sshd/current"
# 封禁IP記錄
LOGFILE="/data/gitlab/logs/bad_gay_22_port.log"
# 要匹配的日誌格式: 2019-04-10_12
TIME=$(date '+%Y-%m-%d_%H')

# 掃描當前 GitLab 日誌,找出所有的錯誤登入行為,並進行計數,篩選出超過允許次數的 IP
BLOCK_IP=$(grep "$TIME" "$SCAN_LOG"|grep "Invalid user"|awk '{print $(NF-3)}'|sort|uniq -c|awk '$1>"$LIMIT"{print $1":"$2}')
for i in $BLOCK_IP
do
    IP=$(echo $i|awk -F: '{print $2}')
	# 驗證該IP是否已經被封禁
    iptables-save|grep INPUT|grep DROP|grep $IP>/dev/null
	# 如果未被封禁,則進行封禁操作
    if [ $? -gt 0 ];then
        iptables -A INPUT -s $IP -p tcp --dport 22 -j DROP
        NOW=$(date '+%Y-%m-%d %H:%M')
        echo -e "$NOW : $IP">>${LOGFILE}
    fi
done
複製程式碼

將上面的內容儲存為 gitlab_ssh.sh ,然後賦予指令碼可執行許可權。

chmod 755 gitlab_ssh.sh && chmod +x gitlab_ssh.sh
複製程式碼

接著將指令碼放到 GitLab 應用目錄中(或者任意你方便管理的地方),舉個例子: /data/gitlab/gitlab_ssh.sh

最後將指令碼新增到 crontab 中,以10分鐘為粒度執行 (結合自己情況進行調整)。

echo "*/10 * * * * root /data/gitlab/gitlab_ssh.sh" >>/etc/crontab
複製程式碼

不出意外,往後如果還有這類掃描器,他們最多隻能撲騰個10分鐘左右。

至於這個指令碼的戰績,可以通過檢視 /data/gitlab/logs/bad_gay_22_port.log 日誌檔案來進行了解:

2019-04-10 19:33 : 113.172.217.15
2019-04-10 19:33 : 14.186.202.33
複製程式碼

監控介面登入

前面已經在網路層新增了訪問授權,但是如果授權密碼洩露,被針對性攻擊,比如在介面/應用介面層面進行弱口令掃描,那麼又該如何處理呢。

配合 fail2ban 可以減少這類事情的影響,下面給出一段參考指令碼

[Init]
maxlines = 6

[Definition]

# The relevant log file is in /var/log/gitlab/gitlab-rails/production.log
# Note that a single failure can appear in the logs up to 3 times with just one login attempt. Adjust your maxfails accordingly.

## Example fail - clone repo via https
#Started GET "/" for 10.0.0.91 at 2016-10-25 00:01:24 +0200
#Processing by RootController#index as HTML
#Completed 401 Unauthorized in 69ms (ActiveRecord: 23.7ms)

## Example fail - login via GUI
#Started GET "//chmielu/test.git/info/refs?service=git-upload-pack" for 10.0.0.91 at 2016-10-25 00:01:09 +0200
#Processing by Projects::GitHttpController#info_refs as */*
#  Parameters: {"service"=>"git-upload-pack", "namespace_id"=>"chmielu", "project_id"=>"test.git"}
#Filter chain halted as :authenticate_user rendered or redirected
#Completed 401 Unauthorized in 50ms (Views: 0.8ms | ActiveRecord: 8.1ms)


failregex = ^Started .* for <HOST> at .*<SKIPLINES>Completed 401 Unauthorized

ignoreregex =
複製程式碼

使用者層

使用者層面其實問題不多,如果你能確定你可以堅持使用以下措施的話:

  • 關注官方版本更新和 changelog,及時更新應用版本,減少 XSSCVE 漏洞問題。
  • 進行最小許可權授予,減少錯誤授權帶來的風險。
    • 在系統設定中設定所有專案都是 private 的,避免某雲平臺的事故重演。
    • 避免新增過多的全域性 Admin 角色,針對專案群組和專案進行管理員設定。
  • 僅允許使用 SSH 方式進行程式碼 ClonePush,推薦使用祕鑰認證的方式進行系統互動。
  • 儘可能減少與外部系統的互動,比如匯入外部倉庫,僅支援你覺得必要的來源;比如服務呼叫,僅呼叫你覺得安全可靠的。
  • 關閉預設註冊方式,使用邀請制度,或者使用 SSO/LDAP 方式進行註冊。
  • 根據實際情況進行使用者頻率限制(系統功能)。
  • 要求你的使用者使用隨機生成的強密碼,並定期更換。

最後

使用容器在公網環境搭建 GitLab 就先介紹到這裡,效能監控部分,等把 WordPress 的坑填完,再細聊吧。


我現在有一個小小的折騰群,裡面聚集了一些喜歡折騰的小夥伴。

在不發廣告的情況下,我們在裡面會一起聊聊軟體、HomeLab、程式設計上的一些問題,也會在群裡不定期的分享一些技術沙龍的資料。

喜歡折騰的小夥伴歡迎掃碼新增好友。(請註明來源和目的,否則不會通過稽核)

關於折騰群入群的那些事

相關文章