From 139b81f93c0f54278743e638245aea5d8a1767d4 Mon Sep 17 00:00:00 2001 From: Przemyslaw Roguski Date: Thu, 9 Apr 2026 19:04:34 +0200 Subject: [PATCH 01/16] Fixing db network policy bug, adding new qtodo egress network policies and default deny network policy --- charts/qtodo/templates/db-network-policy.yaml | 26 +++++++++-- .../default-deny-network-policy.yaml | 10 ++++ .../qtodo-egress-network-policy.yaml | 46 +++++++++++++++++++ ...yaml => qtodo-ingress-network-policy.yaml} | 0 4 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 charts/qtodo/templates/default-deny-network-policy.yaml create mode 100644 charts/qtodo/templates/qtodo-egress-network-policy.yaml rename charts/qtodo/templates/{network-policy.yaml => qtodo-ingress-network-policy.yaml} (100%) diff --git a/charts/qtodo/templates/db-network-policy.yaml b/charts/qtodo/templates/db-network-policy.yaml index 61df6712..80571dc8 100644 --- a/charts/qtodo/templates/db-network-policy.yaml +++ b/charts/qtodo/templates/db-network-policy.yaml @@ -1,14 +1,32 @@ apiVersion: networking.k8s.io/v1 kind: NetworkPolicy -apiVersion: networking.k8s.io/v1 metadata: - name: allow-tcp5432 - namespace: qtodo + name: qtodo-db-network-policy + namespace: {{ .Release.Namespace }} spec: podSelector: matchLabels: app: qtodo-db + policyTypes: + - Ingress + - Egress ingress: + # PostgreSQL access restricted to qtodo app pods only + - ports: + - protocol: TCP + port: 5432 + from: + - podSelector: + matchLabels: + app: qtodo + egress: + # DNS resolution only - ports: + - protocol: UDP + port: 53 - protocol: TCP - port: 5432 \ No newline at end of file + port: 53 + to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: openshift-dns diff --git a/charts/qtodo/templates/default-deny-network-policy.yaml b/charts/qtodo/templates/default-deny-network-policy.yaml new file mode 100644 index 00000000..21d36550 --- /dev/null +++ b/charts/qtodo/templates/default-deny-network-policy.yaml @@ -0,0 +1,10 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: default-deny-in-namespace-qtodo + namespace: {{ .Release.Namespace }} +spec: + podSelector: {} + policyTypes: + - Ingress + - Egress diff --git a/charts/qtodo/templates/qtodo-egress-network-policy.yaml b/charts/qtodo/templates/qtodo-egress-network-policy.yaml new file mode 100644 index 00000000..e82c191f --- /dev/null +++ b/charts/qtodo/templates/qtodo-egress-network-policy.yaml @@ -0,0 +1,46 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: qtodo-egress + namespace: {{ .Release.Namespace }} +spec: + podSelector: + matchLabels: + app: qtodo + policyTypes: + - Egress + egress: + # DNS resolution (CoreDNS in openshift-dns namespace) + - ports: + - protocol: UDP + port: 53 + - protocol: TCP + port: 53 + to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: openshift-dns + # PostgreSQL to qtodo-db in same namespace + - ports: + - protocol: TCP + port: 5432 + to: + - podSelector: + matchLabels: + app: qtodo-db + # Vault API (SPIFFE JWT auth for DB credentials) + - ports: + - protocol: TCP + port: 8200 + to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: vault + # Keycloak OIDC (when OIDC is enabled with SPIRE) + - ports: + - protocol: TCP + port: 8443 + to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: keycloak-system diff --git a/charts/qtodo/templates/network-policy.yaml b/charts/qtodo/templates/qtodo-ingress-network-policy.yaml similarity index 100% rename from charts/qtodo/templates/network-policy.yaml rename to charts/qtodo/templates/qtodo-ingress-network-policy.yaml From 0e270ec23ad7f89631ab24e78e3d37e089b89a76 Mon Sep 17 00:00:00 2001 From: Przemyslaw Roguski Date: Fri, 10 Apr 2026 14:21:15 +0200 Subject: [PATCH 02/16] cleaning all changes --- charts/qtodo/templates/db-network-policy.yaml | 2 +- .../default-deny-network-policy.yaml | 10 ---- ...etwork-policy.yaml => network-policy.yaml} | 0 .../qtodo-egress-network-policy.yaml | 46 ------------------- 4 files changed, 1 insertion(+), 57 deletions(-) delete mode 100644 charts/qtodo/templates/default-deny-network-policy.yaml rename charts/qtodo/templates/{qtodo-ingress-network-policy.yaml => network-policy.yaml} (100%) delete mode 100644 charts/qtodo/templates/qtodo-egress-network-policy.yaml diff --git a/charts/qtodo/templates/db-network-policy.yaml b/charts/qtodo/templates/db-network-policy.yaml index 80571dc8..311cd92d 100644 --- a/charts/qtodo/templates/db-network-policy.yaml +++ b/charts/qtodo/templates/db-network-policy.yaml @@ -2,7 +2,7 @@ apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: qtodo-db-network-policy - namespace: {{ .Release.Namespace }} + namespace: qtodo spec: podSelector: matchLabels: diff --git a/charts/qtodo/templates/default-deny-network-policy.yaml b/charts/qtodo/templates/default-deny-network-policy.yaml deleted file mode 100644 index 21d36550..00000000 --- a/charts/qtodo/templates/default-deny-network-policy.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: default-deny-in-namespace-qtodo - namespace: {{ .Release.Namespace }} -spec: - podSelector: {} - policyTypes: - - Ingress - - Egress diff --git a/charts/qtodo/templates/qtodo-ingress-network-policy.yaml b/charts/qtodo/templates/network-policy.yaml similarity index 100% rename from charts/qtodo/templates/qtodo-ingress-network-policy.yaml rename to charts/qtodo/templates/network-policy.yaml diff --git a/charts/qtodo/templates/qtodo-egress-network-policy.yaml b/charts/qtodo/templates/qtodo-egress-network-policy.yaml deleted file mode 100644 index e82c191f..00000000 --- a/charts/qtodo/templates/qtodo-egress-network-policy.yaml +++ /dev/null @@ -1,46 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: qtodo-egress - namespace: {{ .Release.Namespace }} -spec: - podSelector: - matchLabels: - app: qtodo - policyTypes: - - Egress - egress: - # DNS resolution (CoreDNS in openshift-dns namespace) - - ports: - - protocol: UDP - port: 53 - - protocol: TCP - port: 53 - to: - - namespaceSelector: - matchLabels: - kubernetes.io/metadata.name: openshift-dns - # PostgreSQL to qtodo-db in same namespace - - ports: - - protocol: TCP - port: 5432 - to: - - podSelector: - matchLabels: - app: qtodo-db - # Vault API (SPIFFE JWT auth for DB credentials) - - ports: - - protocol: TCP - port: 8200 - to: - - namespaceSelector: - matchLabels: - kubernetes.io/metadata.name: vault - # Keycloak OIDC (when OIDC is enabled with SPIRE) - - ports: - - protocol: TCP - port: 8443 - to: - - namespaceSelector: - matchLabels: - kubernetes.io/metadata.name: keycloak-system From e3d7b4090841f6188eb613d3003a8c3812e8ce32 Mon Sep 17 00:00:00 2001 From: Przemyslaw Roguski Date: Fri, 10 Apr 2026 14:30:18 +0200 Subject: [PATCH 03/16] db network policy file change --- ...k-policy.yaml => qtodo-db-network-policy.yaml} | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) rename charts/qtodo/templates/{db-network-policy.yaml => qtodo-db-network-policy.yaml} (57%) diff --git a/charts/qtodo/templates/db-network-policy.yaml b/charts/qtodo/templates/qtodo-db-network-policy.yaml similarity index 57% rename from charts/qtodo/templates/db-network-policy.yaml rename to charts/qtodo/templates/qtodo-db-network-policy.yaml index 311cd92d..63610949 100644 --- a/charts/qtodo/templates/db-network-policy.yaml +++ b/charts/qtodo/templates/qtodo-db-network-policy.yaml @@ -11,7 +11,9 @@ spec: - Ingress - Egress ingress: - # PostgreSQL access restricted to qtodo app pods only + # PostgreSQL access restricted to qtodo app pods only. + # Ingress NetworkPolicy is evaluated after DNAT (against real pod source IPs), + # so podSelector correctly restricts access to only qtodo pods. - ports: - protocol: TCP port: 5432 @@ -19,14 +21,15 @@ spec: - podSelector: matchLabels: app: qtodo + # Allow DNS responses from CoreDNS pods — same reason as qtodo-ingress-network-policy. + - from: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: openshift-dns egress: - # DNS resolution only + # DNS resolution only (port-only rule — see qtodo-egress-network-policy for explanation) - ports: - protocol: UDP port: 53 - protocol: TCP port: 53 - to: - - namespaceSelector: - matchLabels: - kubernetes.io/metadata.name: openshift-dns From e460c983f71c10b6ef6f16b540a9428d3125f9ba Mon Sep 17 00:00:00 2001 From: Przemyslaw Roguski Date: Fri, 10 Apr 2026 14:41:57 +0200 Subject: [PATCH 04/16] feat: add qtodo egress NetworkPolicy (port-restricted, no default-deny) --- .../qtodo-egress-network-policy.yaml | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 charts/qtodo/templates/qtodo-egress-network-policy.yaml diff --git a/charts/qtodo/templates/qtodo-egress-network-policy.yaml b/charts/qtodo/templates/qtodo-egress-network-policy.yaml new file mode 100644 index 00000000..0c7d8dec --- /dev/null +++ b/charts/qtodo/templates/qtodo-egress-network-policy.yaml @@ -0,0 +1,46 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: qtodo-egress + namespace: {{ .Release.Namespace }} +spec: + podSelector: + matchLabels: + app: qtodo + policyTypes: + - Egress + egress: + # NOTE: OVN-Kubernetes evaluates egress NetworkPolicy before DNAT, meaning the packet + # destination is always the service ClusterIP — a virtual IP not owned by any pod. + # namespaceSelector/podSelector cannot match ClusterIPs, so egress rules are port-restricted + # only (no 'to:' destination). Zero-trust enforcement on the destination side is achieved + # via ingress NetworkPolicies on the receiving namespaces (evaluated after DNAT against + # real pod IPs, which work correctly). This approach is portable across all OCP environments. + + # DNS resolution (port 53 UDP+TCP — CoreDNS ClusterIP in openshift-dns) + - ports: + - protocol: UDP + port: 53 + - protocol: TCP + port: 53 + + # PostgreSQL — qtodo-db service (port 5432) + - ports: + - protocol: TCP + port: 5432 + + # Vault API — SPIFFE JWT auth for DB credentials (port 8200) + - ports: + - protocol: TCP + port: 8200 + + # Keycloak OIDC — internal service URL (port 8443) + - ports: + - protocol: TCP + port: 8443 + + # Keycloak OIDC — external route for token exchange, current auth-server-url uses + # the cluster *.apps hostname which resolves to the external load balancer (port 443) + - ports: + - protocol: TCP + port: 443 From eb893daa8ee67ecfcf9ce467ffbfcbcfccf366b0 Mon Sep 17 00:00:00 2001 From: Przemyslaw Roguski Date: Fri, 10 Apr 2026 14:44:36 +0200 Subject: [PATCH 05/16] fixing the namespace name --- charts/qtodo/templates/qtodo-egress-network-policy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/qtodo/templates/qtodo-egress-network-policy.yaml b/charts/qtodo/templates/qtodo-egress-network-policy.yaml index 0c7d8dec..eaecefe5 100644 --- a/charts/qtodo/templates/qtodo-egress-network-policy.yaml +++ b/charts/qtodo/templates/qtodo-egress-network-policy.yaml @@ -2,7 +2,7 @@ apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: qtodo-egress - namespace: {{ .Release.Namespace }} + namespace: qtodo spec: podSelector: matchLabels: From df49c6639f60018f8d069a296a608ef71c696048 Mon Sep 17 00:00:00 2001 From: Przemyslaw Roguski Date: Fri, 10 Apr 2026 14:56:54 +0200 Subject: [PATCH 06/16] changing the ingress policy, to allow qtodo correct network communication --- charts/qtodo/templates/network-policy.yaml | 15 ----------- .../templates/qtodo-db-network-policy.yaml | 2 +- .../qtodo-egress-network-policy.yaml | 2 +- .../qtodo-ingress-network-policy.yaml | 25 +++++++++++++++++++ 4 files changed, 27 insertions(+), 17 deletions(-) delete mode 100644 charts/qtodo/templates/network-policy.yaml create mode 100644 charts/qtodo/templates/qtodo-ingress-network-policy.yaml diff --git a/charts/qtodo/templates/network-policy.yaml b/charts/qtodo/templates/network-policy.yaml deleted file mode 100644 index 285e9e48..00000000 --- a/charts/qtodo/templates/network-policy.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: allow-from-openshift-ingress -spec: - ingress: - - from: - - namespaceSelector: - matchLabels: - policy-group.network.openshift.io/ingress: "" - podSelector: - matchLabels: - app: qtodo - policyTypes: - - Ingress \ No newline at end of file diff --git a/charts/qtodo/templates/qtodo-db-network-policy.yaml b/charts/qtodo/templates/qtodo-db-network-policy.yaml index 63610949..aca572cb 100644 --- a/charts/qtodo/templates/qtodo-db-network-policy.yaml +++ b/charts/qtodo/templates/qtodo-db-network-policy.yaml @@ -2,7 +2,7 @@ apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: qtodo-db-network-policy - namespace: qtodo + namespace: {{ .Release.Namespace }} spec: podSelector: matchLabels: diff --git a/charts/qtodo/templates/qtodo-egress-network-policy.yaml b/charts/qtodo/templates/qtodo-egress-network-policy.yaml index eaecefe5..0c7d8dec 100644 --- a/charts/qtodo/templates/qtodo-egress-network-policy.yaml +++ b/charts/qtodo/templates/qtodo-egress-network-policy.yaml @@ -2,7 +2,7 @@ apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: qtodo-egress - namespace: qtodo + namespace: {{ .Release.Namespace }} spec: podSelector: matchLabels: diff --git a/charts/qtodo/templates/qtodo-ingress-network-policy.yaml b/charts/qtodo/templates/qtodo-ingress-network-policy.yaml new file mode 100644 index 00000000..ecda5f67 --- /dev/null +++ b/charts/qtodo/templates/qtodo-ingress-network-policy.yaml @@ -0,0 +1,25 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-from-openshift-ingress + namespace: {{ .Release.Namespace }} +spec: + podSelector: + matchLabels: + app: qtodo + policyTypes: + - Ingress + ingress: + # Allow all ingress traffic. + # + # OVN-Kubernetes limitation: when an egress NetworkPolicy is applied to a pod, + # OVN-K stops using conntrack bypass for ingress response packets. DNS responses + # (from the DNS ClusterIP via DNAT) and other ClusterIP service responses are then + # subject to strict ingress NP evaluation. Since these response source IPs cannot + # be matched portably via namespaceSelector/podSelector, allow-all ingress is required. + # + # This policy satisfies the ACS "Has Ingress Network Policy" compliance check. + # Zero-trust security is enforced via: + # - Egress NetworkPolicy (qtodo-egress): restricts what qtodo can initiate + # - Keycloak OIDC authentication: enforced on all application paths (/*) + - {} From 41b8407ebaf1e63ab824e9eefec2efc127effed5 Mon Sep 17 00:00:00 2001 From: Przemyslaw Roguski Date: Fri, 10 Apr 2026 18:17:15 +0200 Subject: [PATCH 07/16] NP tweaks --- .../qtodo-egress-network-policy.yaml | 46 ------------------- .../qtodo-ingress-network-policy.yaml | 41 +++++++++++------ 2 files changed, 28 insertions(+), 59 deletions(-) delete mode 100644 charts/qtodo/templates/qtodo-egress-network-policy.yaml diff --git a/charts/qtodo/templates/qtodo-egress-network-policy.yaml b/charts/qtodo/templates/qtodo-egress-network-policy.yaml deleted file mode 100644 index 0c7d8dec..00000000 --- a/charts/qtodo/templates/qtodo-egress-network-policy.yaml +++ /dev/null @@ -1,46 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: qtodo-egress - namespace: {{ .Release.Namespace }} -spec: - podSelector: - matchLabels: - app: qtodo - policyTypes: - - Egress - egress: - # NOTE: OVN-Kubernetes evaluates egress NetworkPolicy before DNAT, meaning the packet - # destination is always the service ClusterIP — a virtual IP not owned by any pod. - # namespaceSelector/podSelector cannot match ClusterIPs, so egress rules are port-restricted - # only (no 'to:' destination). Zero-trust enforcement on the destination side is achieved - # via ingress NetworkPolicies on the receiving namespaces (evaluated after DNAT against - # real pod IPs, which work correctly). This approach is portable across all OCP environments. - - # DNS resolution (port 53 UDP+TCP — CoreDNS ClusterIP in openshift-dns) - - ports: - - protocol: UDP - port: 53 - - protocol: TCP - port: 53 - - # PostgreSQL — qtodo-db service (port 5432) - - ports: - - protocol: TCP - port: 5432 - - # Vault API — SPIFFE JWT auth for DB credentials (port 8200) - - ports: - - protocol: TCP - port: 8200 - - # Keycloak OIDC — internal service URL (port 8443) - - ports: - - protocol: TCP - port: 8443 - - # Keycloak OIDC — external route for token exchange, current auth-server-url uses - # the cluster *.apps hostname which resolves to the external load balancer (port 443) - - ports: - - protocol: TCP - port: 443 diff --git a/charts/qtodo/templates/qtodo-ingress-network-policy.yaml b/charts/qtodo/templates/qtodo-ingress-network-policy.yaml index ecda5f67..4afef73f 100644 --- a/charts/qtodo/templates/qtodo-ingress-network-policy.yaml +++ b/charts/qtodo/templates/qtodo-ingress-network-policy.yaml @@ -1,7 +1,7 @@ apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: - name: allow-from-openshift-ingress + name: qtodo-network-policy namespace: {{ .Release.Namespace }} spec: podSelector: @@ -9,17 +9,32 @@ spec: app: qtodo policyTypes: - Ingress + - Egress ingress: - # Allow all ingress traffic. - # - # OVN-Kubernetes limitation: when an egress NetworkPolicy is applied to a pod, - # OVN-K stops using conntrack bypass for ingress response packets. DNS responses - # (from the DNS ClusterIP via DNAT) and other ClusterIP service responses are then - # subject to strict ingress NP evaluation. Since these response source IPs cannot - # be matched portably via namespaceSelector/podSelector, allow-all ingress is required. - # - # This policy satisfies the ACS "Has Ingress Network Policy" compliance check. - # Zero-trust security is enforced via: - # - Egress NetworkPolicy (qtodo-egress): restricts what qtodo can initiate - # - Keycloak OIDC authentication: enforced on all application paths (/*) + # Allow all ingress — required for DNS/ClusterIP responses to reach the pod. + # OVN-Kubernetes disrupts DNS DNAT/SNAT when NetworkPolicy ACLs are active. + # Application security enforced via Keycloak OIDC on all paths. - {} + egress: + # DNS resolution (port 53 UDP+TCP) + - ports: + - protocol: UDP + port: 53 + - protocol: TCP + port: 53 + # PostgreSQL — qtodo-db service (port 5432) + - ports: + - protocol: TCP + port: 5432 + # Vault API — SPIFFE JWT auth for DB credentials (port 8200) + - ports: + - protocol: TCP + port: 8200 + # Keycloak OIDC — internal service (port 8443) + - ports: + - protocol: TCP + port: 8443 + # Keycloak OIDC — external route via cluster load balancer (port 443) + - ports: + - protocol: TCP + port: 443 From 95968d2141cba7fb54cb07da2f818a4ac818c71f Mon Sep 17 00:00:00 2001 From: Przemyslaw Roguski Date: Fri, 10 Apr 2026 19:47:20 +0200 Subject: [PATCH 08/16] removing egress qtodo network policies due to problems with OVN-K and later broken DNS --- .../qtodo-ingress-network-policy.yaml | 40 ------------------- .../qtodo/templates/qtodo-network-policy.yaml | 28 +++++++++++++ 2 files changed, 28 insertions(+), 40 deletions(-) delete mode 100644 charts/qtodo/templates/qtodo-ingress-network-policy.yaml create mode 100644 charts/qtodo/templates/qtodo-network-policy.yaml diff --git a/charts/qtodo/templates/qtodo-ingress-network-policy.yaml b/charts/qtodo/templates/qtodo-ingress-network-policy.yaml deleted file mode 100644 index 4afef73f..00000000 --- a/charts/qtodo/templates/qtodo-ingress-network-policy.yaml +++ /dev/null @@ -1,40 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: qtodo-network-policy - namespace: {{ .Release.Namespace }} -spec: - podSelector: - matchLabels: - app: qtodo - policyTypes: - - Ingress - - Egress - ingress: - # Allow all ingress — required for DNS/ClusterIP responses to reach the pod. - # OVN-Kubernetes disrupts DNS DNAT/SNAT when NetworkPolicy ACLs are active. - # Application security enforced via Keycloak OIDC on all paths. - - {} - egress: - # DNS resolution (port 53 UDP+TCP) - - ports: - - protocol: UDP - port: 53 - - protocol: TCP - port: 53 - # PostgreSQL — qtodo-db service (port 5432) - - ports: - - protocol: TCP - port: 5432 - # Vault API — SPIFFE JWT auth for DB credentials (port 8200) - - ports: - - protocol: TCP - port: 8200 - # Keycloak OIDC — internal service (port 8443) - - ports: - - protocol: TCP - port: 8443 - # Keycloak OIDC — external route via cluster load balancer (port 443) - - ports: - - protocol: TCP - port: 443 diff --git a/charts/qtodo/templates/qtodo-network-policy.yaml b/charts/qtodo/templates/qtodo-network-policy.yaml new file mode 100644 index 00000000..e53a5bab --- /dev/null +++ b/charts/qtodo/templates/qtodo-network-policy.yaml @@ -0,0 +1,28 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: qtodo-network-policy + namespace: {{ .Release.Namespace }} +spec: + podSelector: + matchLabels: + app: qtodo + policyTypes: + - Ingress + - Egress + ingress: + # Allow all ingress. + # OVN-Kubernetes limitation: when egress NetworkPolicy ACLs are active, ClusterIP-based + # response packets (DNS, service replies) bypass conntrack and are subject to strict + # ingress NP evaluation. Their source IPs cannot be matched portably. + - {} + egress: + # Allow all egress. + # OVN-Kubernetes limitation: port-based egress rules cause OVN-K to create restrictive + # ACLs that disrupt DNS SNAT (response packets appear to come from the CoreDNS pod IP + # instead of the ClusterIP, causing glibc's connected socket to reject them). + # Allow-all egress prevents OVN-K from creating restrictive ACLs, keeping DNS functional. + # Zero-trust security is enforced via Keycloak OIDC authentication on all application + # paths (/*), and ingress to qtodo-db is restricted to only qtodo pods. + # This policy satisfies both ACS "Has Ingress/Egress NetworkPolicy" compliance checks. + - {} From 6e845a9eaa2820e5f01d4b6ba4a4a06d84a04e4e Mon Sep 17 00:00:00 2001 From: Przemyslaw Roguski Date: Wed, 22 Apr 2026 11:58:55 +0200 Subject: [PATCH 09/16] sync with PR#125 --- charts/ztvp-certificates/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/ztvp-certificates/values.yaml b/charts/ztvp-certificates/values.yaml index 7fe49813..17bc8188 100644 --- a/charts/ztvp-certificates/values.yaml +++ b/charts/ztvp-certificates/values.yaml @@ -119,7 +119,7 @@ configMapName: ztvp-trusted-ca # are auto-injected into all workloads via the cluster network operator. # Required for workloads that verify TLS of routes (e.g., ACS Central reaching Keycloak). proxyCA: - enabled: false + enabled: true configMapName: ztvp-proxy-ca # Automatic rollout configuration From 53cfef994dcbf9681b8b0d7580c55f8335ae44bc Mon Sep 17 00:00:00 2001 From: Przemyslaw Roguski Date: Wed, 22 Apr 2026 16:53:30 +0200 Subject: [PATCH 10/16] Pushing correct, fully covered network polices, with correct DNS port and Keycloak port --- .../templates/qtodo-db-network-policy.yaml | 19 +++---- .../qtodo/templates/qtodo-network-policy.yaml | 56 ++++++++++++++----- 2 files changed, 50 insertions(+), 25 deletions(-) diff --git a/charts/qtodo/templates/qtodo-db-network-policy.yaml b/charts/qtodo/templates/qtodo-db-network-policy.yaml index aca572cb..d246c09b 100644 --- a/charts/qtodo/templates/qtodo-db-network-policy.yaml +++ b/charts/qtodo/templates/qtodo-db-network-policy.yaml @@ -11,9 +11,7 @@ spec: - Ingress - Egress ingress: - # PostgreSQL access restricted to qtodo app pods only. - # Ingress NetworkPolicy is evaluated after DNAT (against real pod source IPs), - # so podSelector correctly restricts access to only qtodo pods. + # PostgreSQL — only from qtodo app pods in the same namespace - ports: - protocol: TCP port: 5432 @@ -21,15 +19,14 @@ spec: - podSelector: matchLabels: app: qtodo - # Allow DNS responses from CoreDNS pods — same reason as qtodo-ingress-network-policy. - - from: - - namespaceSelector: - matchLabels: - kubernetes.io/metadata.name: openshift-dns egress: - # DNS resolution only (port-only rule — see qtodo-egress-network-policy for explanation) + # DNS resolution via CoreDNS — OCP uses port 5353 (not 53) - ports: - protocol: UDP - port: 53 + port: 5353 - protocol: TCP - port: 53 + port: 5353 + to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: openshift-dns diff --git a/charts/qtodo/templates/qtodo-network-policy.yaml b/charts/qtodo/templates/qtodo-network-policy.yaml index e53a5bab..f811f5e0 100644 --- a/charts/qtodo/templates/qtodo-network-policy.yaml +++ b/charts/qtodo/templates/qtodo-network-policy.yaml @@ -11,18 +11,46 @@ spec: - Ingress - Egress ingress: - # Allow all ingress. - # OVN-Kubernetes limitation: when egress NetworkPolicy ACLs are active, ClusterIP-based - # response packets (DNS, service replies) bypass conntrack and are subject to strict - # ingress NP evaluation. Their source IPs cannot be matched portably. - - {} + # Only allow inbound traffic from the OpenShift router (external user traffic via Route) + - ports: + - protocol: TCP + port: 8080 + from: + - namespaceSelector: + matchLabels: + network.openshift.io/policy-group: ingress egress: - # Allow all egress. - # OVN-Kubernetes limitation: port-based egress rules cause OVN-K to create restrictive - # ACLs that disrupt DNS SNAT (response packets appear to come from the CoreDNS pod IP - # instead of the ClusterIP, causing glibc's connected socket to reject them). - # Allow-all egress prevents OVN-K from creating restrictive ACLs, keeping DNS functional. - # Zero-trust security is enforced via Keycloak OIDC authentication on all application - # paths (/*), and ingress to qtodo-db is restricted to only qtodo pods. - # This policy satisfies both ACS "Has Ingress/Egress NetworkPolicy" compliance checks. - - {} + # DNS resolution via CoreDNS — OCP uses port 5353 (not 53) + - ports: + - protocol: UDP + port: 5353 + - protocol: TCP + port: 5353 + to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: openshift-dns + # PostgreSQL — qtodo-db pod in the same namespace + - ports: + - protocol: TCP + port: 5432 + to: + - podSelector: + matchLabels: + app: qtodo-db + # Vault API — SPIFFE JWT auth for DB credentials retrieval + - ports: + - protocol: TCP + port: 8200 + to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: vault + # Keycloak OIDC — external Route via OCP router (QUARKUS_OIDC_AUTH_SERVER_URL resolves to keycloak.apps.*) + - ports: + - protocol: TCP + port: 443 + to: + - namespaceSelector: + matchLabels: + network.openshift.io/policy-group: ingress From edc5e46e9e02295120237e92409f263c6dfa73ad Mon Sep 17 00:00:00 2001 From: Przemyslaw Roguski Date: Wed, 22 Apr 2026 17:20:14 +0200 Subject: [PATCH 11/16] openshift-ingress labels update, because policy-group.network.openshift.io/ingress: triggers OVN-K's special ACL handling for host-network traffic --- charts/qtodo/templates/qtodo-network-policy.yaml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/charts/qtodo/templates/qtodo-network-policy.yaml b/charts/qtodo/templates/qtodo-network-policy.yaml index f811f5e0..68187eaf 100644 --- a/charts/qtodo/templates/qtodo-network-policy.yaml +++ b/charts/qtodo/templates/qtodo-network-policy.yaml @@ -11,14 +11,17 @@ spec: - Ingress - Egress ingress: - # Only allow inbound traffic from the OpenShift router (external user traffic via Route) + # Only allow inbound traffic from the OpenShift router. + # Router pods use hostNetwork:true so traffic arrives from node IPs, not pod IPs. + # OVN-K requires the policy-group.network.openshift.io/ingress label (empty value) + # to generate the correct ACLs for host-network ingress traffic. - ports: - protocol: TCP port: 8080 from: - namespaceSelector: matchLabels: - network.openshift.io/policy-group: ingress + policy-group.network.openshift.io/ingress: "" egress: # DNS resolution via CoreDNS — OCP uses port 5353 (not 53) - ports: @@ -47,10 +50,11 @@ spec: matchLabels: kubernetes.io/metadata.name: vault # Keycloak OIDC — external Route via OCP router (QUARKUS_OIDC_AUTH_SERVER_URL resolves to keycloak.apps.*) + # Same label as ingress: OVN-K special ACL for host-network router traffic - ports: - protocol: TCP port: 443 to: - namespaceSelector: matchLabels: - network.openshift.io/policy-group: ingress + policy-group.network.openshift.io/ingress: "" From a54539160b640ad1bb602495c21fc6dbb5ae3a90 Mon Sep 17 00:00:00 2001 From: Przemyslaw Roguski Date: Wed, 22 Apr 2026 17:44:46 +0200 Subject: [PATCH 12/16] changing the namespaceSelector: for Keycloak, because here Keycloak asnwers on both an internal hostname (for back-channel) and an external hostname (for browser redirects) --- charts/qtodo/templates/qtodo-network-policy.yaml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/charts/qtodo/templates/qtodo-network-policy.yaml b/charts/qtodo/templates/qtodo-network-policy.yaml index 68187eaf..1e2fc5e2 100644 --- a/charts/qtodo/templates/qtodo-network-policy.yaml +++ b/charts/qtodo/templates/qtodo-network-policy.yaml @@ -49,12 +49,9 @@ spec: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: vault - # Keycloak OIDC — external Route via OCP router (QUARKUS_OIDC_AUTH_SERVER_URL resolves to keycloak.apps.*) - # Same label as ingress: OVN-K special ACL for host-network router traffic + # Keycloak OIDC back-channel — external Route resolves to the router's load-balancer IP, + # which is not a pod IP in any namespace. namespaceSelector cannot match external IPs, + # so a port-only rule is required for the code-to-token exchange POST to work. - ports: - protocol: TCP port: 443 - to: - - namespaceSelector: - matchLabels: - policy-group.network.openshift.io/ingress: "" From bed618b7ed61d8a73946abb80ff61f2bcbd47b08 Mon Sep 17 00:00:00 2001 From: Przemyslaw Roguski Date: Wed, 22 Apr 2026 18:03:36 +0200 Subject: [PATCH 13/16] Adding default deny policy --- .../qtodo/templates/default-deny-network-policy.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 charts/qtodo/templates/default-deny-network-policy.yaml diff --git a/charts/qtodo/templates/default-deny-network-policy.yaml b/charts/qtodo/templates/default-deny-network-policy.yaml new file mode 100644 index 00000000..73c0718a --- /dev/null +++ b/charts/qtodo/templates/default-deny-network-policy.yaml @@ -0,0 +1,10 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: default-deny-in-namespace-qtodo + namespace: {{ .Release.Namespace }} +spec: + podSelector: {} + policyTypes: + - Ingress + - Egress \ No newline at end of file From 8706879a4a9502943aa5d8853fe9dbed39524767 Mon Sep 17 00:00:00 2001 From: Przemyslaw Roguski Date: Thu, 30 Apr 2026 17:51:50 +0200 Subject: [PATCH 14/16] Testing Vault ingress/egress network policies --- overrides/values-vault-network-policy.yaml | 72 ++++++++++++++++++++++ values-hub.yaml | 9 ++- 2 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 overrides/values-vault-network-policy.yaml diff --git a/overrides/values-vault-network-policy.yaml b/overrides/values-vault-network-policy.yaml new file mode 100644 index 00000000..4179b0de --- /dev/null +++ b/overrides/values-vault-network-policy.yaml @@ -0,0 +1,72 @@ +defaultDenyNetworkPolicy: + enabled: false + +vault: + server: + networkPolicy: + enabled: true + ingress: + # OCP router — vault Route (reencrypt TLS, UI and API) + # Router pods use hostNetwork:true — requires OVN-K policy-group label + - ports: + - protocol: TCP + port: 8200 + from: + - namespaceSelector: + matchLabels: + policy-group.network.openshift.io/ingress: "" + # qtodo namespace — spiffe-vault-client sidecar authenticates via SPIFFE JWT + - ports: + - protocol: TCP + port: 8200 + from: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: qtodo + # Vault cluster replication port — HA readiness + - ports: + - protocol: TCP + port: 8201 + from: + - podSelector: + matchLabels: + app.kubernetes.io/name: vault + component: server + egress: + # DNS resolution via CoreDNS — OCP uses port 5353 + - ports: + - protocol: UDP + port: 5353 + - protocol: TCP + port: 5353 + to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: openshift-dns + # SPIRE OIDC discovery provider — Vault JWT auth fetches JWKS + # Service port 443 -> pod port 8443, both included for OVN-K DNAT + - ports: + - protocol: TCP + port: 443 + - protocol: TCP + port: 8443 + to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: zero-trust-workload-identity-manager + # Vault cluster replication — outbound to peer Vault pods (HA readiness) + - ports: + - protocol: TCP + port: 8201 + to: + - podSelector: + matchLabels: + app.kubernetes.io/name: vault + component: server + # Kubernetes API server — TokenReview for ESO service account validation + # ClusterIP service is 172.x:443, DNAT to node IPs on 6443 + - ports: + - protocol: TCP + port: 443 + - protocol: TCP + port: 6443 diff --git a/values-hub.yaml b/values-hub.yaml index dae08bf8..01795123 100644 --- a/values-hub.yaml +++ b/values-hub.yaml @@ -318,8 +318,13 @@ clusterGroup: name: vault namespace: vault project: hub - chart: hashicorp-vault - chartVersion: 0.1.* + # TEMPORARY: Using fork for testing network policy changes + # Revert to chart: hashicorp-vault / chartVersion: 0.1.* after PR is merged + path: . + repoURL: https://github.com/p-rog/hashicorp-vault-chart.git + targetRevision: network-policy-vault + extraValueFiles: + - /overrides/values-vault-network-policy.yaml annotations: argocd.argoproj.io/sync-wave: "25" # Custom Vault policies for least-privilege access From ad649cfaba9a5e706d565d6ffbba388562c4f21e Mon Sep 17 00:00:00 2001 From: Przemyslaw Roguski Date: Thu, 30 Apr 2026 18:10:00 +0200 Subject: [PATCH 15/16] Testing Vault network policies --- values-hub.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/values-hub.yaml b/values-hub.yaml index 01795123..4b026442 100644 --- a/values-hub.yaml +++ b/values-hub.yaml @@ -323,8 +323,6 @@ clusterGroup: path: . repoURL: https://github.com/p-rog/hashicorp-vault-chart.git targetRevision: network-policy-vault - extraValueFiles: - - /overrides/values-vault-network-policy.yaml annotations: argocd.argoproj.io/sync-wave: "25" # Custom Vault policies for least-privilege access From 33d9f5bce5033870062663811e598f716ab038aa Mon Sep 17 00:00:00 2001 From: Przemyslaw Roguski Date: Thu, 7 May 2026 10:30:40 +0200 Subject: [PATCH 16/16] adding Vault network policies --- overrides/values-vault-network-policy.yaml | 2 +- values-hub.yaml | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/overrides/values-vault-network-policy.yaml b/overrides/values-vault-network-policy.yaml index 4179b0de..ddd9aeba 100644 --- a/overrides/values-vault-network-policy.yaml +++ b/overrides/values-vault-network-policy.yaml @@ -1,5 +1,5 @@ defaultDenyNetworkPolicy: - enabled: false + enabled: true vault: server: diff --git a/values-hub.yaml b/values-hub.yaml index 4b026442..a356f03d 100644 --- a/values-hub.yaml +++ b/values-hub.yaml @@ -318,11 +318,10 @@ clusterGroup: name: vault namespace: vault project: hub - # TEMPORARY: Using fork for testing network policy changes - # Revert to chart: hashicorp-vault / chartVersion: 0.1.* after PR is merged - path: . - repoURL: https://github.com/p-rog/hashicorp-vault-chart.git - targetRevision: network-policy-vault + chart: hashicorp-vault + chartVersion: 0.1.* + extraValueFiles: + - /overrides/values-vault-network-policy.yaml annotations: argocd.argoproj.io/sync-wave: "25" # Custom Vault policies for least-privilege access