Skip to content

fix(auth): honor ephemeral registration for interactive auth#3078

Open
LouisLau-art wants to merge 1 commit into
juanfont:mainfrom
LouisLau-art:fix/oidc-ephemeral-2719
Open

fix(auth): honor ephemeral registration for interactive auth#3078
LouisLau-art wants to merge 1 commit into
juanfont:mainfrom
LouisLau-art:fix/oidc-ephemeral-2719

Conversation

@LouisLau-art
Copy link
Copy Markdown

Summary

Fixes tailscaled --state=mem: registrations that go through interactive auth (OIDC/web), so nodes are persisted as ephemeral and handled consistently.

Closes #2719.

What changed

  • Add nodes.ephemeral as a node-level persisted flag.
  • Propagate RegisterRequest.Ephemeral into interactive registration cache entries.
  • Carry that flag through auth callback (HandleNodeFromAuthPath) into created/updated nodes.
  • Keep pre-auth key behavior unchanged by mirroring PreAuthKey.Ephemeral into nodes.ephemeral.
  • Update ephemeral lookups/counting to use node-level IsEphemeral() checks.
  • Add DB migration to add/backfill nodes.ephemeral from existing ephemeral pre-auth keys.
  • Extend tests:
    • interactive workflow with Ephemeral: true now asserts node is ephemeral;
    • DB ephemeral listing includes nodes with nodes.ephemeral = true and no pre-auth key.

Verification

  • go test ./hscontrol -run TestAuthenticationFlows -count=1
  • go test ./hscontrol/db -run TestListEphemeralNodes -count=1
  • go test ./hscontrol/state -run TestEphemeralNodeLogoutRaceCondition -count=1
  • go test ./hscontrol/db -run 'TestSQLiteMigrationAndDataValidation|TestSQLiteAllTestdataMigrations' -count=1
  • go test ./hscontrol/types -run TestNodeRegisterMethodToV1Enum -count=1

Comment thread hscontrol/db/db.go
// Only SQLite will be migrated for consistency.
{
ID: "202507021200",
Migrate: func(tx *gorm.DB) error {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This changes a previous migration, which we cannot do, we can only add migrations.

Copy link
Copy Markdown
Collaborator

@kradalby kradalby left a comment

Choose a reason for hiding this comment

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

Looks pretty good, some comments and things needed.

If you are assisted by an AI, remember to declare the agent and model.

Comment thread hscontrol/db/db.go
Rollback: func(db *gorm.DB) error { return nil },
},
{
ID: "202602111000-node-ephemeral-column",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think this is the right approach, the ephemeral field should be on the node and not the auth key. This is essentially the same change as we did for tags in the previous release.

At authentication, the ephemeral field should be set on the node, and from there on, the pre auth key should always be ignored.

Comment thread hscontrol/db/node.go
Comment on lines +103 to +106
err := rx.
Joins("LEFT JOIN pre_auth_keys pak ON pak.id = nodes.auth_key_id").
Where("nodes.ephemeral = ? OR pak.ephemeral = ?", true, true).
Find(&nodes).Error
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This change should be reverted and the authkey part should be removed. We should only be looking at the ephemeral field on the node, so no join should be necessary.

Comment thread hscontrol/state/state.go

nodeToRegister.AuthKey = params.PreAuthKey
nodeToRegister.AuthKeyID = &params.PreAuthKey.ID
nodeToRegister.Ephemeral = params.PreAuthKey.Ephemeral
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This should be the last place PreAuthKey.Ephemeral is used, and after that, the Node field is the "authority".

Comment thread hscontrol/types/node.go
// https://tailscale.com/kb/1111/ephemeral-nodes/
func (node *Node) IsEphemeral() bool {
return node.AuthKey != nil && node.AuthKey.Ephemeral
return node.Ephemeral || (node.AuthKey != nil && node.AuthKey.Ephemeral)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This can drop || (node.AuthKey != nil && node.AuthKey.Ephemeral) as it is only used at auth time.

Comment thread hscontrol/auth_test.go
expectedAuthURLPattern: "/register/",
},
{
name: "full_interactive_workflow_ephemeral_node",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We need an integration test for the webauth and oidc auth to use --state=mem: to validate the behaviour.

@kradalby kradalby added this to the v0.33.0 milestone May 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Unable to register ephemeral nodes using OIDC

2 participants