mirror of
https://github.com/juanfont/headscale.git
synced 2026-10-24 15:26:49 +09:00
state: keep nil expiry for nodes that stay tagged on reauth
The convert-from-tag arm wrongly set an expiry on still-tagged nodes.
This commit is contained in:
1 parent
f61753e737
commit
06d6816dc9
2 files changed
+95
-1
No files matched your search
@@ -0,0 +1,94 @@
|
|||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/juanfont/headscale/hscontrol/db"
|
||||||
|
"github.com/juanfont/headscale/hscontrol/types"
|
||||||
|
"github.com/juanfont/headscale/hscontrol/util"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"tailscale.com/tailcfg"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestTaggedReauthKeepsNilExpiry ensures that when an existing tagged node
|
||||||
|
// re-authenticates through the auth path and re-advertises a tag it is still
|
||||||
|
// permitted to hold, it stays tagged AND keeps key-expiry disabled (nil).
|
||||||
|
// Tagged nodes never expire, so applyAuthNodeUpdate must not assign an expiry
|
||||||
|
// to a node that remains tagged just because the auth used the
|
||||||
|
// convert-from-tag lookup path.
|
||||||
|
func TestTaggedReauthKeepsNilExpiry(t *testing.T) {
|
||||||
|
dbPath := t.TempDir() + "/headscale.db"
|
||||||
|
cfg := persistTestConfig(dbPath)
|
||||||
|
|
||||||
|
database, err := db.NewHeadscaleDatabase(cfg)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
user := database.CreateUserForTest("reauth-user")
|
||||||
|
node := database.CreateRegisteredNodeForTest(user, "reauth-node")
|
||||||
|
machineKey := node.MachineKey
|
||||||
|
nodeID := node.ID
|
||||||
|
nodeKey := node.NodeKey
|
||||||
|
discoKey := node.DiscoKey
|
||||||
|
|
||||||
|
require.NoError(t, database.Close())
|
||||||
|
|
||||||
|
s, err := NewState(cfg)
|
||||||
|
require.NoError(t, err)
|
||||||
|
t.Cleanup(func() { _ = s.Close() })
|
||||||
|
|
||||||
|
// Make the node tagged with tag:foo and Expiry=nil. Leaving UserID nil
|
||||||
|
// indexes it under userID 0 so the same-user machine-key lookup misses and
|
||||||
|
// HandleNodeFromAuthPath takes the convert-from-tag branch. The User field
|
||||||
|
// is retained for created-by tracking, which lets the tag re-advertisement
|
||||||
|
// be permitted.
|
||||||
|
seeded, ok := s.nodeStore.UpdateNode(nodeID, func(n *types.Node) {
|
||||||
|
n.Tags = []string{"tag:foo"}
|
||||||
|
n.UserID = nil
|
||||||
|
n.User = user
|
||||||
|
n.Expiry = nil
|
||||||
|
})
|
||||||
|
require.True(t, ok)
|
||||||
|
require.True(t, seeded.IsTagged(), "precondition: node must be tagged")
|
||||||
|
require.True(t, seeded.Valid())
|
||||||
|
|
||||||
|
policy := fmt.Sprintf(`{"tagOwners":{"tag:foo":["%s@"]}}`, user.Name)
|
||||||
|
_, err = s.SetPolicy([]byte(policy))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, s.NodeCanHaveTag(seeded, "tag:foo"),
|
||||||
|
"precondition: tagged node must be permitted to re-advertise tag:foo")
|
||||||
|
|
||||||
|
// Registration that re-advertises tag:foo and carries a non-nil client
|
||||||
|
// expiry (the normal tailscale client case).
|
||||||
|
clientExpiry := time.Now().Add(180 * 24 * time.Hour)
|
||||||
|
regData := &types.RegistrationData{
|
||||||
|
MachineKey: machineKey,
|
||||||
|
NodeKey: nodeKey,
|
||||||
|
DiscoKey: discoKey,
|
||||||
|
Hostname: "reauth-node",
|
||||||
|
Hostinfo: &tailcfg.Hostinfo{
|
||||||
|
Hostname: "reauth-node",
|
||||||
|
RequestTags: []string{"tag:foo"},
|
||||||
|
},
|
||||||
|
Expiry: &clientExpiry,
|
||||||
|
}
|
||||||
|
|
||||||
|
authID := types.MustAuthID()
|
||||||
|
s.SetAuthCacheEntry(authID, types.NewRegisterAuthRequest(regData))
|
||||||
|
|
||||||
|
finalNode, _, err := s.HandleNodeFromAuthPath(
|
||||||
|
authID,
|
||||||
|
types.UserID(user.ID),
|
||||||
|
nil,
|
||||||
|
util.RegisterMethodOIDC,
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, finalNode.Valid())
|
||||||
|
|
||||||
|
require.True(t, finalNode.IsTagged(),
|
||||||
|
"node should remain tagged after re-advertising a permitted tag")
|
||||||
|
|
||||||
|
require.Nil(t, finalNode.AsStruct().Expiry,
|
||||||
|
"tagged node must keep nil key expiry (tagged nodes never expire)")
|
||||||
|
}
|
||||||
@@ -1606,7 +1606,7 @@ func (s *State) applyAuthNodeUpdate(params authNodeUpdateParams) (types.NodeView
|
|||||||
case !wasTagged && isTagged:
|
case !wasTagged && isTagged:
|
||||||
// Personal → Tagged: clear expiry (tagged nodes don't expire)
|
// Personal → Tagged: clear expiry (tagged nodes don't expire)
|
||||||
node.Expiry = nil
|
node.Expiry = nil
|
||||||
case params.IsConvertFromTag:
|
case params.IsConvertFromTag && !isTagged:
|
||||||
// Explicit conversion from tagged to user-owned: set expiry from client request
|
// Explicit conversion from tagged to user-owned: set expiry from client request
|
||||||
if params.Expiry != nil {
|
if params.Expiry != nil {
|
||||||
node.Expiry = params.Expiry
|
node.Expiry = params.Expiry
|
||||||
|
|||||||
Reference in new issue
Block a user