fix loading policy manager

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
Kristoffer Dalby 2024-10-26 18:19:14 -04:00
parent 8d5b04f3d3
commit 50b62ddfb3
No known key found for this signature in database
2 changed files with 65 additions and 61 deletions

View file

@ -88,7 +88,8 @@ type Headscale struct {
DERPMap *tailcfg.DERPMap DERPMap *tailcfg.DERPMap
DERPServer *derpServer.DERPServer DERPServer *derpServer.DERPServer
polMan policy.PolicyManager polManOnce sync.Once
polMan policy.PolicyManager
mapper *mapper.Mapper mapper *mapper.Mapper
nodeNotifier *notifier.Notifier nodeNotifier *notifier.Notifier
@ -531,8 +532,7 @@ func (h *Headscale) Serve() error {
} }
var err error var err error
if err = h.loadPolicyManager(); err != nil {
if err = h.loadACLPolicy(); err != nil {
return fmt.Errorf("failed to load ACL policy: %w", err) return fmt.Errorf("failed to load ACL policy: %w", err)
} }
@ -814,7 +814,7 @@ func (h *Headscale) Serve() error {
// TODO(kradalby): Reload config on SIGHUP // TODO(kradalby): Reload config on SIGHUP
// TODO(kradalby): Only update if we set a new policy // TODO(kradalby): Only update if we set a new policy
if err := h.loadACLPolicy(); err != nil { if err := h.loadPolicyManager(); err != nil {
log.Error().Err(err).Msg("failed to reload ACL policy") log.Error().Err(err).Msg("failed to reload ACL policy")
} }
@ -1037,22 +1037,9 @@ func readOrCreatePrivateKey(path string) (*key.MachinePrivate, error) {
return &machineKey, nil return &machineKey, nil
} }
func (h *Headscale) loadACLPolicy() error { func (h *Headscale) loadPolicyManager() error {
var ( var errOut error
pm policy.PolicyManager h.polManOnce.Do(func() {
)
switch h.cfg.Policy.Mode {
case types.PolicyModeFile:
path := h.cfg.Policy.Path
// It is fine to start headscale without a policy file.
if len(path) == 0 {
return nil
}
absPath := util.AbsolutePathFromConfigPath(path)
// Validate and reject configuration that would error when applied // Validate and reject configuration that would error when applied
// when creating a map response. This requires nodes, so there is still // when creating a map response. This requires nodes, so there is still
// a scenario where they might be allowed if the server has no nodes // a scenario where they might be allowed if the server has no nodes
@ -1063,54 +1050,67 @@ func (h *Headscale) loadACLPolicy() error {
// allowed to be written to the database. // allowed to be written to the database.
nodes, err := h.db.ListNodes() nodes, err := h.db.ListNodes()
if err != nil { if err != nil {
return fmt.Errorf("loading nodes from database to validate policy: %w", err) errOut = fmt.Errorf("loading nodes from database to validate policy: %w", err)
return
} }
users, err := h.db.ListUsers() users, err := h.db.ListUsers()
if err != nil { if err != nil {
return fmt.Errorf("loading users from database to validate policy: %w", err) errOut = fmt.Errorf("loading users from database to validate policy: %w", err)
return
} }
pm, err = policy.NewPolicyManagerFromPath(absPath, users, nodes) switch h.cfg.Policy.Mode {
if err != nil { case types.PolicyModeFile:
return fmt.Errorf("loading policy from file: %w", err) path := h.cfg.Policy.Path
}
if len(nodes) > 0 { // It is fine to start headscale without a policy file.
_, err = pm.SSHPolicy(nodes[0]) if len(path) == 0 {
h.polMan, err = policy.NewPolicyManager(nil, users, nodes)
if err != nil {
errOut = fmt.Errorf("policy manager with no policy: %w", err)
}
return
}
absPath := util.AbsolutePathFromConfigPath(path)
h.polMan, err = policy.NewPolicyManagerFromPath(absPath, users, nodes)
if err != nil { if err != nil {
return fmt.Errorf("verifying SSH rules: %w", err) errOut = fmt.Errorf("loading policy from file (%s): %w", absPath, err)
} return
}
case types.PolicyModeDB:
p, err := h.db.GetPolicy()
if err != nil {
if errors.Is(err, types.ErrPolicyNotFound) {
return nil
} }
return fmt.Errorf("failed to get policy from database: %w", err) if len(nodes) > 0 {
} _, err = h.polMan.SSHPolicy(nodes[0])
if err != nil {
errOut = fmt.Errorf("verifying SSH rules: %w", err)
return
}
}
nodes, err := h.db.ListNodes() case types.PolicyModeDB:
if err != nil { p, err := h.db.GetPolicy()
return fmt.Errorf("loading nodes from database to validate policy: %w", err) if err != nil {
} if errors.Is(err, types.ErrPolicyNotFound) {
users, err := h.db.ListUsers() return
if err != nil { }
return fmt.Errorf("loading users from database to validate policy: %w", err)
}
pm, err = policy.NewPolicyManager([]byte(p.Data), users, nodes)
if err != nil {
return fmt.Errorf("loading policy from database: %w", err)
}
default:
log.Fatal().
Str("mode", string(h.cfg.Policy.Mode)).
Msg("Unknown ACL policy mode")
}
h.polMan = pm errOut = fmt.Errorf("failed to get policy from database: %w", err)
return
}
return nil h.polMan, err = policy.NewPolicyManager([]byte(p.Data), users, nodes)
if err != nil {
errOut = fmt.Errorf("loading policy from database: %w", err)
return
}
default:
log.Fatal().
Str("mode", string(h.cfg.Policy.Mode)).
Msg("Unknown ACL policy mode")
}
})
return errOut
} }

View file

@ -40,9 +40,13 @@ func NewPolicyManagerFromPath(path string, users []types.User, nodes types.Nodes
} }
func NewPolicyManager(polB []byte, users []types.User, nodes types.Nodes) (PolicyManager, error) { func NewPolicyManager(polB []byte, users []types.User, nodes types.Nodes) (PolicyManager, error) {
pol, err := LoadACLPolicyFromBytes(polB) var pol *ACLPolicy
if err != nil { var err error
return nil, fmt.Errorf("parsing policy: %w", err) if polB != nil && len(polB) > 0 {
pol, err = LoadACLPolicyFromBytes(polB)
if err != nil {
return nil, fmt.Errorf("parsing policy: %w", err)
}
} }
pm := PolicyManagerV1{ pm := PolicyManagerV1{