2021-05-12 14:32:11
通過QEMU-GuestAgent實現從外部注入寫檔案到KVM虛擬機器內部
本文將以宿主上直接寫檔案到VM內部為例講解為何要注入以及如何實現
tag: qemu-ga, qemu guest agent, kvm, guest-file-write, inject
小慢哥的原創文章,歡迎轉載
目錄
? 為什麼要“注入”到VM內部
? 如何實現“注入”
? Step1. 為VM設定channel
? Step2. 部署qemu-ga
? Step3. 注入操作說明
? Step4. Base64計算
? Step5. 開始注入
? 附1. qemu-ga支援的所有指令
? 附2. 設定多個channel
? 參考文件
為什麼要“注入”到VM內部
原因很簡單:在VM外部無法實現,只能進入到VM內來實現
KVM不像Docker(container)只是對進程進行cgroup隔離,KVM是全封閉的環境。
對於基於KVM的虛擬機器來說,通常存在如下需求:
? 線上修改密碼
? 線上增加公鑰
? 線上採集效能(如cpu使用率、負載、記憶體使用量等效能指標)
? 其他各種線上功能
上述這些場景的共性:僅在VM外部是無法實現的。因此就有了多種解決方案,但無論哪種解決方案都要同時滿足以下2點才能實現:
? 通道:在VM內部與外部(宿主)之間開啟一個通道,可以進行資料互動
? agent:在VM內部種下一個agent,用於接收外部的指令並反饋結果
在VM內部種下agent的做法可以形象地稱之為"inject 注入"
如何實現“注入”
第一步,開啟通道
有2類方法:
? 走網路:會複雜一些,需要提前預插入一張管理網絡卡,或者利用已有網絡卡+特殊的路由來確保資料能走出去,這帶來了較為複雜的網路拓撲
? 走裝置:簡單很多,只需在VM內部和宿主之間建立一個裝置通道即可。比如為KVM虛擬機器增加一個字元裝置,並在宿主上對映為一個socket檔案。字元裝置與socket之間形成了一個channel,通過該channel就可以進行內外資料互通
“走網路”不是本文想要介紹的,接下來所有內容均為“走裝置”
第二步,啟動agent
在虛擬機器裡啟動一個agent,實時讀取字元裝置,實現與宿主的資料互動。
在channel中傳送與接收什麼樣的資料,是可以自己定義的,也可以使用KVM官方實現的解決方案,稱為Qemu Guest Agent,簡稱qemu-ga。它包含2方面:
? channel中傳送資料的協定定義:基於JSON的格式
? VM內的agent:啟動一個名叫qemu-ga的守護行程,該進程將從字元裝置裡獲取傳進來的json指令,然後根據指令執行相關命令,並將結果通過字元裝置返回給宿主
qemu-ga的好用之處在於其封裝的指令相容了一些不同的作業系統,比如寫檔案指令guest-file-write,既可以用於linux也可以用於windows。
關於qemu-ga的設定與使用,筆者之前已寫過一篇文章《基於QMP實現對qemu虛擬機器進行互動》,詳細介紹其工作原理及基本使用方法,這裡附上地址
https://www.toutiao.com/i6646012291059810823/
由於本文主題是“注入寫檔案”,因此接下來將重點闡述如何寫檔案,不過也會將qemu-ga的部署與啟用方法再次貼出。
Step1. 為VM設定channel
通過libvirt啟動的虛擬機器,可以在XML裡增加一段設定
<channel type='unix'>
<source mode='bind' path='/tmp/channel.sock'/>
<target type='virtio' name='org.qemu.guest_agent.0'/>
</channel>
注意:上面這段設定要放在<devices>
段落中
Step2. 部署qemu-ga
1?? 安裝qemu-ga
在VM內部安裝並啟動qemu-ga,linux和windows均支援qemu-ga,許多linux發行商都會提供自己的qemu-ga,比如rhel/CentOS、Fedora、Ubuntu、openSUSE都有提供編譯好的qemu-ga,可以直接下載使用。而windows系統需要下載virtio-win,其中有包含一些virtio的win驅動以及qemu-ga安裝包,也可以僅下載qemu-ga安裝包
# rhel/centos
yum install qemu-guest-agent
# windows,最新virtio-win iso
https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/latest-virtio/
# windows,最新qemu-ga安裝包
https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/latest-qemu-ga/
windows的qemu-ga安裝包如圖所示
2?? 啟動qemu-ga
以centos7為例
# 啟動qemu-ga守護行程
systemctl start qemu-guest-agent
# 加入開機啟動
systemctl enable qemu-guest-agent
啟動後通過systemctl status qemu-guest-agent
應當能看到進程已啟動,如圖所示
注意:有的qemu-ga會拒絕部分指令,這是因為qemu-ga的組態檔裡將某些指令給禁用了,比如在centos7裡,組態檔為/etc/sysconfig/qemu-ga
# 修改/etc/sysconfig/qemu-ga,將以下內容註釋掉,或直接刪掉
BLACKLIST_RPC=guest-file-open,guest-file-close,guest-file-read,guest-file-write,guest-file-seek,guest-file-flush,guest-exec,guest-exec-status
# 重新啟動qemu-ga才能生效
systemctl restart qemu-guest-agent
3?? 測試qemu-ga
在VM的宿主機上,執行以下命令:
# ${DOMAIN}表示虛擬機器名字或UUID
virsh qemu-agent-command ${DOMAIN} '{"execute":"guest-ping"}'
如果返回以下內容則表示qemu-ga可用
{"return":{}}
接下來檢視下qemu-ga支援哪些指令
virsh qemu-agent-command ${DOMAIN} --pretty '{"execute":"guest-info"}'
應該會看到支援很多命令,由於接下來做的實驗需要用到如下命令,因此請先確認是否均支援
? guest-exec:執行命令(非同步操作)
? guest-exec-status:檢視執行命令的結果
? guest-file-open:開啟檔案,獲得控制代碼
? guest-file-write:寫檔案(傳遞base64)
? guest-file-close:關閉檔案
Step3. 注入操作說明
實驗目標:將RSA的公鑰內容寫入到/root/.ssh/authorized_keys
這涉及到如下3個步驟:
1. 建立/root/.ssh目錄且許可權為700
2. 建立/root/.ssh/authorized_keys檔案且許可權為600
3. 將RSA公鑰文字進行Base64編碼(guest-file-write不支援明文,僅支援base64),並將編碼後的內容寫入/root/.ssh/authorized_keys
Step4. Base64計算
這裡先假設RSA公鑰內容為
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDVKog04pbbLaarjbpvK7CRaIuUwWxehJIH8tqtX/oV4GYN5WGYPFa1tzsd4Vyoblm4LePX79WeI4kFHgSbH5P6H9i8l3KCTFHHeJT/g0P55/c60yDb3o6lqpWu9IKE3I4lsTp05Y/W0Ks7W27Jndr162ni0Ybthgd9CQyoiburoh35ECiPGwWUOBVJ4IEpSpOZdDUJLS/vVuSQgvEH0fq/G1DP3SOyR+DNasJ00mwonfaUKHZXmWAlH8marNwPmWapyTSQwCFKKh1HwlJEWETV4fYuFwm3iennb8cX1y4aX9AJWnA2cc35rpulivMijeXs/ssT5iFljXXGYzmkX6nR root@localhost.localdomain
進行Base64編碼
echo 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDVKog04pbbLaarjbpvK7CRaIuUwWxehJIH8tqtX/oV4GYN5WGYPFa1tzsd4Vyoblm4LePX79WeI4kFHgSbH5P6H9i8l3KCTFHHeJT/g0P55/c60yDb3o6lqpWu9IKE3I4lsTp05Y/W0Ks7W27Jndr162ni0Ybthgd9CQyoiburoh35ECiPGwWUOBVJ4IEpSpOZdDUJLS/vVuSQgvEH0fq/G1DP3SOyR+DNasJ00mwonfaUKHZXmWAlH8marNwPmWapyTSQwCFKKh1HwlJEWETV4fYuFwm3iennb8cX1y4aX9AJWnA2cc35rpulivMijeXs/ssT5iFljXXGYzmkX6nR root@localhost.localdomain' | base64 -w 0
這樣就獲得了base64編碼內容
c3NoLXJzYSBBQUFBQjNOemFDMXljMkVBQUFBREFRQUJBQUFCQVFEVktvZzA0cGJiTGFhcmpicHZLN0NSYUl1VXdXeGVoSklIOHRxdFgvb1Y0R1lONVdHWVBGYTF0enNkNFZ5b2JsbTRMZVBYNzlXZUk0a0ZIZ1NiSDVQNkg5aThsM0tDVEZISGVKVC9nMFA1NS9jNjB5RGIzbzZscXBXdTlJS0UzSTRsc1RwMDVZL1cwS3M3VzI3Sm5kcjE2Mm5pMFlidGhnZDlDUXlvaWJ1cm9oMzVFQ2lQR3dXVU9CVko0SUVwU3BPWmREVUpMUy92VnVTUWd2RUgwZnEvRzFEUDNTT3lSK0ROYXNKMDBtd29uZmFVS0haWG1XQWxIOG1hck53UG1XYXB5VFNRd0NGS0toMUh3bEpFV0VUVjRmWXVGd20zaWVubmI4Y1gxeTRhWDlBSlduQTJjYzM1cnB1bGl2TWlqZVhzL3NzVDVpRmxqWFhHWXpta1g2blIgcm9vdEBsb2NhbGhvc3QubG9jYWxkb21haW4K
Step5. 開始注入
1?? 建立/root/.ssh目錄且許可權為700
# mkdir /root/.ssh
virsh qemu-agent-command ${DOMAIN} '{"execute":"guest-exec","arguments":{"path":"mkdir","arg":["-p","/root/.ssh"],"capture-output":true}}'
# 假設上一步返回{"return":{"pid":911}},接下來檢視結果(通常可忽略)
virsh qemu-agent-command ${DOMAIN} '{"execute":"guest-exec-status","arguments":{"pid":911}}'
# chmod 700 /root/.ssh,此行其實可不執行,因為上面建立目錄後就是700,但為了防止許可權不正確導致無法使用,這裡還是再刷一次700比較穩妥
virsh qemu-agent-command ${DOMAIN} '{"execute":"guest-exec","arguments":{"path":"chmod","arg":["700","/root/.ssh"],"capture-output":true}}'
# 假設上一步返回{"return":{"pid":912}},接下來檢視結果(通常可忽略)
virsh qemu-agent-command ${DOMAIN} '{"execute":"guest-exec-status","arguments":{"pid":912}}'
2?? 建立/root/.ssh/authorized_keys檔案且許可權為600
# touch /root/.ssh/authorized_keys
virsh qemu-agent-command ${DOMAIN} '{"execute":"guest-exec","arguments":{"path":"touch","arg":["/root/.ssh/authorized_keys"],"capture-output":true}}'
# 假設上一步返回{"return":{"pid":913}},接下來檢視結果(通常可忽略)
virsh qemu-agent-command ${DOMAIN} '{"execute":"guest-exec-status","arguments":{"pid":913}}'
# chmod 600 /root/.ssh/authorized_keys,此行其實可不執行,因為上面建立檔案後就是600,但為了防止許可權不正確導致無法使用,這裡還是再刷一次600比較穩妥
virsh qemu-agent-command ${DOMAIN} '{"execute":"guest-exec","arguments":{"path":"chmod","arg":["600","/root/.ssh/authorized_keys"],"capture-output":true}}'
# 假設上一步返回{"return":{"pid":914}},接下來檢視結果(通常可忽略)
virsh qemu-agent-command ${DOMAIN} '{"execute":"guest-exec-status","arguments":{"pid":914}}'
3?? 將Base64編碼寫入/root/.ssh/authorized_keys
# 開啟檔案(以讀寫方式開啟),獲得控制代碼
virsh qemu-agent-command ${DOMAIN} '{"execute":"guest-file-open", "arguments":{"path":"/root/.ssh/authorized_keys","mode":"w+"}}'
# 寫檔案,假設上一步返回{"return":1000},1000就是控制代碼
virsh qemu-agent-command ${DOMAIN} '{"execute":"guest-file-write", "arguments":{"handle":1000,"buf-b64":"c3NoLXJzYSBBQUFBQjNOemFDMXljMkVBQUFBREFRQUJBQUFCQVFEVktvZzA0cGJiTGFhcmpicHZLN0NSYUl1VXdXeGVoSklIOHRxdFgvb1Y0R1lONVdHWVBGYTF0enNkNFZ5b2JsbTRMZVBYNzlXZUk0a0ZIZ1NiSDVQNkg5aThsM0tDVEZISGVKVC9nMFA1NS9jNjB5RGIzbzZscXBXdTlJS0UzSTRsc1RwMDVZL1cwS3M3VzI3Sm5kcjE2Mm5pMFlidGhnZDlDUXlvaWJ1cm9oMzVFQ2lQR3dXVU9CVko0SUVwU3BPWmREVUpMUy92VnVTUWd2RUgwZnEvRzFEUDNTT3lSK0ROYXNKMDBtd29uZmFVS0haWG1XQWxIOG1hck53UG1XYXB5VFNRd0NGS0toMUh3bEpFV0VUVjRmWXVGd20zaWVubmI4Y1gxeTRhWDlBSlduQTJjYzM1cnB1bGl2TWlqZVhzL3NzVDVpRmxqWFhHWXpta1g2blIgcm9vdEBsb2NhbGhvc3QubG9jYWxkb21haW4K"}}'
# 關閉檔案
virsh qemu-agent-command ${DOMAIN} '{"execute":"guest-file-close", "arguments":{"handle":1000}}'
檢視效果:此時到VM裡檢視/root/.ssh/authorized_keys,應該能看到新增加的一行
附1. qemu-ga支援的所有指令
不同的qemu-ga版本、不同的作業系統,支援的指令都會有所差異,下面是從官網上看到的當前所有引數
? guest-exec
? guest-exec-status
? guest-file-close
? guest-file-flush
? guest-file-open
? guest-file-read
? guest-file-seek
? guest-file-write
? guest-fsfreeze-freeze
? guest-fsfreeze-freeze-list
? guest-fsfreeze-status
? guest-fsfreeze-thaw
? guest-fstrim
? guest-get-fsinfo
? guest-get-host-name
? guest-get-memory-block-info
? guest-get-memory-blocks
? guest-get-osinfo
? guest-get-time
? guest-get-timezone
? guest-get-users
? guest-get-vcpus
? guest-info
? guest-network-get-interfaces
? guest-ping
? guest-set-memory-blocks
? guest-set-time
? guest-set-user-password
? guest-set-vcpus
? guest-shutdown
? guest-suspend-disk
? guest-suspend-hybrid
? guest-suspend-ram
? guest-sync
? guest-sync-delimited
具體使用方法,請參考官網文件
https://qemu.weilnetz.de/doc/qemu-ga-ref.html
附2. 設定多個channel
1?? 可以在XML裡設定多個channel,這樣就可以建立多個裝置通道
<channel type='unix'>
<source mode='bind' path='/tmp/channel.sock'/>
<target type='virtio' name='org.qemu.guest_agent.0'/>
</channel>
<channel type='unix'>
<source mode='bind' path='/tmp/channel.sock-1'/>
<target type='virtio' name='org.qemu.guest_agent.1'/>
</channel>
2?? 在VM裡要啟動2個qemu-ga守護行程,可以將原有的service檔案拷貝一份出來進行修改
cd /usr/lib/systemd/system
cp qemu-guest-agent.service qemu-guest-agent-1.service
然後修改qemu-guest-agent-1.service
3?? 啟動服務
systemctl start qemu-guest-agent-1
相關文章