避免持續整合(CI)成為一個安全隱患
背景
最近臨時交接了一個客戶測試環境和產品環境的維護工作。交接的客戶資產包含:程式碼庫、生產環境主機、測試環境主機、搭建在測試環境主機上的持續整合伺服器以及對應的賬號密碼。這個持續整合伺服器採用Jenkins搭建,並且可以用來部署測試環境和生產環境的應用。
不久,接到了客戶的一個維護請求:把最新的生產環境資料同步到測試環境裡。
這個維護任務需要通過SSH登入到測試環境主機上進行操作。測試主機是通過authorized_keys進行SSH認證的,只要你自己的ssh-key被新增到了主機上,就可以實現無密碼登入。這樣有兩個好處:一方面維護人員無需使用密碼,避免了生產環境密碼的洩露。另一方面可以按需吊銷不再使用的客戶端,及時回收許可權。所以我需要把自己的sshpublickey交給管理員,讓他把我的key加到可訪問列表裡。
悲劇的是,前管理員告訴我,他的key因為更換電腦的關係沒有及時更新。所以,他也無法登入主機。而且之前參與維護的其它管理員的key也都失效了,這意味著我們失去了對主機的控制。此時,我手上只有登入Jenkins的的使用者名稱和密碼,於是一個邪惡的想法就誕生了:
既然Jenkins可以執行指令碼,那麼我是否可以通過Jenkins把我的key注入進去?
於是我把ExecuteShell的Job變成了我的命令列,通過執行日誌得知了宿主使用者的檔案目錄資訊。然後把自己的sshpublickey加到了登入列表裡(此處省略敏感資訊):
1234 | sudo sh-c“cp~/.ssh/authorized_keys~/.ssh/authorized_keys.bak”sudo sh-c"echo‘{我的sshpublickey}’>>~/.ssh/authorized_keys" |
It works !
我成功的登入了機器,但這卻暴露了一個問題:持續整合伺服器成為了一個安全隱患。
首先,持續整合伺服器可以執行程式碼。這就意味著它有可能執行有害程式碼。
其次,持續整合伺服器缺乏足夠的使用者鑑權,這很有可能導致未授權使用者訪問。
無許可權控制的伺服器+可以執行程式碼=裸奔的肉雞
那麼,如何構建一個更安全的持續整合伺服器伺服器?
rootless原則
“神操縱著萬物,你感覺得到他,但永遠看不見他。”——《聖經·希伯來書11:27》
在伺服器的世界裡,root使用者就是神,擁有至高的權力和力量。如果有人獲得了“神之力”,後果可能不堪設想。
無論是Web伺服器、資料庫伺服器還是持續整合伺服器。都是這個世界裡的二等公民,許可權和力量都應該受到約束。執行的時候應該受到控制。
此外,應該極力避免sudo的濫用,尤其是對那些從外部訪問的使用者。很多情況下,為了操作方便,很多使用者都有sudo的許可權。但這恰恰造成了低許可權使用者通過提升自己的訪問許可權進行有害操作。
在上述的故事裡,因為沒有對Jenkins的主機使用者做有效隔離,導致了我可以用sudo注入自己的key獲得機器的訪問許可權。
沙盒隔離原則
因為持續整合伺服器會執行指令碼或執行程式,而這些程式和指令碼有可能是存在惡意程式碼的。所以,對應的任務應該在隔離的安全沙盒中執行,例如:受限的使用者,受限的許可權,受限的空間。
在上述的故事裡,我就通過CI執行了一段不安全的指令碼成功獲得了登入主機的許可權。
如果這些任務在隔離並受控的Docker容器裡執行,那麼會安全得多。
當然,也可以考慮採用TravisCI這樣的第三方持續整合服務來保證安全性。
備份和備份核查原則
在上述故事裡,因為缺乏有效的備份機制,導致了所有人都無法訪問主機。此外,我在修改authorized_keys的時候先進行了備份。這樣,如果我注入失敗,還可以還原。
這裡的備份,不光是對配置、資料的備份,還有崗位的備份。
如果管理員有備份,完全不會出現無法登陸的事情。
如果有備份QA伺服器,完全可以不需要當前的QA伺服器。
在做任何變更前,都應該做好備份以及還原的準備。因為任何變更都會帶來“蝴蝶效應”。
但是,光備份是不夠的。如果備份不能有效還原,那和沒有備份沒有什麼區別。所以,要定時的進行備份恢復測試。確保備份在各種情況下可用。
多重要素身份驗證原則
上述的持續整合伺服器是暴露在網際網路中的,任何一個人訪問到這個站點,通過一定程度的密碼破解,就可以獲得這個持續整合伺服器的訪問控制權限。從而可以做出上述的操作。
所以,有了使用者名稱和密碼,並不一定是可信使用者。還需要通過更多的手段,諸如手機簡訊驗證碼或者第三方認證整合來驗證使用者的身份。
關鍵操作手動驗證原則
試想一下,如果在上述的例子中我並沒有伺服器的訪問許可權。而是通過提交未經審查的程式碼自動執行測試指令碼。實際上也會造成同樣的效果。
有時候我們會為了方便,讓持續整合伺服器自動觸發測試。但是,恰恰是這種“方便”帶來了額外的安全隱患。而這樣的方便,不光方便了自己,也方便了惡意入侵者。
所以,不能為了方便而留下安全隱患。在關鍵操作上設定為手動操作,並通過一定機制保證關鍵操作的可靠性才是最佳實踐。
構建安全CI的幾個實踐:
採用Sibling的方式在Docker裡執行任務。
賬戶密碼管理統一採用LDAP認證,如果過期則從外部修改。
CI的登入許可權和其它的認證方式(比如GitHub、Okta等)整合起來。並用組限制登入。
對於生產環境的CI,通過更加細粒度的許可權限制來隔離一些危險操作。
官方的安全指南
不少持續整合工具的官方都提供了最佳實踐以及安全指南幫助我們構建持續整合伺服器。請務必在構建持續整合伺服器前閱讀並理解這些安全實踐和措施,並遵照安全最佳實踐構建持續整合伺服器:
如果沒有這些如果
上面提到了太多的如果。如果這些“如果”能發生在事前,這些問題就不會產生。持續整合本身是開發的最佳