Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
Signed-off-by: Daichi Sakaue <[email protected]>
  • Loading branch information
yokaze committed Nov 20, 2024
1 parent c57e825 commit e0ab4c0
Show file tree
Hide file tree
Showing 11 changed files with 183 additions and 3 deletions.
6 changes: 6 additions & 0 deletions cmd/npv/app/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,9 @@ var gvrClusterwideNetworkPolicy schema.GroupVersionResource = schema.GroupVersio
Version: "v2",
Resource: "ciliumclusterwidenetworkpolicies",
}

var gvkNetworkPolicy schema.GroupVersionKind = schema.GroupVersionKind{
Group: "cilium.io",
Version: "v2",
Kind: "CiliumNetworkPolicy",
}
17 changes: 17 additions & 0 deletions cmd/npv/app/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,23 @@ func getPodEndpointID(ctx context.Context, d *dynamic.DynamicClient, namespace,
return endpointID, nil
}

func getPodIdentity(ctx context.Context, d *dynamic.DynamicClient, namespace, name string) (int64, error) {
ep, err := d.Resource(gvrEndpoint).Namespace(namespace).Get(ctx, name, metav1.GetOptions{})
if err != nil {
return 0, err
}

identity, found, err := unstructured.NestedInt64(ep.Object, "status", "identity", "id")
if err != nil {
return 0, err
}
if !found {
return 0, errors.New("pod does not have security identity")
}

return identity, nil
}

// key: identity number
// value: CiliumIdentity resource
func getIdentityResourceMap(ctx context.Context, d *dynamic.DynamicClient) (map[int]*unstructured.Unstructured, error) {
Expand Down
1 change: 1 addition & 0 deletions cmd/npv/app/inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"io"
"math/rand"

Check failure on line 8 in cmd/npv/app/inspect.go

View workflow job for this annotation

GitHub Actions / e2e

other declaration of rand
"net/http"
"slices"
"strconv"
Expand Down
13 changes: 13 additions & 0 deletions cmd/npv/app/manifest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package app

import "github.com/spf13/cobra"

func init() {
rootCmd.AddCommand(manifestCmd)
}

var manifestCmd = &cobra.Command{
Use: "manifest",
Short: "Generate CiliumNetworkPolicy",
Long: `Generate CiliumNetworkPolicy`,
}
106 changes: 106 additions & 0 deletions cmd/npv/app/manifest_blast.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package app

import (
"context"
"errors"
"io"
"sort"
"strings"

"github.com/spf13/cobra"
)

var manifestBlastOptions struct {
from string
to string
}

func init() {
manifestBlastCmd.Flags().StringVar(&manifestBlastOptions.from, "from", "", "egress pod")
manifestBlastCmd.Flags().StringVar(&manifestBlastOptions.to, "to", "", "ingress pod")
manifestCmd.AddCommand(manifestBlastCmd)
}

var manifestBlastCmd = &cobra.Command{
Use: "blast",
Short: "Show blast radius of a generated manifest",
Long: `Show blast radius of a generated manifest`,

Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
return runManifestBlast(context.Background(), cmd.OutOrStdout())
},
}

type manifestBlastEntry struct {
Direction string `json:"direction"`
Namespace string `json:"namespace"`
Name string `json:"name"`
}

func lessManifestBlastEntry(x, y *manifestBlastEntry) bool {
ret := strings.Compare(x.Direction, y.Direction)
if ret == 0 {
ret = strings.Compare(x.Namespace, y.Namespace)
}
if ret == 0 {
ret = strings.Compare(x.Name, y.Name)
}
return ret < 0
}

func runManifestBlast(ctx context.Context, w io.Writer) error {
if manifestBlastOptions.from == "" || manifestBlastOptions.to == "" {
return errors.New("--from and --to options are required")
}

fromSlice := strings.Split(manifestBlastOptions.from, "/")
toSlice := strings.Split(manifestBlastOptions.to, "/")
if len(fromSlice) != 2 || len(toSlice) != 2 {
return errors.New("--from and --to should be NAMESPACE/POD")
}

_, dynamicClient, err := createK8sClients()
if err != nil {
return err
}

fromIdentity, err := getPodIdentity(ctx, dynamicClient, fromSlice[0], fromSlice[1])
if err != nil {
return err
}

toIdentity, err := getPodIdentity(ctx, dynamicClient, toSlice[0], toSlice[1])
if err != nil {
return err
}

idEndpoints, err := getIdentityEndpoints(ctx, dynamicClient)
if err != nil {
return err
}

arr := make([]manifestBlastEntry, 0)
sort.Slice(arr, func(i, j int) bool { return lessManifestBlastEntry(&arr[i], &arr[j]) })

for _, ep := range idEndpoints[int(fromIdentity)] {
entry := manifestBlastEntry{
Direction: directionEgress,
Namespace: ep.GetNamespace(),
Name: ep.GetName(),
}
arr = append(arr, entry)
}
for _, ep := range idEndpoints[int(toIdentity)] {
entry := manifestBlastEntry{
Direction: directionIngress,
Namespace: ep.GetNamespace(),
Name: ep.GetName(),
}
arr = append(arr, entry)
}
return writeSimpleOrJson(w, arr, []string{"DIRECTION", "NAMESPACE", "NAME"}, len(arr), func(index int) []any {
ep := arr[index]
return []any{ep.Direction, ep.Namespace, ep.Name}
})
}
5 changes: 4 additions & 1 deletion e2e/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ CACHE_DIR := $(shell pwd)/../cache
POLICY_VIEWER := $(BIN_DIR)/npv
HELM := helm --repository-cache $(CACHE_DIR)/helm/repository --repository-config $(CACHE_DIR)/helm/repositories.yaml

DEPLOYMENT_REPLICAS ?= 1

##@ Basic

.PHONY: help
Expand Down Expand Up @@ -41,14 +43,15 @@ run-test-pod-%:
@echo Hello | yq > /dev/null
cat testdata/template/ubuntu.yaml | \
yq '.metadata.name = "$*"' | \
yq '.spec.replicas = $(DEPLOYMENT_REPLICAS)' | \
yq '.spec.selector.matchLabels = {"test": "$*"}' | \
yq '.spec.template.metadata.labels = {"test": "$*", "group": "test"}' | \
kubectl apply -f -

.PHONY: install-test-pod
install-test-pod:
$(MAKE) --no-print-directory run-test-pod-self
$(MAKE) --no-print-directory run-test-pod-l3-ingress-explicit-allow-all
$(MAKE) --no-print-directory DEPLOYMENT_REPLICAS=2 run-test-pod-l3-ingress-explicit-allow-all
$(MAKE) --no-print-directory run-test-pod-l3-ingress-implicit-deny-all
$(MAKE) --no-print-directory run-test-pod-l3-ingress-explicit-deny-all
$(MAKE) --no-print-directory run-test-pod-l3-egress-implicit-deny-all
Expand Down
30 changes: 30 additions & 0 deletions e2e/manifest_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package e2e

import (
"strings"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

func testManifestGenerate() {

}

func testManifestBlast() {
expected := `Egress,test,self
Ingress,test,l3-ingress-explicit-allow-all
Ingress,test,l3-ingress-explicit-allow-all`

It("should show blast radius", func() {
from := "--from=test/" + onePodByLabelSelector(Default, "test", "test=self")
to := "--to=test/" + onePodByLabelSelector(Default, "test", "test=l3-ingress-explicit-allow-all")
result := runViewerSafe(Default, nil, "manifest", "blast", from, to, "-o=json")
// remove hash suffix from pod names
result = jqSafe(Default, result, "-r", `[.[] | .name = (.name | split("-") | .[0:5] | join("-"))]`)
result = jqSafe(Default, result, "-r", `[.[] | .name = (.name | if startswith("self") then "self" else . end)]`)
result = jqSafe(Default, result, "-r", `.[] | [.direction, .namespace, .name] | @csv`)
resultString := strings.Replace(string(result), `"`, "", -1)
Expect(resultString).To(Equal(expected), "compare failed.\nactual: %s\nexpected: %s", resultString, expected)
})
}
2 changes: 2 additions & 0 deletions e2e/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,6 @@ func runTest() {
Context("id-summary", testIdSummary)
Context("inspect", testInspect)
Context("summary", testSummary)
Context("manifest-generate", testManifestGenerate)
Context("manifest-blast", testManifestBlast)
}
1 change: 1 addition & 0 deletions e2e/summary_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ func testSummary() {
expected := `l3-egress-explicit-deny-all,1,0,0,0
l3-egress-implicit-deny-all,1,0,0,0
l3-ingress-explicit-allow-all,2,0,0,0
l3-ingress-explicit-allow-all,2,0,0,0
l3-ingress-explicit-deny-all,1,1,0,0
l3-ingress-implicit-deny-all,1,0,0,0
l4-egress-explicit-deny-any,1,0,0,0
Expand Down
3 changes: 2 additions & 1 deletion e2e/testdata/policy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

| Target | From self (Egress) | To pod (Ingress) |
|-|-|-|
| l3-ingress-explicit-allow-all | allow | allow |
| l3-ingress-explicit-allow-all (1) | allow | allow |
| l3-ingress-explicit-allow-all (2) | allow | allow |
| l3-ingress-implicit-deny-all | allow | - |
| l3-ingress-explicit-deny-all | allow | deny |
| l3-egress-implicit-deny-all | - | - |
Expand Down
2 changes: 1 addition & 1 deletion e2e/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,6 @@ func onePodByLabelSelector(g Gomega, namespace, selector string) string {
data := kubectlSafe(g, nil, "get", "pod", "-n", namespace, "-l", selector, "-o=json")
count, err := strconv.Atoi(string(jqSafe(g, data, "-r", ".items | length")))
g.Expect(err).NotTo(HaveOccurred())
g.Expect(count).To(Equal(1), "namespace: %s, selector: %s", namespace, selector)
g.Expect(count).To(BeNumerically(">=", 1), "namespace: %s, selector: %s", namespace, selector)
return string(jqSafe(g, data, "-r", ".items[0].metadata.name"))
}

0 comments on commit e0ab4c0

Please sign in to comment.