-
Notifications
You must be signed in to change notification settings - Fork 0
Add opt-in NetworkPolicies for the application namespace #51
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
base: default-security-contexts
Are you sure you want to change the base?
Changes from all commits
5d78d71
ffc27ad
74f69b6
a8fb219
381ceb1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| {{- if .Values.networkPolicy.enabled }} | ||
| {{- /* The namespace-wide default deny (and the optional egress lockdown) live in the | ||
| infra chart; this chart allows traffic to its own workloads. */}} | ||
| apiVersion: networking.k8s.io/v1 | ||
| kind: NetworkPolicy | ||
| metadata: | ||
| name: allow-webserver | ||
| labels: | ||
| {{- include "shopsys.labels" $ | nindent 4 }} | ||
| spec: | ||
| podSelector: | ||
| matchLabels: | ||
| app: webserver-php-fpm | ||
| policyTypes: | ||
| - Ingress | ||
| ingress: | ||
| - from: | ||
| # e-shop and MCP ingresses | ||
| - namespaceSelector: | ||
| {{- toYaml .Values.networkPolicy.ingressControllerNamespace | nindent 12 }} | ||
| # storefront server-side requests (INTERNAL_ENDPOINT) and other app pods | ||
| - podSelector: {} | ||
| ports: | ||
| - port: 8080 | ||
| --- | ||
| apiVersion: networking.k8s.io/v1 | ||
| kind: NetworkPolicy | ||
| metadata: | ||
| name: allow-storefront | ||
| labels: | ||
| {{- include "shopsys.labels" $ | nindent 4 }} | ||
| spec: | ||
| podSelector: | ||
| matchLabels: | ||
| app: storefront | ||
| policyTypes: | ||
| - Ingress | ||
| ingress: | ||
| - from: | ||
| # the webserver's nginx proxies /_next/ and @storefront to the storefront; no | ||
| # shipped ingress targets the storefront directly (use networkPolicy.extraIngress | ||
| # when a project adds one) | ||
| - podSelector: {} | ||
| ports: | ||
| - port: 3000 | ||
| {{- end }} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| suite: opt-in network policies (app) | ||
| values: | ||
| - ./values/required.yaml | ||
| templates: | ||
| - templates/networkpolicy.yaml | ||
| tests: | ||
| - it: renders nothing by default | ||
| asserts: | ||
| - hasDocuments: | ||
| count: 0 | ||
|
|
||
| - it: allows the ingress controller and in-namespace pods to reach the workloads | ||
| set: | ||
| networkPolicy: | ||
| enabled: true | ||
| asserts: | ||
| - hasDocuments: | ||
| count: 2 | ||
| - equal: | ||
| path: spec.podSelector.matchLabels.app | ||
| value: webserver-php-fpm | ||
| documentIndex: 0 | ||
| - equal: | ||
| path: spec.ingress[0].ports[0].port | ||
| value: 8080 | ||
| documentIndex: 0 | ||
| - equal: | ||
| path: spec.ingress[0].from[0].namespaceSelector.matchLabels["kubernetes.io/metadata.name"] | ||
| value: ingress-nginx | ||
| documentIndex: 0 | ||
| - equal: | ||
| path: spec.ingress[0].ports[0].port | ||
| value: 3000 | ||
| documentIndex: 1 | ||
| # no shipped ingress targets the storefront - in-namespace traffic only | ||
| - equal: | ||
| path: spec.ingress[0].from | ||
| value: | ||
| - podSelector: {} | ||
| documentIndex: 1 | ||
|
|
||
| - it: honors a custom ingress-controller namespace selector | ||
| set: | ||
| networkPolicy: | ||
| enabled: true | ||
| ingressControllerNamespace: | ||
| matchLabels: | ||
| role: edge | ||
| asserts: | ||
| - equal: | ||
| path: spec.ingress[0].from[0].namespaceSelector.matchLabels.role | ||
| value: edge | ||
| documentIndex: 0 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,156 @@ | ||
| {{- if .Values.networkPolicy.enabled }} | ||
| {{- /* Namespace-wide policies live in the infra chart (installed first): the default | ||
| ingress deny, the redis/rabbitmq allows, the extra-ingress escape hatch and the | ||
| optional egress lockdown. The app chart adds the webserver/storefront allows. */}} | ||
| apiVersion: networking.k8s.io/v1 | ||
| kind: NetworkPolicy | ||
| metadata: | ||
| name: default-deny-ingress | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. High: This default deny selects every pod in the namespace — including cert-manager's HTTP01 solver pods. Both Suggestion: add a built-in allow policy (podSelector on
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Implemented in ffc27ad - this was a real trap, thanks. Added a built-in |
||
| labels: | ||
| {{- include "shopsys.labels" $ | nindent 4 }} | ||
| spec: | ||
| podSelector: {} | ||
| policyTypes: | ||
| - Ingress | ||
| {{- if .Values.redis.enabled }} | ||
| --- | ||
| apiVersion: networking.k8s.io/v1 | ||
| kind: NetworkPolicy | ||
| metadata: | ||
| name: allow-redis | ||
| labels: | ||
| {{- include "shopsys.labels" $ | nindent 4 }} | ||
| spec: | ||
| podSelector: | ||
| matchLabels: | ||
| app: redis | ||
| policyTypes: | ||
| - Ingress | ||
| ingress: | ||
| # application pods in this namespace | ||
| - from: | ||
| - podSelector: {} | ||
| ports: | ||
| - port: 6379 | ||
| # prometheus scraping of the redis exporter | ||
| - from: | ||
| - namespaceSelector: | ||
| {{- toYaml .Values.networkPolicy.monitoringNamespace | nindent 12 }} | ||
| ports: | ||
| - port: 9121 | ||
| {{- end }} | ||
| {{- if .Values.rabbitmq.enabled }} | ||
| --- | ||
| apiVersion: networking.k8s.io/v1 | ||
| kind: NetworkPolicy | ||
| metadata: | ||
| name: allow-rabbitmq | ||
| labels: | ||
| {{- include "shopsys.labels" $ | nindent 4 }} | ||
| spec: | ||
| podSelector: | ||
| matchLabels: | ||
| app: rabbitmq | ||
| policyTypes: | ||
| - Ingress | ||
| ingress: | ||
| # application pods in this namespace | ||
| - from: | ||
| - podSelector: {} | ||
| ports: | ||
| - port: 5672 | ||
| {{- /* same condition as ingress-rabbitmq.yaml - no rule when no management ingress */}} | ||
| {{- if or .Values.rabbitmq.management.hostname (gt (len .Values.domains) 0) }} | ||
| # management UI through the ingress controller | ||
| - from: | ||
| - namespaceSelector: | ||
| {{- toYaml .Values.networkPolicy.ingressControllerNamespace | nindent 12 }} | ||
| ports: | ||
| - port: 15672 | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Low: 15672 is opened from the ingress-controller namespace unconditionally, but the management ingress only renders under
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Implemented in ffc27ad. The 15672 rule is now gated on exactly the condition that renders |
||
| {{- end }} | ||
| # prometheus scraping of the built-in exporter | ||
| - from: | ||
| - namespaceSelector: | ||
| {{- toYaml .Values.networkPolicy.monitoringNamespace | nindent 12 }} | ||
| ports: | ||
| - port: 15692 | ||
| {{- end }} | ||
| --- | ||
| apiVersion: networking.k8s.io/v1 | ||
| kind: NetworkPolicy | ||
| metadata: | ||
| name: allow-acme-solver | ||
| labels: | ||
| {{- include "shopsys.labels" $ | nindent 4 }} | ||
| spec: | ||
| # cert-manager's HTTP01 solver pods are spawned in this namespace for the ingress | ||
| # certificates and must accept the challenge request on 8089, or every issuance and | ||
| # renewal fails under the default deny. Deliberately not restricted to the | ||
| # ingress-controller namespace: cert-manager's self-check may reach the solver with a | ||
| # different source (load-balancer/externalTrafficPolicy specifics), and the solver | ||
| # serves nothing but the public challenge token. Selects no pods on DNS01 clusters. | ||
| podSelector: | ||
| matchLabels: | ||
| acme.cert-manager.io/http01-solver: "true" | ||
| policyTypes: | ||
| - Ingress | ||
| ingress: | ||
| - ports: | ||
| - port: 8089 | ||
| {{- with .Values.networkPolicy.extraIngress }} | ||
| --- | ||
| apiVersion: networking.k8s.io/v1 | ||
| kind: NetworkPolicy | ||
| metadata: | ||
| name: allow-extra-ingress | ||
| labels: | ||
| {{- include "shopsys.labels" $ | nindent 4 }} | ||
| spec: | ||
| podSelector: {} | ||
| policyTypes: | ||
| - Ingress | ||
| ingress: | ||
| {{- toYaml . | nindent 4 }} | ||
| {{- end }} | ||
| {{- if .Values.networkPolicy.egress.enabled }} | ||
| --- | ||
| apiVersion: networking.k8s.io/v1 | ||
| kind: NetworkPolicy | ||
| metadata: | ||
| name: default-deny-egress | ||
| labels: | ||
| {{- include "shopsys.labels" $ | nindent 4 }} | ||
| spec: | ||
| podSelector: {} | ||
| policyTypes: | ||
| - Egress | ||
| --- | ||
| apiVersion: networking.k8s.io/v1 | ||
| kind: NetworkPolicy | ||
| metadata: | ||
| name: allow-egress | ||
| labels: | ||
| {{- include "shopsys.labels" $ | nindent 4 }} | ||
| spec: | ||
| podSelector: {} | ||
| policyTypes: | ||
| - Egress | ||
| egress: | ||
| # DNS anywhere: the cluster DNS location differs per cluster (kube-system, node-local | ||
| # caches on link-local IPs) and cannot be selected generically. Residual risk: DNS | ||
| # tunneling remains a possible exfiltration path under the lockdown. | ||
| - ports: | ||
| - port: 53 | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Low (hardening): This rule allows egress on port 53 to any destination, including arbitrary internet resolvers — so DNS tunneling remains a viable exfiltration path even under the lockdown, which is one of the main threats an egress lockdown addresses. Consider making the DNS destination configurable, e.g.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Documented in ffc27ad, keeping "anywhere" as the default: the residual DNS-tunneling risk is now called out in the template comment, the values comments and docs/values.md. I did not add a |
||
| protocol: UDP | ||
| - port: 53 | ||
| protocol: TCP | ||
| # everything inside this namespace (redis, rabbitmq, webserver, storefront) | ||
| - to: | ||
| - podSelector: {} | ||
| # project-specific external services (PostgreSQL, Elasticsearch, S3, SMTP, the | ||
| # Kubernetes API for the cron-suspend hook, ...) | ||
| {{- with .Values.networkPolicy.egress.rules }} | ||
| {{- toYaml . | nindent 4 }} | ||
| {{- end }} | ||
| {{- end }} | ||
| {{- end }} | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Medium: Neither chart's
values.schema.jsondeclaresnetworkPolicy(roots keepadditionalProperties: true, so nothing validates it). For a security feature this fails open:networkPolicy.enable: true, a misspelledingressControllerNamespace, or a non-booleanenabledall silently render nothing / wrong selectors while the operator believes the namespace is locked down. A small schema block (enabledboolean,egress.enabledboolean, object shapes for the selectors, arrays forextraIngress/rules) in both charts would catch typos at deploy time. I know #50 also skipped schema entries for its new keys, but here the failure mode is a missing security control rather than a missing hardening default. — Claude Agent (CR)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implemented in ffc27ad. Both charts'
values.schema.jsonnow declare the fullnetworkPolicyshape withadditionalProperties: falseon the block (and onegress), sonetworkPolicy.enable: true, a non-booleanenabledor any misspelled sub-key fails the deploy instead of silently rendering nothing. Verified:--set networkPolicy.enable=truenow errors with "additional properties 'enable' not allowed". The namespace selectors stay looseobject(they legitimately acceptmatchLabelsormatchExpressions), and the roots keepadditionalProperties: trueas required by the shared helmfile state values.