User Data is that feature that allows you to configure new instances at boot using parameters or arbitrary scripts. It’s an essential feature for building automated systems, often used to run a management daemon or to configure services.
Commonly, instances need access to secrets as part of the initialization process. A database connection string, keys to an API, or other sensitive data need to be distributed to the instance, and user data is a handy way to distribute those secrets.
But user data is not encrypted. It is exposed via the web console, in a file on disk, and to any process running on the instance via the instance metadata. The docs even warn against using it for anything sensitive. Some web app attacks have been known to seek access to user data, hoping to reveal secrets and access info.
Here’s one method to secure those secrets and still preserve the usefulness of user data.
Generate a public/private keypair using openssl
Bake the private key in to the AMI. I do this with Packer. The provisioner section of the Packer template might look like this:
Use the public key to encrypt the bootstrap script, including secrets. I use
smime to work around length limits.
Now embed the encrypted, encoded script in a wrapper that decrypts using the private key, and use that as the user data.
Note that this is subject to the user data size limit of 16KB.