Merge branch 'main' into peers-get-account-refactoring

# Conflicts:
#	management/server/peer.go
This commit is contained in:
bcmmbaga
2024-12-06 13:45:23 +03:00
43 changed files with 3183 additions and 390 deletions
+4 -4
View File
@@ -2991,10 +2991,10 @@ func BenchmarkSyncAndMarkPeer(b *testing.B) {
minMsPerOpCICD float64
maxMsPerOpCICD float64
}{
{"Small", 50, 5, 1, 3, 4, 10},
{"Small", 50, 5, 1, 3, 3, 10},
{"Medium", 500, 100, 7, 13, 10, 60},
{"Large", 5000, 200, 65, 80, 60, 170},
{"Small single", 50, 10, 1, 3, 4, 60},
{"Small single", 50, 10, 1, 3, 3, 60},
{"Medium single", 500, 10, 7, 13, 10, 26},
{"Large 5", 5000, 15, 65, 80, 60, 170},
}
@@ -3134,10 +3134,10 @@ func BenchmarkLoginPeer_NewPeer(b *testing.B) {
}{
{"Small", 50, 5, 107, 120, 107, 140},
{"Medium", 500, 100, 105, 140, 105, 170},
{"Large", 5000, 200, 180, 220, 180, 320},
{"Large", 5000, 200, 180, 220, 180, 340},
{"Small single", 50, 10, 107, 120, 105, 140},
{"Medium single", 500, 10, 105, 140, 105, 170},
{"Large 5", 5000, 15, 180, 220, 180, 320},
{"Large 5", 5000, 15, 180, 220, 180, 340},
}
log.SetOutput(io.Discard)
+13 -6
View File
@@ -77,6 +77,8 @@ type JWTValidator struct {
options Options
}
var keyNotFound = errors.New("unable to find appropriate key")
// NewJWTValidator constructor
func NewJWTValidator(ctx context.Context, issuer string, audienceList []string, keysLocation string, idpSignkeyRefreshEnabled bool) (*JWTValidator, error) {
keys, err := getPemKeys(ctx, keysLocation)
@@ -124,12 +126,18 @@ func NewJWTValidator(ctx context.Context, issuer string, audienceList []string,
}
publicKey, err := getPublicKey(ctx, token, keys)
if err != nil {
log.WithContext(ctx).Errorf("getPublicKey error: %s", err)
return nil, err
if err == nil {
return publicKey, nil
}
return publicKey, nil
msg := fmt.Sprintf("getPublicKey error: %s", err)
if errors.Is(err, keyNotFound) && !idpSignkeyRefreshEnabled {
msg = fmt.Sprintf("getPublicKey error: %s. You can enable key refresh by setting HttpServerConfig.IdpSignKeyRefreshEnabled to true in your management.json file and restart the service", err)
}
log.WithContext(ctx).Error(msg)
return nil, err
},
EnableAuthOnOptions: false,
}
@@ -229,7 +237,7 @@ func getPublicKey(ctx context.Context, token *jwt.Token, jwks *Jwks) (interface{
log.WithContext(ctx).Debugf("Key Type: %s not yet supported, please raise ticket!", jwks.Keys[k].Kty)
}
return nil, errors.New("unable to find appropriate key")
return nil, keyNotFound
}
func getPublicKeyFromECDSA(jwk JSONWebKey) (publicKey *ecdsa.PublicKey, err error) {
@@ -310,4 +318,3 @@ func getMaxAgeFromCacheHeader(ctx context.Context, cacheControl string) int {
return 0
}
+35 -20
View File
@@ -496,7 +496,7 @@ func (am *DefaultAccountManager) AddPeer(ctx context.Context, setupKey, userID s
setupKeyName = sk.Name
}
if strings.ToLower(peer.Meta.Hostname) == "iphone" || strings.ToLower(peer.Meta.Hostname) == "ipad" && userID != "" {
if (strings.ToLower(peer.Meta.Hostname) == "iphone" || strings.ToLower(peer.Meta.Hostname) == "ipad") && userID != "" {
if am.idpManager != nil {
userdata, err := am.idpManager.GetUserDataByID(ctx, userID, idp.AppMetadata{WTAccountID: accountID})
if err == nil && userdata != nil {
@@ -701,33 +701,42 @@ func (am *DefaultAccountManager) SyncPeer(ctx context.Context, sync PeerSync, ac
return nil, nil, nil, err
}
if isStatusChanged || (updated && sync.UpdateAccountPeers) {
postureChecks, err := am.getPeerPostureChecks(account, peer.ID)
if err != nil {
return nil, nil, nil, err
}
if isStatusChanged || (updated && sync.UpdateAccountPeers) || (updated && len(postureChecks) > 0){
am.updateAccountPeers(ctx, accountID)
}
return am.getValidatedPeerWithMap(ctx, peerNotValid, accountID, peer)
}
func (am *DefaultAccountManager) handlePeerLoginNotFound(ctx context.Context, login PeerLogin, err error) (*nbpeer.Peer, *NetworkMap, []*posture.Checks, error) {
if errStatus, ok := status.FromError(err); ok && errStatus.Type() == status.NotFound {
// we couldn't find this peer by its public key which can mean that peer hasn't been registered yet.
// Try registering it.
newPeer := &nbpeer.Peer{
Key: login.WireGuardPubKey,
Meta: login.Meta,
SSHKey: login.SSHKey,
Location: nbpeer.Location{ConnectionIP: login.ConnectionIP},
}
return am.AddPeer(ctx, login.SetupKey, login.UserID, newPeer)
}
log.WithContext(ctx).Errorf("failed while logging in peer %s: %v", login.WireGuardPubKey, err)
return nil, nil, nil, status.Errorf(status.Internal, "failed while logging in peer")
}
// LoginPeer logs in or registers a peer.
// If peer doesn't exist the function checks whether a setup key or a user is present and registers a new peer if so.
func (am *DefaultAccountManager) LoginPeer(ctx context.Context, login PeerLogin) (*nbpeer.Peer, *NetworkMap, []*posture.Checks, error) {
accountID, err := am.Store.GetAccountIDByPeerPubKey(ctx, login.WireGuardPubKey)
if err != nil {
if errStatus, ok := status.FromError(err); ok && errStatus.Type() == status.NotFound {
// we couldn't find this peer by its public key which can mean that peer hasn't been registered yet.
// Try registering it.
newPeer := &nbpeer.Peer{
Key: login.WireGuardPubKey,
Meta: login.Meta,
SSHKey: login.SSHKey,
Location: nbpeer.Location{ConnectionIP: login.ConnectionIP},
}
return am.AddPeer(ctx, login.SetupKey, login.UserID, newPeer)
}
log.WithContext(ctx).Errorf("failed while logging in peer %s: %v", login.WireGuardPubKey, err)
return nil, nil, nil, status.Errorf(status.Internal, "failed while logging in peer")
return am.handlePeerLoginNotFound(ctx, login, err)
}
// when the client sends a login request with a JWT which is used to get the user ID,
@@ -754,6 +763,7 @@ func (am *DefaultAccountManager) LoginPeer(ctx context.Context, login PeerLogin)
var updateRemotePeers bool
var isRequiresApproval bool
var isStatusChanged bool
var isPeerUpdated bool
err = am.Store.ExecuteInTransaction(ctx, func(transaction Store) error {
peer, err = transaction.GetPeerByPeerPubKey(ctx, LockingStrengthUpdate, login.WireGuardPubKey)
@@ -795,8 +805,8 @@ func (am *DefaultAccountManager) LoginPeer(ctx context.Context, login PeerLogin)
return err
}
updated := peer.UpdateMetaIfNew(login.Meta)
if updated {
isPeerUpdated = peer.UpdateMetaIfNew(login.Meta)
if isPeerUpdated {
am.metrics.AccountManagerMetrics().CountPeerMetUpdate()
shouldStorePeer = true
}
@@ -821,7 +831,12 @@ func (am *DefaultAccountManager) LoginPeer(ctx context.Context, login PeerLogin)
unlockPeer()
unlockPeer = nil
if updateRemotePeers || isStatusChanged {
postureChecks, err := am.getPeerPostureChecks(account, peer.ID)
if err != nil {
return nil, nil, nil, err
}
if updateRemotePeers || isStatusChanged || (isPeerUpdated && len(postureChecks) > 0) {
am.updateAccountPeers(ctx, accountID)
}
+71 -14
View File
@@ -417,27 +417,84 @@ func (a *Account) getPeerRoutesFirewallRules(ctx context.Context, peerID string,
continue
}
policies := getAllRoutePoliciesFromGroups(a, route.AccessControlGroups)
for _, policy := range policies {
if !policy.Enabled {
continue
}
distributionPeers := a.getDistributionGroupsPeers(route)
for _, rule := range policy.Rules {
if !rule.Enabled {
continue
}
distributionGroupPeers, _ := a.getAllPeersFromGroups(ctx, route.Groups, peerID, nil, validatedPeersMap)
rules := generateRouteFirewallRules(ctx, route, rule, distributionGroupPeers, firewallRuleDirectionIN)
routesFirewallRules = append(routesFirewallRules, rules...)
}
for _, accessGroup := range route.AccessControlGroups {
policies := getAllRoutePoliciesFromGroups(a, []string{accessGroup})
rules := a.getRouteFirewallRules(ctx, peerID, policies, route, validatedPeersMap, distributionPeers)
routesFirewallRules = append(routesFirewallRules, rules...)
}
}
return routesFirewallRules
}
func (a *Account) getRouteFirewallRules(ctx context.Context, peerID string, policies []*Policy, route *route.Route, validatedPeersMap map[string]struct{}, distributionPeers map[string]struct{}) []*RouteFirewallRule {
var fwRules []*RouteFirewallRule
for _, policy := range policies {
if !policy.Enabled {
continue
}
for _, rule := range policy.Rules {
if !rule.Enabled {
continue
}
rulePeers := a.getRulePeers(rule, peerID, distributionPeers, validatedPeersMap)
rules := generateRouteFirewallRules(ctx, route, rule, rulePeers, firewallRuleDirectionIN)
fwRules = append(fwRules, rules...)
}
}
return fwRules
}
func (a *Account) getRulePeers(rule *PolicyRule, peerID string, distributionPeers map[string]struct{}, validatedPeersMap map[string]struct{}) []*nbpeer.Peer {
distPeersWithPolicy := make(map[string]struct{})
for _, id := range rule.Sources {
group := a.Groups[id]
if group == nil {
continue
}
for _, pID := range group.Peers {
if pID == peerID {
continue
}
_, distPeer := distributionPeers[pID]
_, valid := validatedPeersMap[pID]
if distPeer && valid {
distPeersWithPolicy[pID] = struct{}{}
}
}
}
distributionGroupPeers := make([]*nbpeer.Peer, 0, len(distPeersWithPolicy))
for pID := range distPeersWithPolicy {
peer := a.Peers[pID]
if peer == nil {
continue
}
distributionGroupPeers = append(distributionGroupPeers, peer)
}
return distributionGroupPeers
}
func (a *Account) getDistributionGroupsPeers(route *route.Route) map[string]struct{} {
distPeers := make(map[string]struct{})
for _, id := range route.Groups {
group := a.Groups[id]
if group == nil {
continue
}
for _, pID := range group.Peers {
distPeers[pID] = struct{}{}
}
}
return distPeers
}
func getDefaultPermit(route *route.Route) []*RouteFirewallRule {
var rules []*RouteFirewallRule
+128 -4
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"net"
"net/netip"
"sort"
"testing"
"time"
@@ -1486,6 +1487,8 @@ func TestAccount_getPeersRoutesFirewall(t *testing.T) {
peerBIp = "100.65.80.39"
peerCIp = "100.65.254.139"
peerHIp = "100.65.29.55"
peerJIp = "100.65.29.65"
peerKIp = "100.65.29.66"
)
account := &Account{
@@ -1541,6 +1544,16 @@ func TestAccount_getPeersRoutesFirewall(t *testing.T) {
IP: net.ParseIP(peerHIp),
Status: &nbpeer.PeerStatus{},
},
"peerJ": {
ID: "peerJ",
IP: net.ParseIP(peerJIp),
Status: &nbpeer.PeerStatus{},
},
"peerK": {
ID: "peerK",
IP: net.ParseIP(peerKIp),
Status: &nbpeer.PeerStatus{},
},
},
Groups: map[string]*nbgroup.Group{
"routingPeer1": {
@@ -1567,6 +1580,11 @@ func TestAccount_getPeersRoutesFirewall(t *testing.T) {
Name: "Route2",
Peers: []string{},
},
"route4": {
ID: "route4",
Name: "route4",
Peers: []string{},
},
"finance": {
ID: "finance",
Name: "Finance",
@@ -1584,6 +1602,28 @@ func TestAccount_getPeersRoutesFirewall(t *testing.T) {
"peerB",
},
},
"qa": {
ID: "qa",
Name: "QA",
Peers: []string{
"peerJ",
"peerK",
},
},
"restrictQA": {
ID: "restrictQA",
Name: "restrictQA",
Peers: []string{
"peerJ",
},
},
"unrestrictedQA": {
ID: "unrestrictedQA",
Name: "unrestrictedQA",
Peers: []string{
"peerK",
},
},
"contractors": {
ID: "contractors",
Name: "Contractors",
@@ -1631,6 +1671,19 @@ func TestAccount_getPeersRoutesFirewall(t *testing.T) {
Groups: []string{"contractors"},
AccessControlGroups: []string{},
},
"route4": {
ID: "route4",
Network: netip.MustParsePrefix("192.168.10.0/16"),
NetID: "route4",
NetworkType: route.IPv4Network,
PeerGroups: []string{"routingPeer1"},
Description: "Route4",
Masquerade: false,
Metric: 9999,
Enabled: true,
Groups: []string{"qa"},
AccessControlGroups: []string{"route4"},
},
},
Policies: []*Policy{
{
@@ -1685,6 +1738,49 @@ func TestAccount_getPeersRoutesFirewall(t *testing.T) {
},
},
},
{
ID: "RuleRoute4",
Name: "RuleRoute4",
Enabled: true,
Rules: []*PolicyRule{
{
ID: "RuleRoute4",
Name: "RuleRoute4",
Bidirectional: true,
Enabled: true,
Protocol: PolicyRuleProtocolTCP,
Action: PolicyTrafficActionAccept,
Ports: []string{"80"},
Sources: []string{
"restrictQA",
},
Destinations: []string{
"route4",
},
},
},
},
{
ID: "RuleRoute5",
Name: "RuleRoute5",
Enabled: true,
Rules: []*PolicyRule{
{
ID: "RuleRoute5",
Name: "RuleRoute5",
Bidirectional: true,
Enabled: true,
Protocol: PolicyRuleProtocolALL,
Action: PolicyTrafficActionAccept,
Sources: []string{
"unrestrictedQA",
},
Destinations: []string{
"route4",
},
},
},
},
},
}
@@ -1709,7 +1805,7 @@ func TestAccount_getPeersRoutesFirewall(t *testing.T) {
t.Run("check peer routes firewall rules", func(t *testing.T) {
routesFirewallRules := account.getPeerRoutesFirewallRules(context.Background(), "peerA", validatedPeers)
assert.Len(t, routesFirewallRules, 2)
assert.Len(t, routesFirewallRules, 4)
expectedRoutesFirewallRules := []*RouteFirewallRule{
{
@@ -1735,12 +1831,32 @@ func TestAccount_getPeersRoutesFirewall(t *testing.T) {
Port: 320,
},
}
assert.ElementsMatch(t, routesFirewallRules, expectedRoutesFirewallRules)
additionalFirewallRule := []*RouteFirewallRule{
{
SourceRanges: []string{
fmt.Sprintf(AllowedIPsFormat, peerJIp),
},
Action: "accept",
Destination: "192.168.10.0/16",
Protocol: "tcp",
Port: 80,
},
{
SourceRanges: []string{
fmt.Sprintf(AllowedIPsFormat, peerKIp),
},
Action: "accept",
Destination: "192.168.10.0/16",
Protocol: "all",
},
}
assert.ElementsMatch(t, orderRuleSourceRanges(routesFirewallRules), orderRuleSourceRanges(append(expectedRoutesFirewallRules, additionalFirewallRule...)))
// peerD is also the routing peer for route1, should contain same routes firewall rules as peerA
routesFirewallRules = account.getPeerRoutesFirewallRules(context.Background(), "peerD", validatedPeers)
assert.Len(t, routesFirewallRules, 2)
assert.ElementsMatch(t, routesFirewallRules, expectedRoutesFirewallRules)
assert.ElementsMatch(t, orderRuleSourceRanges(routesFirewallRules), orderRuleSourceRanges(expectedRoutesFirewallRules))
// peerE is a single routing peer for route 2 and route 3
routesFirewallRules = account.getPeerRoutesFirewallRules(context.Background(), "peerE", validatedPeers)
@@ -1769,7 +1885,7 @@ func TestAccount_getPeersRoutesFirewall(t *testing.T) {
IsDynamic: true,
},
}
assert.ElementsMatch(t, routesFirewallRules, expectedRoutesFirewallRules)
assert.ElementsMatch(t, orderRuleSourceRanges(routesFirewallRules), orderRuleSourceRanges(expectedRoutesFirewallRules))
// peerC is part of route1 distribution groups but should not receive the routes firewall rules
routesFirewallRules = account.getPeerRoutesFirewallRules(context.Background(), "peerC", validatedPeers)
@@ -1778,6 +1894,14 @@ func TestAccount_getPeersRoutesFirewall(t *testing.T) {
}
// orderList is a helper function to sort a list of strings
func orderRuleSourceRanges(ruleList []*RouteFirewallRule) []*RouteFirewallRule {
for _, rule := range ruleList {
sort.Strings(rule.SourceRanges)
}
return ruleList
}
func TestRouteAccountPeersUpdate(t *testing.T) {
manager, err := createRouterManager(t)
require.NoError(t, err, "failed to create account manager")