Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

explainability #459

Open
wants to merge 35 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
4684411
explainability
tanyaveksler Dec 10, 2024
8217a4b
More delicate handling of intersection of ingress and egress connecti…
tanyaveksler Dec 10, 2024
1e7cbb9
make linter happy
tanyaveksler Dec 23, 2024
faa7ad7
Merge branch 'main' into explainability_new_clean
tanyaveksler Dec 23, 2024
181b9ac
fixing lint errors
tanyaveksler Dec 23, 2024
67ef886
Merge branch 'main' into explainability_new_clean
tanyaveksler Dec 23, 2024
d50485b
More delicate handlinng of IPblock default connections explanation;
tanyaveksler Dec 23, 2024
cd17f64
More compact explainability print
tanyaveksler Jan 6, 2025
326a88e
Fixed lint error
tanyaveksler Jan 6, 2025
4395e77
Merge branch 'main' into explainability_new_clean
tanyaveksler Jan 6, 2025
929c75d
More compact explanation printing.
tanyaveksler Jan 6, 2025
ffe247c
Added more explainability tests.
tanyaveksler Jan 6, 2025
520ef12
Added more explainability tests.
tanyaveksler Jan 7, 2025
9c947f6
Merge branch 'main' into explainability_new_clean
tanyaveksler Jan 7, 2025
409629a
Added a comment
tanyaveksler Jan 7, 2025
48ceb56
Sorting together explanations for specific ports and for all ports in…
tanyaveksler Jan 7, 2025
4686e3e
Added more tests
tanyaveksler Jan 7, 2025
710077a
Added more delicate collect/override implying rules mechanism (consid…
tanyaveksler Jan 13, 2025
49050df
Make linter happy
tanyaveksler Jan 13, 2025
d78e3c4
Changed explanation output for empty Xgress rules;
tanyaveksler Jan 14, 2025
4ab3d96
make linter happy;
tanyaveksler Jan 14, 2025
2f52f7b
make linter happy
tanyaveksler Jan 14, 2025
76c3876
Merge branch 'main' into explainability_new_clean
tanyaveksler Jan 14, 2025
ae74452
changed names of pods in anp_banp_blog_demo test
tanyaveksler Jan 14, 2025
3385cb1
Changed named of pods in anp_banp_blog_demo_2 (according to the chang…
tanyaveksler Jan 14, 2025
2a86aeb
Merge branch 'main' into explainability_new_clean
tanyaveksler Jan 20, 2025
718b34d
Changed default IP block conections explanation to be the same as sys…
tanyaveksler Jan 20, 2025
44f6c94
Added explanability option documtation to REAME
tanyaveksler Jan 20, 2025
2328c54
Removed unused functions / code.
tanyaveksler Jan 20, 2025
8cb665d
Small change in explanability formatting; changed expected results ac…
tanyaveksler Jan 21, 2025
00c229b
Another change in explanation output formatting and in the expected o…
tanyaveksler Jan 21, 2025
3057057
Make linter happy.
tanyaveksler Jan 27, 2025
006dc26
Merge branch 'main' into explainability_new_clean
tanyaveksler Jan 27, 2025
78bf5fe
Added more tests.
tanyaveksler Jan 27, 2025
ac7f587
Fixed explanation handling for ingress controller.
tanyaveksler Jan 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ Examples:
k8snetpolicy list -k ./kube/config

Flags:
--explain Enhance the analysis of permitted connectivity with explainability information
--exposure Enhance the analysis of permitted connectivity with exposure analysis
-f, --file string Write output to specified file
--focusworkload string Focus connections of specified workload in the output (<workload-name> or <workload-namespace/workload-name>)
Expand Down Expand Up @@ -151,6 +152,8 @@ source: default/redis-cart[Deployment], destination: 0.0.0.0-255.255.255.255, di

Additional details about the connectivity analysis and its output is specified [here](docs/connlist_output.md).

Additional details about explainability analysis (`--explain` flag for the `list` command) is specified [here](docs/explain_analysis.md).

Additional details about exposure analysis (`--exposure` flag for the `list` command) is specified [here](docs/exposure_analysis.md).

Additional details about the connectivity diff command and its output is specified [here](docs/diff_output.md).
Expand Down
301 changes: 301 additions & 0 deletions docs/explain_analysis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
# Explain analysis - enhance `list` connectivity analysis

## Motivation

`list` without `--exposure`, produces a report of permitted connectivity between pairs of nodes, without an explanation what resources contributed to this connectivity being allowed.\
Likewise, it does not detail neither explain all denied connectivity.

The goal of explainability analysis is to provide this additional information, specifying the resources (such as network policies, admin network policies, routes and more) that contributed to allowing or denying a connectivity between any pair of nodes.
This report can help testing whether the configured resources induce connectivity as expected, and give hints to where the resources may be changed to
achieve the desired result.

The explainability analysis is currently supported for txt output format of the `list` command.
To run explainability analysis, just run the `list` command with the additional `--explain` flag.

The section below details a comprehensive example of input manifests for workloads and network policies, and shows the output result of explainability analysis.


## Example

### Input Manifests:
`Namespaces and Pods`:
```
---
apiVersion: v1
kind: Namespace
metadata:
name: foo
labels:
security: internal
kubernetes.io/metadata.name: foo

---
apiVersion: v1
kind: Namespace
metadata:
name: bar
labels:
security: internal
kubernetes.io/metadata.name: bar

---
apiVersion: v1
kind: Namespace
metadata:
name: baz
labels:
kubernetes.io/metadata.name: baz

---
apiVersion: v1
kind: Namespace
metadata:
name: monitoring
labels:
kubernetes.io/metadata.name: monitoring

---
apiVersion: v1
kind: Pod
metadata:
namespace: foo
name: myfoo
labels:
security: internal
spec:
containers:
- name: myfirstcontainer
image: fooimage

---
apiVersion: v1
kind: Pod
metadata:
namespace: bar
name: mybar
labels:
security: internal
spec:
containers:
- name: myfirstcontainer
image: barimage

---
apiVersion: v1
kind: Pod
metadata:
namespace: baz
name: mybaz
labels:
security: none
spec:
containers:
- name: myfirstcontainer
image: bazimage

---
apiVersion: v1
kind: Pod
metadata:
namespace: monitoring
name: mymonitoring
labels:
security: monitoring
spec:
containers:
- name: myfirstcontainer
image: monitoringimage

```

`NetworkPolicy`:
```
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-monitoring
namespace: foo
spec:
podSelector:
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: monitoring
```

`BaselineAdminNetworkPolicy`:
```
apiVersion: policy.networking.k8s.io/v1alpha1
kind: BaselineAdminNetworkPolicy
metadata:
name: default
spec:
subject:
namespaces:
matchLabels:
security: internal
ingress:
- name: "deny-ingress-from-all-namespaces-on-TCP1-9000"
action: "Deny"
from:
- namespaces:
matchLabels:
kubernetes.io/metadata.name: monitoring
ports:
- portRange:
protocol: TCP
start: 1
end: 9000
```

`AdminNetworkPolicies`:
```
apiVersion: policy.networking.k8s.io/v1alpha1
kind: AdminNetworkPolicy
metadata:
name: allow-monitoring
spec:
priority: 9
subject:
namespaces: {}
ingress:
- name: "allow-ingress-from-monitoring-on-TCP1234"
action: "Allow"
from:
- namespaces:
matchLabels:
kubernetes.io/metadata.name: monitoring
ports:
- portNumber:
protocol: TCP
port: 1234

---
apiVersion: policy.networking.k8s.io/v1alpha1
kind: AdminNetworkPolicy
metadata:
name: pass-monitoring
spec:
priority: 7
subject:
namespaces:
matchLabels:
security: internal
ingress:
- name: "pass-ingress-from-monitoring-on-TCP8080"
action: "Pass"
from:
- namespaces:
matchLabels:
kubernetes.io/metadata.name: monitoring
ports:
- portNumber:
protocol: TCP
port: 8080

```
#### Textual Result:
```
----------------------------------------------------------------------------------------------------------------------------------------------------------------
CONNECTIONS BETWEEN 0.0.0.0-255.255.255.255 => foo/myfoo[Pod]:

No Connections due to the following policies//rules:
EGRESS DIRECTION (ALLOWED) due to the system default (Allow all)
INGRESS DIRECTION (DENIED)
1) [NP] foo/allow-monitoring//Ingress (captured but not selected by any Ingress rule)

----------------------------------------------------------------------------------------------------------------------------------------------------------------
CONNECTIONS BETWEEN bar/mybar[Pod] => foo/myfoo[Pod]:

No Connections due to the following policies//rules:
EGRESS DIRECTION (ALLOWED) due to the system default (Allow all)
INGRESS DIRECTION (DENIED)
1) [NP] foo/allow-monitoring//Ingress (captured but not selected by any Ingress rule)

----------------------------------------------------------------------------------------------------------------------------------------------------------------
CONNECTIONS BETWEEN baz/mybaz[Pod] => foo/myfoo[Pod]:

No Connections due to the following policies//rules:
EGRESS DIRECTION (ALLOWED) due to the system default (Allow all)
INGRESS DIRECTION (DENIED)
1) [NP] foo/allow-monitoring//Ingress (captured but not selected by any Ingress rule)

----------------------------------------------------------------------------------------------------------------------------------------------------------------
CONNECTIONS BETWEEN monitoring/mymonitoring[Pod] => bar/mybar[Pod]:

ALLOWED TCP:[1234] due to the following policies//rules:
EGRESS DIRECTION (ALLOWED) due to the system default (Allow all)
INGRESS DIRECTION (ALLOWED)
1) [ANP] allow-monitoring//Ingress rule allow-ingress-from-monitoring-on-TCP1234 (Allow)

ALLOWED TCP:[9001-65535] the system default (Allow all)

ALLOWED {SCTP,UDP}:[ALL PORTS] the system default (Allow all)

DENIED TCP:[1-1233,1235-8079,8081-9000] due to the following policies//rules:
EGRESS DIRECTION (ALLOWED) due to the system default (Allow all)
INGRESS DIRECTION (DENIED)
1) [BANP] default//Ingress rule deny-ingress-from-all-namespaces-on-TCP1-9000 (Deny)

DENIED TCP:[8080] due to the following policies//rules:
EGRESS DIRECTION (ALLOWED) due to the system default (Allow all)
INGRESS DIRECTION (DENIED)
1) [ANP] pass-monitoring//Ingress rule pass-ingress-from-monitoring-on-TCP8080 (Pass)
2) [BANP] default//Ingress rule deny-ingress-from-all-namespaces-on-TCP1-9000 (Deny)

----------------------------------------------------------------------------------------------------------------------------------------------------------------
CONNECTIONS BETWEEN monitoring/mymonitoring[Pod] => baz/mybaz[Pod]:

ALLOWED TCP:[1-1233,1235-65535] the system default (Allow all)

ALLOWED TCP:[1234] due to the following policies//rules:
EGRESS DIRECTION (ALLOWED) due to the system default (Allow all)
INGRESS DIRECTION (ALLOWED)
1) [ANP] allow-monitoring//Ingress rule allow-ingress-from-monitoring-on-TCP1234 (Allow)

ALLOWED {SCTP,UDP}:[ALL PORTS] the system default (Allow all)

----------------------------------------------------------------------------------------------------------------------------------------------------------------
CONNECTIONS BETWEEN monitoring/mymonitoring[Pod] => foo/myfoo[Pod]:

ALLOWED TCP:[1-1233,1235-8079,8081-65535] due to the following policies//rules:
EGRESS DIRECTION (ALLOWED) due to the system default (Allow all)
INGRESS DIRECTION (ALLOWED)
1) [NP] foo/allow-monitoring//Ingress rule #1

ALLOWED TCP:[1234] due to the following policies//rules:
EGRESS DIRECTION (ALLOWED) due to the system default (Allow all)
INGRESS DIRECTION (ALLOWED)
1) [ANP] allow-monitoring//Ingress rule allow-ingress-from-monitoring-on-TCP1234 (Allow)

ALLOWED TCP:[8080] due to the following policies//rules:
EGRESS DIRECTION (ALLOWED) due to the system default (Allow all)
INGRESS DIRECTION (ALLOWED)
1) [ANP] pass-monitoring//Ingress rule pass-ingress-from-monitoring-on-TCP8080 (Pass)
2) [NP] foo/allow-monitoring//Ingress rule #1

ALLOWED {SCTP,UDP}:[ALL PORTS] due to the following policies//rules:
EGRESS DIRECTION (ALLOWED) due to the system default (Allow all)
INGRESS DIRECTION (ALLOWED)
1) [NP] foo/allow-monitoring//Ingress rule #1

----------------------------------------------------------------------------------------------------------------------------------------------------------------
The following nodes are connected due to the system default (Allow all):
0.0.0.0-255.255.255.255 => bar/mybar[Pod]
0.0.0.0-255.255.255.255 => baz/mybaz[Pod]
0.0.0.0-255.255.255.255 => monitoring/mymonitoring[Pod]
bar/mybar[Pod] => 0.0.0.0-255.255.255.255
bar/mybar[Pod] => baz/mybaz[Pod]
bar/mybar[Pod] => monitoring/mymonitoring[Pod]
baz/mybaz[Pod] => 0.0.0.0-255.255.255.255
baz/mybaz[Pod] => bar/mybar[Pod]
baz/mybaz[Pod] => monitoring/mymonitoring[Pod]
foo/myfoo[Pod] => 0.0.0.0-255.255.255.255
foo/myfoo[Pod] => bar/mybar[Pod]
foo/myfoo[Pod] => baz/mybaz[Pod]
foo/myfoo[Pod] => monitoring/mymonitoring[Pod]
monitoring/mymonitoring[Pod] => 0.0.0.0-255.255.255.255
```
5 changes: 5 additions & 0 deletions pkg/cli/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
var (
focusWorkload string
exposureAnalysis bool
explain bool
output string // output format
outFile string // output file
)
Expand Down Expand Up @@ -85,6 +86,9 @@ func getConnlistOptions(l *logger.DefaultLogger) []connlist.ConnlistAnalyzerOpti
if exposureAnalysis {
res = append(res, connlist.WithExposureAnalysis())
}
if explain {
res = append(res, connlist.WithExplanation())
}
return res
}

Expand Down Expand Up @@ -130,6 +134,7 @@ defined`,
c.Flags().StringVarP(&focusWorkload, "focusworkload", "", "",
"Focus connections of specified workload in the output (<workload-name> or <workload-namespace/workload-name>)")
c.Flags().BoolVarP(&exposureAnalysis, "exposure", "", false, "Enhance the analysis of permitted connectivity with exposure analysis")
c.Flags().BoolVarP(&explain, "explain", "", false, "Enhance the analysis of permitted connectivity with explainability information")
// output format - default txt
// output format - default txt
supportedFormats := strings.Join(connlist.ValidFormats, ",")
Expand Down
13 changes: 13 additions & 0 deletions pkg/internal/testutils/testutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ var update = flag.Bool("update", false, "write or override golden files")

const (
connlistExpectedOutputFilePartialName = "connlist_output."
explainExpectedOutputFilePartialName = "explain_output."
exposureExpectedOutputFilePartialName = "exposure_output."
underscore = "_"
dotSign = "."
Expand Down Expand Up @@ -56,6 +57,18 @@ func ConnlistTestNameByTestArgs(dirName, focusWorkload, format string, exposureF
return testName, expectedOutputFileName
}

// ExplainTestNameByTestArgs returns explain test name and test's expected output file from some tests args
func ExplainTestNameByTestArgs(dirName, focusWorkload string) (testName, expectedOutputFileName string) {
namePrefix := dirName
if focusWorkload != "" {
namePrefix += focusWlAnnotation + strings.Replace(focusWorkload, "/", underscore, 1)
}
testName = namePrefix
outputPartialName := explainExpectedOutputFilePartialName
expectedOutputFileName = namePrefix + underscore + outputPartialName + output.TextFormat
return testName, expectedOutputFileName
}

// DiffTestNameByTestArgs returns diff test name and test's expected output file from some tests args
func DiffTestNameByTestArgs(ref1, ref2, format string) (testName, expectedOutputFileName string) {
namePrefix := "diff_between_" + ref2 + "_and_" + ref1
Expand Down
Loading