Skip to content

Commit

Permalink
support ANPs on live-cluster (#476)
Browse files Browse the repository at this point in the history
  • Loading branch information
shireenf-ibm authored Jan 16, 2025
1 parent b593622 commit 954e5b9
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 24 deletions.
1 change: 1 addition & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"dictionaries": [],
"words": [
"banp",
"Clientset",
"connlist",
"netpol",
"netpols",
Expand Down
14 changes: 12 additions & 2 deletions pkg/cli/evaluate.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (

utilerrors "k8s.io/apimachinery/pkg/util/errors"

"github.com/np-guard/netpol-analyzer/pkg/internal/common"
"github.com/np-guard/netpol-analyzer/pkg/internal/netpolerrors"
"github.com/np-guard/netpol-analyzer/pkg/logger"
"github.com/np-guard/netpol-analyzer/pkg/manifests/fsscanner"
Expand Down Expand Up @@ -123,8 +124,17 @@ func updatePolicyEngineObjectsFromDirPath(pe *eval.PolicyEngine, podNames []type

func updatePolicyEngineObjectsFromLiveCluster(pe *eval.PolicyEngine, podNames []types.NamespacedName, nsNames []string) error {
// get relevant resources from k8s live cluster
const ctxTimeoutSeconds = 3
ctx, cancel := context.WithTimeout(context.Background(), ctxTimeoutSeconds*time.Second)
// get command's relevant pods, namespaces and policies
err := updatePolicyEngineWithBasicK8sObjects(pe, podNames, nsNames)
if err != nil {
return err
}
// update the policy engine with (B)ANPs
return pe.UpdatePolicyEngineWithK8sPolicyAPIObjects(policyAPIClientset)
}

func updatePolicyEngineWithBasicK8sObjects(pe *eval.PolicyEngine, podNames []types.NamespacedName, nsNames []string) error {
ctx, cancel := context.WithTimeout(context.Background(), common.CtxTimeoutSeconds*time.Second)
defer cancel()

for _, name := range nsNames {
Expand Down
2 changes: 1 addition & 1 deletion pkg/cli/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func runListCommand() error {
if dirPath != "" {
conns, _, err = analyzer.ConnlistFromDirPath(dirPath)
} else {
conns, _, err = analyzer.ConnlistFromK8sCluster(clientset)
conns, _, err = analyzer.ConnlistFromK8sClusterWithPolicyAPI(clientset, policyAPIClientset)
}
if err != nil {
return err
Expand Down
14 changes: 10 additions & 4 deletions pkg/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/spf13/cobra"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
policyapi "sigs.k8s.io/network-policy-api/pkg/client/clientset/versioned"

"github.com/np-guard/netpol-analyzer/pkg/internal/netpolerrors"
"github.com/np-guard/netpol-analyzer/pkg/logger"
Expand All @@ -25,10 +26,11 @@ var (
// resources dir information
dirPath string
// k8s client
clientset *kubernetes.Clientset
quiet bool
verbose bool
stopOnFirstError bool
clientset *kubernetes.Clientset
policyAPIClientset *policyapi.Clientset
quiet bool
verbose bool
stopOnFirstError bool
)

// returns verbosity level based on the -q and -v switches
Expand Down Expand Up @@ -72,6 +74,10 @@ func newCommandRoot() *cobra.Command {
if err != nil {
return err
}
policyAPIClientset, err = policyapi.NewForConfig(k8sconf)
if err != nil {
return err
}
return nil
},
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/internal/common/netpol_constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ const (
// The Priority field in the ANP spec is defined as an integer value within the range 0 to 1000
MinANPPriority = 0
MaxANPPriority = 1000

CtxTimeoutSeconds = 3
)
2 changes: 1 addition & 1 deletion pkg/logger/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ type DefaultLogger struct {
l *log.Logger
}

const DefaultVerbosity = HighVerbosity
const DefaultVerbosity = MediumVerbosity

// NewDefaultLogger creates an instance of DefaultLogger with the highest verbosity.
func NewDefaultLogger() *DefaultLogger {
Expand Down
63 changes: 47 additions & 16 deletions pkg/netpol/connlist/connlist.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes"
policyapi "sigs.k8s.io/network-policy-api/pkg/client/clientset/versioned"

v1 "k8s.io/api/core/v1"

"k8s.io/cli-runtime/pkg/resource"

pkgcommon "github.com/np-guard/netpol-analyzer/pkg/internal/common"
"github.com/np-guard/netpol-analyzer/pkg/internal/netpolerrors"
"github.com/np-guard/netpol-analyzer/pkg/internal/output"
"github.com/np-guard/netpol-analyzer/pkg/logger"
Expand Down Expand Up @@ -220,47 +222,80 @@ func (ca *ConnlistAnalyzer) connsListFromParsedResources(objectsList []parser.K8
return ca.getConnectionsList(pe, ia)
}

// ConnlistFromK8sCluster returns the allowed connections list from k8s cluster resources, and list of all peers names
func (ca *ConnlistAnalyzer) ConnlistFromK8sCluster(clientset *kubernetes.Clientset) ([]Peer2PeerConnection, []Peer, error) {
pe := eval.NewPolicyEngineWithOptions(ca.exposureAnalysis)
// ConnlistFromK8sClusterWithPolicyAPI returns the allowed connections list from k8s cluster resources, and list of all peers names
func (ca *ConnlistAnalyzer) ConnlistFromK8sClusterWithPolicyAPI(clientset *kubernetes.Clientset,
policyAPIClientset *policyapi.Clientset) ([]Peer2PeerConnection, []Peer, error) {
pe, err := eval.NewPolicyEngineWithOptionsList(eval.WithLogger(ca.logger))
if ca.exposureAnalysis {
pe, err = eval.NewPolicyEngineWithOptionsList(eval.WithExposureAnalysis(), eval.WithLogger(ca.logger))
}
if err != nil {
return nil, nil, err
}
// insert namespaces, pods and network-policies from k8s clientset
err = updatePolicyEngineWithK8sBasicObjects(pe, clientset)
if err != nil {
return nil, nil, err
}

// get all resources from k8s cluster
ctx, cancel := context.WithTimeout(context.Background(), ctxTimeoutSeconds*time.Second)
defer cancel()
// insert admin policies from k8s policy-api clientset
err = pe.UpdatePolicyEngineWithK8sPolicyAPIObjects(policyAPIClientset)
if err != nil {
return nil, nil, err
}
return ca.getConnectionsList(pe, nil)
}

// updatePolicyEngineWithK8sBasicObjects inserts to the policy engine all k8s pods, namespaces and network-policies
func updatePolicyEngineWithK8sBasicObjects(pe *eval.PolicyEngine, clientset *kubernetes.Clientset) error {
ctx, cancel := context.WithTimeout(context.Background(), pkgcommon.CtxTimeoutSeconds*time.Second)
defer cancel()
// get all namespaces
nsList, apiErr := clientset.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
if apiErr != nil {
return nil, nil, apiErr
return apiErr
}
for i := range nsList.Items {
ns := &nsList.Items[i]
if err := pe.InsertObject(ns); err != nil {
return nil, nil, err
return err
}
}

// get all netpols
npList, apiErr := clientset.NetworkingV1().NetworkPolicies(metav1.NamespaceAll).List(ctx, metav1.ListOptions{})
if apiErr != nil {
return nil, nil, apiErr
return apiErr
}
for i := range npList.Items {
if err := pe.InsertObject(&npList.Items[i]); err != nil {
return nil, nil, err
return err
}
}

// get all pods
podList, apiErr := clientset.CoreV1().Pods(metav1.NamespaceAll).List(ctx, metav1.ListOptions{})
if apiErr != nil {
return nil, nil, apiErr
return apiErr
}
for i := range podList.Items {
if err := pe.InsertObject(&podList.Items[i]); err != nil {
return nil, nil, err
return err
}
}
return nil
}

// ConnlistFromK8sCluster returns the allowed connections list from k8s cluster resources, and list of all peers names
// Deprecated
func (ca *ConnlistAnalyzer) ConnlistFromK8sCluster(clientset *kubernetes.Clientset) ([]Peer2PeerConnection, []Peer, error) {
pe := eval.NewPolicyEngineWithOptions(ca.exposureAnalysis)

// insert namespaces, pods and network-policies from k8s clientset
err := updatePolicyEngineWithK8sBasicObjects(pe, clientset)
if err != nil {
return nil, nil, err
}

return ca.getConnectionsList(pe, nil)
}
Expand Down Expand Up @@ -316,10 +351,6 @@ func (ca *ConnlistAnalyzer) getFormatter() (connsFormatter, error) {
//////////////////////////////////////////////////////////////////////////////////////////////
// internal type definitions below

const (
ctxTimeoutSeconds = 3
)

// connection implements the Peer2PeerConnection interface
type connection struct {
src Peer
Expand Down
50 changes: 50 additions & 0 deletions pkg/netpol/eval/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,32 @@ SPDX-License-Identifier: Apache-2.0
package eval

import (
"context"
"errors"
"fmt"
"sort"
"time"

appsv1 "k8s.io/api/apps/v1"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
netv1 "k8s.io/api/networking/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
apisv1a "sigs.k8s.io/network-policy-api/apis/v1alpha1"
policyapi "sigs.k8s.io/network-policy-api/pkg/client/clientset/versioned"

"github.com/np-guard/models/pkg/netset"

pkgcommon "github.com/np-guard/netpol-analyzer/pkg/internal/common"
"github.com/np-guard/netpol-analyzer/pkg/internal/netpolerrors"
"github.com/np-guard/netpol-analyzer/pkg/logger"
"github.com/np-guard/netpol-analyzer/pkg/manifests/parser"
"github.com/np-guard/netpol-analyzer/pkg/netpol/eval/internal/k8s"
"github.com/np-guard/netpol-analyzer/pkg/netpol/internal/alerts"
"github.com/np-guard/netpol-analyzer/pkg/netpol/internal/common"
)

Expand Down Expand Up @@ -257,6 +263,50 @@ func (pe *PolicyEngine) sortAdminNetpolsByPriority() error {
return err
}

// UpdatePolicyEngineWithK8sPolicyAPIObjects inserts to the policy-engine all (baseline)admin network policies
func (pe *PolicyEngine) UpdatePolicyEngineWithK8sPolicyAPIObjects(clientset *policyapi.Clientset) error {
ctx, cancel := context.WithTimeout(context.Background(), pkgcommon.CtxTimeoutSeconds*time.Second)
defer cancel()
// get all admin-network-policies
anpList, apiErr := clientset.PolicyV1alpha1().AdminNetworkPolicies().List(ctx, metav1.ListOptions{})
if apiErr != nil {
// if the apiErr is of type "apierrors.IsNotFound";
// it means the api server could not find the requested resource (get adminnetworkpolicies.policy.networking.k8s.io); i.e. the
// cluster does not support this type of object (network-policy-api objects)
if apierrors.IsNotFound(apiErr) {
pe.logger.Debugf(alerts.K8sClusterDoesNotSupportNetworkPolicyAPI)
return nil // don't proceed this client is not used
}
return apiErr
}
for i := range anpList.Items {
if err := pe.InsertObject(&anpList.Items[i]); err != nil {
return err
}
}
// sort the admin-netpols by the priority - since their priority ordering is critic for computing allowed conns
err := pe.sortAdminNetpolsByPriority()
if err != nil {
return err
}
// get baseline-admin-netpol
banpList, apiErr := clientset.PolicyV1alpha1().BaselineAdminNetworkPolicies().List(ctx, metav1.ListOptions{})
if apiErr != nil {
if apierrors.IsNotFound(apiErr) { // even though it would not be reached; since if banp is not
// supported by the cluster; ANPs would not be supported too
pe.logger.Debugf(alerts.K8sClusterDoesNotSupportNetworkPolicyAPI)
return nil
}
return apiErr
}
for i := range banpList.Items {
if err := pe.InsertObject(&banpList.Items[i]); err != nil {
return err
}
}
return nil
}

func (pe *PolicyEngine) resolveMissingNamespaces() error {
for _, pod := range pe.podsMap {
ns := pod.Namespace
Expand Down
2 changes: 2 additions & 0 deletions pkg/netpol/internal/alerts/warnings.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ const (
WarnPrefixPortName = "port name: "
WarnNamedPortIgnoredForIP = "named port is not defined for IP addresses; skipped"
// example raising this warning: tests/anp_test_named_ports_multiple_peers

K8sClusterDoesNotSupportNetworkPolicyAPI = "cluster does not support admin network policies"
)

var (
Expand Down

0 comments on commit 954e5b9

Please sign in to comment.