Skip to content

[enhancement]: Support for an encrypted metadata/userdata workflow via the Datasource interface #4417

@akutz

Description

@akutz

Enhancement

There is often a need to bootstrap a guest with information that may be classified as sensitive, ex. a public/private key pair written out as files. While it is true that every datasource could develop their own means of handling this problem, it occurred to me recently that perhaps they do not have to do so.

I've been busy of late digging into how to leverage TPMs as a means to securely bootstrap machines (see google/go-tpm#343 and vmware/govmomi#3222), and developed a workflow for bootstrapping guests that leverages the TPM's existing endorsement key (EK):

  1. Client creates a shared secret used to encrypt bootstrap data. This shared secret is no larger than 128 bytes as it is the recommended MAX_SYM_SIZE for portability with respect to TPMs.
  2. Client encrypts bootstrap data using this shared secret.
  3. Client encrypts shared secret using the public EK from the target machine's TPM.
  4. Client shares/injects the encrypted shared secret and encrypted bootstrap data with the machine and powers it on.
  5. Bootstrap engine decrypts the shared secret using the TPM's EK.
  6. Bootstrap engine decrypts the bootstrap data using the decrypted, shared secret.

A secure bootstrap model has been demonstrated before, which eventually became Keylime. I think Keylime is great, and there is nothing in the above workflow that precludes leveraging Keylime for on-going, secure communication with a guest, either from/to Cloud-Init or other actors.

However, the bootstrap problem is one that I think can be addressed without requiring key exchange, either explicit attestation or storage, and instead rely on a simplified version (there may or may not be an auth policy that relies on a specific combination of PCRs) of implicit attestation via the EK, something every TPM already has or has the ability to determinstically generate. To that end, if an EK-based bootstrap model were adopted, it could be something that Cloud-Init directly utilized as opposed to leaving it up to every datasource implementation. For example, the Datasource interface could be augmented with:

    def _get_encrypted_data(self) -> bool:
        """Walk metadata sources, process crawled data and save attributes."""
        raise NotImplementedError(
            "Subclasses of DataSource must implement _get_encrypted_data which"
            " sets self.encrypted_metadata, encrypted_vendordata_raw and encrypted_userdata_raw."
        )

    @property
    def encrypted_shared_secret(self):
        """Returns the encrypted shared secret that is used to decrypt one 
        or all of metadata, userdata, and vendordata."""
        return None

    @property
    def encyption_type(self):
        """Returns the type of encryption used with the shared secret to decrypt
         one or all of metadata, userdata, and vendordata."""
        return None

With something like the above, it could be up to Cloud-Init core to handle interfacing with the TPM to decrypt the shared secret via the example outlined in tpm2-ekunseal.sh. I imagine shell execing to tpm2-tools is a better option than requiring Cloud-Init to depend on tpm2-pytss directly. Plus, this way it keeps the use of encryption as optional without adding another dependency to Cloud-Init's Python modules.

Anyway, I am quite happy to discuss this further if there is interest. Thanks!

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions