-
Notifications
You must be signed in to change notification settings - Fork 169
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
1,420 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
package nsg | ||
|
||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the Apache License 2.0. | ||
|
||
import ( | ||
"context" | ||
"net/http" | ||
"net/netip" | ||
"strings" | ||
|
||
"github.com/sirupsen/logrus" | ||
|
||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" | ||
mgmtnetwork "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2020-08-01/network" | ||
|
||
"github.com/Azure/ARO-RP/pkg/api" | ||
"github.com/Azure/ARO-RP/pkg/metrics" | ||
"github.com/Azure/ARO-RP/pkg/util/azureclient" | ||
"github.com/Azure/ARO-RP/pkg/util/azureclient/mgmt/network" | ||
"github.com/Azure/ARO-RP/pkg/util/refreshable" | ||
) | ||
|
||
const ( | ||
dimSubnet = "subnet" | ||
dimVnet = "vNet" | ||
dimResourceGroup = "resourceGroup" | ||
dimSubscriptionID = "subscriptionID" | ||
dimSecurityGroup = "networkSecurityGroup" | ||
dimNSGRuleName = "ruleName" | ||
dimNSGRuleSources = "sources" | ||
dimNSGRuleDestinations = "destinations" | ||
dimNSGRuleDirection = "direction" | ||
dimNSGRulePriority = "priority" | ||
dimClusterResourceID = "clusterresourceid" | ||
dimLocation = "location" | ||
|
||
metricInvalidDenyRule = "preconfigurednsg.invalid.denyrule" | ||
metricSubnetAcessResponseCode = "preconfiguredNSG.subnetaccess.responsecode" | ||
metricSubnetAccessForbidden = "preconfigurednsg.subnetaccess.forbidden" | ||
|
||
expandNSG = "NetworkSecurityGroup" | ||
) | ||
|
||
// NSGMonitor is responsible for performing NSG rule validations when preconfiguredNSG is enabled | ||
type NSGMonitor struct { | ||
log *logrus.Entry | ||
emitter metrics.Emitter | ||
oc *api.OpenShiftCluster | ||
|
||
subnetCli network.SubnetsClient | ||
} | ||
|
||
func NewNSGMonitor(log *logrus.Entry, oc *api.OpenShiftCluster, subscriptionID string, environment *azureclient.AROEnvironment, cred refreshable.Authorizer, emitter metrics.Emitter) *NSGMonitor { | ||
return &NSGMonitor{ | ||
log: log, | ||
emitter: emitter, | ||
oc: oc, | ||
|
||
subnetCli: network.NewSubnetsClient(environment, subscriptionID, cred), | ||
} | ||
} | ||
|
||
type subnetNSGConfig struct { | ||
// subnet CIDR range | ||
prefix netip.Prefix | ||
// The rules from the subnet NSG | ||
nsg *mgmtnetwork.SecurityGroup | ||
} | ||
|
||
func (n *NSGMonitor) toSubnetConfig(ctx context.Context, subnetID string) (subnetNSGConfig, error) { | ||
r, err := arm.ParseResourceID(subnetID) | ||
if err != nil { | ||
return subnetNSGConfig{}, err | ||
} | ||
|
||
dims := map[string]string{ | ||
dimClusterResourceID: n.oc.ID, | ||
dimLocation: n.oc.Location, | ||
dimSubnet: r.Name, | ||
dimVnet: r.Parent.Name, | ||
dimResourceGroup: r.ResourceGroupName, | ||
dimSubscriptionID: r.SubscriptionID, | ||
} | ||
|
||
subnet, err := n.subnetCli.Get(ctx, r.ResourceGroupName, r.Parent.Name, r.Name, expandNSG) | ||
// response code is always returned as part of autorest | ||
n.emitter.EmitGauge(metricSubnetAcessResponseCode, int64(subnet.StatusCode), dims) | ||
if err != nil { | ||
if subnet.StatusCode == http.StatusForbidden { | ||
n.emitter.EmitGauge(metricSubnetAccessForbidden, int64(1), dims) | ||
} | ||
n.log.Errorf("Error while getting subnet %s. %s", subnetID, err) | ||
return subnetNSGConfig{}, err | ||
} | ||
|
||
cidr, err := toPrefix(*subnet.AddressPrefix) | ||
if err != nil { | ||
return subnetNSGConfig{}, err | ||
} | ||
return subnetNSGConfig{cidr, subnet.NetworkSecurityGroup}, nil | ||
} | ||
|
||
func (n *NSGMonitor) Monitor(ctx context.Context) error { | ||
masterSubnet, err := n.toSubnetConfig(ctx, n.oc.Properties.MasterProfile.SubnetID) | ||
if err != nil { | ||
// FP has no access to the subnet | ||
return err | ||
} | ||
|
||
// need this to get the right workerProfiles | ||
workerProfiles, _ := api.GetEnrichedWorkerProfiles(n.oc.Properties) | ||
workerSubnets := make([]subnetNSGConfig, 0, len(workerProfiles)) | ||
workerPrefixes := make([]netip.Prefix, 0, len(workerProfiles)) | ||
for _, wp := range workerProfiles { | ||
s, err := n.toSubnetConfig(ctx, wp.SubnetID) | ||
if err != nil { | ||
// FP has no access to the subnet | ||
return err | ||
} | ||
workerSubnets = append(workerSubnets, s) | ||
workerPrefixes = append(workerPrefixes, s.prefix) | ||
} | ||
|
||
// to make sure each NSG is processed only once | ||
nsgSet := map[string]*mgmtnetwork.SecurityGroup{ | ||
*masterSubnet.nsg.ID: masterSubnet.nsg, | ||
} | ||
for _, w := range workerSubnets { | ||
nsgSet[*w.nsg.ID] = w.nsg | ||
} | ||
|
||
for nsgID, nsg := range nsgSet { | ||
for _, rule := range *nsg.SecurityRules { | ||
if rule.Access == mgmtnetwork.SecurityRuleAccessAllow { | ||
// Allow rule - skip. | ||
continue | ||
} | ||
// Deny rule | ||
nsgResource, err := arm.ParseResourceID(nsgID) | ||
if err != nil { | ||
n.log.Errorf("Unable to parse NSG resource ID: %s. %s", nsgID, err) | ||
continue | ||
} | ||
|
||
r := newRuleChecker(n.log, masterSubnet.prefix, workerPrefixes, &rule) | ||
|
||
if r.isInvalidDenyRule() { | ||
dims := map[string]string{ | ||
dimClusterResourceID: n.oc.ID, | ||
dimLocation: n.oc.Location, | ||
dimSubscriptionID: nsgResource.SubscriptionID, | ||
dimResourceGroup: nsgResource.ResourceGroupName, | ||
dimSecurityGroup: nsgResource.Name, | ||
dimNSGRuleName: *rule.Name, | ||
dimNSGRuleSources: strings.Join(r.sourceStrings, ","), | ||
dimNSGRuleDestinations: strings.Join(r.destinationStrings, ","), | ||
dimNSGRuleDirection: string(rule.Direction), | ||
dimNSGRulePriority: string(*rule.Priority), | ||
} | ||
n.emitter.EmitGauge(metricInvalidDenyRule, int64(1), dims) | ||
} | ||
} | ||
} | ||
return nil | ||
} |
Oops, something went wrong.