1
0
mirror of https://github.com/juanfont/headscale.git synced 2025-07-27 13:48:02 +02:00
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
Kristoffer Dalby 2025-02-14 16:49:58 +01:00
parent d28194ac86
commit 7633c2e4cc
No known key found for this signature in database
7 changed files with 73 additions and 64 deletions

View File

@ -449,7 +449,6 @@ func TestBackfillIPAddresses(t *testing.T) {
"UserID", "UserID",
"Endpoints", "Endpoints",
"Hostinfo", "Hostinfo",
"Routes",
"CreatedAt", "CreatedAt",
"UpdatedAt", "UpdatedAt",
)) ))

View File

@ -101,15 +101,22 @@ func generateUserProfiles(
node *types.Node, node *types.Node,
peers types.Nodes, peers types.Nodes,
) []tailcfg.UserProfile { ) []tailcfg.UserProfile {
userMap := make(map[uint]types.User) userMap := make(map[uint]*types.User)
userMap[node.User.ID] = node.User ids := make([]uint, 0, len(userMap))
userMap[node.User.ID] = &node.User
ids = append(ids, node.User.ID)
for _, peer := range peers { for _, peer := range peers {
userMap[peer.User.ID] = peer.User // not worth checking if already is there userMap[peer.User.ID] = &peer.User
ids = append(ids, peer.User.ID)
} }
slices.Sort(ids)
slices.Compact(ids)
var profiles []tailcfg.UserProfile var profiles []tailcfg.UserProfile
for _, user := range userMap { for _, id := range ids {
profiles = append(profiles, user.TailscaleUserProfile()) if userMap[id] != nil {
profiles = append(profiles, userMap[id].TailscaleUserProfile())
}
} }
return profiles return profiles

View File

@ -11,7 +11,6 @@ import (
"github.com/juanfont/headscale/hscontrol/policy" "github.com/juanfont/headscale/hscontrol/policy"
"github.com/juanfont/headscale/hscontrol/routes" "github.com/juanfont/headscale/hscontrol/routes"
"github.com/juanfont/headscale/hscontrol/types" "github.com/juanfont/headscale/hscontrol/types"
"gopkg.in/check.v1"
"gorm.io/gorm" "gorm.io/gorm"
"tailscale.com/net/tsaddr" "tailscale.com/net/tsaddr"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
@ -24,51 +23,6 @@ var iap = func(ipStr string) *netip.Addr {
return &ip return &ip
} }
func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) {
mach := func(hostname, username string, userid uint) *types.Node {
return &types.Node{
Hostname: hostname,
UserID: userid,
User: types.User{
Model: gorm.Model{
ID: userid,
},
Name: username,
},
}
}
nodeInShared1 := mach("test_get_shared_nodes_1", "user1", 1)
nodeInShared2 := mach("test_get_shared_nodes_2", "user2", 2)
nodeInShared3 := mach("test_get_shared_nodes_3", "user3", 3)
node2InShared1 := mach("test_get_shared_nodes_4", "user1", 1)
userProfiles := generateUserProfiles(
nodeInShared1,
types.Nodes{
nodeInShared2, nodeInShared3, node2InShared1,
},
)
c.Assert(len(userProfiles), check.Equals, 3)
users := []string{
"user1", "user2", "user3",
}
for _, user := range users {
found := false
for _, userProfile := range userProfiles {
if userProfile.DisplayName == user {
found = true
break
}
}
c.Assert(found, check.Equals, true)
}
}
func TestDNSConfigMapResponse(t *testing.T) { func TestDNSConfigMapResponse(t *testing.T) {
tests := []struct { tests := []struct {
magicDNS bool magicDNS bool

View File

@ -2165,6 +2165,9 @@ func TestReduceFilterRules(t *testing.T) {
netip.MustParsePrefix("10.33.0.0/16"), netip.MustParsePrefix("10.33.0.0/16"),
}, },
}, },
ApprovedRoutes: []netip.Prefix{
netip.MustParsePrefix("10.33.0.0/16"),
},
}, },
peers: types.Nodes{ peers: types.Nodes{
&types.Node{ &types.Node{
@ -2292,6 +2295,7 @@ func TestReduceFilterRules(t *testing.T) {
Hostinfo: &tailcfg.Hostinfo{ Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: tsaddr.ExitRoutes(), RoutableIPs: tsaddr.ExitRoutes(),
}, },
ApprovedRoutes: tsaddr.ExitRoutes(),
}, },
peers: types.Nodes{ peers: types.Nodes{
&types.Node{ &types.Node{
@ -2398,6 +2402,7 @@ func TestReduceFilterRules(t *testing.T) {
Hostinfo: &tailcfg.Hostinfo{ Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: tsaddr.ExitRoutes(), RoutableIPs: tsaddr.ExitRoutes(),
}, },
ApprovedRoutes: tsaddr.ExitRoutes(),
}, },
peers: types.Nodes{ peers: types.Nodes{
&types.Node{ &types.Node{
@ -2513,6 +2518,10 @@ func TestReduceFilterRules(t *testing.T) {
netip.MustParsePrefix("16.0.0.0/16"), netip.MustParsePrefix("16.0.0.0/16"),
}, },
}, },
ApprovedRoutes: []netip.Prefix{
netip.MustParsePrefix("8.0.0.0/16"),
netip.MustParsePrefix("16.0.0.0/16"),
},
}, },
peers: types.Nodes{ peers: types.Nodes{
&types.Node{ &types.Node{
@ -2603,6 +2612,10 @@ func TestReduceFilterRules(t *testing.T) {
netip.MustParsePrefix("16.0.0.0/8"), netip.MustParsePrefix("16.0.0.0/8"),
}, },
}, },
ApprovedRoutes: []netip.Prefix{
netip.MustParsePrefix("8.0.0.0/8"),
netip.MustParsePrefix("16.0.0.0/8"),
},
}, },
peers: types.Nodes{ peers: types.Nodes{
&types.Node{ &types.Node{
@ -2683,6 +2696,7 @@ func TestReduceFilterRules(t *testing.T) {
Hostinfo: &tailcfg.Hostinfo{ Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: []netip.Prefix{netip.MustParsePrefix("172.16.0.0/24")}, RoutableIPs: []netip.Prefix{netip.MustParsePrefix("172.16.0.0/24")},
}, },
ApprovedRoutes: []netip.Prefix{netip.MustParsePrefix("172.16.0.0/24")},
ForcedTags: []string{"tag:access-servers"}, ForcedTags: []string{"tag:access-servers"},
}, },
peers: types.Nodes{ peers: types.Nodes{

View File

@ -9,8 +9,8 @@ import (
) )
type Match struct { type Match struct {
Srcs *netipx.IPSet srcs *netipx.IPSet
Dests *netipx.IPSet dests *netipx.IPSet
} }
func MatchFromFilterRule(rule tailcfg.FilterRule) Match { func MatchFromFilterRule(rule tailcfg.FilterRule) Match {
@ -42,16 +42,16 @@ func MatchFromStrings(sources, destinations []string) Match {
destsSet, _ := dests.IPSet() destsSet, _ := dests.IPSet()
match := Match{ match := Match{
Srcs: srcsSet, srcs: srcsSet,
Dests: destsSet, dests: destsSet,
} }
return match return match
} }
func (m *Match) SrcsContainsIPs(ips []netip.Addr) bool { func (m *Match) SrcsContainsIPs(ips ...netip.Addr) bool {
for _, ip := range ips { for _, ip := range ips {
if m.Srcs.Contains(ip) { if m.srcs.Contains(ip) {
return true return true
} }
} }
@ -59,9 +59,29 @@ func (m *Match) SrcsContainsIPs(ips []netip.Addr) bool {
return false return false
} }
func (m *Match) DestsContainsIP(ips []netip.Addr) bool { func (m *Match) DestsContainsIP(ips ...netip.Addr) bool {
for _, ip := range ips { for _, ip := range ips {
if m.Dests.Contains(ip) { if m.dests.Contains(ip) {
return true
}
}
return false
}
func (m *Match) SrcsOverlapsPrefixes(prefixes ...netip.Prefix) bool {
for _, prefix := range prefixes {
if m.srcs.ContainsPrefix(prefix) {
return true
}
}
return false
}
func (m *Match) DestsOverlapsPrefixes(prefixes ...netip.Prefix) bool {
for _, prefix := range prefixes {
if m.dests.ContainsPrefix(prefix) {
return true return true
} }
} }

View File

@ -202,11 +202,15 @@ func (node *Node) CanAccess(filter []tailcfg.FilterRule, node2 *Node) bool {
} }
for _, matcher := range matchers { for _, matcher := range matchers {
if !matcher.SrcsContainsIPs(src) { if !matcher.SrcsContainsIPs(src...) {
continue continue
} }
if matcher.DestsContainsIP(allowedIPs) { if matcher.DestsContainsIP(allowedIPs...) {
return true
}
if matcher.DestsOverlapsPrefixes(node2.SubnetRoutes()...) {
return true return true
} }
} }

View File

@ -55,6 +55,13 @@ type User struct {
ProfilePicURL string ProfilePicURL string
} }
func (u *User) StringID() string {
if u == nil {
return ""
}
return strconv.FormatUint(uint64(u.ID), 10)
}
// Username is the main way to get the username of a user, // Username is the main way to get the username of a user,
// it will return the email if it exists, the name if it exists, // it will return the email if it exists, the name if it exists,
// the OIDCIdentifier if it exists, and the ID if nothing else exists. // the OIDCIdentifier if it exists, and the ID if nothing else exists.
@ -63,7 +70,11 @@ type User struct {
// should be used throughout headscale, in information returned to the // should be used throughout headscale, in information returned to the
// user and the Policy engine. // user and the Policy engine.
func (u *User) Username() string { func (u *User) Username() string {
return cmp.Or(u.Email, u.Name, u.ProviderIdentifier.String, strconv.FormatUint(uint64(u.ID), 10)) return cmp.Or(
u.Email,
u.Name,
u.ProviderIdentifier.String,
u.StringID())
} }
// DisplayNameOrUsername returns the DisplayName if it exists, otherwise // DisplayNameOrUsername returns the DisplayName if it exists, otherwise