diff --git a/docs/pages/admin-guides/management/security/revoking-access.mdx b/docs/pages/admin-guides/management/security/revoking-access.mdx index 42472b7802693..91de6f5dd9506 100644 --- a/docs/pages/admin-guides/management/security/revoking-access.mdx +++ b/docs/pages/admin-guides/management/security/revoking-access.mdx @@ -24,9 +24,11 @@ how to execute the procedure. Teleport locks allow you to permanently or temporarily revoke access to a number of different "targets". Supported lock targets include: specific users, roles, -servers, desktops, or MFA devices. After you create a lock, all existing -sessions where the lock applies are terminated and new sessions are rejected -while the lock remains in force. +servers, desktops, or MFA devices. For Machine & Workload Identity bots, this +additionally includes the join token name (for delegated join methods) and the +bot instance UUID. After you create a lock, all existing sessions where the loc +applies are terminated and new sessions are rejected while the lock remains in +force. For more information, read our [Session and Identity Locking Guide](../../../identity-governance/locking.mdx). diff --git a/docs/pages/identity-governance/locking.mdx b/docs/pages/identity-governance/locking.mdx index 434ab6b774449..f2eed1318ab63 100644 --- a/docs/pages/identity-governance/locking.mdx +++ b/docs/pages/identity-governance/locking.mdx @@ -22,6 +22,8 @@ A lock can target the following objects or attributes: cluster) - a Windows desktop by the desktop's name - an [Access Request](access-requests/access-requests.mdx) by UUID +- a bot instance ID (for Machine & Workload Identity bots) +- a join token name (for Machine & Workload Identity bots using a [delegated join method](../reference/join-methods.mdx#delegated-join-methods)) ## Prerequisites @@ -83,6 +85,29 @@ with one of the following options: # Created a lock with name "dc7cee9d-fe5e-4534-a90d-db770f0234a1". ``` + + The most appropriate locking target for a Machine & Workload Identity bot + depends on its join method. + + For [delegated join methods](../reference/join-methods.mdx#secret-vs-delegated), + it's best to target the specific join token the bot is using to join: + ```code + $ tctl lock --join-token=example-token-name + ``` + + The join token name cannot be targeted for bots joined using the `token` join + method, so it's best to use the + [bot instance ID](../reference/architecture/machine-id-architecture.mdx#bot-instances): + ```code + $ tctl lock --bot-instance-id aabbccdd-1234-5678-0000-3b04d7d03acc + ``` + + In all cases, you may also target the bot user, which will lock all instances + of a bot that share the same underlying user: + ```code + $ tctl lock --user bot-example + ``` +
@@ -209,7 +234,7 @@ auth_service: Restart or redeploy the Auth Service for the change to take effect. -If not, edit your cluster authentication preference resource: +If not, edit your cluster authentication preference resource: ```code $ tctl edit cap diff --git a/docs/pages/includes/provision-token/bound-keypair-spec.mdx b/docs/pages/includes/provision-token/bound-keypair-spec.mdx new file mode 100644 index 0000000000000..2e4d38b383d54 --- /dev/null +++ b/docs/pages/includes/provision-token/bound-keypair-spec.mdx @@ -0,0 +1,65 @@ +```yaml +kind: token +version: v2 +metadata: + name: example-token +spec: + roles: [Bot] + join_method: bound_keypair + bot_name: example + + # Fields related to the bound keypair joining process. + bound_keypair: + # Fields related to the initial join attempt. + onboarding: + # If set to a public key in SSH authorized_keys format, the + # joining client must have the corresponding private key to join. This + # keypair may be created using `tbot keypair create`. If set, + # `registration_secret` and `must_register_before` are ignored. + initial_public_key: "" + + # If set to a secret string value, a client may use this secret to perform + # the first join without pre-registering a public key in + # `initial_public_key`. If unset and no `initial_public_key` is provided, + # a random value will be generated automatically into + # `.status.bound_keypair.registration_secret`. + registration_secret: "" + + # If set to an RFC 3339 timestamp, attempts to register via + # `registration_secret` will be denied once the timestamp has elapsed. If + # more time is needed, this field can be edited to extend the registration + # period. + must_register_before: "" + + # Fields related to recovery after certificates have expired. + recovery: + # The maximum number of allowed recovery attempts. This value may + # be raised or lowered after creation to allow additional recovery + # attempts should the initial limit be exhausted. If `mode` is set to + # `standard`, recovery attempts will only be allowed if + # `.status.bound_keypair.recovery_count` is less than this limit. This + # limit is not enforced if `mode` is set to `relaxed` or `insecure`. This + # value must be at least 1 to allow for the initial join during + # onboarding, which counts as a recovery. + limit: 1 + + # The recovery rule enforcement mode. Valid values: + # - standard (or unset): all configured rules enforced. The recovery limit + # and client join state are required and verified. This is the most + # secure recovery mode. + # - relaxed: recovery limit is not enforced, but client join state is + # still required. This effectively allows unlimited recovery attempts, + # but client join state still helps mitigate stolen credentials. + # - insecure: neither the recovery limit nor client join state are + # enforced. This allows any client with the private key to join freely. + # This is less secure, but can be useful in certain situations, like in + # otherwise unsupported CI/CD providers. This mode should be used with + # care, and RBAC rules should be configured to heavily restrict which + # resources this identity can access. + mode: "standard" + + # If set to an RFC 3339 timestamp, once elapsed, a keypair rotation will be + # forced on next join if it has not already been rotated. The most recent + # rotation is recorded in `.status.bound_keypair.last_rotated_at`. + rotate_after: "" +``` \ No newline at end of file diff --git a/docs/pages/machine-workload-identity/machine-id/deployment/bound-keypair.mdx b/docs/pages/machine-workload-identity/machine-id/deployment/bound-keypair.mdx new file mode 100644 index 0000000000000..fa012d0cf61e3 --- /dev/null +++ b/docs/pages/machine-workload-identity/machine-id/deployment/bound-keypair.mdx @@ -0,0 +1,196 @@ +--- +title: Deploying Machine ID with Bound Keypair Joining +description: "How to install and configure Machine ID with Bound Keypair Joining" +--- + +In this guide, you will install Machine & Workload Identity's agent, `tbot`, on +an arbitrary host using Bound Keypair Joining. This host could be a bare-metal +machine, a VM, a container, or any other host - the only requirement is that the +host has persistent storage. + +Bound Keypair Joining is an improved alternative to +[secret-based join methods][secret] and can function as a drop-in replacement. +It is more secure than static token joining, and is more flexible than ephemeral +token joining with renewable certificates: when its certificates expire, it can +perform an automated recovery to ensure the bot can rejoin even after an +extended outage. + +Note that platform-specific join methods may be available that are better suited +to your environment; refer to the [deployment guides](./deployment.mdx) for a +full list of options. + +## How it works + +With Bound Keypair Joining, Machine & Workload Identity bots generate a unique +keypair which is persistently stored in their internal data directory. Teleport +is then configured to trust this public key for future joining attempts. + +Later, when the bot attempts to join the cluster, Teleport issues it a challenge +that can only be completed using its private key. The bot returns the solved +challenge, attesting to its own identity, and is conditionally allowed to join +the cluster. This process is repeated for every join attempt, but if the bot has +been offline long enough for its certificates to expire, it is additionally +forced to perform an automatic recovery to join again. + +As self attestation is inherently less secure than the external verification +that would be provided by a cloud provider like AWS or a dedicated TPM, Bound +Keypair Joining enforces a number of additional checks to prevent abuse, +including: +- Join state verification to ensure the keypair cannot be usefully shared or + duplicated +- Certificate generation counter checks to ensure regular bot certificates + cannot be usefully shared or duplicated +- Configurable limits on how often - if at all - bots may be allowed to + automatically recover using this keypair + +An important benefit to Bound Keypair Joining is that all joining restrictions +can be reconfigured at any time, and bots that expire or go offline can be +recovered by making a server-side exemption without any client-side +intervention. + +Refer to the [admin guide][guide] for further details on how this join method +works. + +## Prerequisites + +{/* note: consider edition-prereqs-tabs.mdx include for v19; it is misleading due to the minor launch release */} + +- A running Teleport cluster version 18.1.0 or above. +- The `tsh` and `tctl` clients. +- (!docs/pages/includes/tctl.mdx!) +- This guide assumes the bot host has mutable persistent storage for internal + bot data. While it is possible to use Bound Keypair Joining can on immutable + hosts (like CI runs), doing so will reduce security guarantees; see the + [admin guide][guide] for further information. + +## Step 1/5. 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/5. Create a Bot + +**This step is completed on your local machine.** + +(!docs/pages/includes/machine-id/create-a-bot.mdx!) + +## Step 3/5. Create a join token + +**This step is completed on your local machine.** + +In this guide, we'll demonstrate joining a bot using a registration secret: this +is a one-time use secret the bot can provide to Teleport to authenticate its +first join. Once authenticated, the bot automatically generates a keypair and +registers its public key with Teleport for use in all future join attempts. + +Create `token-example.yaml`: + +```yaml +kind: token +version: v2 +metadata: + # This name will be used in tbot's `onboarding.token` field. + name: example +spec: + roles: [Bot] + # bot_name should match the name of the bot created earlier in this guide. + bot_name: example + join_method: bound_keypair + bound_keypair: + recovery: + mode: standard + limit: 1 +``` + +Replace `example` in `spec.bot_name` with the name of the bot you created in the +second step. + +For this example, we don't need to set any additional options for the bound +keypair token. We've allowed a single recovery attempt, which will be used to +allow the bot's initial join, and Teleport will generate a registration secret +automatically when the token is created as we have not preregistered a public +key to use. + + +This example makesĀ use of registration secrets to authenticate the initial join. +If desired, it is also possible to generate a key on the bot host first and +register it with Teleport out-of-band, avoiding the need to copy secrets between +hosts. + +To learn more about preregistering public keys and Bound Keypair Joining's other +onboarding and recovery options, refer to the +[Reference and Admin Guide][guide]. + + +Use `tctl` to apply this file: + +```code +$ tctl create -f token-example.yaml +``` + +Next, retrieve the generated registration secret, which will be needed for the +next step: +```code +$ tctl get token/example --format=json | jq -r '.[0].status.bound_keypair.registration_secret' +``` + +This assumes `jq` is installed. If not, run `tctl get token/example` and inspect +the `.status.bound_keypair.registration_secret` field. + +## Step 4/5. Configure `tbot` + +**This step is completed on the bot host.** + +Create `/etc/tbot.yaml`: + +```yaml +version: v2 +proxy_server: example.teleport.sh:443 +onboarding: + join_method: bound_keypair + token: example + bound_keypair: + registration_secret: SECRET +storage: + type: directory + path: /var/lib/teleport/bot +# outputs will be filled in during the completion of an access guide. +outputs: [] +``` + +Replace the following: +- `example.teleport.sh:443` with the address of your Teleport Proxy. +- `example` with the name of the token created in the previous step, if you + changed it from `example`. +- `SECRET` with the registration secret retrieved in the previous step. + +(!docs/pages/includes/machine-id/daemon-or-oneshot.mdx!) + +## Step 5/5. Configure outputs + +(!docs/pages/includes/machine-id/configure-outputs.mdx!) + +## Next steps + +- Read the [Bound Keypair Joining Reference andĀ Admin Guide][guide] + for more details about the join method and the available configuration options. +- Follow the [access guides](../access-guides/access-guides.mdx) to finish configuring `tbot` for + your environment. +- Read the [configuration reference](../../../reference/machine-id/configuration.mdx) to explore + all the available configuration options. +- [More information about `TELEPORT_ANONYMOUS_TELEMETRY`.](../../../reference/machine-id/telemetry.mdx) + +{/* +TODO: guide link above is a placeholder, link to the real guide once merged in +follow-up PR. +[guide]: ../../../reference/machine-id/bound-keypair.mdx +*/} + +[secret]: ../../../reference/join-methods.mdx#secret-vs-delegated +[guide]: ../../../reference/machine-id/machine-id.mdx 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 96ea2de48f299..007456b504327 100644 --- a/docs/pages/machine-workload-identity/machine-id/deployment/deployment.mdx +++ b/docs/pages/machine-workload-identity/machine-id/deployment/deployment.mdx @@ -52,14 +52,15 @@ and [Architecture](../../../reference/architecture/machine-id-architecture.mdx) Read the following guides for how to deploy Machine ID on your cloud platform or on-prem infrastructure. -| Platform | Installation method | Join method | -|-------------------------------------------|-------------------------------------------------|-----------------------------------------------------| -| [Linux](linux.mdx) | Package manager or TAR archive | Static join token | -| [Linux (TPM)](linux-tpm.mdx) | Package manager or TAR archive | Attestation from TPM 2.0 | -| [GCP](gcp.mdx) | Package manager, TAR archive, or Kubernetes pod | Identity document signed by GCP | -| [AWS](aws.mdx) | Package manager, TAR archive, or Kubernetes pod | Identity document signed by AWS | -| [Azure](azure.mdx) | Package manager or TAR archive | Identity document signed by Azure | -| [Kubernetes](kubernetes.mdx) | Kubernetes pod | Identity document signed by your Kubernetes cluster | +| Platform | Installation method | Join method | +|--------------------------------------------|-------------------------------------------------|-----------------------------------------------------| +| [Linux](linux.mdx) | Package manager or TAR archive | Static join token | +| [Linux (TPM)](linux-tpm.mdx) | Package manager or TAR archive | Attestation from TPM 2.0 | +| [Linux (Bound Keypair)](bound-keypair.mdx) | Package manager or TAR archive | Bound Keypair | +| [GCP](gcp.mdx) | Package manager, TAR archive, or Kubernetes pod | Identity document signed by GCP | +| [AWS](aws.mdx) | Package manager, TAR archive, or Kubernetes pod | Identity document signed by AWS | +| [Azure](azure.mdx) | Package manager or TAR archive | Identity document signed by Azure | +| [Kubernetes](kubernetes.mdx) | Kubernetes pod | Identity document signed by your Kubernetes cluster | ### CI/CD diff --git a/docs/pages/reference/cli/tbot.mdx b/docs/pages/reference/cli/tbot.mdx index 6b74a335e9d81..362727ec41b0c 100644 --- a/docs/pages/reference/cli/tbot.mdx +++ b/docs/pages/reference/cli/tbot.mdx @@ -269,21 +269,22 @@ These flags are available to all `tbot start` commands. Note that `tbot start legacy` supports slightly different options, so refer to its specific section for details when using a YAML config file or legacy output. -| Flag | Description | -|----------------------|-------------| -| `-d/--debug` | Enable verbose logging to stderr. | -| `--[no-]fips` | Whether to run tbot in FIPS compliance mode. This requires the FIPS `tbot` binary. | -| `--log-format` | Controls the format of output logs. Can be `json` or `text`. Defaults to `text`. | -| `-a/--auth-server` | Address of the Teleport Auth Service. Prefer using `--proxy-server` where possible. | -| `--proxy-server` | Address of the Teleport Proxy Server. | -| `--token` | A bot join token or path to file with token value, if attempting to onboard a new bot; used on first connect. | -| `--ca-pin` | CA pin to validate the Teleport Auth Service; used on first connect. | -| `--certificate-ttl` | TTL of short-lived machine certificates. | -| `--renewal-interval` | Interval at which short-lived certificates are renewed; must be less than the certificate TTL. | -| `--join-method` | Method to use to join the cluster. One of: `azure`, `circleci`, `gcp`, `github`, `gitlab`, `iam`, `kubernetes`, `spacelift`, `token`, `tpm`, `terraform_cloud` | -| `--[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. | +| Flag | Description | +|-------------------------|-------------| +| `-d/--debug` | Enable verbose logging to stderr. | +| `--[no-]fips` | Whether to run tbot in FIPS compliance mode. This requires the FIPS `tbot` binary. | +| `--log-format` | Controls the format of output logs. Can be `json` or `text`. Defaults to `text`. | +| `-a/--auth-server` | Address of the Teleport Auth Service. Prefer using `--proxy-server` where possible. | +| `--proxy-server` | Address of the Teleport Proxy Service. | +| `--token` | A bot join token or path to file with token value, if attempting to onboard a new bot; used on first connect. | +| `--ca-pin` | CA pin to validate the Teleport Auth Service; used on first connect. | +| `--certificate-ttl` | TTL of short-lived machine certificates. | +| `--renewal-interval` | Interval at which short-lived certificates are renewed; must be less than the certificate TTL. | +| `--join-method` | Method to use to join the cluster. One of: `azure`, `circleci`, `gcp`, `github`, `gitlab`, `iam`, `kubernetes`, `spacelift`, `token`, `tpm`, `terraform_cloud` | +| `--[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. | ## tbot start legacy @@ -710,6 +711,53 @@ $ tbot install systemd \ --write ``` +## tbot keypair create + +Generates a keypair for use with `bound_keypair` joining and stores it in the +specified bot internal storage directory. If a key already exists in the storage +directory, no new key will be generated and the existing public key will be +printed to the console; use the `--overwrite` flag to force the generation of a +new keypair. + +### Flags + +| 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` | + +### Examples + +First, ensure the desired internal storage directory exists: +```code +$ mkdir -p /var/lib/teleport/bot +``` + +Next, generate a keypair: +```code +$ tbot keypair create --proxy-server example.teleport.sh:443 --storage /var/lib/teleport/bot +2025-07-09T00:00:00.000-00:00 INFO [TBOT] keypair has been written to storage storage:directory: /var/lib/teleport/bot tbot/keypair.go:135 + +To register the keypair with Teleport, include this public key in the token's +`spec.bound_keypair.onboarding.initial_public_key`: + + ssh-ed25519 +``` + +This public key, including the algorithm identifier (`ssh-ed25519`, but may vary +depending on your cluster configuration) can then be copied into a Bound Keypair +join token to be used as a preregistered key. + +{/* +TODO: Replace with a link into the admin guide once the follow up PR has merged. +[preregistered key](../machine-id/bound-keypair.mdx#preregistered-key-example). +*/} + +Note that the Teleport Proxy Service address is required to fetch the currently +enabled [signature suite](../signature-algorithms.mdx). No authentication takes +place at this time. + ## Destination URIs Many `tbot start` subcommands accept destination URIs via the `--storage` and diff --git a/docs/pages/reference/join-methods.mdx b/docs/pages/reference/join-methods.mdx index e9e9df0d349d2..f66dd9b0ea0b1 100644 --- a/docs/pages/reference/join-methods.mdx +++ b/docs/pages/reference/join-methods.mdx @@ -117,6 +117,7 @@ Delegated join methods are: - [`azure`](#azure-managed-identity-azure) - [`azure_devops`](#azure-devops-azure_devops) - [`bitbucket`](#bitbucket-pipelines-bitbucket) +- [`bound_keypair`](#bound-keypair-bound_keypair) - [`circleci`](#circleci-circleci) - [`ec2`](#aws-ec2-identity-document-ec2) - [`gcp`](#gcp-service-account-gcp) @@ -143,6 +144,7 @@ Renewable join-methods are: - [ephemeral `token`](#ephemeral-tokens) - [static `token`](#static-tokens) - [`ec2`](#aws-ec2-identity-document-ec2) +- [`bound_keypair`](#bound-keypair-bound_keypair) Nodes with non-renewable certificates must join again in order to get a new certificate before expiry. The instance will have to prove again that it is legitimate. @@ -257,13 +259,41 @@ Or as Teleport resources: (!docs/pages/includes/provision-token/ephemeral-spec.mdx!) -When a MachineID bot uses an ephemeral join token, the token is deleted. +When a Machine ID bot uses an ephemeral join token, the token is deleted. + + +New Machine & Workload Identity bot deployments should consider upgrading to the +[`bound_keypair` join method](#bound-keypair-bound_keypair). + - How to [Join Services with a Secure Token](../enroll-resources/agents/join-token.mdx). - [Deploying Machine ID on Linux](../machine-workload-identity/machine-id/deployment/linux.mdx) +### Bound Keypair: `bound_keypair` + +Bound Keypair tokens are an alternative to +[secret-based join methods](#secret-based-join-methods) that improve security +and flexibility. They are best used on platforms with persistent storage, but +can be configured for use in any environment. + +This join method is recommended for on-prem environments +[without TPMs](#trusted-platform-module-tpm) or cloud platforms +without a specialized [delegated join method](#delegated-join-methods). + +(!docs/pages/includes/provision-token/bound-keypair-spec.mdx!) + + +- [Deploying Machine ID with Bound Keypair joining](../machine-workload-identity/machine-id/deployment/bound-keypair.mdx) + +{/* +TODO: Uncomment after follow-up PR with admin guide has merged. +- [Bound Keypair Reference and Admin Guide](./machine-id/bound-keypair.mdx) +*/} + + + ### AWS IAM role: `iam` The IAM join method is available to any Teleport process running anywhere with access to IAM credentials, diff --git a/docs/pages/reference/machine-id/configuration.mdx b/docs/pages/reference/machine-id/configuration.mdx index 20349a7e599a7..3fc743005c5b4 100644 --- a/docs/pages/reference/machine-id/configuration.mdx +++ b/docs/pages/reference/machine-id/configuration.mdx @@ -121,6 +121,11 @@ onboarding: # multiple Teleport clusters from a single GitLab CI job. token_env_var_name: "MY_GITLAB_ID_TOKEN" + # 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. + registration_secret: "secret" # storage specifies the destination that `tbot` should use to store its # internal state. This state is sensitive, and you should ensure that the @@ -216,7 +221,7 @@ output used in context. # always be `identity`. type: identity # ssh_config controls whether the identity output will attempt to generate an -# OpenSSH configuration file. This requires that `tbot` can connect to the +# OpenSSH configuration file. This requires that `tbot` can connect to the # Teleport Proxy Service. Must be "on" or "off". If unspecified, this defaults to # "on". ssh_config: on