Skip to content

Commit

Permalink
initial implementation of autoApprovers support
Browse files Browse the repository at this point in the history
  • Loading branch information
tsujamin committed Aug 25, 2022
1 parent cc0bec1 commit 004ebca
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 5 deletions.
18 changes: 13 additions & 5 deletions acls_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ import (

// ACLPolicy represents a Tailscale ACL Policy.
type ACLPolicy struct {
Groups Groups `json:"groups" yaml:"groups"`
Hosts Hosts `json:"hosts" yaml:"hosts"`
TagOwners TagOwners `json:"tagOwners" yaml:"tagOwners"`
ACLs []ACL `json:"acls" yaml:"acls"`
Tests []ACLTest `json:"tests" yaml:"tests"`
Groups Groups `json:"groups" yaml:"groups"`
Hosts Hosts `json:"hosts" yaml:"hosts"`
TagOwners TagOwners `json:"tagOwners" yaml:"tagOwners"`
ACLs []ACL `json:"acls" yaml:"acls"`
Tests []ACLTest `json:"tests" yaml:"tests"`
AutoApprovers AutoApprovers `json:"autoApprovers" yaml:"autoApprovers"`
}

// ACL is a basic rule for the ACL Policy.
Expand All @@ -42,6 +43,13 @@ type ACLTest struct {
Deny []string `json:"deny,omitempty" yaml:"deny,omitempty"`
}

// AutoApprovers specify which users (namespaces?), groups or tags have their advertised routes
// or exit node status automatically enabled
type AutoApprovers struct {
Routes map[string][]string `json:"routes" yaml:"routes"`
ExitNode []string `json:"exitNode" yaml:"exitNode"`
}

// UnmarshalJSON allows to parse the Hosts directly into netaddr objects.
func (hosts *Hosts) UnmarshalJSON(data []byte) error {
newHosts := Hosts{}
Expand Down
75 changes: 75 additions & 0 deletions machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -930,6 +930,81 @@ func (h *Headscale) EnableRoutes(machine *Machine, routeStrs ...string) error {
return nil
}

// Enabled any routes advertised by a machine that match the ACL autoApprovers policy
// TODO simplify by expanding only for current machine, and by checking if approvedIPs contains machine.IPs[0]
func (h *Headscale) EnableAutoApprovedRoutes(machine *Machine) error {
approvedRoutes := make([]netaddr.IPPrefix, 0, len(machine.HostInfo.RoutableIPs))
machines, err := h.ListMachines()

if err != nil {
log.Err(err)
return err
}

for _, advertisedRoute := range machine.HostInfo.RoutableIPs {
log.Debug().
Uint64("machine", machine.ID).
Str("advertisedRoute", advertisedRoute.String()).
Msg("Client requested to advertise route")

approved := false
routeApprovers := h.aclPolicy.AutoApprovers.Routes[advertisedRoute.String()]

if advertisedRoute.Bits() == 0 {
routeApprovers = h.aclPolicy.AutoApprovers.ExitNode
}

if len(routeApprovers) > 0 {
for _, approvedAlias := range routeApprovers {

approvedIps, err := expandAlias(machines, *h.aclPolicy, approvedAlias, h.cfg.OIDC.StripEmaildomain)

if err != nil {
log.Err(err).
Str("alias", approvedAlias).
Msg("Failed to expand alias when processing autoApprovers policy")
return err
}

for _, machineIp := range machine.IPAddresses {
for _, approvedIp := range approvedIps {
approved = machineIp.String() == approvedIp

if approved {
break
}
}

if approved {
break
}
}
}
} else {
log.Debug().
Uint64("client", machine.ID).
Str("advertisedRoute", advertisedRoute.String()).
Msg("Advertised route is not automatically approved")
}

if approved {
approvedRoutes = append(approvedRoutes, advertisedRoute)
}
}

for _, approvedRoute := range approvedRoutes {
if !contains(machine.EnabledRoutes, approvedRoute) {
log.Info().
Str("route", approvedRoute.String()).
Uint64("client", machine.ID).
Msg("Enabling autoApproved route for client")
machine.EnabledRoutes = append(machine.EnabledRoutes, approvedRoute)
}
}

return nil
}

func (machine *Machine) RoutesToProto() *v1.Routes {
availableRoutes := machine.GetAdvertisedRoutes()

Expand Down
7 changes: 7 additions & 0 deletions protocol_common_poll.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,14 @@ func (h *Headscale) handlePollCommon(
Str("machine", machine.Hostname).
Err(err)
}

// update routes with peer information
err = h.EnableAutoApprovedRoutes(machine)
if err != nil {
//TODO
}
}

// From Tailscale client:
//
// ReadOnly is whether the client just wants to fetch the MapResponse,
Expand Down

0 comments on commit 004ebca

Please sign in to comment.