The below instructions are manual steps to validate the implementation of the Standardized Image Encryption on a DevStack as per upstream patchsets uploaded at https://review.opendev.org/q/topic:%22LUKS-image-encryption%22
For further information see the corresponding Glance Spec and Cinder Spec.
Note: the instructions were created during the implementation phase of the patchsets and details about the process of using image encryption might change in the future and may not reflect the behavior of stable OpenStack releases.
source /opt/stack/devstack/openrc admin admin
# security group to allow SSH access
openstack security group create --description "SSH" admin-access-group
openstack security group rule create --proto tcp --dst-port 22 admin-access-group
# prepare networks and router
PUBLIC_NETWORK=... # name of public network with Floating IPs
openstack network create admin-private
openstack subnet create --network admin-private --subnet-range 10.1.2.0/24 admin-private-subnet
openstack router create admin-public-router
openstack router set --external-gateway $PUBLIC_NETWORK admin-public-router
openstack router add subnet admin-public-router admin-private-subnet
# encrypted Volume Type
# (this assumes that you are using LVM-based Cinder backend in DevStack)
BASIC_TYPE=$(openstack volume type list -f value -c Name | grep lvm)
openstack volume type create \
--property volume_backend_name="$BASIC_TYPE" \
--encryption-provider luks \
--encryption-cipher aes-xts-plain64 \
--encryption-key-size 256 \
--encryption-control-location front-end \
"LVM-LUKS"
Example image file for testing:
wget https://download.cirros-cloud.net/0.6.2/cirros-0.6.2-x86_64-disk.img
qemu-img convert -O raw cirros-0.6.2-x86_64-disk.img cirros.raw
- image format will be uncompressed LUKS, which can later be directly transferred to a volume by Cinder
- passphrase will be uploaded as "passphrase" secret type in Barbican; this will make Cinder skip
binascii.hexlify()
and use it as LUKS passphrase directly
echo -n "muchsecretsuchwow" > secret_file.key
# upload passphrase to Barbican in plain text
SECRET_TYPE="passphrase"
openstack secret store --file secret_file.key \
--name luks-image-passphrase --secret-type $SECRET_TYPE
SECRET_ID=$(openstack secret list -f value --name luks-image-passphrase \
| head -n1 | cut -d' ' -f1 | rev | cut -d'/' -f1 | rev)
echo $SECRET_ID
# encrypt the image with LUKS using qemu-img
qemu-img convert -f raw -O luks \
--object secret,id=sec,file=secret_file.key \
-o key-secret=sec \
-o cipher-alg=aes-256 \
-o cipher-mode=xts \
-o hash-alg=sha256 \
-o ivgen-alg=plain64 \
-o ivgen-hash-alg=sha256 \
cirros.raw cirros.luks
# upload the encrypted image and reference Barbican secret
openstack image create --file cirros.luks \
--property os_encrypt_key_id=$SECRET_ID \
--property os_encrypt_key_deletion_policy=on_image_deletion \
--property os_encrypt_format=luks \
--property os_encrypt_cipher=aes-xts-plain64 \
cirros-luks
- image format will be uncompressed LUKS, which can later be directly transferred to a volume by Cinder
- passphrase will be uploaded as "symmetric" secret type in Barbican; this will make Cinder convert it to passphrase using
binascii.hexlify()
(like it does for its own images of LUKS volumes)
echo -n "muchsecretsuchwow" > secret_file.key
# it is important that the file does not end with a newline!
python3 -c "import binascii;
print(binascii.hexlify('$(cat secret_file.key)'\
.encode('utf-8')).decode('utf-8'), end='')" \
> secret_file.hex
# upload passphrase to Barbican in plain text (Cinder will hexlify later)
SECRET_TYPE="symmetric"
openstack secret store --file secret_file.key \
--name luks-image-key --secret-type $SECRET_TYPE
SECRET_ID=$(openstack secret list -f value --name luks-image-key \
| head -n1 | cut -d' ' -f1 | rev | cut -d'/' -f1 | rev)
echo $SECRET_ID
# encrypt the image with LUKS using qemu-img, use hexlified passphrase
qemu-img convert -f raw -O luks \
--object secret,id=sec,file=secret_file.hex \
-o key-secret=sec \
-o cipher-alg=aes-256 \
-o cipher-mode=xts \
-o hash-alg=sha256 \
-o ivgen-alg=plain64 \
-o ivgen-hash-alg=sha256 \
cirros.raw cirros.luks.hexlified
# upload the encrypted image and reference Barbican secret
openstack image create --file cirros.luks.hexlified \
--property os_encrypt_key_id=$SECRET_ID \
--property os_encrypt_key_deletion_policy=on_image_deletion \
--property os_encrypt_format=luks \
--property os_encrypt_cipher=aes-xts-plain64 \
cirros-luks-hexlified
- image format will be qcow2-wrapped LUKS, which will later be converted to raw LUKS in Cinder
- passphrase will be uploaded as "passphrase" secret type in Barbican; this will make Cinder skip
binascii.hexlify()
and use it as LUKS passphrase directly
echo -n "muchsecretsuchwow" > secret_file.key
# upload passphrase to Barbican in plain text
SECRET_TYPE="passphrase"
openstack secret store --file secret_file.key \
--name qcow-image-passphrase --secret-type $SECRET_TYPE
SECRET_ID=$(openstack secret list -f value --name qcow-image-passphrase \
| head -n1 | cut -d' ' -f1 | rev | cut -d'/' -f1 | rev)
echo $SECRET_ID
# encrypt the image with qcow2+LUKS using qemu-img
qemu-img convert -f raw -O qcow2 \
--object secret,id=sec,file=secret_file.key \
-o encrypt.format=luks \
-o encrypt.key-secret=sec \
-o encrypt.cipher-alg=aes-256 \
-o encrypt.cipher-mode=xts \
-o encrypt.hash-alg=sha256 \
-o encrypt.ivgen-alg=plain64 \
-o encrypt.ivgen-hash-alg=sha256 \
cirros.raw cirros.luks.qcow2
# upload the encrypted image and reference Barbican secret
openstack image create --file cirros.luks.qcow2 \
--disk-format qcow2 \
--property os_encrypt_key_id=$SECRET_ID \
--property os_encrypt_key_deletion_policy=on_image_deletion \
--property os_encrypt_format=luks \
--property os_encrypt_cipher=aes-xts-plain64 \
cirros-qcow2luks
- image format will be qcow2-wrapped LUKS, which will later be converted to raw LUKS in Cinder
- passphrase will be uploaded as "symmetric" secret type in Barbican; this will make Cinder convert it to passphrase using
binascii.hexlify()
(like it does for its own images of LUKS volumes)
echo -n "muchsecretsuchwow" > secret_file.key
# it is important that the file does not end with a newline!
python3 -c "import binascii;
print(binascii.hexlify('$(cat secret_file.key)'\
.encode('utf-8')).decode('utf-8'), end='')" \
> secret_file.hex
# upload passphrase to Barbican in plain text (Cinder will hexlify later)
SECRET_TYPE="symmetric"
openstack secret store --file secret_file.key \
--name qcow-image-key --secret-type $SECRET_TYPE
SECRET_ID=$(openstack secret list -f value --name qcow-image-key \
| head -n1 | cut -d' ' -f1 | rev | cut -d'/' -f1 | rev)
echo $SECRET_ID
# encrypt the image with qcow2+LUKS using qemu-img, use hexlified passphrase
qemu-img convert -f raw -O qcow2 \
--object secret,id=sec,file=secret_file.hex \
-o encrypt.format=luks \
-o encrypt.key-secret=sec \
-o encrypt.cipher-alg=aes-256 \
-o encrypt.cipher-mode=xts \
-o encrypt.hash-alg=sha256 \
-o encrypt.ivgen-alg=plain64 \
-o encrypt.ivgen-hash-alg=sha256 \
cirros.raw cirros.luks.qcow2.hexlified
# upload the encrypted image and reference Barbican secret
openstack image create --file cirros.luks.qcow2.hexlified \
--disk-format qcow2 \
--property os_encrypt_key_id=$SECRET_ID \
--property os_encrypt_key_deletion_policy=on_image_deletion \
--property os_encrypt_format=luks \
--property os_encrypt_cipher=aes-xts-plain64 \
cirros-qcow2luks-hexlified
openstack volume create --type LVM-LUKS --size 1 \
--image cirros-luks vol-from-luks-img
openstack volume create --type LVM-LUKS --size 1 \
--image cirros-luks-hexlified vol-from-luks-hexlified-img
openstack volume create --type LVM-LUKS --size 1 \
--image cirros-qcow2luks vol-from-qcow2luks-img
openstack volume create --type LVM-LUKS --size 1 \
--image cirros-qcow2luks-hexlified vol-from-qcow2luks-hexlified-img
PUBLIC_NETWORK=... # name of public network with Floating IPs
# the commands below only create Floating IPs that do not exist with the same
# description already
if [ $(openstack floating ip list --long -f value -c Description \
| grep "serverIP1" | wc -l) == 0 ]; \
then openstack floating ip create --description "serverIP1" $PUBLIC_NETWORK; fi
if [ $(openstack floating ip list --long -f value -c Description \
| grep "serverIP2" | wc -l) == 0 ]; \
then openstack floating ip create --description "serverIP2" $PUBLIC_NETWORK; fi
if [ $(openstack floating ip list --long -f value -c Description \
| grep "serverIP3" | wc -l) == 0 ]; \
then openstack floating ip create --description "serverIP3" $PUBLIC_NETWORK; fi
if [ $(openstack floating ip list --long -f value -c Description \
| grep "serverIP4" | wc -l) == 0 ]; \
then openstack floating ip create --description "serverIP4" $PUBLIC_NETWORK; fi
# get Floating IP addresses
FIP1=$(openstack floating ip list --long -f value -c Description -c "Floating IP Address" \
| grep "serverIP1" | cut -d ' ' -f1)
FIP2=$(openstack floating ip list --long -f value -c Description -c "Floating IP Address" \
| grep "serverIP2" | cut -d ' ' -f1)
FIP3=$(openstack floating ip list --long -f value -c Description -c "Floating IP Address" \
| grep "serverIP3" | cut -d ' ' -f1)
FIP4=$(openstack floating ip list --long -f value -c Description -c "Floating IP Address" \
| grep "serverIP4" | cut -d ' ' -f1)
FLAVOR=m1.tiny
openstack server create --flavor $FLAVOR --network admin-private \
--volume vol-from-luks-img --security-group admin-access-group \
vm-from-luks-img-vol
openstack server create --flavor $FLAVOR --network admin-private \
--volume vol-from-luks-hexlified-img --security-group admin-access-group \
vm-from-luks-img-vol-hex
openstack server create --flavor $FLAVOR --network admin-private \
--volume vol-from-qcow2luks-img --security-group admin-access-group \
vm-from-qcow2luks-img-vol
openstack server create --flavor $FLAVOR --network admin-private \
--volume vol-from-qcow2luks-hexlified-img \
--security-group admin-access-group \
vm-from-qcow2luks-img-vol-hex
openstack server add floating ip vm-from-luks-img-vol $FIP1
openstack server add floating ip vm-from-luks-img-vol-hex $FIP2
openstack server add floating ip vm-from-qcow2luks-img-vol $FIP3
openstack server add floating ip vm-from-qcow2luks-img-vol-hex $FIP4
# default password for cirros image is 'gocubsgo'
ssh cirros@$FIP1
ssh cirros@$FIP2
ssh cirros@$FIP3
ssh cirros@$FIP4