diff --git a/docs/pages/machine-workload-identity/machine-id/deployment/deployment.mdx b/docs/pages/machine-workload-identity/machine-id/deployment/deployment.mdx index ec0a2214137cd..d620332406cba 100644 --- a/docs/pages/machine-workload-identity/machine-id/deployment/deployment.mdx +++ b/docs/pages/machine-workload-identity/machine-id/deployment/deployment.mdx @@ -42,7 +42,7 @@ authentication methods: ## Deployment guides -The guides in this section show you how to deploy Machine ID and join it to your cluster. +The guides in this section show you how to deploy Machine ID and join it to your cluster. Choose a guide based on the platform where you intend to run Machine ID. If a specific guide does not exist for your platform, the [Linux @@ -114,9 +114,9 @@ and [Architecture](../../../reference/architecture/machine-id-architecture.mdx) }, { icon: , - to: "../../reference/machine-workload-identity/machine-id/bound-keypair/getting-started", + to: "../../../reference/machine-workload-identity/machine-id/bound-keypair/getting-started", name: "Bound Keypair Joining", - } + } ]} /> @@ -165,6 +165,18 @@ Read the following guides for how to deploy Machine ID on a continuous integrati icon: , to: "../../zero-trust-access/infrastructure-as-code/terraform-provider/terraform-cloud", name: "Terraform Cloud", + }, + { + icon: , + to: "../../../reference/machine-workload-identity/machine-id/bound-keypair/static-keys", + name: "Bound Keypair static keys (Generic)", } ]} /> + + +If your CI/CD provider does not have a dedicated join method listed above, +consider using [Bound Keypair static keys][bound-keypair-static] as a fallback. + + +[bound-keypair-static]: ../../../reference/machine-workload-identity/machine-id/bound-keypair/static-keys.mdx diff --git a/docs/pages/reference/cli/tbot.mdx b/docs/pages/reference/cli/tbot.mdx index f8a2db9dbd6ed..0829763f4a13a 100644 --- a/docs/pages/reference/cli/tbot.mdx +++ b/docs/pages/reference/cli/tbot.mdx @@ -288,7 +288,9 @@ specific section for details when using a YAML config file or legacy output. | `--[no-]oneshot` | If set, quit after the first renewal. | | `--diag-addr` | If set and the bot is in debug mode, a diagnostics service will listen on specified address. | | `--storage` | A destination URI for tbot's internal storage, e.g. `file:///foo/bar`. See [Destination URIs](#destination-uris) for more info. | -| `--registration-secret` | An optional joining secret to use on first join with the `bound_keypair` join method. | +| `--registration-secret` | An optional joining secret to use on first join with the `bound_keypair` join method. This can also be provided via the `TBOT_REGISTRATION_SECRET` environment variable. | +| `--registration-secret-path` | An optional path to a file containing a joining secret to use on first join with the `bound_keypair` join method. | +| `--static-key-path` | An optional path to a file containing a static private key for use with the `bound_keypair` join method. A base64-encoded key can also be provided via the `TBOT_BOUND_KEYPAIR_STATIC_KEY` environment variable. | ## tbot start legacy @@ -727,9 +729,12 @@ new keypair. | Flag | Description | |------|-------------| -| `--storage` | A destination URI to be used for bot internal storage. Required. | -| `--proxy-server` | A Teleport Proxy Service address. Required. | -| `--overwrite` | If set, always generate a new key. If unset, the existing public key will be printed if one already exists in the destination specified by `--storage` | +| `--storage` | A destination URI to be used for bot internal storage. Required. | +| `--proxy-server` | A Teleport Proxy Service address. Required. | +| `--overwrite` | If set, always generate a new key. If unset, the existing public key will be printed if one already exists in the destination specified by `--storage`. | +| `--static` | If set, generates a static keypair. For more information, see [the static key guide](../machine-workload-identity/machine-id/bound-keypair/static-keys.mdx). | +| `--static-key-path` | If set with `--static`, writes the static keypair to a file. | +| `--format` | Override the output format, supported values: `text`, `json`. | ### Examples diff --git a/docs/pages/reference/machine-workload-identity/machine-id/bound-keypair/admin-guide.mdx b/docs/pages/reference/machine-workload-identity/machine-id/bound-keypair/admin-guide.mdx index 71a3ac32ec158..2771d4946f853 100644 --- a/docs/pages/reference/machine-workload-identity/machine-id/bound-keypair/admin-guide.mdx +++ b/docs/pages/reference/machine-workload-identity/machine-id/bound-keypair/admin-guide.mdx @@ -274,10 +274,65 @@ its previous value. If [bot keypairs were rotated](#requesting-a-keypair-rotation) between the snapshot and restore of the Teleport cluster, note that bots only keep a record -of the previous 10 keypairs. This means server-side recovery may impossible if -the keypair expected by the restored Teleport cluster has been rotated out of +of the previous 10 keypairs. This means server-side recovery may be impossible +if the keypair expected by the restored Teleport cluster has been rotated out of the client-side history, or if the client-side history has been lost or deleted. +## Manually rotating static keys + +Static keys prevent automatic key rotation as the `tbot` client cannot update +keys in an arbitrary keystore. However, it may still possible to automate +rotation if your environment or secret store allows you to update secrets +through an API. + +The specific steps needed to automate these will vary based on your environment, +but the general steps are: + +1. Generate a new keypair on any node using the tbot client: + + ```code + $ tbot keypair create --proxy-server example.teleport.sh:443 --static --format json + ``` + +2. Parse the `.public_key` and `.private_key` values using your tool of choice, + like `jq` or any other JSON parser. + +3. Replace the token in Teleport to trust the new public key, using the value in + the `.public_key` field: + ```code + $ cat my-token.yaml + version: v2 + kind: token + metadata: + name: my-token + spec: + bot_name: example-bot + bound_keypair: + onboarding: + initial_public_key: + recovery: + mode: insecure + join_method: bound_keypair + roles: + - Bot + $ tctl create -f my-token.yaml + ``` + +3. Insert the new private key into your keystore. This will vary depending on + which keystore or provider you are using. + * If passing the private key via an environment variable, copy the value directly + * If passing the private key via file, decode the base64-encoded private key first: + ```code + $ tbot keypair create --proxy-server example.teleport.sh:443 --static --format json | jq -r .private_key | base64 -d + ``` + ...and store the result as needed. + +4. Future jobs should now use the new keypair. + +Frequently rotating static keys can help to mitigate the security tradeoffs of +`insecure` recovery. See the [concepts page](concepts.mdx#static-keys) for more +information about static keys. + [rbac]: ../../../access-controls/roles.mdx [ephemeral]: ../../../architecture/machine-id-architecture.mdx#ephemeral-token [delegated]: ../../../deployment/join-methods.mdx#delegated-join-methods diff --git a/docs/pages/reference/machine-workload-identity/machine-id/bound-keypair/bound-keypair.mdx b/docs/pages/reference/machine-workload-identity/machine-id/bound-keypair/bound-keypair.mdx index 64b107a8ec928..534a42d7303fa 100644 --- a/docs/pages/reference/machine-workload-identity/machine-id/bound-keypair/bound-keypair.mdx +++ b/docs/pages/reference/machine-workload-identity/machine-id/bound-keypair/bound-keypair.mdx @@ -45,10 +45,10 @@ a bot initially. When ready to deploy to production, it's trivial to adjust onboarding and recovery settings to select your desired balance between resiliency and security. -Additionally, with [`insecure` recovery](admin-guide.mdx#disabling-join-state-verification) -and in situations that can accommodate the security complications, Bound Keypair -Joining can be used to join bots in otherwise unsupported CI/CD providers by -persisting the bot's keypair in the platform keystore. +Additionally, with [static keys](static-keys.mdx) and in situations that can +accommodate the security complications, Bound Keypair Joining can be used to +join bots in otherwise unsupported CI/CD providers by persisting the bot's +keypair in a platform keystore. ## Limitations @@ -75,6 +75,9 @@ You can read step-by-step guides on using Bound Keypair Joining with Machine ID: - [Using Machine ID with Bound Keypair Joining](./getting-started.mdx): How to install and configure Machine ID with Bound Keypair Joining +- [Using Machine ID with Bound Keypair static keys](./static-keys.mdx): How to + use Bound Keypair static keys with stateless hosts, like otherwise + unsupported CI/CD providers - [Bound Keypair Joining Concepts](./concepts.mdx): Learn more about the components and architecture of Bound Keypair Joining - [Bound Keypair Joining Admin Guide](./admin-guide.mdx): Learn how to deploy diff --git a/docs/pages/reference/machine-workload-identity/machine-id/bound-keypair/concepts.mdx b/docs/pages/reference/machine-workload-identity/machine-id/bound-keypair/concepts.mdx index 1d65d8946c0c7..1653a46349e12 100644 --- a/docs/pages/reference/machine-workload-identity/machine-id/bound-keypair/concepts.mdx +++ b/docs/pages/reference/machine-workload-identity/machine-id/bound-keypair/concepts.mdx @@ -118,6 +118,10 @@ values: [See the admin guide](admin-guide.mdx#disabling-join-state-verification) for more information on using `insecure` mode in practice. + Static key joining (`tbot keypair create --static ...`) requires use of + `insecure` mode as it disables client-side credential storage, meaning bots + cannot complete [join state verification](#join-state-verification). + ## Join state verification A **join state document** is an additional piece of information provided to @@ -145,5 +149,42 @@ credentials in use by the attacker. Note that join state verification is disabled when the token's `spec.bound_keypair.recovery.mode` is set to `insecure`. +## Static Keys + +Static keys trade some security guarantees for flexibility, and can help enable +use of `tbot` in environments where bots otherwise could not authenticate, such +as: +- CI/CD providers for which Teleport has no dedicated join method +- Ephemeral bare-metal nodes without TPMs +- Any other environment where persistent storage is not available +- Any environment where an arbitrary number of instances may join, like + ephemeral CI runners. + +However, static keys have disadvantages: +- `insecure` mode means any client with the private key can join without + restrictions. If the key is stolen, the attacker will have access to Teleport + and any resources the bot is allowed to access. +- `insecure` mode additionally means you cannot limit the number of uses of the + bound keypair token (`spec.bound_keypair.recovery.limit`). +- Keypair rotation is not supported, as client storage is not writable, and + attempting to require a rotation will prevent bots from joining. + +When using static keys, a keypair is generated ahead of time and [preregistered +with Teleport](#onboarding). You can then provide the keypair to the `tbot` +client using a file or the `TBOT_BOUND_KEYPAIR_STATIC_KEY` environment variable. + +The `insecure` recovery mode is required when using static keys (with +`tbot keypair create --static ...`) as this mode disables client-side storage +for join state documents. With no previous state, bots will not be able to +complete join state verification, and will be unable to recover after their +first authentication attempt; as such, join state verification must be disabled. + +With this context in mind, when using static keys, be aware that any client with +knowledge of the private key can authenticate to Teleport with no additional +restrictions. Before deploying a bot with static keys, take additional care to +fully understand your environment's threat model and security needs, and reduce +access using [Teleport's RBAC](../../../access-controls/roles.mdx) to ensure the +minimum possible blast radius in the event the keypair is compromised. + [renewable]: ../../../deployment/join-methods.mdx#renewable-vs-non-renewable [token]: ../../../deployment/join-methods.mdx#ephemeral-tokens diff --git a/docs/pages/reference/machine-workload-identity/machine-id/bound-keypair/static-keys.mdx b/docs/pages/reference/machine-workload-identity/machine-id/bound-keypair/static-keys.mdx new file mode 100644 index 0000000000000..9193d3af66945 --- /dev/null +++ b/docs/pages/reference/machine-workload-identity/machine-id/bound-keypair/static-keys.mdx @@ -0,0 +1,298 @@ +--- +title: Deploying Machine ID with Bound Keypair Static Keys +description: "How to install and configure Machine ID with Bound Keypair static keys" +tags: + - mwi + - how-to + - infrastructure-identity +--- + +In this guide, you will install Machine and Workload Identity's agent, `tbot`, +on an arbitrary stateless environment using Bound Keypair static keys. This +might be a CI/CD job on a provider not yet supported by one of Teleport's +[dedicated join methods](../../../deployment/join-methods.mdx), an ephemeral +bare-metal node, or any other environment without writable persistent storage. + +Bound Keypair Joining is a more flexible alternative to [secret-based join +methods][secret], and static keys allow arbitrary nodes to join by relaxing some +of its built-in security requirements. + + +If your provider has writable persistent storage, like a Kubernetes PVC or +similar writable persistent storage, you should instead follow the [standard +Bound Keypair guide](./getting-started.mdx). + + +## How it works + +Bound Keypair joining typically assumes you have access to writable client-side +storage to store additional identity proofs, but this requirement is lifted when +using Bound Keypair static keys. + +Normally, the actual Bound Keypair keys are managed internally by the `tbot` +client, which allows them to be generated and rotated on demand. With static +keys, you take direct ownership of the private key and can store it as you see +fit - like in a platform keystore. The `tbot` client is then configured to use +your static private key instead of managing it automatically. + + +Static key joining relaxes certain security checks otherwise performed when +client-side storage is available. This is designed to be used where no other +join methods are possible. Before continuing, consider: +- Using a [dedicated join method](../../../deployment/join-methods.mdx) for your + platform if available. +- Using [standard Bound Keypair joining](./getting-started.mdx) if your + environment has writable persistent storage. + +[Read more about static keys and what tradeoffs they +require](./concepts.mdx#static-keys) before using them in a production +environment. + + +## Prerequisites + +{/* note: consider edition-prereqs-tabs.mdx include for v19; it is misleading due to the minor launch release */} + +{/* TODO: replace version with version containing `tbot keypair create --static` */} + +- A running Teleport cluster version 18.2.0 or above. +- The `tsh`, `tctl`, and `tbot` clients. +- (!docs/pages/includes/tctl.mdx!) + +## Step 1/7. Install `tbot` + +**This step is completed on the bot host.** + +First, `tbot` needs to be installed on the host that you wish to use Machine ID +on. + +Download and install the appropriate Teleport package for your platform: + +(!docs/pages/includes/install-linux.mdx!) + +## Step 2/7. Create a Bot + +**This step is completed on your local machine.** + +(!docs/pages/includes/machine-id/create-a-bot.mdx!) + +## Step 3/7. Create a keypair and a join token + +Next, we'll need to generate a keypair, consisting of: +* A private key that will need to be available on your bot host at runtime +* A public key that will need to be configured in the Teleport join token + +We'll use a helper in the `tbot` client to generate a static keypair. This +step can be run anywhere and does not require any configuration, but you will +need to decide how the bot will access the key, which may vary depending on your +environment or secret store: +1. Via an environment variable, useful for providers or environments that inject + secrets as environment variables +2. Via a standard file which you transfer to the bot host + +With that in mind, if you intend to make the key available to `tbot` via an +environment variable, run the following: + +```code +$ tbot keypair create --proxy-server :443 --static +``` + +Otherwise, if using a file, run the following: +```code +$ tbot keypair create --proxy-server :443 --static --static-key-path ./path/to/key +``` + +Adjust the `--static-key-path` value as desired. If you didn't run this command +on the bot host directly, be prepared to transfer this to the bot host in the +next step. + +In either case, the command will generate a keypair and print some instructions. +For example, this is shown when using an environment variable key: +```code +To register the keypair with Teleport, include this public key in the token's +'spec.bound_keypair.onboarding.initial_public_key' field: + + + +Refer to this token example as a reference: + + kind: token + metadata: + name: example-token + spec: + bot_name: example + bound_keypair: + onboarding: + initial_public_key: + recovery: + limit: 0 + mode: insecure + join_method: bound_keypair + roles: + - Bot + version: v2 + +Configure your bot to use this static key by inserting the following private key +value into the bot's environment, ideally via a platform-specific keystore if +available: + + export TBOT_BOUND_KEYPAIR_STATIC_KEY="<...encoded private key...>" + +Note that bots joined with static tokens do not support keypair rotation and +will be unable to join if a rotation is requested server-side via the token's +'rotate_after' field. Additionally, 'insecure' recovery mode must be used, as +shown above. Read more at: + + https://goteleport.com/docs/reference/machine-workload-identity/machine-id/bound-keypair/concepts/#recovery +``` + +This command will print the public key, a join token example, and the private +key needed to configure your bot. Keep this output available as you'll need it +through the next step. + +Using the example token printed by the above command as a template, +create `token.yaml` containing the following content: +```yaml +kind: token +metadata: + name: example-token +spec: + bot_name: example + bound_keypair: + onboarding: + initial_public_key: + recovery: + limit: 0 + mode: insecure + join_method: bound_keypair + roles: + - Bot +version: v2 +``` + +Be sure to set `bot_name` to match the bot created in the previous step, and +ensure the public key matches the value printed by `tbot keypair create ...`. + +When ready, create the join token in your Teleport cluster using `tctl`: +```code +$ tctl create -f token.yaml +``` + +## Step 4/7. Store the private key + +Now that you've generated the private key, it needs to be stored and made +available to your job. Exactly how to do this will depend on your provider and +environment, as well as whether you'll be making it available via an environment +variable or a file. + +### Storing the key in a file on the bot host + +Ensure the private key file you generated is made available on the bot host. If +you generated it on your local machine, this might mean copying it via `scp`, +provisioning the key via Ansible, or transferring it to the bot host through +whichever means you prefer. + +Take care to ensure the resulting file on the bot host will only be readable by +the account that will run the `tbot` process. + +### Storing the key in an environment variable + +If using an environment variable, depending on your platform and environment, +you'll likely want to store the key in a platform-specific keystore - such that +the environment variable is set on the bot host when it starts - rather than +on the bot host directly. + +Regardless of backend, set the variable as follows: +- Name: `TBOT_BOUND_KEYPAIR_STATIC_KEY` +- Value: the base64-encoded value as printed by `tbot keypair create --static ...` + +The value is the same content that would otherwise be written to a file if you'd +used `tbot keypair create ... --static --static-key-path /path/to/key`, but the +content has been base64 encoded to simplify use as an environment variable +value. + +## Step 5/7. Configure `tbot` + +**This step is completed on the bot host.** + +### With an environment variable key + +If exposing the secret via an environment variable, ensure it's available in the +`$TBOT_BOUND_KEYPAIR_STATIC_KEY` variable. + +Create `/etc/tbot.yaml` with the following content: + +```yaml +version: v2 +proxy_server: :443 +onboarding: + join_method: bound_keypair + token: example-token +storage: + type: directory + path: /var/lib/teleport/bot +# outputs will be filled in during the completion of an access guide. +outputs: [] +``` + +Aside from specifying the `join_method` and `token`, no additional configuration +is needed; the key will be read from the environment as needed during the join +process. + +### With a file key + +Copy the key to the bot environment or otherwise make sure it's available, and +write the following to `/etc/tbot.yaml`: + +```yaml +version: v2 +proxy_server: :443 +onboarding: + join_method: bound_keypair + token: example-token + bound_keypair: + static_private_key_path: /path/to/key +storage: + type: directory + path: /var/lib/teleport/bot +# outputs will be filled in during the completion of an access guide. +outputs: [] +``` + +Set `static_private_key_path` to point to the location the key will be +available and save the file. + +## Step 6/7. Verify `tbot` can authenticate to Teleport + +Run `tbot` using the `tbot.yaml` you created, and ensure the private key is +available as expected, either via file (at the path you configured earlier) or +via the environment in `$TBOT_BOUND_KEYPAIR_STATIC_KEY`: + +```code +$ tbot start -c /etc/tbot.yaml --oneshot +``` + +If everything has been setup correctly, `tbot` should run, authenticate with +Teleport, and exit cleanly. In production, you can remove the `--oneshot` flag +if you want `tbot` to continually provide updated certificates for +longer-running jobs, otherwise the certificates issued will expire eventually +(1 hour by default). + +## Step 7/7. Configure outputs + +(!docs/pages/includes/machine-id/configure-outputs.mdx!) + +## Next Steps + +- Read more about [Bound Keypair static keys](./concepts.mdx#static-keys). +- Read about [static key rotation](./admin-guide.mdx#manually-rotating-static-keys) +- Read the [Bound Keypair Joining Reference][reference] + for more details about the join method and the available configuration options. +- Follow the [access guides](../../../../machine-workload-identity/machine-id/access-guides/access-guides.mdx) + to finish configuring `tbot` for your environment. +- Read the [configuration reference](../configuration.mdx) to explore + all the available configuration options. +- [More information about `TELEPORT_ANONYMOUS_TELEMETRY`.](../telemetry.mdx) + +[secret]: ../../../deployment/join-methods.mdx#secret-vs-delegated +[reference]: ./bound-keypair.mdx diff --git a/docs/pages/reference/machine-workload-identity/machine-id/configuration.mdx b/docs/pages/reference/machine-workload-identity/machine-id/configuration.mdx index 941b1c09d1cf2..0951b40bd0c14 100644 --- a/docs/pages/reference/machine-workload-identity/machine-id/configuration.mdx +++ b/docs/pages/reference/machine-workload-identity/machine-id/configuration.mdx @@ -128,9 +128,18 @@ onboarding: # bound_keypair holds parameters specific to the "bound_keypair" join method bound_keypair: # registration_secret is an optional secret to use on first join in lieu of - # a preregistered keypair. + # a preregistered keypair. You can also set this in the + # `TBOT_REGISTRATION_SECRET` environment variable. registration_secret: "secret" + # registration_secret_path is an optional path to a file containing a + # registration secret; conflicts with `registration_secret` + registration_secret_path: ./path/to/secret + + # static_key_path is an optional path to a file containing a static private + # key. + static_key_path: ./path/to/secret + # storage specifies the destination that `tbot` should use to store its # internal state. This state is sensitive, and you should ensure that the # destination you specify here can only be accessed by `tbot`.