Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upstream Image Encryption Standardization #560

Open
5 tasks
josephineSei opened this issue Apr 11, 2024 · 17 comments
Open
5 tasks

Upstream Image Encryption Standardization #560

josephineSei opened this issue Apr 11, 2024 · 17 comments
Assignees
Labels
question Further information is requested SCS-VP10 Related to tender lot SCS-VP10 standards Issues / ADR / pull requests relevant for standardization & certification upstream Implemented directly in the upstream

Comments

@josephineSei
Copy link
Contributor

Currently there is the possibility in Cinder to encrypt volumes and in Nova to use qcow2 encrypted images (still under development).
Both can lead to and use LUKS-encrpyted images, but those are different and not aligned:

  • qcow2 with LUKS in it for Nova
  • LUKS raw blocks for Cinder

@markus-hentsch also found out in #541 that uploading a LUKS-encrypted image (that was created from a volume) to another cloud in combination with setting a few parameteres (cinder_encryption_key, etc...) will result in an image that can be used to create an encrpyted and functional volume.

As a user it would be good to have a streamlined operation to use encrypted images in openstack for both volumes and ephemeral storage and to also allow interoperability between clouds.
Therefore we need and will propose standardized parameters to describe and detect an encrypted image, which might be similar to the parameters described here: https://specs.openstack.org/openstack/cinder-specs/specs/zed/image-encryption.html
But will use the LUKS encryption.
So those encrypted images could be natively mounted in Nova or just formed into a volume (raw LUKS images can be directly used, qcow images need to be flattened).

With such a way encrypted backup images can be easily downloaded and transferred to another cloud.

  • A spec has to be written for Glance (and maybe Cinder?)
  • Implementation in Glance to standardize image parameters
  • Implementation in OSC/SDK to encrypt while uploading and decrypt while downloading an image
  • Implementation in Cinder to align to new standard parameters
  • Implementation in Cinder to flatten encrypted qcow to raw volumes

This is a result from a lengthy discussion at the PTG with Nova, Cinder and Glance ( https://etherpad.opendev.org/p/dalmatian-ptg-cinder#L376 )

Followup tasks may be to implement re-encryption to fully change keys for LUKS volumes and images.

@josephineSei josephineSei added the question Further information is requested label Apr 11, 2024
@josephineSei josephineSei added SCS-VP10 Related to tender lot SCS-VP10 standards Issues / ADR / pull requests relevant for standardization & certification labels Apr 11, 2024
@josephineSei
Copy link
Contributor Author

I have discussed and outlined the spec with @markus-hentsch and currently writing it.

@markus-hentsch
Copy link
Contributor

markus-hentsch commented Apr 11, 2024

On the user side we'd need to be able to create LUKS-encrypted files. Nova devs made us aware of the QEMU tooling being able to do so allegedly.

Here is the Nova implementation:

Here's a shortened example:

echo "muchsecretsuchwow" > secret_file.key

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 \
$INPUT_FILE $OUTPUT_LUKS_FILE

It works within a Docker container running Ubuntu LTS but interestingly it doesn't on my NixOS setup, failing with:

qemu-img: output.luks: error while converting luks: Unsupported cipher mode xts

Versions of qemu-img are 6.2.0 (Ubuntu) and 8.1.5 (NixOS) respectively. I wonder if there is another system dependency in play here or if it's the version difference. We should investigate because this impacts the ability to create images on the client side.

EDIT:

Tried it on ubuntu:noble image which ships qemu-img 8.2.1 and it still works. So no deprecation problem here in regards to the qemu-img version. I think we can safely ignore it as a NixOS-specific issue then.

@markus-hentsch
Copy link
Contributor

As I was adding restore instructions to the user data backup guide in SovereignCloudStack/docs#176 I reproduced the process of creating volumes from previously encrypted (LUKS) images originating from Cinder itself on my DevStack and noticed that Cinder does not verify that the target volume actually has an encrypted type:

$ file image.raw
image.raw: LUKS encrypted file, ver 1 [aes, xts-plain64, sha256]

$ file image.key
image.key: data

$ openstack secret store --algorithm aes --bit-length 256 --mode cbc \
  --secret-type symmetric --file image.key --name restored-image-key
  
$ openstack secret list -f value -c "Secret href" -c "Name"
http://10.0.1.116/key-manager/v1/secrets/6ea6b0a8-de50-45b8-90b7-9470c4dd201a restored-image-key

$ export SECRET_ID=6ea6b0a8-de50-45b8-90b7-9470c4dd201a

$ openstack image create --file image.raw \
  --property cinder_encryption_key_id=$SECRET_ID \
  --property cinder_encryption_key_deletion_policy=on_image_deletion \
  restored-image

$ openstack volume create --size 1 \
  --image restored-image volume-restored-notype

$ openstack volume show -f value -c type volume-restored-notype
lvmdriver-1  # <- this is an *unencrypted* volume type! (which contains LUKS blocks now)

$ openstack volume create --size 1 \
  --image restored-image --type lvmdriver-1-LUKS \
  volume-restored-lukstype

$ openstack volume show -f value -c type volume-restored-lukstype
lvmdriver-1-LUKS

$ openstack server create \
  --volume volume-restored-notype \
  ... server-from-untyped-volume
  
$ openstack server create \
  --volume volume-restored-lukstype \
  ... server-from-luks-volume

This results in the server server-from-untyped-volume being stuck at "No bootable device found" on the virtual console. Only server-from-luks-volume where a LUKS volume type was explicitly chosen while restoring the LUKS image was bootable.

It seems that this is an oversight in Cinder in its current implementation?

@josephineSei
Copy link
Contributor Author

I tested this with a simple encrypted volume to encrypted image to volume:

stack@devstack:~/devstack$ openstack image show encrypted-volume-image
+------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Field            | Value                                                                                                                                                                   |
+------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| checksum         | 4f0538390cb7728f6e02d6a58a824e11                                                                                                                                        |
| container_format | bare                                                                                                                                                                    |
| created_at       | 2024-04-11T13:09:08Z                                                                                                                                                    |
| disk_format      | raw                                                                                                                                                                     |
| file             | /v2/images/49febf2e-4f20-47f0-8230-660c2dcb19dc/file                                                                                                                    |
| id               | 49febf2e-4f20-47f0-8230-660c2dcb19dc                                                                                                                                    |
| min_disk         | 0                                                                                                                                                                       |
| min_ram          | 0                                                                                                                                                                       |
| name             | encrypted-volume-image                                                                                                                                                  |
| owner            | 15f2ab0eaa5b4372b759bde609e86224                                                                                                                                        |
| properties       | cinder_encryption_key_deletion_policy='on_image_deletion', cinder_encryption_key_id='286339bc-5484-4a27-b24a-816f89a2968c', hw_rng_model='virtio', locations='[{'url':  |
|                  | 'rbd://adbc1d67-adf7-4a13-94b5-2a2570d41ed9/images/49febf2e-4f20-47f0-8230-660c2dcb19dc/snap', 'metadata': {}}]', os_hash_algo='sha512',                                |
|                  | os_hash_value='7160b7451962496108194179757221dbbb5af7654b205a63cf6284a70dd1caed8ee289b98a56f0cb0d37e363adc18cc334d0f1bf6be937e1e9af9036009b0856', os_hidden='False',    |
|                  | owner_specified.openstack.md5='', owner_specified.openstack.object='images/cirros-0.6.2-x86_64-disk', owner_specified.openstack.sha256='', signature_verified='False'   |
| protected        | False                                                                                                                                                                   |
| schema           | /v2/schemas/image                                                                                                                                                       |
| size             | 3221225472                                                                                                                                                              |
| status           | active                                                                                                                                                                  |
| tags             |                                                                                                                                                                         |
| updated_at       | 2024-04-11T13:11:31Z                                                                                                                                                    |
| virtual_size     | 3221225472                                                                                                                                                              |
| visibility       | shared                                                                                                                                                                  |
+------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
stack@devstack:~/devstack$ openstack volume create --size 3 --image encrypted-volume-image volume-from-LUKS-image
+---------------------+--------------------------------------+
| Field               | Value                                |
+---------------------+--------------------------------------+
| attachments         | []                                   |
| availability_zone   | nova                                 |
| bootable            | false                                |
| consistencygroup_id | None                                 |
| created_at          | 2024-04-12T11:23:52.246101           |
| description         | None                                 |
| encrypted           | False                                |
| id                  | f790ecd4-eea6-4a8d-8cf6-d1a038c91d60 |
| migration_status    | None                                 |
| multiattach         | False                                |
| name                | volume-from-LUKS-image               |
| properties          |                                      |
| replication_status  | None                                 |
| size                | 3                                    |
| snapshot_id         | None                                 |
| source_volid        | None                                 |
| status              | creating                             |
| type                | ceph                                 |
| updated_at          | None                                 |
| user_id             | 6cf194afebb6469e8423f50500b5c3fc     |
+---------------------+--------------------------------------+

A seemingly unencrypted volume is created from the encrypted image. But as far as we know, there is no decrypting mechanism implemented in Cinder to go from such a Cinder-specific LUKS encryption in the image to an unencrypted volume.

We should definitely file a bug in Cinder for this one.

@josephineSei
Copy link
Contributor Author

How to test, if a server booted

When creating a server from a volume, like my above case, it will be shown as active even though this cannot be true

stack@devstack:~/devstack$ openstack server list
+-----------------+-----------------+--------+-----------------+------------------+----------+
| ID              | Name            | Status | Networks        | Image            | Flavor   |
+-----------------+-----------------+--------+-----------------+------------------+----------+
| 8ba1b529-d1d9-  | will-this-      | ACTIVE | private=10.0.0. | N/A (booted from | m1.small |
| 4f5b-a97d-      | server-boot?    |        | 47, fd13:d046:e | volume)          |          |
| 6756f61451e0    |                 |        | 727:0:f816:3eff |                  |          |
|                 |                 |        | :feae:b8e1      |                  |          |
| 66f8f821-ec26-  | my-new-server   | ACTIVE | private=10.0.0. | N/A (booted from | m1.small |
| 4264-807e-      |                 |        | 41, fd13:d046:e | volume)          |          |
| 36ec016d51f9    |                 |        | 727:0:f816:3eff |                  |          |
|                 |                 |        | :fe98:3e70      |                  |          |
+-----------------+-----------------+--------+-----------------+------------------+----------+

To show the boot log of a server the following command can be used:

stack@devstack:~/devstack$ openstack console log show my-new-server > other-server.log

The log will then look something like:

[    0.000000] Linux version 5.15.0-71-generic (buildd@lcy02-amd64-044) (gcc (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0>
[    0.000000] Command line: LABEL=cirros-rootfs ro console=tty1 console=ttyS0
[    0.000000] KERNEL supported cpus:
[    0.000000]   Intel GenuineIntel
[    0.000000]   AMD AuthenticAMD
[    0.000000]   Hygon HygonGenuine
[    0.000000]   Centaur CentaurHauls
[    0.000000]   zhaoxin   Shanghai
[    0.000000] x86/fpu: x87 FPU will use FXSAVE
[    0.000000] signal: max sigframe size: 1440
[    0.000000] BIOS-provided physical RAM map:
[    0.000000] BIOS-e820: [mem 0x0000000000000000-0x000000000009fbff] usable
[    0.000000] BIOS-e820: [mem 0x000000000009fc00-0x000000000009ffff] reserved
[    0.000000] BIOS-e820: [mem 0x00000000000f0000-0x00000000000fffff] reserved
[    0.000000] BIOS-e820: [mem 0x0000000000100000-0x000000007ffdcfff] usable
[    0.000000] BIOS-e820: [mem 0x000000007ffdd000-0x000000007fffffff] reserved
[    0.000000] BIOS-e820: [mem 0x00000000feffc000-0x00000000feffffff] reserved
[    0.000000] BIOS-e820: [mem 0x00000000fffc0000-0x00000000ffffffff] reserved
.....

But if created from a server with an unencrypted volume from an encrypted image the log file remains empty:

stack@devstack:~/devstack$ openstack console log show 02d13a4c-a6a0-4b40-850c-9b7ddd13d4bb
     <- log output should be here
stack@devstack:~/devstack$

@josephineSei
Copy link
Contributor Author

I uploaded the new spec to gerrit: https://review.opendev.org/c/openstack/glance-specs/+/915726

@markus-hentsch
Copy link
Contributor

I reported the bug uncovered in #560 (comment) at https://bugs.launchpad.net/cinder/+bug/2061154

@josephineSei
Copy link
Contributor Author

I reported the bug uncovered in #560 (comment) at https://bugs.launchpad.net/cinder/+bug/2061154

I raised this in the IRC, the following was the answer:

<Luzi> eharney, while trying some things with the LUKS-image we found this behavior: https://bugs.launchpad.net/cinder/+bug/2061154
<Luzi> is this known?
<eharney> Luzi: i think this is done on purpose because we didn't want to have users accidentally decrypting volume images, but it might still be useful to let them copy the data around (rather than failing)
<eharney> it would make sense to document this behavior because it is kind of surprising in the standard use of volumes/images
<eharney> generally, people should make sure they are using an encrypted volume type
<Luzi> or maybe add a check for the a volume type with encryption type?
<eharney> i think we didn't want it to fail because there are situations (copying data around to different places rather than booting instances) where it's useful to allow this

@anjastrunk anjastrunk added the upstream Implemented directly in the upstream label Apr 15, 2024
@anjastrunk anjastrunk mentioned this issue Apr 15, 2024
60 tasks
@josephineSei
Copy link
Contributor Author

We currently evaluate whether we will also need a spec for Cinder. What we definitely need is a blueprint for Cinder to track all development. I have written a blueprint and tried to include all possible points, where there will be implementation needed in Cinder:
https://blueprints.launchpad.net/cinder/+spec/standardize-image-encryption-metadata
From my opinion there should be a spec, to at least show all use cases where encrypted images need to be considered.

@josephineSei
Copy link
Contributor Author

We got a review for the Spec (I am currently updating it) and there is one last question to discuss:

We wanted to introduce a new container format "encrypted" - so it would be easily visible to anyone, that this image is encrypted. To identify what the underlying format is, we introduced a property: "os_decrypt_container_format".

Now Nova and Cinder both would like to have the container format showing up in the original property and Cinder additionally would need some parameter to know whether the encrypted image is compressed or not.

The thing here is, that we could check whether encrypted images are always qcow or raw when the container_format and decrypt_container_format is set. The metadata could be set after creating an image in the upload step. So it may be allowed to create an encrypted image with a format neither cinder nor nova could use. We would like to avoid such bad user experience.

So we want to ask the following questions in the Glance meeting this thursday:

  1. would it be feasible to introduce a new top level property "encrypted"?
  2. what would be the implications?

@josephineSei
Copy link
Contributor Author

From the Cinder meeting today I got the wish to also create a small Cinder spec: https://etherpad.opendev.org/p/cinder-dalmatian-meetings

@josephineSei
Copy link
Contributor Author

I created the Cidner spec today: https://review.opendev.org/c/openstack/cinder-specs/+/919499

@josephineSei
Copy link
Contributor Author

We got reviews on the spec, and Markus and I are going through them and answering questions.

@josephineSei
Copy link
Contributor Author

I adjusted the Glance spec and removed the container_format 'encrypted', because this was one of the most discussed parts of the spec: https://review.opendev.org/c/openstack/glance-specs/+/915726

@josephineSei
Copy link
Contributor Author

I got feedback on the Glance patch with the question about what to do when the image conversion plugin is activated.
It needs among other things this config adjustment: https://github.com/openstack/glance/blob/master/etc/glance-image-import.conf.sample#L26

Image conversion as I understand it, is not something that can be triggered by a CLI command. Instead when the plugin is activated ALL images that are created will be automatically converted to the ( through the config ) specified format after uploading and before storing it.

This creates a few questions regarding encrypted images:

  1. Is this behavior also triggered when Nova or Cinder upload an image? If yes, then it will currently result in unusable images or worse: strange Errors. If not, this feature is not very well implemented, because in this case disk-formats will differ and not all be the same (as assumed by the user or the plugin).
  2. The currently supported target formats are qcow2, raw and vmdk. We will only allow qcow2 and raw images to be encrypted and uploaded, because other ones cannot be used by Cinder and Nova. Converting qcow2 to raw is possible, vice-versa needs testing. But we do not support conversion to vmdk, because we don't know the behavior and whether transforming from encrypted qcow2/raw to encrypted vmdk would be possible with qemu AND neither Cinder nor Nova use the encrypted vmdk format. So we would have to convert again in Nova and Cinder.

As this optional feature of Glance may already have problems with 1, and we would need to at least forbid uploading encrypted images when the target format is vmdk (maybe also when the target format is qcow2) this would be a lot more of implementation work. So we will for now render this out of scope for the spec. Maybe this can be done after the image encryption is in place and if it is needed by operators and users.

@markus-hentsch
Copy link
Contributor

I got feedback on the Glance patch with the question about what to do when the image conversion plugin is activated. It needs among other things this config adjustment: https://github.com/openstack/glance/blob/master/etc/glance-image-import.conf.sample#L26

Image conversion as I understand it, is not something that can be triggered by a CLI command. Instead when the plugin is activated ALL images that are created will be automatically converted to the ( through the config ) specified format after uploading and before storing it.

This creates a few questions regarding encrypted images:

1. Is this behavior also triggered when Nova or Cinder upload an image? If yes, then it will currently result in unusable images or worse: strange Errors. If not, this feature is not very well implemented, because in this case disk-formats will differ and not all be the same (as assumed by the user or the plugin).

2. The currently supported target formats are qcow2, raw and vmdk. We will only allow qcow2 and raw images to be encrypted and uploaded, because other ones cannot be used by Cinder and Nova. Converting qcow2 to raw is possible, vice-versa needs testing. But we do not support conversion to vmdk, because we don't know the behavior and whether transforming from encrypted qcow2/raw to encrypted vmdk would be possible with qemu AND neither Cinder nor Nova use the encrypted vmdk format. So we would have to convert again in Nova and Cinder.

As this optional feature of Glance may already have problems with 1, and we would need to at least forbid uploading encrypted images when the target format is vmdk (maybe also when the target format is qcow2) this would be a lot more of implementation work. So we will for now render this out of scope for the spec. Maybe this can be done after the image encryption is in place and if it is needed by operators and users.

Seems like this is part of the "Interoperable Image Import"1. Which references the following methods2:

  • glance-direct
  • web-download
  • copy-image
  • glance-download

This makes me wonder if this is applicable to images uploaded to Glance by Nova or Cinder at all. This is limited to images from external sources I think.

Nonetheless, at the latest when the user is initiating such an image upload we need to make sure that we account for this concerning the image encryption.
I agree that we should make it out of scope for the initial contribution in any case. Improving compatibility with advanced features later on after getting the base work done is always an option imho.

Footnotes

  1. https://docs.openstack.org/glance/latest/admin/interoperable-image-import.html#the-image-conversion

  2. https://docs.openstack.org/api-ref/image/v2/index.html#interoperable-image-import

@josephineSei
Copy link
Contributor Author

I am looking into the Cinder Side a little bit more because, we got a comment from Dan Smith regarding image conversion and Glance checking a few Image parameters:

Again, you either need to call out that image conversion will not be possible on encrypted images, or say that glance will do those things if it has the key_id (and access to it). Also note that without access to the key, glance won't be able to do any sort of image inspection (for virtual_size, format confirmation, backing file rejection, etc). I think it's worth mentioning those drawbacks here.

The image conversion does only seem to be triggered when a user is uploading an image, not if Cinder or Nova upload one. Forbidding that would put more responsibility on the user side, but we also do that with the image encryption.

The check for the virtual size can be omitted, because we only allow 2 types of encrypted images: qcow2 and raw and we want to introduce the "os_decrypt_size" parameter that should discribe the size of the unencrypted image. We may even mandate using this parameter. The format for encrypted raw images is only checkable, when decrypting the image.

So while this is a valid point to discuss, as Glance will reject images, that do not have the format, they say they have, the Glance team did not wanted to have the power to encrypt or decrypt images back when we discussed gpg-based image encryption. I doubt that this has changed and i also do not see a good reason, Glance should be able to do this.
Rather we should discuss, if these checks need to take place, and whether they could be moved to either the Client-side or better to Cinder and Nova.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested SCS-VP10 Related to tender lot SCS-VP10 standards Issues / ADR / pull requests relevant for standardization & certification upstream Implemented directly in the upstream
Projects
Status: Doing
Development

No branches or pull requests

3 participants