Skip to content

Commit 600dc4f

Browse files
authored
issue #113: default-deny netpol for each namespace (#119)
* issue #113: default-deny netpol for each namespace * Fix lint issues Signed-off-by: Ziv Nevo <[email protected]>
1 parent ed2ff93 commit 600dc4f

12 files changed

+186
-13
lines changed

pkg/controller/controller_test.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -66,23 +66,26 @@ func TestPoliciesSynthesizerAPIFailFast(t *testing.T) {
6666

6767
func TestExtractConnectionsNoK8sResources(t *testing.T) {
6868
dirPath := filepath.Join(getTestsDir(), "bad_yamls", "irrelevant_k8s_resources.yaml")
69-
conns, errs := extractConnections(dirPath, false)
69+
resources, conns, errs := extractConnections(dirPath, false)
7070
require.Len(t, errs, 1)
7171
require.Empty(t, conns)
72+
require.Empty(t, resources)
7273
}
7374

7475
func TestExtractConnectionsNoK8sResourcesFailFast(t *testing.T) {
7576
dirPath := filepath.Join(getTestsDir(), "bad_yamls")
76-
conns, errs := extractConnections(dirPath, true)
77+
resources, conns, errs := extractConnections(dirPath, true)
7778
require.Len(t, errs, 1)
7879
require.Empty(t, conns)
80+
require.Empty(t, resources)
7981
}
8082

8183
func TestExtractConnectionsBadConfigMapRefs(t *testing.T) {
8284
dirPath := filepath.Join(getTestsDir(), "bad_yamls", "bad_configmap_refs.yaml")
83-
conns, errs := extractConnections(dirPath, false)
85+
resources, conns, errs := extractConnections(dirPath, false)
8486
require.Len(t, errs, 3)
8587
require.Empty(t, conns)
88+
require.Len(t, resources, 2) // the two deployments in this example get read
8689
}
8790

8891
func readLines(path string) ([]string, error) {

pkg/controller/engine.go

+6-5
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,26 @@ import (
66
)
77

88
// Scans the given directory for YAMLs with k8s resources and extracts required connections between workloads
9-
func extractConnections(dirPath string, stopOn1stErr bool) ([]*common.Connections, []FileProcessingError) {
9+
func extractConnections(dirPath string, stopOn1stErr bool) ([]common.Resource, []*common.Connections, []FileProcessingError) {
1010
// 1. Get all relevant resources from the repo and parse them
1111
dObjs, fileErrors := getK8sDeploymentResources(dirPath, stopOn1stErr)
1212
if stopProcessing(stopOn1stErr, fileErrors) {
13-
return nil, fileErrors
13+
return nil, nil, fileErrors
1414
}
1515
if len(dObjs) == 0 {
1616
fileErrors = appendAndLogNewError(fileErrors, noK8sResourcesFound())
17-
return []*common.Connections{}, fileErrors
17+
return []common.Resource{}, []*common.Connections{}, fileErrors
1818
}
1919

2020
resources, links, parseErrors := parseResources(dObjs)
2121
fileErrors = append(fileErrors, parseErrors...)
2222
if stopProcessing(stopOn1stErr, fileErrors) {
23-
return nil, fileErrors
23+
return nil, nil, fileErrors
2424
}
2525

2626
// 2. Discover all connections between resources
27-
return discoverConnections(resources, links), fileErrors
27+
connections := discoverConnections(resources, links)
28+
return resources, connections, fileErrors
2829
}
2930

3031
func parseResources(objs []parsedK8sObjects) ([]common.Resource, []common.Service, []FileProcessingError) {

pkg/controller/example_test.go

+15
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,21 @@ func ExamplePoliciesSynthesizer() {
118118
// "Egress"
119119
// ]
120120
// }
121+
// },
122+
// {
123+
// "kind": "NetworkPolicy",
124+
// "apiVersion": "networking.k8s.io/v1",
125+
// "metadata": {
126+
// "name": "default-deny-in-namespace-",
127+
// "creationTimestamp": null
128+
// },
129+
// "spec": {
130+
// "podSelector": {},
131+
// "policyTypes": [
132+
// "Ingress",
133+
// "Egress"
134+
// ]
135+
// }
121136
// }
122137
// ]
123138
}

pkg/controller/policies_synthesizer.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,10 @@ func (ps *PoliciesSynthesizer) Errors() []FileProcessingError {
6464
// PoliciesFromFolderPath returns a slice of Kubernetes NetworkPolicies that allow only the connections discovered
6565
// while processing K8s resources under the provided directory or one of its subdirectories (recursively).
6666
func (ps *PoliciesSynthesizer) PoliciesFromFolderPath(dirPath string) ([]*networking.NetworkPolicy, error) {
67-
connections, errs := extractConnections(dirPath, ps.stopOnError)
67+
resources, connections, errs := extractConnections(dirPath, ps.stopOnError)
6868
policies := []*networking.NetworkPolicy{}
6969
if !stopProcessing(ps.stopOnError, errs) {
70-
policies = synthNetpols(connections)
70+
policies = synthNetpols(resources, connections)
7171
}
7272

7373
ps.errors = errs
@@ -81,7 +81,7 @@ func (ps *PoliciesSynthesizer) PoliciesFromFolderPath(dirPath string) ([]*networ
8181
// ConnectionsFromFolderPath returns a slice of Connections, listing the connections discovered
8282
// while processing K8s resources under the provided directory or one of its subdirectories (recursively).
8383
func (ps *PoliciesSynthesizer) ConnectionsFromFolderPath(dirPath string) ([]*common.Connections, error) {
84-
connections, errs := extractConnections(dirPath, ps.stopOnError)
84+
_, connections, errs := extractConnections(dirPath, ps.stopOnError)
8585
ps.errors = errs
8686
if err := hasFatalError(errs); err != nil {
8787
return nil, err

pkg/controller/synth_netpols.go

+38-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
const (
1616
dnsPort = 53
1717
networkAPIVersion = "networking.k8s.io/v1"
18+
networkPolicyKind = "NetworkPolicy"
1819
)
1920

2021
type deploymentConnectivity struct {
@@ -45,9 +46,44 @@ func (deployConn *deploymentConnectivity) addEgressRule(
4546
deployConn.egressConns = append(deployConn.egressConns, rule)
4647
}
4748

48-
func synthNetpols(connections []*common.Connections) []*network.NetworkPolicy {
49+
// Generate a default-deny NetworkPolicy for the given namespace
50+
func getNsDefaultDenyPolicy(namespace string) *network.NetworkPolicy {
51+
return &network.NetworkPolicy{
52+
TypeMeta: metaV1.TypeMeta{
53+
Kind: networkPolicyKind,
54+
APIVersion: networkAPIVersion,
55+
},
56+
ObjectMeta: metaV1.ObjectMeta{
57+
Name: "default-deny-in-namespace-" + namespace,
58+
Namespace: namespace,
59+
},
60+
Spec: network.NetworkPolicySpec{
61+
PodSelector: metaV1.LabelSelector{}, // select all pods in the namespace
62+
Ingress: []network.NetworkPolicyIngressRule{}, // deny all ingress
63+
Egress: []network.NetworkPolicyEgressRule{}, // deny all egress
64+
PolicyTypes: []network.PolicyType{network.PolicyTypeIngress, network.PolicyTypeEgress},
65+
},
66+
}
67+
}
68+
69+
// Generate default-deny NetworkPolicy for each namespace of the given resources
70+
func getNsDefaultDenyPolicies(resources []common.Resource) []*network.NetworkPolicy {
71+
denyNetpols := []*network.NetworkPolicy{}
72+
namespaces := map[string]bool{}
73+
for resIdx := range resources {
74+
namespace := resources[resIdx].Resource.Namespace
75+
if _, ok := namespaces[namespace]; !ok {
76+
namespaces[namespace] = true
77+
denyNetpols = append(denyNetpols, getNsDefaultDenyPolicy(namespace))
78+
}
79+
}
80+
return denyNetpols
81+
}
82+
83+
func synthNetpols(resources []common.Resource, connections []*common.Connections) []*network.NetworkPolicy {
4984
deployConnectivity := determineConnectivityPerDeployment(connections)
5085
netpols := buildNetpolPerDeployment(deployConnectivity)
86+
netpols = append(netpols, getNsDefaultDenyPolicies(resources)...)
5187
return netpols
5288
}
5389

@@ -152,7 +188,7 @@ func buildNetpolPerDeployment(deployConnectivity []*deploymentConnectivity) []*n
152188
}
153189
netpol := network.NetworkPolicy{
154190
TypeMeta: metaV1.TypeMeta{
155-
Kind: "NetworkPolicy",
191+
Kind: networkPolicyKind,
156192
APIVersion: networkAPIVersion,
157193
},
158194
ObjectMeta: metaV1.ObjectMeta{

tests/bookinfo/expected_netpol_output.json

+15
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,21 @@
300300
"Egress"
301301
]
302302
}
303+
},
304+
{
305+
"kind": "NetworkPolicy",
306+
"apiVersion": "networking.k8s.io/v1",
307+
"metadata": {
308+
"name": "default-deny-in-namespace-",
309+
"creationTimestamp": null
310+
},
311+
"spec": {
312+
"podSelector": {},
313+
"policyTypes": [
314+
"Ingress",
315+
"Egress"
316+
]
317+
}
303318
}
304319
]
305320
}

tests/k8s_guestbook/expected_netpol_output.json

+32
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,38 @@
233233
"Egress"
234234
]
235235
}
236+
},
237+
{
238+
"kind": "NetworkPolicy",
239+
"apiVersion": "networking.k8s.io/v1",
240+
"metadata": {
241+
"name": "default-deny-in-namespace-default",
242+
"namespace": "default",
243+
"creationTimestamp": null
244+
},
245+
"spec": {
246+
"podSelector": {},
247+
"policyTypes": [
248+
"Ingress",
249+
"Egress"
250+
]
251+
}
252+
},
253+
{
254+
"kind": "NetworkPolicy",
255+
"apiVersion": "networking.k8s.io/v1",
256+
"metadata": {
257+
"name": "default-deny-in-namespace-redis",
258+
"namespace": "redis",
259+
"creationTimestamp": null
260+
},
261+
"spec": {
262+
"podSelector": {},
263+
"policyTypes": [
264+
"Ingress",
265+
"Egress"
266+
]
267+
}
236268
}
237269
]
238270
}

tests/k8s_wordpress_example/expected_netpol_output.json

+15
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,21 @@
100100
"Egress"
101101
]
102102
}
103+
},
104+
{
105+
"kind": "NetworkPolicy",
106+
"apiVersion": "networking.k8s.io/v1",
107+
"metadata": {
108+
"name": "default-deny-in-namespace-",
109+
"creationTimestamp": null
110+
},
111+
"spec": {
112+
"podSelector": {},
113+
"policyTypes": [
114+
"Ingress",
115+
"Egress"
116+
]
117+
}
103118
}
104119
]
105120
}

tests/onlineboutique/expected_netpol_interface_output.json

+15
Original file line numberDiff line numberDiff line change
@@ -850,5 +850,20 @@
850850
"Egress"
851851
]
852852
}
853+
},
854+
{
855+
"kind": "NetworkPolicy",
856+
"apiVersion": "networking.k8s.io/v1",
857+
"metadata": {
858+
"name": "default-deny-in-namespace-",
859+
"creationTimestamp": null
860+
},
861+
"spec": {
862+
"podSelector": {},
863+
"policyTypes": [
864+
"Ingress",
865+
"Egress"
866+
]
867+
}
853868
}
854869
]

tests/onlineboutique/expected_netpol_output.json

+15
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,21 @@
854854
"Egress"
855855
]
856856
}
857+
},
858+
{
859+
"kind": "NetworkPolicy",
860+
"apiVersion": "networking.k8s.io/v1",
861+
"metadata": {
862+
"name": "default-deny-in-namespace-",
863+
"creationTimestamp": null
864+
},
865+
"spec": {
866+
"podSelector": {},
867+
"policyTypes": [
868+
"Ingress",
869+
"Egress"
870+
]
871+
}
857872
}
858873
]
859874
}

tests/onlineboutique/expected_netpol_output.yaml

+10
Original file line numberDiff line numberDiff line change
@@ -402,5 +402,15 @@ items:
402402
policyTypes:
403403
- Ingress
404404
- Egress
405+
- apiVersion: networking.k8s.io/v1
406+
kind: NetworkPolicy
407+
metadata:
408+
creationTimestamp: null
409+
name: default-deny-in-namespace-
410+
spec:
411+
podSelector: {}
412+
policyTypes:
413+
- Ingress
414+
- Egress
405415
kind: NetworkPolicyList
406416
metadata: {}

tests/sockshop/expected_netpol_output.json

+16
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,22 @@
338338
"Egress"
339339
]
340340
}
341+
},
342+
{
343+
"kind": "NetworkPolicy",
344+
"apiVersion": "networking.k8s.io/v1",
345+
"metadata": {
346+
"name": "default-deny-in-namespace-sock-shop",
347+
"namespace": "sock-shop",
348+
"creationTimestamp": null
349+
},
350+
"spec": {
351+
"podSelector": {},
352+
"policyTypes": [
353+
"Ingress",
354+
"Egress"
355+
]
356+
}
341357
}
342358
]
343359
}

0 commit comments

Comments
 (0)