fix auto approver on register and new policy (#2506)
* fix issue auto approve route on register bug This commit fixes an issue where routes where not approved on a node during registration. This cause the auto approval to require the node to readvertise the routes. Fixes #2497 Fixes #2485 Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * hsic: only set db policy if exist Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * policy: calculate changed based on policy and filter v1 is a bit simpler than v2, it does not pre calculate the auto approver map and we cannot tell if it is changed. Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> --------- Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
parent
e3521be705
commit
5a18e91317
10 changed files with 575 additions and 217 deletions
|
@ -866,6 +866,11 @@ func (h *Headscale) Serve() error {
|
|||
log.Info().
|
||||
Msg("ACL policy successfully reloaded, notifying nodes of change")
|
||||
|
||||
err = h.autoApproveNodes()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to approve routes after new policy")
|
||||
}
|
||||
|
||||
ctx := types.NotifyCtx(context.Background(), "acl-sighup", "na")
|
||||
h.nodeNotifier.NotifyAll(ctx, types.UpdateFull())
|
||||
}
|
||||
|
@ -1166,3 +1171,36 @@ func (h *Headscale) loadPolicyManager() error {
|
|||
|
||||
return errOut
|
||||
}
|
||||
|
||||
// autoApproveNodes mass approves routes on all nodes. It is _only_ intended for
|
||||
// use when the policy is replaced. It is not sending or reporting any changes
|
||||
// or updates as we send full updates after replacing the policy.
|
||||
// TODO(kradalby): This is kind of messy, maybe this is another +1
|
||||
// for an event bus. See example comments here.
|
||||
func (h *Headscale) autoApproveNodes() error {
|
||||
err := h.db.Write(func(tx *gorm.DB) error {
|
||||
nodes, err := db.ListNodes(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, node := range nodes {
|
||||
changed := policy.AutoApproveRoutes(h.polMan, node)
|
||||
if changed {
|
||||
err = tx.Save(node).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h.primaryRoutes.SetRoutes(node.ID, node.SubnetRoutes()...)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("auto approving routes for nodes: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/juanfont/headscale/hscontrol/db"
|
||||
"github.com/juanfont/headscale/hscontrol/policy"
|
||||
"github.com/juanfont/headscale/hscontrol/types"
|
||||
"github.com/juanfont/headscale/hscontrol/util"
|
||||
"gorm.io/gorm"
|
||||
|
@ -212,6 +213,9 @@ func (h *Headscale) handleRegisterWithAuthKey(
|
|||
nodeToRegister.Expiry = ®Req.Expiry
|
||||
}
|
||||
|
||||
// Ensure any auto approved routes are handled before saving.
|
||||
policy.AutoApproveRoutes(h.polMan, &nodeToRegister)
|
||||
|
||||
ipv4, ipv6, err := h.ipAlloc.Next()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("allocating IPs: %w", err)
|
||||
|
@ -266,7 +270,7 @@ func (h *Headscale) handleRegisterInteractive(
|
|||
return nil, fmt.Errorf("generating registration ID: %w", err)
|
||||
}
|
||||
|
||||
newNode := types.RegisterNode{
|
||||
nodeToRegister := types.RegisterNode{
|
||||
Node: types.Node{
|
||||
Hostname: regReq.Hostinfo.Hostname,
|
||||
MachineKey: machineKey,
|
||||
|
@ -278,12 +282,15 @@ func (h *Headscale) handleRegisterInteractive(
|
|||
}
|
||||
|
||||
if !regReq.Expiry.IsZero() {
|
||||
newNode.Node.Expiry = ®Req.Expiry
|
||||
nodeToRegister.Node.Expiry = ®Req.Expiry
|
||||
}
|
||||
|
||||
// Ensure any auto approved routes are handled before saving.
|
||||
policy.AutoApproveRoutes(h.polMan, &nodeToRegister.Node)
|
||||
|
||||
h.registrationCache.Set(
|
||||
registrationId,
|
||||
newNode,
|
||||
nodeToRegister,
|
||||
)
|
||||
|
||||
return &tailcfg.RegisterResponse{
|
||||
|
|
|
@ -739,6 +739,11 @@ func (api headscaleV1APIServer) SetPolicy(
|
|||
|
||||
// Only send update if the packet filter has changed.
|
||||
if changed {
|
||||
err = api.h.autoApproveNodes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx := types.NotifyCtx(context.Background(), "acl-update", "na")
|
||||
api.h.nodeNotifier.NotifyAll(ctx, types.UpdateFull())
|
||||
}
|
||||
|
|
|
@ -53,14 +53,15 @@ func NewPolicyManager(polB []byte, users []types.User, nodes types.Nodes) (*Poli
|
|||
}
|
||||
|
||||
type PolicyManager struct {
|
||||
mu sync.Mutex
|
||||
pol *ACLPolicy
|
||||
mu sync.Mutex
|
||||
pol *ACLPolicy
|
||||
polHash deephash.Sum
|
||||
|
||||
users []types.User
|
||||
nodes types.Nodes
|
||||
|
||||
filterHash deephash.Sum
|
||||
filter []tailcfg.FilterRule
|
||||
filterHash deephash.Sum
|
||||
}
|
||||
|
||||
// updateLocked updates the filter rules based on the current policy and nodes.
|
||||
|
@ -71,13 +72,16 @@ func (pm *PolicyManager) updateLocked() (bool, error) {
|
|||
return false, fmt.Errorf("compiling filter rules: %w", err)
|
||||
}
|
||||
|
||||
polHash := deephash.Hash(pm.pol)
|
||||
filterHash := deephash.Hash(&filter)
|
||||
if filterHash == pm.filterHash {
|
||||
|
||||
if polHash == pm.polHash && filterHash == pm.filterHash {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
pm.filter = filter
|
||||
pm.filterHash = filterHash
|
||||
pm.polHash = polHash
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue