龍星澧,黃傳波,胡曉勤
(1.四川大學網(wǎng)絡空間安全學院,成都 610065;2.成都云祺科技有限公司,成都 610041)
隨著云計算技術高速發(fā)展和企業(yè)信息化的不斷加速,數(shù)據(jù)安全逐漸成為大家越來越關注的內容,而容災備份作為保障數(shù)據(jù)安全的一個重要方式,也越來越受到重視。
持續(xù)性數(shù)據(jù)保護技術(Continuous Data Protection,CDP)是近年來容災備份領域提出的一個全新概念。傳統(tǒng)數(shù)據(jù)保護方式一般為定時或手動備份,當數(shù)據(jù)發(fā)生損壞時,該方式只能恢復到數(shù)據(jù)備份的時間點,而這期間的數(shù)據(jù)則會發(fā)生丟失。采用持續(xù)性數(shù)據(jù)保護技術,則能連續(xù)捕獲并保存數(shù)據(jù)的變化,在數(shù)據(jù)發(fā)生損壞時,其恢復目標點可以是任意時間,所以不會丟失數(shù)據(jù)。
服務器虛擬化技術是云計算中最核心的技術,基于內核的虛擬機(Kernel-Based Virtual Machine,KVM)是云計算中常用虛擬化技術,這是一種內建于Linux 系統(tǒng)的開源虛擬化技術解決方案。本文針對KVM 虛擬化技術,提出了一種持續(xù)性數(shù)據(jù)保護方法,其能在KVM 虛擬機運行期間,連續(xù)地捕獲KVM 虛擬機產生的數(shù)據(jù),實現(xiàn)持續(xù)性數(shù)據(jù)保護。
常用的KVM 虛擬化分為KVM 部分與QEMU部分,KVM 部分通過KVM.ko 這個內核模塊來實現(xiàn)核心虛擬化的功能,也就是CPU 和內存的虛擬化;而QEMU 部分則提供了包括網(wǎng)卡在內的其他硬件的虛擬化,其運行在用戶態(tài),作為一個應用程序存在。兩個部分互相協(xié)作,形成一套完整的虛擬化技術。
當KVM 虛擬化的guest 機要發(fā)起IO 請求時,guest 機首先會將指令發(fā)送給內核中的KVM 內核模塊,KVM 內核模塊對指令進行相應處理后,會發(fā)送數(shù)據(jù)給應用層的QEMU,QEMU 進行處理后,在宿主機上完成最后的IO 操作。具體的流程如圖1所示。
圖1 KVM虛擬機架構
由于KVM 虛擬機的IO 都是通過QEMU 執(zhí)行的,所以要對KVM 進行持續(xù)性數(shù)據(jù)捕獲,就需要捕獲應用程序QEMU 的IO。本文采用了Linux Hook 技術編寫IO 過濾驅動,對KVM 虛擬機數(shù)據(jù)進行捕獲。
常見的Linux Hook 技術分為ring0 層Hook 和ring3 層Hook,其中ring0 層Hook 主要針對Linux系統(tǒng)內核,通過編寫內核模塊替換系統(tǒng)調用以及內核函數(shù)來實現(xiàn)Hook。ring3 層hook 被分為了動態(tài)注入和靜態(tài)注入,靜態(tài)注入為程序還沒運行時,修改替換程序的.so 文件實現(xiàn);而動態(tài)注入則需要在程序運行時,動態(tài)地將函數(shù)地址替換為我們所需要的函數(shù)。
QEMU 在Linux 中采用的是Linux 標準IO 流程,其過程如圖2所示。
圖2 Linux IO流程
編寫IO 過濾驅動,首先需要確定過濾驅動的插入層次,常見過濾驅動一般在系統(tǒng)調用層、虛擬文件系統(tǒng)層或通用塊層編寫。
本文選取在系統(tǒng)調用層和應用層分別編寫IO過濾驅動,以實現(xiàn)截獲KVM虛擬機的IO。
要實現(xiàn)系統(tǒng)調用替換,首先通過Linux 內核導出函數(shù)kallsyms_lookup_name()獲取系統(tǒng)調用表,接著替換掉系統(tǒng)調用表中的目標系統(tǒng)調用函數(shù),最后在替換函數(shù)中執(zhí)行完相關邏輯,并再調用原系統(tǒng)調用函數(shù),保證系統(tǒng)正常運作。
本文所設計持續(xù)性數(shù)據(jù)保護系統(tǒng)架構如圖3所示。
圖3 持續(xù)性數(shù)據(jù)保護架構
KVM 虛擬機支持多種不同的磁盤格式,如raw、qcow2 以及ceph 格式,不同的磁盤格式有不同的元數(shù)據(jù)用以描述數(shù)據(jù)的儲存方式,在捕獲數(shù)據(jù)IO的同時進行備份并不需要這些元數(shù)據(jù)。故針對不同格式的磁盤,需要設計不同的hook模塊,過濾不同磁盤格式下的元數(shù)據(jù)。
Raw 格式為磁盤原生格式,可以直接掛載在不同的虛擬機中,沒有額外元數(shù)據(jù)需要處理。直接捕獲其IO 數(shù)據(jù)即可。整個過濾驅動運行流程如圖4所示。
圖4 raw格式磁盤Hook模塊運行流程
如圖4 所示,當QEMU 產生了IO 請求時,首先會通過IO 過濾驅動,由過濾驅動判斷是否是目標磁盤文件,若為目標文件,則通過數(shù)據(jù)傳輸模塊將IO 數(shù)據(jù)傳到遠端數(shù)據(jù)備份服務器進行備份,之后將參數(shù)傳回原系統(tǒng)調用函數(shù)進行正常寫入操作。
對qcow2磁盤進行數(shù)據(jù)捕獲操作時,需要對元數(shù)據(jù)進行過濾操作,只保存實際數(shù)據(jù)。
qcow2 磁盤開頭是一個固定存放在文件頭的結構體,這個結構體描述了該qcow2文件的一些相關信息,結構體代碼如下。
typedef struct QCowHeader{
uint32_t magic;
uint32_t version;
uint64_t backing_file_offset;
uint32_t backing_file_size;
uint32_t cluster_bits;
uint64_t size;/*in bytes*/
uint32_t crypt_method;
uint32_t l1_size;
uint64_t l1_table_offset;
uint64_t refcount_table_offset;
uint32_t refcount_table_clusters;
uint32_t nb_snapshots;
uint64_t snapshots_offset;
}QCowHeader;
其中,需要關注的是l1_size、l1_table_offset以及cluster_bits 這幾項,l1_size是指l1table的大小,l1_table_offset 是l1 表在文件中的偏移,而cluster bits 則是用來計算qcow2 格式的一個cluster的大小,計算方法為1< l1 表和l2 表是一個二級表項的儲存結構,其中儲存的是raw 磁盤格式道qocw2 磁盤格式的偏移信息。l1表的大小不固定,隨著qcow2文件增長而增長,其中每個表項長度為8個字節(jié),而l2 表固定為一個cluster 大小,其中每個表項為8個字節(jié),一個qcow2 只能有一個有效的l1table,但是同時擁有多個l2table。l1table的第一項指向qcow2 文件中的第一個l2table 項,而第一個l2table 的第一項所指向的那個cluster,在轉換成raw 格式后,其就是raw 格式的第一個cluster。所以,根據(jù)此二級表項,便可將qcow2格式動態(tài)轉化為raw 格式數(shù)據(jù),整個qcow2 磁盤數(shù)據(jù)捕獲具體流程如圖5所示。 圖5 qcow2格式磁盤Hook模塊運行流程 模塊加載時,首先會讀取磁盤文件中的元數(shù)據(jù)信息,根據(jù)元數(shù)據(jù)信息解析l1table 和l2table,并等待數(shù)據(jù)下發(fā)。當有針對目標文件寫IO 到達時,首先根據(jù)解析出來的數(shù)據(jù)判斷是否是元數(shù)據(jù),若為元數(shù)據(jù),直接下發(fā)IO,并重新解析l1table和l2table,重新解析兩個表是因為如果寫入數(shù)據(jù)是元數(shù)據(jù),則有可能改變l1table 和l2table 內容。若非元數(shù)據(jù),則先將數(shù)據(jù)寫入備份文件,之后將IO下發(fā)到目標文件。 KVM+ceph 是當前云環(huán)境下的常用組合,作為一款分布式文件系統(tǒng),ceph在云環(huán)境中相對于傳統(tǒng)文件系統(tǒng)能夠發(fā)揮出更好的性能。 KVM 虛擬機使用ceph 作為儲存,主要是用到librbd調用的ceph的IO接口,librbd是ceph基于librados的一個用戶態(tài)接口,當我們在KVM中通過QEMU使用ceph文件系統(tǒng)時,QEMU通過librbd.so文件調用接口進行IO,所以其數(shù)據(jù)我們無法通過系統(tǒng)調用進行hook,想要獲取ceph儲存的IO數(shù)據(jù),需要在應用層進行hook操作。 在應用層的hook 通常是指PLT/GOT hook,也就是對程序的got 表進行替換。PLT(Problogcedure Linkage Table)和GOT(Global Offset Table)是GCC 中生成shared library 的重要元素。Linux 對外部函數(shù)的引用是采用動態(tài)鏈接的,也就是說,只有在用到某個函數(shù)時,才會具體定位其在內存中的位置。 對于正在運行的程序,可以通過修改.got.plt 表將目標函數(shù)地址改為hook 函數(shù)的地址,實現(xiàn)對函數(shù)的hook,若程序還未運行,則可通過LD_PRELOAD 環(huán)境變量,直接將目標so 文件替換為hook所需的so文件。 針對ceph文件系統(tǒng)的IO函數(shù)Hook具體過程如圖6所示。 圖6 ceph磁盤Hook模塊運行流程 如圖6所示,首先程序會判斷kvm 虛擬機是否啟動,若未啟動,則通過LD_PRELOAD 環(huán)境變量,讓kvm 啟動時自動加載hook 所需的so 文件,程序啟動后,so 文件的代碼會自動替換qemu 中關于ceph 的IO 函數(shù)。若KVM 虛擬機已啟動,則需要首先調用程序libc 庫中dlopen 函數(shù),將hook 所需so 文件載入內存,之后通過Linux 的proc 文件系統(tǒng)修改進程內存,將.got.plt表中的目標函數(shù)地址修改為hook 函數(shù)地址,實現(xiàn)對數(shù)據(jù)的捕獲。 針對數(shù)據(jù)恢復,本文設計了兩種恢復模式。第一種為數(shù)據(jù)卷恢復模式,即當數(shù)據(jù)卷出現(xiàn)損壞或誤操作時,將數(shù)據(jù)恢復到最初的數(shù)據(jù)卷狀態(tài),此時恢復模塊會直接讀取備份庫中的數(shù)據(jù)卷快照文件,將數(shù)據(jù)卷恢復到最初狀態(tài)。第二種則為目標點恢復模式,首先,在IO 數(shù)據(jù)備份時,系統(tǒng)會按照時間順序將IO 數(shù)據(jù)進行排序,當用戶指定一個恢復的時間點后,恢復模塊會通過原始磁盤鏡像以及備份庫中所儲存的IO 數(shù)據(jù),按時間順序對磁盤進行IO 重放,直到恢復到目標時間點為止。由此實現(xiàn)恢復到任意時間點的目標。 本文測試系統(tǒng)由一臺測試服務器和一臺備份服務器組成,測試服務器用于運行KVM 虛擬機,備份服務器用于保存以及讀取數(shù)據(jù)捕獲模塊所捕獲的數(shù)據(jù)。 實驗首先隨機產生一個數(shù)據(jù)文件,計算文件MD5 碼并進行保存,之后在測試服務器上開啟KVM 虛擬機,分別使用raw、qcow2 以及ceph格式作為測試磁盤,將產生的數(shù)據(jù)文件寫入測試磁盤,然后對磁盤進行格式化操作。最后,在使用恢復模塊將磁盤恢復到數(shù)據(jù)格式化前,計算出恢復數(shù)據(jù)文件MD5 碼,若MD5 碼不變,則證明本文所設計系統(tǒng)能夠正確保護數(shù)據(jù)。實驗結果見表1。 表1 實驗結果 實驗結果表明,本文提出的持續(xù)性數(shù)據(jù)保護方法能夠正確地對不同格式磁盤數(shù)據(jù)進行保護。 針對KVM 虛擬機數(shù)據(jù)備份,本文提出了一種KVM 虛擬機數(shù)據(jù)持續(xù)性保護的設計以及開發(fā)思路,通過在應用層和內核層編寫Hook 函數(shù),實現(xiàn)了對KVM虛擬機IO的數(shù)據(jù)捕獲,并通過IO重放的機制,將數(shù)據(jù)恢復到任意時間點,該設計具有一定的創(chuàng)新型,為KVM 虛擬機的數(shù)據(jù)保護提供了一種全新的思路。但目前針對不同的磁盤格式IO 捕獲,仍存在適配性問題,下一步工作將針對此問題做進一步研究,以適應更多的磁盤格式。2.3 ceph格式磁盤Hook模塊設計
2.4 數(shù)據(jù)恢復模塊設計
3 實驗
4 結語