使用Jenkins + git submodule 實現自動化編譯,解決程式碼安全性問題

sewain發表於2021-04-01

道哥的第 030 篇原創

一、一個真實的程式碼洩漏故事

事情發生在功能機的時代,我們專案組開發一款手機,軟體開發成員大概有 20 人左右吧。結果在手機發布的一週後,另一家小廠就推出了軟體介面、功能幾乎完全一樣的手機,除了開機介面。

因為那個時代,大家幾乎都是使用 MTK、高通提供的解決方案,都是統一的選單式功能,你沒法拿出有力的證據來說明別人偷竊了你的程式碼

後來內部查明,的確是有開發人員把程式碼洩漏出去了,於是後來所有的電腦上 USB 口全部被禁掉了。

這是我親身經歷的真實故事,當時每個人負責一個模組,比如:A 負責通話管理和電話簿,B 負責系統設定,C 負責簡訊和彩信。。。在編譯的時候,是需要所有的程式碼放在一起,統一編譯的,這也就意味著所有的軟體人員都可以拿到全部原始碼,這也就為程式碼洩漏埋下了隱患,出現了這次嚴重的事件,畢竟人為財死、鳥為食亡!

那麼,是否有一些程式碼管控方式,來解決這個許可權問題呢?

現在專案中,都強調要分層、分模組,這是從軟體工程的角度來考慮的。如果再進一步, 把這些模組都劃分為一個小的子系統,每個開發人員只負責自己的模組,並且只能有許可權拉取自己的程式碼,這樣他就沒法獲取到一個專案中所有模組的程式碼了

只有專案整合人員(管理員),才有全部許可權來拉取所有原始碼來構建整個系統,這樣的話,就可以對程式碼的安全問題有更好的掌控了。

要實現這樣的程式碼管控,使用 git 工具中的 submodule 就可以完成,這篇文章,我們就來詳細的講解一下 git submodule 的使用

這篇文章是工具型的,可能比較長,為了提供一站式服務,我會把相關的資源、步驟、遇到的錯誤資訊等細節都記錄下來,方便以後查閱。

不論如何,經過這篇文章,你可以學習、瞭解下面這幾個方面的知識點:

  1. Jenkins 的基本使用方法;
  2. git submodule 的基本指令用法;
  3. 通過三個 demo 專案,一步一步操作實現程式碼的安全管控;
  4. 利用 Jenkins + git submodule 來實現自動化編譯;
  5. git subtree 與 submodule 的區別;

如果您需要文中提到的軟體和程式碼資源,在文章末尾可以找到下載方式。

二、Jenkins 的基本使用

1. Jenkins 是什麼?

Jenkins是一個開源、由 Java 編寫的持續整合工具,也就是說它幫助我們自動構建各類專案。Jenkins 執行在 Servlet 容器中(例如 Apache Tomcat),在 Ubuntu 系統中使用 apt-get 就可以一鍵安裝。

Jenkins 有下面幾個特點:

  1. 嵌入在 Web 伺服器中,通過瀏覽器來操作,非常方便;
  2. 可以執行基於Apache Ant和Apache Maven的專案,以及任意的Shell指令碼和Windows批處理命令;
  3. 可以通過各種手段觸發構建。例如提交給版本控制系統時被觸發,通過類似Cron的機制排程,在其他的構建已經完成時,還可以通過一個特定的URL進行請求;
  4. Jenkins強大的外掛式,使得Jenkins可以整合很多軟體,可能幫助我們持續整合我們的工程專案;
  5. 給使用者很大的許可權和靈活性來自動釋出、部署等等。

其他的有點我就不吹了,我覺得很好用,如果有機會,你也可以試一下。另外,我測試用的虛擬機器是新安裝的 Ubuntu16.04-64,按照下面的流程操作,保證可以順利執行。

JDK 和 Jenkins 的安裝方法,在網路上很多資料,有些過程是有問題的,或者某些關鍵步驟沒寫清楚。為了便於你一次就操作成功,我還是記錄在這裡。

如果你對安裝過程已經很熟悉了,可以直接滑到下一個主題。

2. 安裝 JDK8

(1) 下載,解壓

下載 jdk-8u221-linux-x64.tar.gz,(文末提供下載地址),解壓到目錄 /home/sewain/OpenSource ,解壓指令:

sudo tar -zxvf jdk-8u221-linux-x64.tar.gz -C /opt

(2) 設定環境變數

執行指令:vim ~/.bashrc,在末尾新增如下內容:

export JAVA_HOME=/opt/jdk1.8.0_221
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=./$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jart

我建議你也用這樣的環境變數,以後如果升級 JDK 版本,只需要修改 JAVA_HOME 就可以了。

(3) )重新載入環境變數

執行指令:source ~/.bashrc,此時環境變數就生效了。

驗證一下: java -version,出現如下資訊就說明 OK 了:

java version "1.8.0_221"
Java(TM) SE Runtime Environment (build 1.8.0_221-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.221-b11, mixed mode)

3. 安裝 Jenkins

(1) 匯入 Jenkins 儲存庫的 GPG 金鑰

sudo wget -q -O - https://pkg.jenkins.io/debian/jenkins.io.key | sudo apt-key add -

上面的命令應輸出 OK,這意味著金鑰已匯入成功,並且來自此儲存庫的軟體包將被視為受信任。

(2) Jenkins儲存庫新增到系統中

sudo sh -c 'echo deb http://pkg.jenkins.io/debian-stable binary/ > /etc/apt/sources.list.d/jenkins.list'

(3) 使用 apt 安裝

sudo apt update
sudo apt install jenkins

Jenkins 服務將在安裝過程完成後自動啟動,可以通過指令 systemctl status jenkins 進行驗證。

systemctl status jenkins

(4) 配置埠

Jenkins 是嵌入在一個 tomcat 伺服器中的,預設使用埠 8080,容易與其他服務衝突,因此需要修改一下。涉及到 2 個檔案:

檔案一:/etc/init.d/jenkins

第一行的 PATH 變數中,新增自己的 JDK 地址:

PATH=/bin:/usr/bin:/sbin:/usr/sbin:/opt/jdk1.8.0_221/bin

檔案二:/etc/default/jenkins

HTTP_PORT 的值修改為新的埠號,例如: HTTP_PORT=9090

(5) Jenkins 的啟動和停止指令

sudo service jenkins start
sudo service jenkins stop

如果不幸遇到錯誤,可以反覆使用這兩個指令來排除錯誤。

4. 在瀏覽器中配置 Jenkins

在瀏覽器中輸入: htpp://localhost:9090,稍等一會,出現介面:

按照介面提示,從 /var/lib/jenkins/secrets/initialAdminPassword 檔案中複製安全密碼(需要 root 許可權),填入到瀏覽器視窗中。

此時出現安裝外掛視窗,一般只需要安裝推薦的外掛即可:

外掛安裝結束後,進入管理員配置介面:

至此,Jenkins 的安裝就順利完成了!

5. 在 Jenkins 中配置一個小專案

(1) 準備一個測試程式碼 Test1

Jenkins 僅僅是一個構件框架,具體的編譯過程是由使用者來決定的。 Jenkins 首先通過 git 工具獲取遠端倉庫中的程式碼,然後執行使用者指定的編譯指令。

因此,我們需要先提前準備好一份測試程式碼,並放到 Jenkins 可以訪問到的遠端倉庫中,當然了,你在本地的 Ubuntu 系統中部署一個 git 倉庫也是可以的。為了方便,我測試的程式碼 Test1 放在 gitee 中了。

(2) 建立一個新專案

(3) 輸入專案名稱,並選擇第一個自由風格(Free project)

(4) 在第一個標籤 General下,輸入專案的描述資訊(Description)

內容可以隨便寫。

(5) Source Code Management 原始碼管理

Jenkins 在構建(編譯)的過程中,需要獲取到原始碼,因此需要配置 git 倉庫的地址和賬號資訊(使用者名稱和密碼)

首先在 Add 下拉按鈕下,選擇 Jenkins:

輸入 Username Password

賬號新增之後,在 Credentials 的下拉框中,選擇剛才新增的 gitee 賬戶,此時可以看到 紅色的許可權錯誤提示消失了,說明可以拉取到遠端倉庫中的原始碼了。

(6) Build Triggers 選擇編譯觸發器

可以根據需要選擇不同的方式來觸發,比如:定時觸發,當其他某個專案構建成功之後觸發等等。

我們這裡不選擇任何專案,下面我們會在主介面,手動點選按鈕來觸發。

(7) Build Environment 編譯環境

這部分我用的比較少,利用其他工具來輔助 Jenkins 的功能。

(8) Build 編譯

就是告訴 Jenkins 如何來構建系統,也就是說:Jenkins只是一個自動化的構建系統,具體的編譯過程,可以由使用者自己來決定,有如下選擇專案:

我們這裡選擇直接執行指令碼(Execute Shell),輸入如下指令:

(9) Post-build Actions 編譯後操作

告訴 Jenkins: 編譯一個工程之後,需要做哪些事情?例如:傳送郵件,觸發下一個工程的自動編譯等等,而且可以新增多個動作。可選項如下:

以上步驟配置好之後,Save 儲存,此時在主介面就可以看到這個專案的全貌了,如下圖:

在 Jenkins 後臺中,這個專案的儲存路徑是:/var/lib/jenkins/jobs/Test1。

6. 手動觸發編譯一次

由於在上面的步驟(6)中,我們沒有選擇任何觸發條件,所以需要我們在專案 Test1 的主介面中,手動單擊左側的 Build Now 按鈕來觸發。

此時,在左側的 Build History 中,可以看到編譯歷史記錄,單擊某次編譯記錄編號,可以看到這一次編譯的詳細資訊。

編譯詳細資訊中,單擊左側的 Console Output 按鈕,可以看到編譯的輸出資訊:成功編譯得到可執行檔案。

我們可以在 Jenkins 後臺中看到,原始碼被拉到 /var/lib/jenkins/jobs/Test1/workspace 目錄中了:

到這裡,你已經學會了 Jenkins 的最基本操作!

下面我們繼續講一下 git submodule 的使用,這部分才是核心內容

三、git submodule 基本使用

1. git submodule 是什麼?

git submodule 是用於多模組管理的工具,它允許一個專案作為 repository,其他專案作為子模組存在於在父專案中。

父專案和子專案的提交是分開的,也就是說父專案提交的資訊只包含子專案的資訊,而不會包含子專案的程式碼;子專案有自己獨立的 commit,push,pull操作。

git submodule 一般用在比較大的專案中,為了便於複用,或者為了程式碼的安全性,常常需要分成若干個子專案來進行程式碼管理。

常用的指令包括:

新增子模組: git submodule add
更新子模組: git submodule update
初始化子模組: git submodule init
遞迴的方式克隆整個專案: git clone --recursive
拉取所有子模組: git submodule foreach git pull

2. 利用三個小專案,來測試一下 submodule 的用法

為了便於演示,我們我們建立 3 個專案,把它們都推送到遠端倉庫中,這裡使用 gitee。

  1. Test1:編譯得到一個動態庫:libtest1.so;
  2. Test2:編譯得到一個動態度:libtest2.so;
  3. Test3:編譯得到一個可執行程式,載入、呼叫上面 2 個動態庫中的函式。

為什麼要這樣設計模組: 安全!

  1. 開發人員A:負責 Test1,沒有許可權拿到其他模組的程式碼;
  2. 開發人員B:負責 Test2,沒有許可權拿到其他模組的程式碼;
  3. 專案經理:負責 Test3 和 程式碼整合,能拿到所有的程式碼;

專案經理需要把 Test1 和 Test2 作為 sub module,新增到 Test3 中,執行下面的指令:

git submodule add https://gitee.com/[你的賬號]/test1.git test1
git submodule add https://gitee.com/[你的賬號]/test2.git test2

把 Test1 和 Test2 作為子模組新增到 Test3 中之後,看一下檔案有什麼變化:

你還可以看一下 .gitmodules 檔案中的內容,可以看出,git 工具就是通過這個配置檔案來管理子模組的。

管理員需要對所有的模組進行整合、編譯,因此,我們在 Test3 目錄下新增一個指令碼 build.sh,所有的編譯指令,都寫在這個指令碼中,內容如下:

內容都是最最基本的,直接呼叫 make 指令即可,執行一下,輸出:

到這裡,我們就完成了子模組的新增功能。

3. 在一個空目錄中來編譯、驗證一下可行性

我們在另一個空目錄中,clone 一下 Test3 這個專案,可以發現:克隆下來的 test1 和 test2 資料夾中是空的,如下所示:

需要手動獲取所有的子模組,執行指令:

git submodule update --init --recursive

此時,再使用 tree 命令看一下檔案變化,可以看到 test1 和 test2 的檔案都被拉取下來了。這裡有一個問題需要注意:雖然子模組的程式碼被拉取下來了,但是其 head 並沒有指向 master 分支,需要手動處理一下,如圖:

這個時候,我們在 test3 目錄下再次執行指令碼 build.sh,就可以順利編譯所有的子模組了。

以上這幾個步驟,我們是在本地的一個臨時目錄,手動測試 submodule 的編譯過程。

下一個章節我們把這個過程部署到 Jenkins 系統中,所以剛才執行的這幾個指令,就需要寫在 build.sh 指令碼中了。build.sh 的內容變為:

四、在 Jenkins 中使用 git module 來編譯所有的模組

下面的操作,都是在瀏覽器的 Jenkins 皮膚中來操作的。

1. 重新配置專案

因為我們是在 Test3 中,來編譯整個專案(Test1 和 Test2 被作為子模組包括進來),因此首先把之前新增的 Test1 專案刪除掉,如圖:

然後重新新增專案 Test3,複習一下步驟:

  1. 輸入描述資訊,選擇自由風格專案;
  2. 輸入 git 倉庫地址和賬戶資訊,選擇 master 分支;
  3. 觸發器不設定;
  4. 編譯環境不設定;
  5. 編譯:選擇 Execute shell 執行指令碼,輸入編譯指令:./build.sh。(剛才說了,Jenkins 這是一個自動化構建框架,具體的編譯過程由使用者決定,所以我們這裡的編譯過程就是執行 Test3 下的 build.sh 這個指令碼。);
  6. 編譯後動作不設定;

當然,也可以直接在之前的 Test1 專案基礎上進行修改。

此時,我們在 Jenkins 中直接點選 Build Now 按鈕,如果不出意外的話,會提示編譯錯誤(左側的 Build History 下面出現紅色的錯誤圓圈)。

點進去,看一下輸出資訊(Console Output 按鈕),提示錯誤:

原因正如前文所說,子模組在獲取之後,head 沒有指向 master 分支,需要我們在第一次編譯時手動修改一下(我沒有找到其它方法,如果你知道的話,請不吝賜教!)

手動解決:在命令列視窗中,進入 Jenkins 系統的 Test3 目錄 /var/lib/jenkins/jobs/Test3/workspace ,執行如下幾條指令:

git submodule update --init --recursive
cd test1/
git checkout master
cd -
cd test2/
git checkout master
cd -

此時,重新觸發編譯一次,一定可以成功的!

五、總結

這篇文章是屬於工具型的,一旦部署好之後,每次編譯只需要在瀏覽器中點一下按鈕就行,再也不用 ssh 登入到遠端電腦中去手動操作了。

如果你還想繼續深入一下的話,下面幾件事情可以研究一下:

1. Jenkins 是如何保持編譯歷史記錄的

在目錄 /var/lib/jenkins/jobs/Test3/builds 下面,可以看到很多以數字命名的資料夾,記錄了每一次的編譯資訊。

2. 編譯後動作

在我們的編譯指令碼 build.sh 檔案中,僅僅是生成了可執行檔案,你還可以繼續擴充功能,例如:自動部署。

或者在專案配置的 [Post-build Actions] 中,重新寫一個專門用來自動部署的指令碼檔案。

3. git subtree 功能

它與 git submodule 很類似,但是本質不一樣。

  1. subtree直接把子模組程式碼拷貝到主模組中,使用命令簡單;
  2. submodule 使用的是“指標”指向子模組,使用命令相對複雜一些,功能也更強大;

4. 繼續研究 Jenkins 中的外掛功能

六、資源下載

文中用到的資源,放在了網盤中。如果需要的話,請在公眾號留言區傳送數字:030,即可收到下載地址。


好文章,要轉發;越分享,越幸運!


如果您的網站想轉載這篇文章,只要保留作者、文章來源和上圖二維碼即可。

如果您的公眾號想轉載這篇文章,請私信或留言,我給您開長白。



推薦閱讀

【C 語言】
C語言指標-從底層原理到花式技巧,用圖文和程式碼幫你講解透徹
原來gdb的底層除錯原理這麼簡單
一步步分析-如何用C實現物件導向程式設計
提高程式碼逼格的利器:巨集定義-從入門到放棄
利用C語言中的setjmp和longjmp,來實現異常捕獲和協程

【應用程式設計】
都說軟體架構要分層、分模組,具體應該怎麼做(一)
都說軟體架構要分層、分模組,具體應該怎麼做(二)
物聯網閘道器開發:基於MQTT訊息匯流排的設計過程(上)
物聯網閘道器開發:基於MQTT訊息匯流排的設計過程(下)
我最喜歡的程式之間通訊方式-訊息匯流排

【作業系統】
為什麼航天器、導彈喜歡用微控制器,而不是嵌入式系統?

【物聯網】
關於加密、證照的那些事
深入LUA指令碼語言,讓你徹底明白除錯原理

【胡說八道】
以我失敗的職業經歷:給初入職場的技術人員幾個小建議

相關文章