mirror of
https://github.com/juanfont/headscale.git
synced 2026-04-09 18:53:48 +02:00
state: apply default node key expiry on registration
Use the node.expiry config to apply a default expiry to non-tagged nodes when the client does not request a specific expiry. This covers all registration paths: new node creation, re-authentication, and pre-auth key re-registration. Tagged nodes remain exempt and never expire. Fixes #1711
This commit is contained in:
@@ -1431,6 +1431,17 @@ func (s *State) applyAuthNodeUpdate(params authNodeUpdateParams) (types.NodeView
|
||||
}
|
||||
}
|
||||
// Tagged → Tagged: keep existing expiry (nil) - no action needed
|
||||
|
||||
// Apply default node expiry for non-tagged nodes when the
|
||||
// resolved expiry is still nil or zero (e.g., CLI registration
|
||||
// where the client did not request a specific expiry).
|
||||
needsDefaultExpiry := !node.IsTagged() &&
|
||||
(node.Expiry == nil || node.Expiry.IsZero()) &&
|
||||
s.cfg.Node.Expiry > 0
|
||||
if needsDefaultExpiry {
|
||||
exp := time.Now().Add(s.cfg.Node.Expiry)
|
||||
node.Expiry = &exp
|
||||
}
|
||||
})
|
||||
|
||||
if !ok {
|
||||
@@ -1553,6 +1564,17 @@ func (s *State) createAndSaveNewNode(params newNodeParams) (types.NodeView, erro
|
||||
}
|
||||
}
|
||||
|
||||
// Apply default node expiry for non-tagged nodes when the client
|
||||
// did not request a specific expiry.
|
||||
// Tagged nodes are exempt — they never expire.
|
||||
needsDefaultExpiry := !nodeToRegister.IsTagged() &&
|
||||
(nodeToRegister.Expiry == nil || nodeToRegister.Expiry.IsZero()) &&
|
||||
s.cfg.Node.Expiry > 0
|
||||
if needsDefaultExpiry {
|
||||
exp := time.Now().Add(s.cfg.Node.Expiry)
|
||||
nodeToRegister.Expiry = &exp
|
||||
}
|
||||
|
||||
// Validate before saving
|
||||
err := validateNodeOwnership(&nodeToRegister)
|
||||
if err != nil {
|
||||
@@ -2030,9 +2052,18 @@ func (s *State) HandleNodeFromPreAuthKey(
|
||||
node.LastSeen = new(time.Now())
|
||||
|
||||
// Tagged nodes keep their existing expiry (disabled).
|
||||
// User-owned nodes update expiry from the client request.
|
||||
// User-owned nodes update expiry from the client request,
|
||||
// falling back to the configured default if the client
|
||||
// did not request a specific expiry.
|
||||
if !node.IsTagged() {
|
||||
node.Expiry = ®Req.Expiry
|
||||
if !regReq.Expiry.IsZero() {
|
||||
node.Expiry = ®Req.Expiry
|
||||
} else if s.cfg.Node.Expiry > 0 {
|
||||
exp := time.Now().Add(s.cfg.Node.Expiry)
|
||||
node.Expiry = &exp
|
||||
} else {
|
||||
node.Expiry = ®Req.Expiry
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@ func TestOIDCExpiryAfterRestart(t *testing.T) {
|
||||
"HEADSCALE_OIDC_CLIENT_ID": scenario.mockOIDC.ClientID(),
|
||||
"CREDENTIALS_DIRECTORY_TEST": "/tmp",
|
||||
"HEADSCALE_OIDC_CLIENT_SECRET_PATH": "${CREDENTIALS_DIRECTORY_TEST}/hs_client_oidc_secret",
|
||||
"HEADSCALE_OIDC_EXPIRY": "72h",
|
||||
"HEADSCALE_NODE_EXPIRY": "72h",
|
||||
}
|
||||
|
||||
err = scenario.CreateHeadscaleEnvWithLoginURL(
|
||||
|
||||
@@ -1394,11 +1394,12 @@ func TestNodeExpireCommand(t *testing.T) {
|
||||
|
||||
assert.Len(t, listAll, 5)
|
||||
|
||||
assert.True(t, listAll[0].GetExpiry().AsTime().IsZero())
|
||||
assert.True(t, listAll[1].GetExpiry().AsTime().IsZero())
|
||||
assert.True(t, listAll[2].GetExpiry().AsTime().IsZero())
|
||||
assert.True(t, listAll[3].GetExpiry().AsTime().IsZero())
|
||||
assert.True(t, listAll[4].GetExpiry().AsTime().IsZero())
|
||||
// With node.expiry defaulting to 0, non-tagged nodes have zero expiry
|
||||
// (never expire unless explicitly expired).
|
||||
for i := range 5 {
|
||||
assert.True(t, listAll[i].GetExpiry().AsTime().IsZero(),
|
||||
"node %d should have zero expiry (no default node.expiry)", i)
|
||||
}
|
||||
|
||||
for idx := range 3 {
|
||||
_, err := headscale.Execute(
|
||||
|
||||
Reference in New Issue
Block a user