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

support exposure analysis + tests #8

Draft
wants to merge 15 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions roxctl/netpol/connectivity/map/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ The `--focus-workload` parameter allows specifying a workload name, such that th
The supported formats for the input workload name are: `name` or `namespace/name`. For example, if the workload of interest from the report is `default/backend[Deployment]`, the input workload specified can be `--focus-workload=default/backend` or `--focus-workload=backend`. In addition, to focus connections inferred from Route/Ingress resources, the specified workload can be `--focus-workload=ingress-controller`.
If the input `focus-workload` value speifies a workload name that does not exist in the input resources YAML manifests, the netpol connectivity map output will be empty.

The `--exposure` parameter prints the permitted connectivity with additional information of potential permitted connectivity to workload entities that may be matched by policy rules, but do not have a match in the input manifests. More details about exposure analysis and its output are [here](./exposure_analysis.md)

When running in a CI pipeline, `roxctl netpol connectivity map` may benefit from the `--fail` option that stops the processing on the first encountered error.

Using the `--strict` parameter produces an error "_there were errors during execution_" if any warnings appeared during the processing. Note that the combination of `--strict` and `--fail` will not stop on the first warning, as the interpretation of warnings as errors happens at the end of execution.
2 changes: 2 additions & 0 deletions roxctl/netpol/connectivity/map/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Cmd struct {
outputToFile bool
focusWorkload string
outputFormat string
exposure bool

// injected or constructed values
env environment.Environment
Expand Down Expand Up @@ -84,5 +85,6 @@ func (cmd *Cmd) AddFlags(c *cobra.Command) *cobra.Command {
c.Flags().StringVarP(&cmd.outputFilePath, "output-file", "f", "", "Save connections list output into specific file")
c.Flags().StringVarP(&cmd.focusWorkload, "focus-workload", "", "", "Focus on connections of specified workload name in the output")
c.Flags().StringVarP(&cmd.outputFormat, "output-format", "o", defaultOutputFormat, "Configure the connections list in specific format, supported formats: txt|json|md|dot|csv")
c.Flags().BoolVar(&cmd.exposure, "exposure", false, "Enhance the analysis of permitted connectivity with exposure analysis")
return c
}
67 changes: 67 additions & 0 deletions roxctl/netpol/connectivity/map/exposure-graph-example.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
68 changes: 68 additions & 0 deletions roxctl/netpol/connectivity/map/exposure_analysis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Exposure analysis

The goal of exposure analysis, is to provide additional information of potential permitted connectivity to the output of `roxctl netpol connectivity map` command.

The report contains hints to where network policies may be tightened, or help validate that no unexpected exposure is present due to policies misconfiguration.

Exposure-analysis is supported with `txt, md, csv, json, dot` output formats.

### Textual Output Example

Example run with `txt` output to `stdout`:
```shell
$ roxctl netpol connectivity map --exposure tests/roxctl/bats-tests/test-data/np-guard/exposure-example
hello-world/workload-a[Deployment] => 0.0.0.0-255.255.255.255 : All Connections

Exposure Analysis Result:
Egress Exposure:
hello-world/workload-a[Deployment] => 0.0.0.0-255.255.255.255 : All Connections
hello-world/workload-a[Deployment] => entire-cluster : All Connections

Ingress Exposure:
hello-world/workload-a[Deployment] <= [namespace with {effect=NoSchedule}]/[pod with {role=monitoring}] : TCP 8050

Workloads not protected by network policies:
hello-world/workload-a[Deployment] is not protected on Egress
```

#### Understanding the output

The results of exposure analysis are reported in a new `Exposure Analysis Result` section, which contains three parts:
1. `Egress Exposure`: provides potentially permitted egress exposure of input workloads;
each line is of the format `src => dst : connnectivity-attributes` where:
- `src` is a workload from the input manifests.
- `dst` is an entity represented by namespace and pod labels that appear in policy rules, but do not have a match in the input workload resources;\
note that:
- if `dst` represents all possible namespaces and pods in the cluster `entire-cluster` label is used.
- if `dst` represents possible external entity(ies), IP addresses range is used
- `connnectivity-attributes` the potentially permitted egress connection from src to dst.
2. `Ingress Exposure`: provides the potentially permitted ingress exposure of input workloads;
each line is of the format `dst <= src : connnectivity-attributes` where:
- `dst` is a workload from the input manifests.
- `src` is an entity represented by namespace and pod labels that were matched by policy rules, but do not have a match in the input workload resources;\
note that:
- if `src` represents all possible namespaces and pods in the cluster `entire-cluster` label is used.
- if `src` represents possible external entity(ies), IP addresses range is used
- `connnectivity-attributes` the potentially permitted ingress connection from src to dst.
3. `Workloads not protected by network policies`: lists input workloads which are not selected by any input network-policy that affects the mentioned direction, and thus exposed widely.\
Note that those workloads appear also in the exposure section of that direction within two exposure lines; since an unprotected workload is exposed to whole world, i.e: all external addresses `0.0.0.0-255.255.255.255` and `entire-cluster`.

### Visual Output Example:

The following graph is produced from the output of `$ roxctl netpol connectivity map --exposure tests/roxctl/bats-tests/test-data/np-guard/exposure-example -o dot`:

![graph](exposure-graph-example.svg)

#### Understanding the graph

- `gold` edges are edges produced by the original connectivity map report
- `blue` nodes and labels represent input workloads.
- `black` frames and labels represent namespaces of input manifests
- `red` nodes and labels are used to represent:
- representative entities which are matched by policy rules, but do not exist in the input manifests
- external IP addresses
- `red` diamond with an `entire-cluster` label inside is used to represent the entire cluster entity
- `dashed orange` edges represent ingress connections which are reported by running exposure analysis
- `dashed dark orange` edges represent egress connections which are reported by running exposure analysis


3 changes: 3 additions & 0 deletions roxctl/netpol/connectivity/map/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ func (cmd *Cmd) construct(args []string) (netpolAnalyzer, error) {
if cmd.outputFilePath != "" {
cmd.outputToFile = true
}
if cmd.exposure {
opts = append(opts, npguard.WithExposureAnalysis())
}
return npguard.NewConnlistAnalyzer(opts...), nil
}

Expand Down
16 changes: 14 additions & 2 deletions roxctl/netpol/connectivity/map/map_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func (d *connectivityMapTestSuite) TestAnalyzeNetpol() {
focusWorkload string
outputFormat string
removeOutputPath bool
exposure bool

expectedErrors []string
expectedWarnings []string
Expand Down Expand Up @@ -158,6 +159,13 @@ func (d *connectivityMapTestSuite) TestAnalyzeNetpol() {
expectedWarnings: []string{},
outputToFile: true,
},
"generate connections list with exposure analysis": {
inputFolderPath: "testdata/acs-security-demos",
expectedErrors: []string{},
expectedWarnings: []string{},
outputToFile: true,
exposure: true,
},
}

for name, tt := range cases {
Expand All @@ -173,6 +181,7 @@ func (d *connectivityMapTestSuite) TestAnalyzeNetpol() {
outputToFile: tt.outputToFile,
focusWorkload: tt.focusWorkload,
outputFormat: tt.outputFormat,
exposure: tt.exposure,
env: env,
}

Expand All @@ -199,8 +208,11 @@ func (d *connectivityMapTestSuite) TestAnalyzeNetpol() {
}
output, err := os.ReadFile(defaultFile)
d.Assert().NoError(err)

expectedOutput, err := os.ReadFile(path.Join(tt.inputFolderPath, "output."+formatSuffix))
expectedOutputFileName := "output." + formatSuffix
if tt.exposure {
expectedOutputFileName = "exposure_output." + formatSuffix
}
expectedOutput, err := os.ReadFile(path.Join(tt.inputFolderPath, expectedOutputFileName))
d.Assert().NoError(err)
d.Equal(string(expectedOutput), string(output))

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
backend/checkout[Deployment] => backend/notification[Deployment] : TCP 8080
backend/checkout[Deployment] => backend/recommendation[Deployment] : TCP 8080
backend/checkout[Deployment] => payments/gateway[Deployment] : TCP 8080
backend/recommendation[Deployment] => backend/catalog[Deployment] : TCP 8080
backend/reports[Deployment] => backend/catalog[Deployment] : TCP 8080
backend/reports[Deployment] => backend/recommendation[Deployment] : TCP 8080
frontend/webapp[Deployment] => backend/checkout[Deployment] : TCP 8080
frontend/webapp[Deployment] => backend/recommendation[Deployment] : TCP 8080
frontend/webapp[Deployment] => backend/reports[Deployment] : TCP 8080
frontend/webapp[Deployment] => backend/shipping[Deployment] : TCP 8080
payments/gateway[Deployment] => payments/mastercard-processor[Deployment] : TCP 8080
payments/gateway[Deployment] => payments/visa-processor[Deployment] : TCP 8080
{ingress-controller} => frontend/asset-cache[Deployment] : TCP 8080
{ingress-controller} => frontend/webapp[Deployment] : TCP 8080

Exposure Analysis Result:
Egress Exposure:
backend/checkout[Deployment] => entire-cluster : UDP 5353
backend/recommendation[Deployment] => entire-cluster : UDP 5353
backend/reports[Deployment] => entire-cluster : UDP 5353
frontend/webapp[Deployment] => entire-cluster : UDP 5353
payments/gateway[Deployment] => entire-cluster : UDP 5353

Ingress Exposure:
frontend/asset-cache[Deployment] <= entire-cluster : TCP 8080
frontend/webapp[Deployment] <= entire-cluster : TCP 8080
Loading
Loading