mirror of
https://github.com/juanfont/headscale.git
synced 2026-07-08 00:50:20 +09: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
|
// 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 {
|
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
|
// Validate before saving
|
||||||
err := validateNodeOwnership(&nodeToRegister)
|
err := validateNodeOwnership(&nodeToRegister)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -2030,9 +2052,18 @@ func (s *State) HandleNodeFromPreAuthKey(
|
|||||||
node.LastSeen = new(time.Now())
|
node.LastSeen = new(time.Now())
|
||||||
|
|
||||||
// Tagged nodes keep their existing expiry (disabled).
|
// 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() {
|
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(),
|
"HEADSCALE_OIDC_CLIENT_ID": scenario.mockOIDC.ClientID(),
|
||||||
"CREDENTIALS_DIRECTORY_TEST": "/tmp",
|
"CREDENTIALS_DIRECTORY_TEST": "/tmp",
|
||||||
"HEADSCALE_OIDC_CLIENT_SECRET_PATH": "${CREDENTIALS_DIRECTORY_TEST}/hs_client_oidc_secret",
|
"HEADSCALE_OIDC_CLIENT_SECRET_PATH": "${CREDENTIALS_DIRECTORY_TEST}/hs_client_oidc_secret",
|
||||||
"HEADSCALE_OIDC_EXPIRY": "72h",
|
"HEADSCALE_NODE_EXPIRY": "72h",
|
||||||
}
|
}
|
||||||
|
|
||||||
err = scenario.CreateHeadscaleEnvWithLoginURL(
|
err = scenario.CreateHeadscaleEnvWithLoginURL(
|
||||||
|
|||||||
@@ -1394,11 +1394,12 @@ func TestNodeExpireCommand(t *testing.T) {
|
|||||||
|
|
||||||
assert.Len(t, listAll, 5)
|
assert.Len(t, listAll, 5)
|
||||||
|
|
||||||
assert.True(t, listAll[0].GetExpiry().AsTime().IsZero())
|
// With node.expiry defaulting to 0, non-tagged nodes have zero expiry
|
||||||
assert.True(t, listAll[1].GetExpiry().AsTime().IsZero())
|
// (never expire unless explicitly expired).
|
||||||
assert.True(t, listAll[2].GetExpiry().AsTime().IsZero())
|
for i := range 5 {
|
||||||
assert.True(t, listAll[3].GetExpiry().AsTime().IsZero())
|
assert.True(t, listAll[i].GetExpiry().AsTime().IsZero(),
|
||||||
assert.True(t, listAll[4].GetExpiry().AsTime().IsZero())
|
"node %d should have zero expiry (no default node.expiry)", i)
|
||||||
|
}
|
||||||
|
|
||||||
for idx := range 3 {
|
for idx := range 3 {
|
||||||
_, err := headscale.Execute(
|
_, err := headscale.Execute(
|
||||||
|
|||||||
Reference in New Issue
Block a user