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

add cli tests and update readme #64

Merged
merged 5 commits into from
Jan 17, 2023
Merged
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
124 changes: 123 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,124 @@
# netpol-analyzer
A Golang library for analyzing connectivity-configuration resources (a.k.a. network policies)

## About netpol-analyzer
This repo contains a Golang library and CLI for analyzing k8s connectivity-configuration resources (a.k.a. network policies).


## CLI usage

### Evaluate command
```
Evaluate if a specific connection allowed

Usage:
k8snetpolicy evaluate [flags]

Aliases:
evaluate, eval, check, allow

Examples:
# Evaluate if a specific connection is allowed on given resources from dir path
k8snetpolicy eval --dirpath ./resources_dir/ -s pod-1 -d pod-2 -p 80

# Evaluate if a specific connection is allowed on a live k8s cluster
k8snetpolicy eval -k ./kube/config -s pod-1 -d pod-2 -p 80

Flags:
--destination-ip string Destination (external) IP address
--destination-namespace string Destination pod namespace (default "default")
-d, --destination-pod string Destination pod name
-p, --destination-port string Destination port (name or number)
-h, --help help for evaluate
--protocol string Protocol in use (tcp, udp, sctp) (default "tcp")
--source-ip string Source (external) IP address
-n, --source-namespace string Source pod namespace (default "default")
-s, --source-pod string Source pod name, required

Global Flags:
-c, --context string Kubernetes context to use when evaluating connections in a live cluster
--dirpath string Resources dir path when evaluating connections from a dir
-k, --kubeconfig string Path and file to use for kubeconfig when evaluating connections in a live cluster (default "/home/adisos/.kube/config")
```

### List command
```
Lists all allowed connections based on the workloads and network policies
defined

Usage:
k8snetpolicy list [flags]

Examples:
# Get list of allowed connections from resources dir path
k8snetpolicy list --dirpath ./resources_dir/

# Get list of allowed connections from live k8s cluster
k8snetpolicy list -k ./kube/config

Flags:
-h, --help help for list

Global Flags:
-c, --context string Kubernetes context to use when evaluating connections in a live cluster
--dirpath string Resources dir path when evaluating connections from a dir
-k, --kubeconfig string Path and file to use for kubeconfig when evaluating connections in a live cluster (default "/home/adisos/.kube/config")
```



### Example outputs:
```
$ k8snetpolicy eval --dirpath tests/onlineboutique -s adservice-77d5cd745d-t8mx4 -d emailservice-54c7c5d9d-vp27n -p 80

default/adservice-77d5cd745d-t8mx4 => default/emailservice-54c7c5d9d-vp27n over tcp/80: false



$ k8snetpolicy list --dirpath tests/onlineboutique_workloads

0.0.0.0-255.255.255.255 => default/redis-cart[Deployment] : All Connections
default/adservice[Deployment] => default/adservice[Deployment] : All Connections
default/cartservice[Deployment] => default/cartservice[Deployment] : All Connections
default/checkoutservice[Deployment] => default/cartservice[Deployment] : TCP 7070
default/checkoutservice[Deployment] => default/checkoutservice[Deployment] : All Connections
default/checkoutservice[Deployment] => default/currencyservice[Deployment] : TCP 7000
default/checkoutservice[Deployment] => default/emailservice[Deployment] : TCP 8080
default/checkoutservice[Deployment] => default/paymentservice[Deployment] : TCP 50051
default/checkoutservice[Deployment] => default/productcatalogservice[Deployment] : TCP 3550
default/checkoutservice[Deployment] => default/shippingservice[Deployment] : TCP 50051
default/currencyservice[Deployment] => default/currencyservice[Deployment] : All Connections
default/emailservice[Deployment] => default/emailservice[Deployment] : All Connections
default/frontend[Deployment] => default/adservice[Deployment] : TCP 9555
default/frontend[Deployment] => default/cartservice[Deployment] : TCP 7070
default/frontend[Deployment] => default/checkoutservice[Deployment] : TCP 5050
default/frontend[Deployment] => default/currencyservice[Deployment] : TCP 7000
default/frontend[Deployment] => default/frontend[Deployment] : All Connections
default/frontend[Deployment] => default/productcatalogservice[Deployment] : TCP 3550
default/frontend[Deployment] => default/recommendationservice[Deployment] : TCP 8080
default/frontend[Deployment] => default/shippingservice[Deployment] : TCP 50051
default/loadgenerator[Deployment] => default/frontend[Deployment] : TCP 8080
default/paymentservice[Deployment] => default/paymentservice[Deployment] : All Connections
default/productcatalogservice[Deployment] => default/productcatalogservice[Deployment] : All Connections
default/recommendationservice[Deployment] => default/productcatalogservice[Deployment] : TCP 3550
default/recommendationservice[Deployment] => default/recommendationservice[Deployment] : All Connections
default/redis-cart[Deployment] => 0.0.0.0-255.255.255.255 : All Connections
default/redis-cart[Deployment] => default/redis-cart[Deployment] : All Connections
default/shippingservice[Deployment] => default/shippingservice[Deployment] : All Connections

```

## Build the project

Make sure you have golang 1.18+ on your platform

```commandline
git clone [email protected]:np-guard/netpol-analyzer.git
cd netpol-analyzer
make mod
make build
```

adisos marked this conversation as resolved.
Show resolved Hide resolved
Test your build by running `./bin/k8snetpolicy -h`.



159 changes: 159 additions & 0 deletions cmd/netpolicy/cmd/command_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package cmd

import (
_ "embed"
"errors"
"io"
"os"
"path/filepath"
"strings"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

var (
stdoutFile *os.File
stderrFile *os.File
testOutR *os.File
testOutW *os.File
testErrR *os.File
testErrW *os.File

//go:embed tests_outputs/test_legal_list.txt
testLegalListOutput string
)

func preTestRun() {
stdoutFile = os.Stdout
stderrFile = os.Stderr
testOutR, testOutW, _ = os.Pipe()
os.Stdout = testOutW
testErrR, testErrW, _ = os.Pipe()
os.Stderr = testErrW
}

// finalize test and get its output
func postTestRun(isErr bool) string {
testOutW.Close()
testErrW.Close()
out, _ := io.ReadAll(testOutR)
errOut, _ := io.ReadAll(testErrR)
os.Stdout = stdoutFile
os.Stderr = stderrFile
actualOutput := string(out)
actualErr := string(errOut)
if isErr {
return actualErr
}
return actualOutput
}

func runTest(test cmdTest, t *testing.T) {
// run the test and get its output
preTestRun()
rootCmd.SetArgs(test.args)
err := rootCmd.Execute()
if !test.isErr {
require.Nilf(t, err, "expected no errors, but got %v", err)
} else {
require.NotNil(t, err, "expected error, but got no error")
}
actual := postTestRun(test.isErr)

// compare actual to test.expectedOutput
if test.exact {
assert.Equal(t, test.expectedOutput, actual, "error - unexpected output")
} else if test.containment {
isContained := strings.Contains(actual, test.expectedOutput)
assert.True(t, isContained, "test %s error: %s not contained in %s", test.name, test.expectedOutput, actual)
} else {
assert.Error(t, errors.New(""), "test %s: missing containment or equality flag for test")
}
}

type cmdTest struct {
name string
args []string
expectedOutput string
exact bool
containment bool
isErr bool
}

func TestCommannds(t *testing.T) {
tests := []cmdTest{
{
name: "test_illegal_command",
args: []string{"A"},
expectedOutput: "Error: unknown command \"A\" for \"k8snetpolicy\"",
containment: true,
isErr: true,
},

{
name: "test_illegal_eval_no_args",
args: []string{"eval"},
expectedOutput: "no source defined",
containment: true,
isErr: true,
},

{
name: "test_illegal_eval_peer_not_found",
args: []string{
"eval",
"--dirpath",
filepath.Join(getTestsDir(), "onlineboutique"),
"-s",
"default/adservice-77d5cd745d-t8mx4",
"-d",
"default/emailservice-54c7c5d9d-vp27n",
"-p",
"80"},
expectedOutput: "could not find peer default/default/adservice-77d5cd745d",
containment: true,
isErr: true,
},

{
name: "test_legal_eval",
args: []string{
"eval",
"--dirpath",
filepath.Join(getTestsDir(), "onlineboutique"),
"-s",
"adservice-77d5cd745d-t8mx4",
"-d",
"emailservice-54c7c5d9d-vp27n",
"-p",
"80"},
expectedOutput: "default/adservice-77d5cd745d-t8mx4 => default/emailservice-54c7c5d9d-vp27n over tcp/80: false\n",
exact: true,
isErr: false,
},

{
name: "test_legal_list",
args: []string{
"list",
"--dirpath",
filepath.Join(getTestsDir(), "onlineboutique"),
},
expectedOutput: testLegalListOutput,
exact: true,
isErr: false,
},
}

for _, test := range tests {
runTest(test, t)
}
}

func getTestsDir() string {
currentDir, _ := os.Getwd()
res := filepath.Join(currentDir, "..", "..", "..", "tests")
return res
}
4 changes: 2 additions & 2 deletions cmd/netpolicy/cmd/evaluate.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ var evaluateCmd = &cobra.Command{
Short: "Evaluate if a specific connection allowed",
Aliases: []string{"eval", "check", "allow"}, // TODO: close on fewer, consider changing command name?
Example: ` # Evaluate if a specific connection is allowed on given resources from dir path
k8snetpolicy eval --dirpath ./resources_dir/ -s default/pod-1 -d default/pod-2 -p 80
k8snetpolicy eval --dirpath ./resources_dir/ -s pod-1 -d pod-2 -p 80

# Evaluate if a specific connection is allowed on a live k8s cluster
k8snetpolicy eval -k ./kube/config -s default/pod-1 -d default/pod-2 -p 80`,
k8snetpolicy eval -k ./kube/config -s pod-1 -d pod-2 -p 80`,

// TODO: can this check be done in an Args function (e.g., incl. built-in's such as MinArgs(3))?
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
Expand Down
29 changes: 29 additions & 0 deletions cmd/netpolicy/cmd/tests_outputs/test_legal_list.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
0.0.0.0-255.255.255.255 => default/redis-cart-78746d49dc[ReplicaSet] : All Connections
default/adservice-77d5cd745d[ReplicaSet] => default/adservice-77d5cd745d[ReplicaSet] : All Connections
default/cartservice-74f56fd4b[ReplicaSet] => default/cartservice-74f56fd4b[ReplicaSet] : All Connections
default/checkoutservice-69c8ff664b[ReplicaSet] => default/cartservice-74f56fd4b[ReplicaSet] : TCP 7070
default/checkoutservice-69c8ff664b[ReplicaSet] => default/checkoutservice-69c8ff664b[ReplicaSet] : All Connections
default/checkoutservice-69c8ff664b[ReplicaSet] => default/currencyservice-77654bbbdd[ReplicaSet] : TCP 7000
default/checkoutservice-69c8ff664b[ReplicaSet] => default/emailservice-54c7c5d9d[ReplicaSet] : TCP 8080
default/checkoutservice-69c8ff664b[ReplicaSet] => default/paymentservice-bbcbdc6b6[ReplicaSet] : TCP 50051
default/checkoutservice-69c8ff664b[ReplicaSet] => default/productcatalogservice-68765d49b6[ReplicaSet] : TCP 3550
default/checkoutservice-69c8ff664b[ReplicaSet] => default/shippingservice-5bd985c46d[ReplicaSet] : TCP 50051
default/currencyservice-77654bbbdd[ReplicaSet] => default/currencyservice-77654bbbdd[ReplicaSet] : All Connections
default/emailservice-54c7c5d9d[ReplicaSet] => default/emailservice-54c7c5d9d[ReplicaSet] : All Connections
default/frontend-99684f7f8[ReplicaSet] => default/adservice-77d5cd745d[ReplicaSet] : TCP 9555
default/frontend-99684f7f8[ReplicaSet] => default/cartservice-74f56fd4b[ReplicaSet] : TCP 7070
default/frontend-99684f7f8[ReplicaSet] => default/checkoutservice-69c8ff664b[ReplicaSet] : TCP 5050
default/frontend-99684f7f8[ReplicaSet] => default/currencyservice-77654bbbdd[ReplicaSet] : TCP 7000
default/frontend-99684f7f8[ReplicaSet] => default/frontend-99684f7f8[ReplicaSet] : All Connections
default/frontend-99684f7f8[ReplicaSet] => default/productcatalogservice-68765d49b6[ReplicaSet] : TCP 3550
default/frontend-99684f7f8[ReplicaSet] => default/recommendationservice-5f8c456796[ReplicaSet] : TCP 8080
default/frontend-99684f7f8[ReplicaSet] => default/shippingservice-5bd985c46d[ReplicaSet] : TCP 50051
default/loadgenerator-555fbdc87d[ReplicaSet] => default/frontend-99684f7f8[ReplicaSet] : TCP 8080
default/loadgenerator-555fbdc87d[ReplicaSet] => default/loadgenerator-555fbdc87d[ReplicaSet] : All Connections
default/paymentservice-bbcbdc6b6[ReplicaSet] => default/paymentservice-bbcbdc6b6[ReplicaSet] : All Connections
default/productcatalogservice-68765d49b6[ReplicaSet] => default/productcatalogservice-68765d49b6[ReplicaSet] : All Connections
default/recommendationservice-5f8c456796[ReplicaSet] => default/productcatalogservice-68765d49b6[ReplicaSet] : TCP 3550
default/recommendationservice-5f8c456796[ReplicaSet] => default/recommendationservice-5f8c456796[ReplicaSet] : All Connections
default/redis-cart-78746d49dc[ReplicaSet] => 0.0.0.0-255.255.255.255 : All Connections
default/redis-cart-78746d49dc[ReplicaSet] => default/redis-cart-78746d49dc[ReplicaSet] : All Connections
default/shippingservice-5bd985c46d[ReplicaSet] => default/shippingservice-5bd985c46d[ReplicaSet] : All Connections
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ go 1.18
require (
github.com/hashicorp/golang-lru v0.5.4
github.com/spf13/cobra v1.5.0
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
github.com/stretchr/testify v1.8.1
gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.24.2
k8s.io/apimachinery v0.24.2
k8s.io/client-go v0.24.2
Expand Down Expand Up @@ -34,6 +35,7 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
Expand Down
10 changes: 8 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -245,12 +245,17 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
Expand Down Expand Up @@ -611,8 +616,9 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
Expand Down