作者:老Z,中電信數智科技有限公司山東分公司運維架構師,雲原生愛好者,目前專注於雲原生運維,雲原生領域技術棧涉及 Kubernetes、KubeSphere、DevOps、OpenStack、Ansible 等。
簡介
生產環境 KubeSphere 3.3.0 部署的 Kubernetes 叢集在安全評估的時候發現安全漏洞,其中一項漏洞提示目標可能存在 Apache Log4j2 遠端程式碼執行漏洞 (CVE-2021-44228)。
本文記錄了該漏洞修復的全部過程,文中介紹了修復該漏洞的兩種解決方案,其中涉及自定義構建 KubeSphere 適用的 Jenkins Image 的詳細操作。
漏洞修復方案
漏洞詳細資訊
漏洞報告中涉及漏洞 目標可能存在 Apache Log4j2 遠端程式碼執行漏洞 (CVE-2021-44228) 的具體資訊如下:
漏洞分析
- 分析漏洞報告資訊,發現對應的服務埠為 30180,對應的服務為 Jenkins。使用 Curl 訪問服務埠,檢視返回頭資訊。
[root@ks-k8s-master-0 ~]# curl -I http://192.168.9.91:30180
HTTP/1.1 403 Forbidden
Date: Thu, 09 Feb 2023 00:36:45 GMT
X-Content-Type-Options: nosniff
Set-Cookie: JSESSIONID.b1c3bc24=node084x6l5z2ss0ghsb2t9tde2gl16558.node0; Path=/; HttpOnly
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Type: text/html;charset=utf-8
X-Hudson: 1.395
X-Jenkins: 2.319.1
X-Jenkins-Session: 1fde6067
Content-Length: 541
Server: Jetty(9.4.43.v20210629)
說明: 從結果中可以看到 KubeSphere 3.3.0 採用的 Jenkins 使用的 Jetty 版本為 9.4.43.v20210629,跟漏掃報告中的結果一致。
- 在 K8s 中檢視 Jenkins 服務使用的 Image 版本。
[root@ks-k8s-master-0 ~]# kubectl get deploy devops-jenkins -n kubesphere-devops-system -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
devops-jenkins 1/1 1 1 101d devops-jenkins registry.cn-beijing.aliyuncs.com/kubesphereio/ks-jenkins:v3.3.0-2.319.1 component=devops-jenkins-master
說明: 從結果中可以看到 KubeSphere 3.3.0 採用的 Jenkins 版本為 2.319.1,跟漏掃報告中的結果一致。
- 在 K8s 中確認 Jenkins 哪些元件用到了跟 Log4j 有關的 JAR 包。
- 查詢 Jenkins 服務對應的 Pod。
[root@ks-k8s-master-0 ~]# kubectl get pod -n kubesphere-devops-system -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
devops-27930510-52tck 0/1 Completed 0 87m 10.233.116.224 ks-k8s-master-2 <none> <none>
devops-27930540-6b7cz 0/1 Completed 0 57m 10.233.116.225 ks-k8s-master-2 <none> <none>
devops-27930570-t5k6b 0/1 Completed 0 27m 10.233.116.226 ks-k8s-master-2 <none> <none>
devops-apiserver-6b468c95cb-grx4j 1/1 Running 2 (4h30m ago) 101d 10.233.116.211 ks-k8s-master-2 <none> <none>
devops-controller-667f8449d7-w8mvf 1/1 Running 2 (4h37m ago) 101d 10.233.117.173 ks-k8s-master-0 <none> <none>
devops-jenkins-67fbd685bf-4jmn4 1/1 Running 7 (4h23m ago) 101d 10.233.117.162 ks-k8s-master-0 <none> <none>
s2ioperator-0 1/1 Running 2 (4h36m ago) 101d 10.233.87.33 ks-k8s-master-1 <none> <none>
- 進入 Jenkins Pod 的命令列控制檯
[root@ks-k8s-master-0 ~]# kubectl exec -it devops-jenkins-67fbd685bf-4jmn4 -n kubesphere-devops-system -- bash
Defaulted container "devops-jenkins" out of: devops-jenkins, copy-default-config (init)
root@devops-jenkins-67fbd685bf-4jmn4:/#
- 在 Jenkins 掛載的主目錄中查詢跟 Log4j 有關的 JAR 包 (所有操作都是在容器內部)。
# 進入 Jenkins 資料主目錄
root@devops-jenkins-67fbd685bf-4jmn4:~# cd /var/jenkins_home
# 查詢帶 log4j 關鍵字帶所有 jar 包
root@devops-jenkins-67fbd685bf-4jmn4:/var/jenkins_home# find ./ -name "*log4j*"
./war/WEB-INF/lib/log4j-over-slf4j-1.7.32.jar
./plugins/ssh-slaves/WEB-INF/lib/log4j-over-slf4j-1.7.26.jar
./plugins/prometheus/WEB-INF/lib/log4j-over-slf4j-1.7.30.jar
說明: 並沒有搜到到 log4j 的核心包,但是搜尋到了 log4j-over-slf4j,該軟體包並不屬於 log4j 官方 MAVEN 倉庫(也可能是我沒有找到),而且居然有 3 個不同版本,Jenkins 自己用了一個版本,還有兩個外掛也用了兩個不同的版本。
百度了一下 log4j-over-slf4j 是用來把日誌框架的 Log4j API 橋接到 SLF4J API,實際上底層使用的 SLF4J,細節我並沒有深究,也沒有去研究漏洞掃描工具是如何判斷的。
但是由於搜尋不到其他的 log4j 相關的 JAR 包,初步判定是由於 log4j-over-slf4j 包引起的漏洞。
- 漏洞原因
- log4j-over-slf4j 包引起的漏洞
- Jenkins 服務執行環境使用的 Jetty 版本過低。
- 以上二者均有。
漏洞修復方案
- 方案一: 替換查詢到的所有 log4j-over-slf4j 相關的 jar 包
- 只考慮 Log4j 是造成該漏洞資訊的主要原因,應該是 Jenkins 程式呼叫的 log4j-over-slf4j 引起的,可以只換這一個,但是為了安全起見,我把 3 個都替換了。
- 硬替 jar 包的方式可能引起某些特殊場景的異常,一定要備份好之前的 jar 包,便於回滾。
- 解決方案可能並不徹底,只是根據 Log4j 的相關修復建議,替換了有關的 JAR 包,但是並不符合 Jetty 版本要求,目前版本為 9.4.43.v20210629 需要升級到不低於 11.0.7 的版本。
- 如果沒有特殊需求,建議首選這種修復方案,雖然沒有解決根本,但是勝在簡單,回滾方便,整體出問題的可能性更小。
說明: 該方案已經驗證, 替換 jar 包後重啟 devops-jenkins,重新執行漏洞掃描,該漏洞沒再次發現。
重啟後也執行過相應的流水線任務,目前看來功能也都正常。畢竟沒有完全充分測試,所以不排除某些特殊場景可能存在問題。
- 方案二: 升級 Jenkins 和相應的外掛到比較新的版本
- 升級比較徹底, Jetty 和 Log4j 都升級到新的版本。
- 需要考慮版本相容性問題,是否與 KubeSphere 適配,沒在官網找到相關介紹,只能升級嘗試。
- 自己打包比較麻煩,需要有一定的動手能力,需要自己重新構建 ks-jenkins 使用的 Image。
- 官方的打包專案文件比較簡單,需要有一定的基礎。
- 利用重新構建的 Image 重啟已有的 Jenkins 服務後,原有資料配置是否絲毫不受影響?還需要進一步深層次驗證。
- 升級 Jenkins 和外掛還有一個簡單方法,直接在 Jenkins 的管理介面把提示升級的元件都升級了,這些更簡單便捷的解決了版本相容性的問題,有興趣的可以自己嘗試。
- 如果選全升級的解決方案,一定要自己做充分的驗證測試,才可以在生產環境升級。畢竟,未知的永遠是可怕的。
- 本文在實驗過程中遇到了很多外掛版本相容性問題,後面文件中的內容都是不斷摸索、試錯整理的,而且最終也沒有完美解決。
- 再次強調,除非明確的知道你在做什麼,能搞定所有變動帶來對風險,否則不要輕易的在生產環境用該方案!!!
說明: 為了 get 一個新的技巧,本文後面的內容重點介紹手動自定義構建 ks-jenkins Image 的實戰操作。
自定義構建 Jenkins 映象
KubeSphere 使用的 Jenkins Image 是採用 GitHub 中 ks-jenkins 專案下的相關工具和配置進行打包的,該專案底層工具使用的是 jcli,具體說明可以檢視專案介紹。
專案文件比較簡單,沒有一定的程式碼開發基礎,實戰起來還是比較困難的,我也是根據自己的理解和搜尋引擎整理了操作過程,有些細節知識點並沒有深入瞭解,目前也僅僅是解決了能打包的需求。
重新構建的映象思路和方法是對的,但是整體的功能性與相容性,並沒有進行過充分測試,而且實戰過程中也遇到了很多沒有解決的問題,後續操作的價值僅限於參考。生產環境使用請慎重。
配置開發環境
- 安裝 Java
從 Oracle 官網下載軟體包 jdk-8u361-linux-x64.tar.gz,這個需要自己註冊賬戶才能下載。大家也可以使用自己習慣的方式配置 安裝配置 JAVA 環境。
# 建立工具存放目錄,並解壓 jdk 軟體包
mkdir /data/tools
tar xvf jdk-8u361-linux-x64.tar.gz -C /data/tools
# 配置 java profile
cat >> /etc/profile << "EOF"
# java environment
export JAVA_HOME=/data/tools/jdk1.8.0_361
export CLASSPATH=.:${JAVA_HOME}/jre/lib/rt.jar:${JAVA_HOME}/lib/dt.jar:${JAVA_HOME}/lib/tools.jar
export PATH=$PATH:${JAVA_HOME}/bin
EOF
# 驗證 Java
source /etc/profile
java -version
# 輸出資訊如下,表示 Java 安裝成功
java version "1.8.0_361"
Java(TM) SE Runtime Environment (build 1.8.0_361-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.361-b09, mixed mode)
- 安裝 Maven
# 下載 並解壓 Maven
wget https://archive.apache.org/dist/maven/maven-3/3.8.7/binaries/apache-maven-3.8.7-bin.tar.gz
tar xvf apache-maven-3.8.7-bin.tar.gz -C /data/tools/
# 配置 maven profile
cat >> /etc/profile << "EOF"
# maven environment
export M2_HOME=/data/tools/apache-maven-3.8.7
export PATH=$PATH:$M2_HOME/bin
EOF
# 驗證 Maven
source /etc/profile
mvn -version
# 輸出資訊如下,表示 Maven 安裝成功
Apache Maven 3.8.6 (84538c9988a25aec085021c365c560670ad80f63)
Maven home: /data/tools/apache-maven
Java version: 1.8.0_361, vendor: Oracle Corporation, runtime: /data/tools/jdk1.8.0_361/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "3.10.0-1160.83.1.el7.x86_64", arch: "amd64", family: "unix"
- 安裝 jcli
cd /tmp
curl -L https://github.com/jenkins-zh/jenkins-cli/releases/latest/download/jcli-linux-amd64.tar.gz | tar xzv
mv jcli /usr/local/bin/
說明: 受限於網路原因,可能需要多次操作,最好是使用其他方式提前下載並傳送到伺服器。
- 安裝 Git(可選項)
安裝 Git 的目的是從 GitHub 上拉取程式碼,也可以不使用 Git,直接在 GitHub 上下載打包好的程式碼檔案。考慮 GitOps 配置管理及以後的擴充套件需求,本文重點介紹 Git 的方式。
CentOS 系統自帶的 Git 版本比較老,在某些場景使用時會有版本相容性問題。因此,選擇了第三方的軟體源安裝新版的 Git。
# CentOS 7 安裝 ius 軟體源
curl https://setup.ius.io | sh
# 安裝新版本 Git
yum install git236
# 驗證 Git
git version
# 輸出資訊如下,表示 Git 安裝成功
git version 2.36.4
- 安裝 Docker
構建映象的過程中會直接生成 Jenkins Docker 映象。因此,需要打包伺服器安裝 Docker 執行環境。
# 配置 Docker 安裝源
cat >> /etc/yum.repos.d/docker.repo << "EOF"
[docker-ce-stable]
name=Docker CE Stable - $basearch
baseurl=https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/centos/$releasever/$basearch/stable
enabled=1
gpgcheck=1
gpgkey=https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/centos/gpg
EOF
# 安裝 Docker
yum install docker-ce
# 配置 Docker
mkdir /etc/docker
cat >> /etc/docker/daemon.json << "EOF"
{
"data-root": "/data/docker",
"registry-mirrors": [
"https://docker.mirrors.ustc.edu.cn",
"https://mirror.ccs.tencentyun.com",
"https://registry.docker-cn.com"
]
}
EOF
# 啟動 Docker
systemctl start docker --now
# 驗證 Docker
docker version
# 輸出資訊如下,表示安裝成功
Client: Docker Engine - Community
Version: 23.0.0
API version: 1.42
Go version: go1.19.5
Git commit: e92dd87
Built: Wed Feb 1 17:49:02 2023
OS/Arch: linux/amd64
Context: default
Server: Docker Engine - Community
Engine:
Version: 23.0.0
API version: 1.42 (minimum version 1.12)
Go version: go1.19.5
Git commit: d7573ab
Built: Wed Feb 1 17:46:49 2023
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.6.16
GitCommit: 31aa4358a36870b21a992d3ad2bef29e1d693bec
runc:
Version: 1.1.4
GitCommit: v1.1.4-0-g5fd4c4d
docker-init:
Version: 0.19.0
GitCommit: de40ad0
# 安裝 docker-compose,使用國內的映象,官方 GitHub 上下載實在是太慢了。
curl -L https://get.daocloud.io/docker/compose/releases/download/v2.15.1/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
chmod u+x /usr/local/bin/docker-compose
# 驗證 docker-compose
docker-compose version
# 輸出資訊如下,表示安裝成功
Docker Compose version v2.15.1
自定義構建映象
- 升級版本規劃
- Jenkins:2.375.3,使用 LTS 最新版,具體版本資訊查詢 入口。
- Jenkins 2.375.3 映象地址:jenkins/jenkins:2.375.3,具體 Image tag 資訊查詢 入口。
- Plugins ssh-slaves:1.834.v622da_57f702c,外掛版本及需要的 Jenkins 最低版本等詳細資訊查詢 入口。
- Plugins prometheus:2.1.1,外掛版本及需要的 Jenkins 最低版本等詳細資訊查詢 入口。
說明: 初步只更新 Jenkins 版本和有關聯的兩個 Plugins 的版本,其他 Plugins 由於不清楚依賴關係及影響,暫時不動。
這也是一個試錯的過程,在後續的測試中發現,僅僅修改兩個外掛的版本號是遠遠不夠的。
- 下載 ks-jenkins 程式碼
mkdir /data/build
cd /data/build
git clone https://github.com/kubesphere/ks-jenkins.git
說明: 受限於網路原因,可能需要多次操作。
- 建立一個新的程式碼分支
# 根據 Master 分支建立新分支,命名方式參考的已有分支
cd ks-jenkins
git checkout -b v3.3.0-2.375.3
- 根據規劃修改配置檔案 formula.yaml
主要修改 jenkins 基礎映象的版本,外掛 ssh-slaves 和 prometheus 的版本,同時因為依賴關係更新了這兩個外掛依賴的其他外掛的版本。
說明: 這個梳理外掛之間的版本依賴關係的過程比較麻煩,我目前也沒有找到更好的辦法,都是在管理介面根據提示的需求修改的。
同時,還修改了 build 的一些引數,具體原因請檢視常見問題。
修改後的主要內容大致如下:
buildSettings:
docker:
base: jenkins/jenkins:2.375.3
tag: {{.tag}}
outputDir: {{.output}}
build: true
war:
groupId: org.jenkins-ci.main
artifactId: jenkins-war
source:
version: 2.375.3
plugins:
......
- groupId: org.jenkins-ci.plugins
artifactId: ssh-slaves
source:
version: 1.834.v622da_57f702c
- groupId: org.jenkins-ci.plugins
artifactId: prometheus
source:
version: "2.1.1"
- groupId: org.jenkins-ci.plugins
artifactId: metrics
source:
version: 4.1.6.1
- groupId: org.jenkins-ci.plugins.pipeline-stage-view
artifactId: pipeline-rest-api
source:
version: "2.21"
- groupId: org.jenkins-ci.plugins
artifactId: trilead-api
source:
version: 1.57.v6e90e07157e1
- groupId: org.jenkins-ci.plugins
artifactId: credentials
source:
version: 1139.veb_9579fca_33b_
- groupId: org.jenkins-ci.plugins
artifactId: ssh-credentials
source:
version: 291.v8211e4f8efb_c
- groupId: io.jenkins
artifactId: configuration-as-code
source:
version: "1569.vb_72405b_80249"
注意: 上面的配置檔案只列出了部分外掛的變更資訊,並不是完整的,並沒有實現完整的效果,僅供參考。
- 修改構建配置檔案 Makefile
主要修改 build 相關的內容
build:
jcli cwp --install-artifacts --config-path formula.yaml \
--value-set output=tmp \
--value-set tag=kubesphere/ks-jenkins:v3.3.0-2.375.3
- 製作 Image
# 執行構建命令
make build
# 正確的構建會有類似下面的輸出
[root@zdevops-main ks-jenkins]# make build
jcli cwp --install-artifacts --config-path formula.yaml \
--value-set output=tmp \
--value-set tag=kubesphere/ks-jenkins:v3.3.0-2.375.3
Feb 09, 2023 2:31:00 PM io.jenkins.tools.warpackager.lib.impl.Builder buildIfNeeded
INFO: Component org.jenkins-ci.main:jenkins-war:2.375.3: no build required
Feb 09, 2023 2:31:00 PM io.jenkins.tools.warpackager.lib.impl.Builder buildIfNeeded
INFO: Component io.jenkins:configuration-as-code:1.53: no build required
....
Feb 09, 2023 2:31:00 PM io.jenkins.tools.warpackager.lib.impl.Builder checkoutIfNeeded
INFO: Will checkout bundle-plugins from local directory: remove-bundle-plugins.groovy
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------< io.github.ks-jenkins:ks-jenkins >-------------------
[INFO] Building ks-jenkins 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
Downloading from repo.jenkins-ci.org: https://repo.jenkins-ci.org/public/org/jenkins-ci/main/jenkins-war/2.375.3/jenkins-war-2.375.3.pom
Downloaded from repo.jenkins-ci.org: https://repo.jenkins-ci.org/public/org/jenkins-ci/main/jenkins-war/2.375.3/jenkins-war-2.375.3.pom (6.4 kB at 625 B/s)
Downloading from incrementals: https://repo.jenkins-ci.org/incrementals/com/microsoft/azure/azure-client-runtime/maven-metadata.xml
...
INFO: Discovered hooks: init.groovy.d
Feb 09, 2023 4:14:50 PM io.jenkins.tools.warpackager.lib.util.DockerfileBuilder build
INFO: Generating Dockerfile
Feb 09, 2023 4:14:50 PM io.jenkins.tools.warpackager.lib.util.DockerfileBuilder build
INFO: Building Docker image kubesphere/ks-jenkins:v3.3.0-2.375.3
[+] Building 329.0s (7/7) FINISHED
=> [internal] load .dockerignore 0.6s
=> => transferring context: 2B 0.0s
=> [internal] load build definition from Dockerfile 0.8s
=> => transferring dockerfile: 295B 0.0s
=> [internal] load metadata for docker.io/jenkins/jenkins:2.375.3 126.8s
=> [internal] load build context 2.3s
=> => transferring context: 334.65MB 2.2s
=> [1/2] FROM docker.io/jenkins/jenkins:2.375.3@sha256:8656eb80548f7d9c7be5d1f4c367ef43 177.5s
=> => resolve docker.io/jenkins/jenkins:2.375.3@sha256:8656eb80548f7d9c7be5d1f4c367ef432f 0.2s
=> => sha256:f16216f97fcb99c68e03372f08859d8efd86e075e8bc22a0707d991b05 13.12kB / 13.12kB 0.0s
=> => sha256:8656eb80548f7d9c7be5d1f4c367ef432f2dd62f81efa86795c915525801 2.36kB / 2.36kB 0.0s
=> => sha256:ed0d0b4f22e27ee95718f7e701f5e189b58f8a4ffbc60b9769124ccabfb1 2.77kB / 2.77kB 0.0s
=> => sha256:699c8a97647f5789e5850bcf1a3d5afe9730edb654e1ae0714d5bd198 55.03MB / 55.03MB 47.3s
=> => sha256:656837bc63c37068f9786473270235ab4830753280df7f0350ad09fb0 51.63MB / 51.63MB 47.7s
=> => sha256:81ba850015575dd93d4b7b63eb5cb6a1aea458b325242ab10531b482cba 8.93MB / 8.93MB 34.5s
=> => sha256:f565cdb160feddd496a91f8857376382ff4e1b48333c1c0f994a1765bf3 1.24kB / 1.24kB 66.2s
=> => extracting sha256:699c8a97647f5789e5850bcf1a3d5afe9730edb654e1ae0714d5bd198a67a3ed 2.2s
=> => extracting sha256:656837bc63c37068f9786473270235ab4830753280df7f0350ad09fb0374f1ee 1.9s
=> => extracting sha256:81ba850015575dd93d4b7b63eb5cb6a1aea458b325242ab10531b482cba6d2f1 0.2s
=> => extracting sha256:f565cdb160feddd496a91f8857376382ff4e1b48333c1c0f994a1765bf3dd519 0.0s
=> => sha256:7f0db80857b0730f0a39ac7c71449bb1166d945c66bb6a1ea0fd6d75190e250 189B / 189B 79.0s
=> => extracting sha256:7f0db80857b0730f0a39ac7c71449bb1166d945c66bb6a1ea0fd6d75190e2505 0.0s
=> => sha256:b113c3f8acf6fadd7bf46f1085836b609f006c6582857c849361969394 5.68MB / 5.68MB 112.4s
=> => sha256:a02b1ab95401fafdd2c8a936bba450cdebe3182ffddb7d6ca8a319e52a8f3f5 199B / 199B 96.8s
=> => sha256:b51e09c8a0bdd4feb08358353f7214e20375f292d3e18ce11dabb77e 94.02MB / 94.02MB 112.3s
=> => sha256:90c616f07a2df86bef2ad9cc09ca44f34c9d0bc5e7bf6463c53037c7 76.93MB / 76.93MB 150.1s
=> => extracting sha256:b51e09c8a0bdd4feb08358353f7214e20375f292d3e18ce11dabb77e3b21a2a2 0.6s
=> => extracting sha256:a02b1ab95401fafdd2c8a936bba450cdebe3182ffddb7d6ca8a319e52a8f3f53 0.0s
=> => extracting sha256:b113c3f8acf6fadd7bf46f1085836b609f006c6582857c849361969394671a06 0.2s
=> => sha256:32fdd8eaf030672618b17bb89dfd41e06b48b4e5698b5d749b7df02ceb 1.17kB / 1.17kB 143.6s
=> => sha256:22b92623028315e2f81b64231e48c1e8cc5656c2d95b4d43dbea67f1ff 1.93kB / 1.93kB 143.6s
=> => sha256:e04af8aa1a0565689acfc84c92700ee9726771b3e9f2fb14139c382dce6f39 374B / 374B 174.7s
=> => sha256:3938fe646b557890e41807ff23870f648fd4eed439d13bc617f3d1f267e56a 269B / 269B 174.9s
=> => extracting sha256:90c616f07a2df86bef2ad9cc09ca44f34c9d0bc5e7bf6463c53037c786653ee2 2.0s
=> => extracting sha256:22b92623028315e2f81b64231e48c1e8cc5656c2d95b4d43dbea67f1ff8b7fb4 0.0s
=> => extracting sha256:32fdd8eaf030672618b17bb89dfd41e06b48b4e5698b5d749b7df02ceba2d8db 0.0s
=> => extracting sha256:e04af8aa1a0565689acfc84c92700ee9726771b3e9f2fb14139c382dce6f3979 0.0s
=> => extracting sha256:3938fe646b557890e41807ff23870f648fd4eed439d13bc617f3d1f267e56ae4 0.0s
=> [2/2] ADD target/ks-jenkins-1.0-SNAPSHOT.war /usr/share/jenkins/jenkins.war 21.8s
=> exporting to image 2.0s
=> => exporting layers 1.9s
=> => writing image sha256:23c59911c9309bb8436716797ab488e80b3d7dbeea0135c9b8c9faa230d24f 0.0s
=> => naming to docker.io/kubesphere/ks-jenkins:v3.3.0-2.375.3
注意: 構建過程會透過 Maven 下載很多依賴包,具體時長視網路和機器配置而定。
映象驗證
映象構建完成後,我們需要驗證映象是否可用,新的映象是否符合安全需求。
- 檢視構建的 Image
[root@zdevops-main ks-jenkins]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
kubesphere/ks-jenkins v3.3.0-2.375.3 23c59911c930 About a minute ago 801MB
- 驗證 Image 是否可用
一定要驗證構建的 Image 是否能建立 Jenkins,並正常使用。
在開發伺服器上使用 Docker 啟動一個 Jenkins。
# 建立容器執行目錄(個人習慣)
mkdir -p /data/containers/jenkins/volumes/jenkins_home
cd /data/containers/jenkins/
chown 1000:1000 volumes/jenkins_home/
# 建立 docker-compose.yml
cat >> docker-compose.yml << "EOF"
version: '3'
services:
jenkins:
image: 'kubesphere/ks-jenkins:v3.3.0-2.375.3'
container_name: jenkins
privileged: true
restart: always
ports:
- '8080:8080'
- '50000:50000'
volumes:
- './volumes/jenkins_home:/var/jenkins_home'
EOF
# 執行 Jenkins
docker-compose up -d
# 執行結果正常如下
[+] Running 2/2
⠿ Network jenkins_default Created 0.1s
⠿ Container jenkins Started 0.7s
# 檢視容器是否成功啟動
[root@zdevops-main jenkins]# docker-compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
jenkins kubesphere/ks-jenkins:v3.3.0-2.375.3 "tini -- /usr/local/…" jenkins About a minute ago Up About a minute 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp, 0.0.0.0:50000->50000/tcp, :::50000->50000/tcp
- 驗證 Jenkins 是否正常
登入 Jenkins 管理控制檯,需要輸入系統初始化密碼 docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
。
登入後,跳過系統初始化,直接進「系統管理」即可(截圖略)。
注意: 實際操作中,外掛管理裡有很多紅色的警告,我也是根據警告不斷的調整不同外掛的版本,再次打包再次驗證。
簡單的方案是在介面把所有存在問題的外掛告警都處理完,然後根據最終結果,整理配置檔案中外掛版本號,然後再次打包。
- 驗證漏洞是否修復
檢查請求響應頭,發現 Jetty 版本依舊不符合要求的 11.0.7。
[root@zdevops-main ~]# curl -I 192.168.9.10:8080
HTTP/1.1 403 Forbidden
Date: Fri, 10 Feb 2023 02:43:37 GMT
X-Content-Type-Options: nosniff
Set-Cookie: JSESSIONID.cb5c1002=node0qid22cxyj1f81jjc8verbvj5e2.node0; Path=/; HttpOnly
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Type: text/html;charset=utf-8
X-Hudson: 1.395
X-Jenkins: 2.375.3
X-Jenkins-Session: d2fd27f1
Content-Length: 541
Server: Jetty(10.0.12)
檢查 log4j 相關的 jar 包,只存在一個 log4j-over-slf4j 的包且已經是比較新的版本
[root@zdevops-main jenkins_home]# find ./ -name "log4j*"
./war/WEB-INF/lib/log4j-over-slf4j-2.0.3.jar
- 驗證結論
新構建的映象 Jetty 版本並不符合安全要求,log4j 相關的包符合要求。
根據配置檔案構建的映象測試效果並不完美,很多外掛無法使用,主要問題在於外掛的相容性問題。這個也確實是我們測試之前擔心的,暫時我也沒有更好的解決辦法。
如果有強烈的更新需求,建議使用新版的 Jenkins 構建映象,部署完成後從外掛管理裡解決外掛依賴問題。
替換 KubeSphere 使用的 Jenkins Image
鑑於自定義構建 Jenkins Image 的結果並不完美。因此,本節後面的操作,切記不要輕易在生產環境直接使用!僅把思路和操作方法提供給各位,作為參考。如果要在生產環境使用,一定要經過充分的測試驗證。
將自定義的 Jenkins 映象傳送到服務
如果使用離線映象自定義域名部署的叢集,推送映象到內網 Harbor 倉庫 (此過程略)。
如果用的線上網際網路的映象,比如我的環境就是使用的 registry.cn-beijing.aliyuncs.com/kubesphereio/ks-jenkins:v3.3.0-2.319.1。 手工推送映象到伺服器並修改 tag 名字。
# 在打包伺服器匯出自定義構建的 Image
docker tag kubesphere/ks-jenkins:v3.3.0-2.375.3 registry.cn-beijing.aliyuncs.com/kubesphereio/ks-jenkins:v3.3.0-2.375.3
docker save registry.cn-beijing.aliyuncs.com/kubesphereio/ks-jenkins:v3.3.0-2.375.3 -o ks-jenkins-v3.3.0-2.375.3.tar
# 上傳到 devops-jenkins 所在的伺服器(過程略)
# 在 devops-jenkins 伺服器匯入, 沒有 docker 命令,只能用 ctr 命令
ctr -n k8s.io image import ks-jenkins-v3.3.0-2.375.3.tar
# 驗證映象是否匯入
ctr -n k8s.io image ls |grep jenkins
修改 Deployment devops-jenkins 的配置
如果採用的線上網際網路映象的模式,需要更改兩個配置項,image 和 imagePullPolicy,預設的 imagePullPolicy 為 Always,需要修改為 IfNotPresent。
有條件的儘量使用自建的映象倉庫,只需要修改 Image 配置即可。
# 執行修改命令
kubectl edit deploy devops-jenkins -n kubesphere-devops-system
# 修改的主要內容如下(一共有兩處相同的內容需要修改)
# 原始內容
image: registry.cn-beijing.aliyuncs.com/kubesphereio/ks-jenkins:v3.3.0-2.319.1
imagePullPolicy: Always
# 修改後
image: registry.cn-beijing.aliyuncs.com/kubesphereio/ks-jenkins:v3.3.0-2.375.3
imagePullPolicy: IfNotPresent
注意: 修改完成後儲存退出,Deploy 會發現配置變更自動重建 Pod。
驗證狀態
- 檢視 deployment
[root@ks-k8s-master-0 ~]# kubectl get deploy -n kubesphere-devops-system -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
devops-apiserver 1/1 1 1 102d ks-devops registry.cn-beijing.aliyuncs.com/kubesphereio/devops-apiserver:v3.3.0 app.kubernetes.io/instance=devops,app.kubernetes.io/name=ks-devops,devops.kubesphere.io/component=apiserver
devops-controller 1/1 1 1 102d ks-devops registry.cn-beijing.aliyuncs.com/kubesphereio/devops-controller:v3.3.0 app.kubernetes.io/instance=devops,app.kubernetes.io/name=ks-devops,devops.kubesphere.io/component=controller
devops-jenkins 1/1 1 1 102d devops-jenkins registry.cn-beijing.aliyuncs.com/kubesphereio/ks-jenkins:v3.3.0-2.375.3 component=devops-jenkins-master
- 驗證 Pod
# 檢視 pod
[root@ks-k8s-master-0 ~]# kubectl get pod -n kubesphere-devops-system -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
devops-27932190-njblk 0/1 Completed 0 88m 10.233.116.37 ks-k8s-master-2 <none> <none>
devops-27932220-xrgnj 0/1 Completed 0 58m 10.233.116.38 ks-k8s-master-2 <none> <none>
devops-27932250-rvl2r 0/1 Completed 0 28m 10.233.116.39 ks-k8s-master-2 <none> <none>
devops-apiserver-6b468c95cb-grx4j 1/1 Running 2 (32h ago) 102d 10.233.116.211 ks-k8s-master-2 <none> <none>
devops-controller-667f8449d7-w8mvf 1/1 Running 2 (32h ago) 102d 10.233.117.173 ks-k8s-master-0 <none> <none>
devops-jenkins-56d7d75d9-zj7lh 1/1 Running 0 4m34s 10.233.117.177 ks-k8s-master-0 <none> <none>
s2ioperator-0 1/1 Running 2 (32h ago) 102d 10.233.87.33 ks-k8s-master-1 <none> <none>
注意: 啟動過程較慢,需要多重新整理幾次,確保狀態 READY 值為 1/1
登入 Jenkins 管理介面
確保能正常登入系統。
系統管理裡沒有明顯報錯。
原有的 Jenkins 專案都能檢視。
驗證原有的流水線任務
執行原有的流水線任務,檢查任務是否能正常執行,並且結果符合預期。
常見問題
採用預設配置檔案時 build 失敗
- 報錯資訊
[root@zdevops-main ks-jenkins]# make build
jcli cwp --install-artifacts --config-path formula.yaml \
--value-set output=load \
--value-set tag=kubespheredev/ks-jenkins:test \
--value-set platform=linux/amd64
Exception in thread "main" com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "output" (class io.jenkins.tools.warpackager.lib.config.DockerBuildSettings), not marked as ignorable (5 known properties: "base", "tag", "outputDir", "build", "customSettings"])
at [Source: (FileInputStream); line: 10, column: 17] (through reference chain: io.jenkins.tools.warpackager.lib.config.Config["buildSettings"]->io.jenkins.tools.warpackager.lib.config.BuildSettings["docker"]->io.jenkins.tools.warpackager.lib.config.DockerBuildSettings["output"])
at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:61)
at com.fasterxml.jackson.databind.DeserializationContext.handleUnknownProperty(DeserializationContext.java:855)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:1212)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1604)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1582)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:299)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:156)
at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:138)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:293)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:156)
at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:138)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:293)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:156)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4526)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3505)
at io.jenkins.tools.warpackager.lib.config.Config.load(Config.java:77)
at io.jenkins.tools.warpackager.lib.config.Config.loadConfig(Config.java:106)
at io.jenkins.tools.warpackager.cli.Main.main(Main.java:38)
make: *** [build] Error 1
- 解決方案
經過簡單分析,判斷應該是現有的 ks-jenkins 程式碼庫已經好幾個月沒有更新,使用的底層 jcli 不斷在更新,導致現有版本的 cwp-cli.jar 的輸入引數與預設配置檔案中使用的配置不匹配。解決方案有兩種:
- 下載老版本的 cwp-cli 的 jar 包,具體方法只能靠各位自己研究了,我不熟悉也沒深入探究。
- 根據當前版本的引數要求,修改配置檔案 formula.yaml(比較簡單,因此本文采用了該方案)。
# 原配置檔案內容
buildSettings:
docker:
base: jenkins/jenkins:2.319.1
tag: {{.tag}}
output: {{.output}}
platform: {{.platform}}
buildx: true
build: true
# 修改後的配置檔案
buildSettings:
docker:
base: jenkins/jenkins:2.375.3
tag: {{.tag}}
outputDir: {{.output}}
build: true
Jenkins 系統管理介面中外掛管理顯示很多紅色警告資訊
- 報錯資訊
不截圖了,主要是在 Jenkins 的系統管理介面中外掛管理的頁面,會顯示很多紅色警告資訊。有外掛版本依賴關係、外掛降級等紅色或是黃色的提示。
- 解決方案
目前看來最簡單的解決方案是在外掛管理的頁面根據提示解決。
如果想自動打包映象的時候就處理好外掛的依賴,我感覺還是需要在 jcli 這個工具入手。官方文件不夠詳細,資料也少,暫時沒時間和精力去深入探究。所以先放棄了,後續有新的研究成果再分享。
但是 Jenkins 本身的問題解決後,與現有的 KubeSphere 是否相容還需要充分驗證。
結束語
本文以修復 Jenkins 存在的 Apache Log4j2 漏洞為背景,介紹了修復該漏洞的兩種解決方案。並且,初步探究了在 KubeSphere 中整合的 Jenkins 如何自定義構建映象以及如何升級更新。
探究過程並不是很完美,效果並沒有達到預期。主要是由於外掛依賴性的問題,導致自定義構建的映象部署的 Jenkins 外掛管理裡存在很多告警。
由於能力和時間很有限,只能以一個不完美的結果結束本文了,非常抱歉!
因此,本文僅作為拋磚引玉之作,實戰過程不適合在生產環境直接使用。動手能力超強的讀者可將文中的思路和操作方法作為參考,繼續改造。如果有更加完美的解決辦法,請聯絡我分享給我,我將不勝感激!
本文由部落格一文多發平臺 OpenWrite 釋出!