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

Support encrypted and signed user data #5599

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

TheRealFalcon
Copy link
Member

Proposed Commit Message

feat: Support encrypted and signed user data

Cloud-init user data often contains user secrets including passwords
and private keys. This data has always been submitted in plain text.
To protect this data's confidentiality and guarantee its authenticity,
add the ability to have this data encrypted and signed.

A new user data format is added allowing for an ASCII armored PGP
MESSAGE. If detected, cloud-init will import into a temporary keyring
any keys provided in /etc/cloud/keys and use these keys to decrypt
and/or verify the provided data.

After decryption, the resulting message will be treated as user data
as before.

Additional Context

The squash: commits are just to make reviewing easier. I plan to squash them before merging.

Test Steps

Merge type

  • Squash merge using "Proposed Commit Message"
  • Rebase and merge unique commits. Requires commit messages per-commit each referencing the pull request number (#<PR_NUM>)

@@ -0,0 +1,129 @@
"""These tests are for code in the cloudinit.user_data module.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that I moved non-PGP related tests here as they fit better here.

# that we attempt to decode said payload so that the transformed
# data can be examined.
parent_ctype = None
if ctype in TRANSFORM_TYPES:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made a necessary refactor to this section involving setting the mime type of transformed messages. Please double check my logic 🙂 .

I did ensure that gzipped user data still works properly with this code.

@@ -0,0 +1,201 @@
.. _pgp:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If running this guide, don't be like me and forget to install this branch into the container and then be mad it doesn't work at the end.

Copy link
Member

@holmanb holmanb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First pass review


Save this file to your working directory as `cloud-config.yaml`.

Encrypt the user data using the public key of the encrypting user and the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Encrypt the user data using the public key of the encrypting user and the private key of the signing user:

The signing key doesn't do encryption. How about:

Encrypt the user data using the public key of the encrypting user and sign it using the private key of the signing user:

using the existing key ring in the snapshot, we do this for a few reasons:

* Users may not want these keys in any key ring by default on a new instance
* Keys may be exported from any system without needing to be concerned with
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand what this is trying to say.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can export keys from a system I'm not making an instance from without trying to move gpg keyrings around that may not even be compatible with the target system.

I can wordsmith it.

def decrypt(self, data: str) -> str:
"""Process data using gpg.

This can be used to decrypt encrypted data, verify signed data,
Copy link
Member

@holmanb holmanb Aug 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only verifies signed data if the data is signed, correct?

IOW, if a user wants their user data to be signed and encrypted, an attacker would need only to acquire the public key of the encryption key and then provide their own unsigned encrypted payload to cloud-init, and cloud-init will not verify it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. I pushed a new version that checks that gpg verified the signature if we require the signature.

@github-actions github-actions bot added the documentation This Pull Request changes documentation label Aug 15, 2024
@TheRealFalcon TheRealFalcon force-pushed the encrypted branch 2 times, most recently from 884b803 to 344c3bc Compare August 15, 2024 04:28
Copy link

Hello! Thank you for this proposed change to cloud-init. This pull request is now marked as stale as it has not seen any activity in 14 days. If no activity occurs within the next 7 days, this pull request will automatically close.

If you are waiting for code review and you are seeing this message, apologies! Please reply, tagging TheRealFalcon, and he will ensure that someone takes a look soon.

(If the pull request is closed and you would like to continue working on it, please do tag TheRealFalcon to reopen it.)

@github-actions github-actions bot added the stale-pr Pull request is stale; will be auto-closed soon label Aug 30, 2024
@github-actions github-actions bot closed this Sep 6, 2024
@TheRealFalcon TheRealFalcon reopened this Sep 6, 2024
@TheRealFalcon TheRealFalcon removed the stale-pr Pull request is stale; will be auto-closed soon label Sep 6, 2024
@TheRealFalcon
Copy link
Member Author

@holmanb , I have rebased the branch (only minor format.rst conflicts). It should be ready for review again.

@holmanb holmanb self-assigned this Sep 16, 2024
cloudinit/gpg.py Outdated
data=data,
update_env=self.env,
)
if require_signature and "gpg: Good signature" not in result.stderr:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't love depending on stderr for this behavior. Are there alternatives that might be better?

Copy link
Member Author

@TheRealFalcon TheRealFalcon Sep 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated. I thought the same thing as I wrote it but also couldn't think of a better idea. Not sure why I didn't think of the verify command sooner.

Rather than an top-level `allow_userdata` key, instead use a `user_data`
dict. This is to better align with the top-level `vendor_data` keys.
Cloud-init user data often contains user secrets including passwords
and private keys. This data has always been submitted in plain text.
To protect this data's confidentiality and guarantee its authenticity,
this commit add the ability to have this data encrypted and signed.

A new user data format is added allowing for an ASCII armored PGP
MESSAGE. If detected, cloud-init will import into a temporary keyring
any keys provided in /etc/cloud/keys and use these keys to decrypt
and/or verify the provided data.

After decryption, the resulting message will be treated as user data
as before.
@TheRealFalcon
Copy link
Member Author

Rebased away the conflict.

@holmanb , can I get priority on this review? We're getting close to the end of the cycle and I'd like to get this one done so I can focus on my other items with less context switching.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation This Pull Request changes documentation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants