Skip to content

Commit

Permalink
fea(networkpolicy) test that pods have a matching networkpolicy
Browse files Browse the repository at this point in the history
  • Loading branch information
zegl committed Sep 20, 2018
1 parent f56e024 commit cf0378c
Show file tree
Hide file tree
Showing 12 changed files with 232 additions and 51 deletions.
49 changes: 49 additions & 0 deletions score/networkpolicy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package score

import (
corev1 "k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
)

func scorePodHasNetworkPolicy(allNetpols []networkingv1.NetworkPolicy) func(spec corev1.PodTemplateSpec) TestScore {
return func(podSpec corev1.PodTemplateSpec) (score TestScore) {
score.Name = "Pod Has NetworkPolicy"

hasMatchingEgressNetpol := false
hasMatchingIngressNetpol := false

for _, netPol := range allNetpols {
matchLabels := netPol.Spec.PodSelector.MatchLabels

for labelKey, labelVal := range matchLabels {
if podLabelVal, ok := podSpec.Labels[labelKey]; ok && podLabelVal == labelVal {

for _, policyType := range netPol.Spec.PolicyTypes {
if policyType == networkingv1.PolicyTypeIngress {
hasMatchingIngressNetpol = true
}
if policyType == networkingv1.PolicyTypeEgress {
hasMatchingEgressNetpol = true
}
}

}
}
}

if hasMatchingEgressNetpol && hasMatchingIngressNetpol {
score.Grade = 10
} else if hasMatchingEgressNetpol && !hasMatchingIngressNetpol {
score.Grade = 5
score.Comments = append(score.Comments, "The pod does not have a matching ingress network policy")
} else if hasMatchingIngressNetpol && !hasMatchingEgressNetpol {
score.Grade = 5
score.Comments = append(score.Comments, "The pod does not have a matching egress network policy")
} else {
score.Grade = 0
score.Comments = append(score.Comments, "The pod does not have a matching network policy")
}

return
}
}
104 changes: 58 additions & 46 deletions score/score.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
package score

import (
"bytes"
"gopkg.in/yaml.v2"
"io"
"io/ioutil"
"log"
"strings"

//"errors"
//"fmt"
// "github.com/labstack/echo"
// "k8s.io/api/admission/v1beta1"
appsv1 "k8s.io/api/apps/v1"
// batchv1 "k8s.io/api/batch/v1"
// batchv1beta1 "k8s.io/api/batch/v1beta1"
corev1 "k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"

"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
// "reflect"
)

var scheme = runtime.NewScheme()
Expand All @@ -30,9 +26,7 @@ func init() {
func addToScheme(scheme *runtime.Scheme) {
corev1.AddToScheme(scheme)
appsv1.AddToScheme(scheme)
// batchv1.AddToScheme(scheme)
// batchv1beta1.AddToScheme(scheme)
// v1beta1.AddToScheme(scheme)
networkingv1.AddToScheme(scheme)
}

type Scorecard struct {
Expand All @@ -47,7 +41,7 @@ type TestScore struct {
}

func Score(file io.Reader) (*Scorecard, error) {
allData, err := ioutil.ReadAll(file)
allFiles, err := ioutil.ReadAll(file)
if err != nil {
return nil, err
}
Expand All @@ -56,75 +50,89 @@ func Score(file io.Reader) (*Scorecard, error) {
Kind string `yaml:"kind"`
}

var detect detectKind
err = yaml.Unmarshal(allData, &detect)
if err != nil {
return nil, err
}

var pods []corev1.Pod
var deployments []appsv1.Deployment
var statefulsets []appsv1.StatefulSet
var networkPolies []networkingv1.NetworkPolicy

decode := func(data []byte, object runtime.Object) {
deserializer := codecs.UniversalDeserializer()
if _, _, err := deserializer.Decode(data, nil, object); err != nil {
panic(err)
for _, fileContents := range bytes.Split(allFiles, []byte("---\n")) {
var detect detectKind
err = yaml.Unmarshal(fileContents, &detect)
if err != nil {
return nil, err
}
}

switch detect.Kind {
case "Pod":
var pod corev1.Pod
decode(allData, &pod)
pods = append(pods, pod)

case "Deployment":
var deployment appsv1.Deployment
decode(allData, &deployment)
deployments = append(deployments, deployment)

case "StatefulSet":
var statefulSet appsv1.StatefulSet
decode(allData, &statefulSet)
statefulsets = append(statefulsets, statefulSet)
decode := func(data []byte, object runtime.Object) {
deserializer := codecs.UniversalDeserializer()
if _, _, err := deserializer.Decode(data, nil, object); err != nil {
panic(err)
}
}

default:
log.Panicf("Unknown datatype: %s", detect.Kind)
switch detect.Kind {
case "Pod":
var pod corev1.Pod
decode(fileContents, &pod)
pods = append(pods, pod)

case "Deployment":
var deployment appsv1.Deployment
decode(fileContents, &deployment)
deployments = append(deployments, deployment)

case "StatefulSet":
var statefulSet appsv1.StatefulSet
decode(fileContents, &statefulSet)
statefulsets = append(statefulsets, statefulSet)

case "NetworkPolicy":
var netpol networkingv1.NetworkPolicy
decode(fileContents, &netpol)
networkPolies = append(networkPolies, netpol)

default:
log.Panicf("Unknown datatype: %s", detect.Kind)
}
}

podTests := []func(corev1.PodSpec) TestScore{
podTests := []func(corev1.PodTemplateSpec) TestScore{
scoreContainerLimits,
scoreContainerImageTag,
scoreContainerImagePullPolicy,
scorePodHasNetworkPolicy(networkPolies),
}

scoreCard := Scorecard{}

for _, pod := range pods {
for _, podTest := range podTests {
scoreCard.Scores = append(scoreCard.Scores, podTest(pod.Spec))
scoreCard.Scores = append(scoreCard.Scores, podTest(corev1.PodTemplateSpec{
ObjectMeta: pod.ObjectMeta,
Spec: pod.Spec,
}))
}
}

for _, deployment := range deployments {
for _, podTest := range podTests {
scoreCard.Scores = append(scoreCard.Scores, podTest(deployment.Spec.Template.Spec))
scoreCard.Scores = append(scoreCard.Scores, podTest(deployment.Spec.Template))
}
}

for _, statefulset := range statefulsets {
for _, podTest := range podTests {
scoreCard.Scores = append(scoreCard.Scores, podTest(statefulset.Spec.Template.Spec))
scoreCard.Scores = append(scoreCard.Scores, podTest(statefulset.Spec.Template))
}
}

return &scoreCard, nil
}

func scoreContainerLimits(pod corev1.PodSpec) (score TestScore) {
func scoreContainerLimits(podTemplate corev1.PodTemplateSpec) (score TestScore) {
score.Name = "Container Resources"

pod := podTemplate.Spec

allContainers := pod.InitContainers
allContainers = append(allContainers, pod.Containers...)

Expand Down Expand Up @@ -164,9 +172,11 @@ func scoreContainerLimits(pod corev1.PodSpec) (score TestScore) {
return
}

func scoreContainerImageTag(pod corev1.PodSpec) (score TestScore) {
func scoreContainerImageTag(podTemplate corev1.PodTemplateSpec) (score TestScore) {
score.Name = "Container Image Tag"

pod := podTemplate.Spec

allContainers := pod.InitContainers
allContainers = append(allContainers, pod.Containers...)

Expand All @@ -191,9 +201,11 @@ func scoreContainerImageTag(pod corev1.PodSpec) (score TestScore) {
return
}

func scoreContainerImagePullPolicy(pod corev1.PodSpec) (score TestScore) {
func scoreContainerImagePullPolicy(podTemplate corev1.PodTemplateSpec) (score TestScore) {
score.Name = "Container Image Pull Policy"

pod := podTemplate.Spec

allContainers := pod.InitContainers
allContainers = append(allContainers, pod.Containers...)

Expand Down
17 changes: 17 additions & 0 deletions score/score_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,26 @@ func TestPodContainerTagFixed(t *testing.T) {
func TestPodContainerPullPolicyUndefined(t *testing.T) {
testExpectedScore(t, "pod-image-pullpolicy-undefined.yaml", "Container Image Pull Policy", 0)
}

func TestPodContainerPullPolicyNever(t *testing.T) {
testExpectedScore(t, "pod-image-pullpolicy-never.yaml", "Container Image Pull Policy", 0)
}

func TestPodContainerPullPolicyAlways(t *testing.T) {
testExpectedScore(t, "pod-image-pullpolicy-always.yaml", "Container Image Pull Policy", 10)
}

func TestPodHasNoMatchingNetworkPolicy(t *testing.T) {
testExpectedScore(t, "networkpolicy-not-matching.yaml", "Pod Has NetworkPolicy", 0)
}

func TestPodHasMatchingNetworkPolicy(t *testing.T) {
testExpectedScore(t, "networkpolicy-matching.yaml", "Pod Has NetworkPolicy", 10)
}

func TestPodHasMatchingIngressNetworkPolicy(t *testing.T) {
testExpectedScore(t, "networkpolicy-matching-only-ingress.yaml", "Pod Has NetworkPolicy", 5)
}
func TestPodHasMatchingEgressNetworkPolicy(t *testing.T) {
testExpectedScore(t, "networkpolicy-matching-only-egress.yaml", "Pod Has NetworkPolicy", 5)
}
28 changes: 28 additions & 0 deletions score/testdata/networkpolicy-matching-only-egress.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: testapp-netpol
namespace: testspace
spec:
podSelector:
matchLabels:
app: testapp
egress:
- ports:
- port: 53
protocol: TCP
- port: 53
protocol: UDP
policyTypes:
- Egress
---
apiVersion: v1
kind: Pod
metadata:
name: pod-test-1
labels:
app: testapp
spec:
containers:
- name: foobar
image: foo/bar:latest
28 changes: 28 additions & 0 deletions score/testdata/networkpolicy-matching-only-ingress.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: testapp-netpol
namespace: testspace
spec:
podSelector:
matchLabels:
app: testapp
ingress:
- ports:
- port: 53
protocol: TCP
- port: 53
protocol: UDP
policyTypes:
- Ingress
---
apiVersion: v1
kind: Pod
metadata:
name: pod-test-1
labels:
app: testapp
spec:
containers:
- name: foobar
image: foo/bar:latest
33 changes: 33 additions & 0 deletions score/testdata/networkpolicy-matching.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: testapp-netpol
namespace: testspace
spec:
podSelector:
matchLabels:
app: testapp
egress:
- ports:
- port: 53
protocol: TCP
- port: 53
protocol: UDP
to:
- namespaceSelector:
matchLabels:
service: kube-system
policyTypes:
- Ingress
- Egress
---
apiVersion: v1
kind: Pod
metadata:
name: pod-test-1
labels:
app: testapp
spec:
containers:
- name: foobar
image: foo/bar:latest
19 changes: 19 additions & 0 deletions score/testdata/networkpolicy-not-matching.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: testapp-netpol
namespace: testspace
spec:
podSelector:
app: testapp
---
apiVersion: v1
kind: Pod
metadata:
name: pod-test-1
labels:
app: not-testapp
spec:
containers:
- name: foobar
image: foo/bar:latest
1 change: 0 additions & 1 deletion score/testdata/pod-image-pullpolicy-always.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@ metadata:
spec:
containers:
- name: foobar
resources:
image: foo/bar:123
imagePullPolicy: Always
1 change: 0 additions & 1 deletion score/testdata/pod-image-pullpolicy-never.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@ metadata:
spec:
containers:
- name: foobar
resources:
image: foo/bar:123
imagePullPolicy: Never

0 comments on commit cf0378c

Please sign in to comment.