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