• Qemu镜像安全加密测试


    简介

    本文介绍两种qemu镜像加密方式,一种是已经过时的方式,另外一种是luks方式,作为对比和学习使用。

    1.已经过时的qemu自带的加密方式介绍

    这种加密方式已经被原作者(Daniel P. Berrange berrange@redhat.com)废除(deprecated),本章节先介绍一下这种方式的基本操作和废除原因123,以避免日后使用使用方式或者研究思路有错误。

    1.1.创建secret uuid的xml

    # cat demo-secret.xml
    <secret ephemeral='no' private='no'>
      <uuid>0a81f5b2-8403-7b23-c8d6-21ccc2f80d6f</uuid>
      <usage type='volume'>
        <volume>/home/VirtualMachines/demo.qcow2</volume>
      </usage>
    </secret>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    ephemeral是短暂的意思,如果设置为yes,则vm重启之后,加密就会消失,这一般应用于有第三方加密管理的情况下,这里选择no。
    privacy是表明是否libvirt可以给用户看到与该uuid关联的secret信息,如果设置为yes,就是writeonly的。
    uuid就是一个secret的唯一标识,如果不指定uuid,下一步会随机生成一个。

    1.2.产生uuid

    # virsh secret-define demo-secret.xml
      Secret 1a81f5b2-8403-7b23-c8d6-21ccc2f80d6f created
    
    • 1
    • 2

    1.3.给secret赋值

    # MYSECRET=`echo "open seseme" | base64`
      # virsh secret-set-value 0a81f5b2-8403-7b23-c8d6-21ccc2f80d6f $MYSECRET
      Secret value set
    
    • 1
    • 2
    • 3

    base64是一种编码格式,这样,一个拥有唯一标识符uuid的secret就产生完整了。

    1.4.创建一个存储池

    #virsh pool-dumpxml VirtualMachines

    <pool type='dir'>
      <name>VirtualMachines</name>
      <source>
      </source>
      <target>
        <path>/home/VirtualMachines</path>
      </target>
    </pool>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这是一个目录类型的存储池,必须先用mkdir创建/home/VirtualMachines目录

    #virsh pool-define xxxx.xml
    
    • 1

    1.5.在存储池中创建一个镜像

    # cat demo-disk.xml
    <volume>
      <name>demo.qcow2</name>
      <capacity>5368709120</capacity>
      <target>
        <format type='qcow2'/>
        <encryption format='qcow'>
          <secret type='passphrase' uuid='0a81f5b2-8403-7b23-c8d6-21ccc2f80d6f'/>
        </encryption>
      </target>
    </volume>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    # virsh vol-create VirtualMachines demo-disk.xml
    Vol demo.qcow2 created from demo-disk.xml
    
    • 1
    • 2

    可以查看该demo.qcow2的信息

    # file demo.qcow2 
    demo.qcow2: QEMU QCOW Image (v2), 5368709120 bytes, AES-encrypted.
    
    • 1
    • 2

    这里注意,该’passphrase’ 并没有按照例子设置具体值,也能正常工作。

    1.6.在虚拟机中使用该镜像

    # cat demo-guest.xml
    <domain type='qemu'>
      <name>demo</name>
      <memory>500200</memory>
      <vcpu>4</vcpu>
      <os>
        <type arch='i686' machine='pc'>hvm</type>
        <kernel>/home/vmlinuz-PAE</kernel>
        <initrd>/home/initrd-PAE.img</initrd>
        <boot dev='hd'/>
      </os>
      <devices>
        <emulator>/usr/bin/qemu-kvm</emulator>
        <disk type='file' device='disk'>
          <driver name='qemu' type='qcow2'/>
          <source file='/home/VirtualMachines/demo.qcow2'/>
          <target dev='hda' bus='ide'/>
          <encryption format='qcow'>
            <secret type='passphrase' uuid='0a81f5b2-8403-7b23-c8d6-21ccc2f80d6f'/>
          </encryption>
        </disk>
        <input type='tablet' bus='usb'/>
        <input type='mouse' bus='ps2'/>
        <graphics type='vnc' port='-1' autoport='yes'/>
      </devices>
    </domain>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    这是一个空硬盘,可以直接用内核文件vmlinuz和initrd做PXE启动。

    2.弃用以上加密方式

    2.1.原作者Daniel Berrange的观点

    A. AES-CBC加密算法本身很脆弱,容易受到选择明文攻击
    B. 本实现直接用的1.3小节中的passphrase当作加密密钥,密钥的选择不安全,太短并且缺乏随机性
    C. 当加密被入侵之后,没有机制可以修改密钥,镜像安全得不到保证

    2.2.Markus Armbruster更深入的操作

    首先创建三个文件4

    [root@localhost home]# qemu-img create -f raw backing.img 4m
    Formatting 'backing.img', fmt=raw size=4194304
    [root@localhost home]# qemu-img create -f qcow2 -o encryption,backing_file=backing.img,backing_fmt=raw geheim.qcow2 4m
    Formatting 'geheim.qcow2', fmt=qcow2 size=4194304 backing_file=backing.img backing_fmt=raw encryption=on cluster_size=65536 lazy_refcounts=off refcount_bits=16
    [root@localhost home]# qemu-img create -f qcow2 -o backing_file=backing.img, backing_fmt=raw normal.qcow2 4m
    Formatting 'normal.qcow2', fmt=qcow2 size=4194304 backing_file=backing.img backing_fmt=raw encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这里qemu-img执行了三次,可以看到,出现了一个backing_file参数,如果“-o”选项中使用了backing_file这个选项来指定其后端镜像文件,那么这个创建的镜像文件仅记录与后端镜像文件的差异部分。后端镜像文件不会被修改,除非在QEMU monitor中使用“commit”命令或者使用“qemu-img commit”命令去手动提交这些改动。这种情况下,size参数不是必须需的,其值默认为后端镜像文件的大小。另外,直接使用“-b backfile”参数也与“-o backing_file=backfile”效果相同。
    剩下的操作,现在的QEMU版本已经不支持,会提示报错

    [root@localhost home]# qemu-system-x86_64 -nodefaults -display none -monitor stdio geheim.qcow2
    qemu-system-x86_64: -monitor stdio: Use of AES-CBC encrypted qcow2 images is no longer supported in system emulators
    
    • 1
    • 2

    剩下的分析可以查看附录[2],当前QEMU版本推荐的硬盘加密方式是LUKS(Linux Unified Key Setup)。

    3. LUKS(Linux Unified Key Setup)加密简介

    #用vmware添加一块新磁盘,/dev/sdc,然后进行加密
    [root@localhost ~]#  cryptsetup -v -y -c aes-cbc-plain luksFormat /dev/sdc
    
    WARNING!
    ========
    This will overwrite data on /dev/sdc irrevocably.
    
    Are you sure? (Type uppercase yes): YES
    Enter passphrase: 
    Verify passphrase: 
    Command successful.
    #映射分区,需要输入密码,之后就不用了
    [root@localhost ~]# cryptsetup luksOpen /dev/sdc test_disk //把sdb1映射为test_disk
    Enter passphrase for /dev/sdc: 
    [root@localhost ~]# ll -d /dev/mapper/test_disk 
    lrwxrwxrwx. 1 root root 7 Oct 19 02:02 /dev/mapper/test_disk -> ../dm-2
    #查看映射分区状态
    [root@localhost ~]# cryptsetup status /dev/mapper/
    cl-root    cl-swap    control    test_disk  
    [root@localhost ~]# cryptsetup status /dev/mapper/cl-root 
    [root@localhost ~]# cryptsetup status /dev/mapper/cl-swap 
    [root@localhost ~]# cryptsetup status /dev/mapper/control 
    /dev/mapper/control is inactive.
    [root@localhost ~]# cryptsetup status /dev/mapper/test_disk 
    /dev/mapper/test_disk is active.
      type:    LUKS1
      cipher:  aes-cbc-plain
      keysize: 256 bits
      device:  /dev/sdc
      offset:  4096 sectors
      size:    10481664 sectors
      mode:    read/write
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    可以看到,未加密的映射分区是没有加密信息的,其中有个/dev/mapper/control不知道是做什么的,以后再研究。可以看到/dev/mapper/test_disk分区的加密信息,使用的aes-cbc-plain加密算法,用qemu-img命令生成的加密镜像,从qemu报错可以看出,现在的qemu版本不支持这种aes-cbc算法,目前无法确定用LUKS生成的加密镜像如果用这种算法,qemu会不会报错,猜测是会报错的,后面再进行验证。

    #挂载分区
    [root@localhost home]# mkfs.xfs /dev/mapper/test_disk 
    meta-data=/dev/mapper/test_disk  isize=512    agcount=4, agsize=327552 blks
             =                       sectsz=512   attr=2, projid32bit=1
             =                       crc=1        finobt=0, sparse=0
    data     =                       bsize=4096   blocks=1310208, imaxpct=25
             =                       sunit=0      swidth=0 blks
    naming   =version 2              bsize=4096   ascii-ci=0 ftype=1
    log      =internal log           bsize=4096   blocks=2560, version=2
             =                       sectsz=512   sunit=0 blks, lazy-count=1
    realtime =none                   extsz=4096   blocks=0, rtextents=0
    [root@localhost home]# mount /dev/sdc /home/crypto_test/
    mount: unknown filesystem type 'crypto_LUKS'  //不能直接挂载,要挂载在映射设备上
    [root@localhost home]# mount /dev/mapper/test_disk /home/crypto_test/
    [root@localhost home]# df -h
    Filesystem             Size  Used Avail Use% Mounted on
    /dev/mapper/test_disk  5.0G   33M  5.0G   1% /home/crypto_test
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    挂载成功。

    #关闭映射,先卸载后关闭
    [root@localhost home]# umount /home/crypto_test/
    [root@localhost home]# cryptsetup status /dev/mapper/test_disk 
    /dev/mapper/test_disk is active.
      type:    LUKS1
      cipher:  aes-cbc-plain
      keysize: 256 bits
      device:  /dev/sdc
      offset:  4096 sectors
      size:    10481664 sectors
      mode:    read/write
    [root@localhost home]# cryptsetup luksClose test_disk
    [root@localhost home]# cryptsetup status /dev/mapper/test_disk 
    /dev/mapper/test_disk is inactive.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    4.Libvirt对镜像进行LUKS加密

    4.1.需要的文件

    参考第1节,步骤和需要的文件几乎完全一样,只是加密方式不同。本节只标出不同的地方,其他步骤参考第1节5
    需要的文件为:demo-disk.xml demo-pool.xml demo-secret.xml

    #demo-secret.xml
    <secret ephemeral='no' private='yes'>
      <uuid>0a81f5b2-8403-7b23-c8d6-21ccc2f80d6f</uuid>
      <usage type='volume'>
        <volume>/home/crypto-test/twofish.luks</volume>
      </usage>
    </secret>
    
    #demo-pool.xml
    <pool type='dir'>
      <name>test-pool</name>
      <source>
      </source>
      <target>
        <path>/home/crypto-test</path>
      </target>
    </pool>
    
    #demo-disk.xml
    <volume>
      <name>twofish.luks</name>
      <capacity>3111111111</capacity>
      <target>
        <format type='raw'/>
        <encryption format='luks'>
          <secret type='passphrase' uuid='0a81f5b2-8403-7b23-c8d6-21ccc2f80d6f'/>
          <cipher name='twofish' size='256' mode='cbc' hash='sha256'/>
          <ivgen name='plain64' hash='sha256'/>
        </encryption>
      </target>
    </volume>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    差异在于

    <format type='raw'/><encryption format='luks'>
    
    • 1

    4.2.步骤

    #查看镜像文件格式

    [root@localhost crypto-test]# file twofish.luks 
    twofish.luks: LUKS encrypted file, ver 1 [twofish, cbc-plain64:sha256, sha256] UUID: c46eef30-b6c2-473a-959d-84d941b0d893
    
    • 1
    • 2

    #虚拟机disk配置

    <disk type='file' device='disk'>
          <driver name='qemu' type='raw'/>
          <source file='/home/crypto-test/twofish.luks'/>
          <backingStore/>
          <target dev='hda' bus='ide'/>
          <encryption format='luks'>
            <secret type='passphrase' uuid='0a81f5b2-8403-7b23-c8d6-21ccc2f80d6f'/>
          </encryption>
          <alias name='ide0-0-0'/>
          <address type='drive' controller='0' bus='0' target='0' unit='0'/>
    </disk>
    <disk type='file' device='cdrom'>
          <driver name='qemu' type='raw'/>
          <source file='/home/ubuntu-14.04.1-server-amd64-20170531.iso'/>
          <backingStore/>
          <target dev='hdc' bus='ide'/>
          <readonly/>
          <alias name='ide0-1-0'/>
          <address type='drive' controller='0' bus='1' target='0' unit='0'/>
    </disk>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在使用加密镜像的时候,由于实验中建立的是一个空镜像,里面没有操作系统,因此要带上一个cdrom,系统安装好之后,测试把删除,会找不到硬盘,加密生效。
    在这里插入图片描述

    故意将passphrase的最后一个字母从f改成e,再启动虚拟机

    <encryption format='luks'>
            <secret type='passphrase' uuid='0a81f5b2-8403-7b23-c8d6-21ccc2f80d6e'/>
    </encryption>
    
    • 1
    • 2
    • 3
    [root@localhost home]# virsh start vm_test_2
    error: Failed to start domain vm_test_2
    error: Secret not found: no secret with matching uuid '0a81f5b2-8403-7b23-c8d6-21ccc2f80d6e'
    
    • 1
    • 2
    • 3

    4.3.结论

    使用了LUKS之后,libvirt配置的passphrase不再用来当作生成加密密钥,仅仅用来解锁加密密钥,并且可以随时更改[3]。并且在demo-disk.xml的encryption标签里面,还可以配置成其他的选项来选择加密算法和密钥生成方式。

    Reference


    1. Using QCow2 disk encryption with libvirt in Fedora 12(deprecated) ↩︎

    2. [Qemu-devel] [PULL 03/17] block: Deprecate QCOW/QCOW2 encryption ↩︎

    3. QEMU QCow2 built-in encryption: just say no. Deprecated now, to be deleted soon ↩︎

    4. KVM虚拟机磁盘镜像文件管理命令qemu-img介绍及简单使用总结 ↩︎

    5. Secret XML format ↩︎

  • 相关阅读:
    Eclipse的安装以及环境配置
    CSS时间线样式
    你绝对没想到的GPT的底层意义
    前端:用Sass简化媒体查询
    面试官:完全背包都不会,是你自己走还是我送你?
    python资源库
    内核网络协议栈传输层协议框架
    websocket给指定客户端推送消息
    进入C++
    “高强度”干活还不够,马斯克又出“新招”:写一页绩效自证价值,以获得股票!...
  • 原文地址:https://blog.csdn.net/daxiatou/article/details/133942841