Skip to content

MWI: Default to creating bound keypair tokens for bots in tctl#63422

Merged
timothyb89 merged 10 commits intomasterfrom
timothyb89/tctl-bots-add-bound-keypair
Feb 9, 2026
Merged

MWI: Default to creating bound keypair tokens for bots in tctl#63422
timothyb89 merged 10 commits intomasterfrom
timothyb89/tctl-bots-add-bound-keypair

Conversation

@timothyb89
Copy link
Contributor

@timothyb89 timothyb89 commented Feb 3, 2026

This adjusts the default behavior in tctl to create bound keypair tokens when creating new bot tokens in tctl bots add and tctl bots instances add.

This adds a new "V2" message template intended for use with joining URIs and bound keypair tokens, and defaults to that. The existing template is still available, with minor additive changes, using the --legacy flag. Additional flags to manage bound keypair token parameters (recovery mode, etc) were also added and are specific to this join method.

Additionally, joining URI parsing utilities were moved into their own package to be importable from tctl without importing all of lib/tbot.

changelog: MWI: tctl bots add now generates bound keypair tokens instead of legacy tokens

Closes #59999


Command example:

$ tctl bot add example --roles=access
This is an admin-level action and requires MFA to complete
Tap any security key
Detected security key tap
The bot joining URI: tbot+proxy+bound-keypair://bot-example-0a6f812a:abc123@example.teleport.sh:443
This token must be used within 59 minutes after which it must be recreated.

To start a new tbot running the identity service, run:

> tbot start identity \
   --join-uri=tbot+proxy+bound-keypair://bot-example-0a6f812a:abc123@example.teleport.sh:443 \
   --destination=./destination

Alternatively, if you'd like to generate a tbot.yaml config file, you can
instead run:

> tbot configure identity \
  --join-uri=tbot+proxy+bound-keypair://bot-example-0a6f812a:abc123@example.teleport.sh:443 \
  --destination=./destination > tbot.yaml

Then, run tbot with:

> tbot start -c tbot.yaml

Advanced parameters:
  Proxy:               example.teleport.sh:443
  Token:               bot-example-0a6f812a
  Join Method:         bound_keypair
  Registration Secret: abc123

Please note:
  - The ./destination destination directory can be changed as desired.
  - /var/lib/teleport/bot must be accessible to the bot user, or --storage
    must point to another accessible directory to store internal bot data.
  - This example shows only use of the 'identity' service. See our documentation
    for all supported service types:
    https://goteleport.com/docs/reference/cli/tbot/
  - This token will be permanently bound to a single 'tbot' instance upon first
    join. For scalable alternatives, see our documentation on other supported
    join methods:
    https://goteleport.com/docs/enroll-resources/machine-id/deployment/

This adjusts the default behavior in `tctl` to create bound keypair
tokens when creating new bot tokens in `tctl bots add` and
`tctl bots instances add`.

This adds a new "V2" message template intended for use with joining
URIs and bound keypair tokens, and defaults to that. The existing
template is still available, with minor additive changes, using the
`--legacy` flag. Additional flags to manage bound keypair token
parameters (recovery mode, etc) were also added and are specific to
this join method.

Additionally, joining URI parsing utilities were moved into their
own package to be importable from `tctl` without importing all of
`lib/tbot`.
A slight control flow change introduced a dependency on a working
proxy, which isn't enabled by tctl's test harness. This enables the
proxy for this test, with a new test flag to support this.
The new helper unconditionally overrode cfg.Proxy.Enabled, which
broke other tests that overrode the parameter in other ways. It
now only toggles it on explicitly.
@@ -252,7 +306,9 @@ func bold(text string) string {

var startMessageTemplate = template.Must(template.New("node").Funcs(template.FuncMap{
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I debated keeping the old message at all and have so far just kept it (with some minor additive changes, e.g. including the joining URI) behind a --legacy flag. I figured that was the safest approach but if anyone thinks we should handle this differently I'm happy to try another approach.

@timothyb89 timothyb89 marked this pull request as ready for review February 4, 2026 03:22
@github-actions github-actions bot added machine-id size/md tctl tctl - Teleport admin tool labels Feb 4, 2026
There's a lot of random parameters in the rendered token templates
which caused failures in the golden-style tests. This plumbs through
a few static parameters along with adding a template parameter mutator
to override some parameters to ensure test consistency.
c.botsList.Flag("format", "Output format, 'text' or 'json'").Hidden().Default(teleport.Text).EnumVar(&c.format, teleport.Text, teleport.JSON)

c.botsAdd = bots.Command("add", "Add a new certificate renewal bot to the cluster.")
c.botsAdd = bots.Command("add", "Add a new Machine & Workload Identity bot to the cluster.")
Copy link
Contributor

Choose a reason for hiding this comment

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

Long-term, I wonder if we ought to decouple bot creation from token creation here and instead force people to call tctl bots add and then tctl bot instances add? My main worry is growing complexity in the number of CLI flags.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've gone and moved all the shared flags into a helper function, at least, so hopefully the flag complexity issue is alleviated? I'm a bit wary of making larger UX changes to simplify our impl, but if we want to make more meaningful changes the flow here (especially if it helps with docs) I'm not opposed to further follow-up work.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm a bit wary of making larger UX changes to simplify our impl, but if we want to make more meaningful changes the flow here (especially if it helps with docs) I'm not opposed to further follow-up work.

Yep - I'm not sure a change that's appropriate here - more thinking long term. Less worried about implementation complexity, but actual UX complexity - someone listing the command flags for tctl bots add sees a mixture of flags: some pertaining to the configuration of the bot, and some pertaining to the configuration of joining. For someone newer to the product, it feels like a missed opportunity to clarify how the two parts of the process are seperate.

Moves bot token flag initialization into a shared
`initSharedBotTokenFlags()` helper.
@timothyb89
Copy link
Contributor Author

timothyb89 commented Feb 7, 2026

While writing up docs I figured it would be useful to mention tbot configure in the help text as well. I've updated the message to provide instructions for both:

The bot joining URI: tbot+proxy+bound-keypair://bot-example-0a6f812a:abc123@example.teleport.sh:443
This token must be used within 59 minutes after which it must be recreated.

To start a new tbot running the identity service, run:

> tbot start identity \
   --join-uri=tbot+proxy+bound-keypair://bot-example-0a6f812a:abc123@example.teleport.sh:443 \
   --destination=./destination

Alternatively, if you'd like to generate a tbot.yaml config file, you can
instead run:

> tbot configure identity \
  --join-uri=tbot+proxy+bound-keypair://bot-example-0a6f812a:abc123@example.teleport.sh:443 \
  --destination=./destination > tbot.yaml

Then, run tbot with:

> tbot start -c tbot.yaml

Advanced parameters:
  Proxy:               example.teleport.sh:443
  Token:               bot-example-0a6f812a
  Join Method:         bound_keypair
  Registration Secret: abc123

Please note:
  - The ./destination destination directory can be changed as desired.
  - /var/lib/teleport/bot must be accessible to the bot user, or --storage
    must point to another accessible directory to store internal bot data.
  - This example shows only use of the 'identity' service. See our documentation
    for all supported service types:
    https://goteleport.com/docs/reference/cli/tbot/
  - This token will be permanently bound to a single 'tbot' instance upon first
    join. For scalable alternatives, see our documentation on other supported
    join methods:
    https://goteleport.com/docs/enroll-resources/machine-id/deployment/

I also made a separate PR to include this new functionality in the help template for tbot keypair create: #63622

timothyb89 added a commit that referenced this pull request Feb 7, 2026
This includes some examples of using new `tctl` support for adding
bound keypair tokens via `tctl bots add`, which is added in #63422.
cthach pushed a commit that referenced this pull request Feb 9, 2026
* MWI: Add tctl examples to `tctl keypair create`

This includes some examples of using new `tctl` support for adding
bound keypair tokens via `tctl bots add`, which is added in #63422.

* Improve insecure template rendering

* Fix missing newline at EOF
@timothyb89 timothyb89 added this pull request to the merge queue Feb 9, 2026
Merged via the queue into master with commit bec78b1 Feb 9, 2026
44 checks passed
@timothyb89 timothyb89 deleted the timothyb89/tctl-bots-add-bound-keypair branch February 9, 2026 23:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

machine-id size/md tctl tctl - Teleport admin tool

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MWI: Generated token for tctl bots add should be bound keypair

3 participants