From 3bdbc7546b2d9fa5ba279544b8d4872ce86e1083 Mon Sep 17 00:00:00 2001 From: Thomas Tuffin Date: Fri, 7 Aug 2026 14:03:03 +0200 Subject: [PATCH 01/14] feat: runtime NetworkPolicies for EDA operator workloads --- config/rbac/role.yaml | 13 ++- molecule/default/tasks/eda_test.yml | 63 +++++++++++++- roles/eda/tasks/main.yml | 3 + roles/eda/tasks/networkpolicy.yml | 42 +++++++++ .../db-management.networkpolicy.yaml.j2 | 42 +++++++++ .../default-deny.networkpolicy.yaml.j2 | 18 ++++ ...da-activation-worker.networkpolicy.yaml.j2 | 65 ++++++++++++++ .../templates/eda-api.networkpolicy.yaml.j2 | 85 +++++++++++++++++++ .../eda-default-worker.networkpolicy.yaml.j2 | 62 ++++++++++++++ .../eda-event-stream.networkpolicy.yaml.j2 | 74 ++++++++++++++++ .../templates/eda-ui.networkpolicy.yaml.j2 | 66 ++++++++++++++ roles/postgres/tasks/main.yml | 6 ++ roles/postgres/tasks/networkpolicy.yml | 6 ++ .../templates/postgres.networkpolicy.yaml.j2 | 55 ++++++++++++ 14 files changed, 595 insertions(+), 5 deletions(-) create mode 100644 roles/eda/tasks/networkpolicy.yml create mode 100644 roles/eda/templates/db-management.networkpolicy.yaml.j2 create mode 100644 roles/eda/templates/default-deny.networkpolicy.yaml.j2 create mode 100644 roles/eda/templates/eda-activation-worker.networkpolicy.yaml.j2 create mode 100644 roles/eda/templates/eda-api.networkpolicy.yaml.j2 create mode 100644 roles/eda/templates/eda-default-worker.networkpolicy.yaml.j2 create mode 100644 roles/eda/templates/eda-event-stream.networkpolicy.yaml.j2 create mode 100644 roles/eda/templates/eda-ui.networkpolicy.yaml.j2 create mode 100644 roles/postgres/tasks/networkpolicy.yml create mode 100644 roles/postgres/templates/postgres.networkpolicy.yaml.j2 diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 93ecaf59..48246988 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -81,13 +81,24 @@ rules: - watch - apiGroups: - apps - - networking.k8s.io resources: - deployments - daemonsets - replicasets - statefulsets + verbs: + - get + - list + - create + - delete + - patch + - update + - watch + - apiGroups: + - networking.k8s.io + resources: - ingresses + - networkpolicies verbs: - get - list diff --git a/molecule/default/tasks/eda_test.yml b/molecule/default/tasks/eda_test.yml index 05482c56..4135cc9c 100644 --- a/molecule/default/tasks/eda_test.yml +++ b/molecule/default/tasks/eda_test.yml @@ -12,7 +12,62 @@ vars: cr_file: 'eda_v1alpha1_eda.yaml' -- name: Add assertions here - assert: - that: false - fail_msg: FIXME Add real assertions for your operator +- name: Verify default-deny NetworkPolicy exists + kubernetes.core.k8s_info: + api_version: networking.k8s.io/v1 + kind: NetworkPolicy + namespace: '{{ namespace }}' + name: '{{ namespace }}-default-deny' + register: np_default_deny + until: np_default_deny.resources | length > 0 + retries: 10 + delay: 5 + +- name: Assert default-deny selects eda-operator managed pods and denies all traffic + ansible.builtin.assert: + that: + - np_default_deny.resources[0].spec.podSelector.matchExpressions | length == 1 + - np_default_deny.resources[0].spec.podSelector.matchExpressions[0].key == 'app.kubernetes.io/managed-by' + - "'eda-operator' in np_default_deny.resources[0].spec.podSelector.matchExpressions[0].values" + - "'Ingress' in np_default_deny.resources[0].spec.policyTypes" + - "'Egress' in np_default_deny.resources[0].spec.policyTypes" + - np_default_deny.resources[0].spec.ingress is not defined or np_default_deny.resources[0].spec.ingress | length == 0 + - np_default_deny.resources[0].spec.egress is not defined or np_default_deny.resources[0].spec.egress | length == 0 + fail_msg: "default-deny NetworkPolicy does not match expected spec: {{ np_default_deny.resources }}" + +- name: Verify postgres NetworkPolicy exists + kubernetes.core.k8s_info: + api_version: networking.k8s.io/v1 + kind: NetworkPolicy + namespace: '{{ namespace }}' + name: '{{ namespace }}-postgres-15' + register: np_postgres + until: np_postgres.resources | length > 0 + retries: 10 + delay: 5 + +- name: Assert postgres NetworkPolicy allows app=eda ingress for activation Job pod pg_notify connectivity + ansible.builtin.assert: + that: + - np_postgres.resources[0].spec.podSelector.matchLabels['app.kubernetes.io/component'] == 'database' + - np_postgres.resources[0].spec.ingress | length > 0 + - np_postgres.resources[0].spec.ingress[0].from | selectattr('podSelector.matchLabels.app', 'defined') | selectattr('podSelector.matchLabels.app', 'equalto', 'eda') | list | length > 0 + fail_msg: "Postgres NetworkPolicy missing app=eda ingress rule: {{ np_postgres.resources }}" + +- name: Verify per-workload NetworkPolicies exist + kubernetes.core.k8s_info: + api_version: networking.k8s.io/v1 + kind: NetworkPolicy + namespace: '{{ namespace }}' + name: '{{ namespace }}-{{ item }}' + register: np_workload + until: np_workload.resources | length > 0 + retries: 10 + delay: 5 + loop: + - api + - ui + - default-worker + - activation-worker + - event-stream + - db-management diff --git a/roles/eda/tasks/main.yml b/roles/eda/tasks/main.yml index 33149b2b..37c3f378 100644 --- a/roles/eda/tasks/main.yml +++ b/roles/eda/tasks/main.yml @@ -74,6 +74,9 @@ - ingress_type | lower == 'route' - route_tls_secret | length +- name: Apply NetworkPolicies for EDA operand pods + include_tasks: networkpolicy.yml + - name: Deploy EDA include_tasks: deploy_eda.yml diff --git a/roles/eda/tasks/networkpolicy.yml b/roles/eda/tasks/networkpolicy.yml new file mode 100644 index 00000000..90da2fb0 --- /dev/null +++ b/roles/eda/tasks/networkpolicy.yml @@ -0,0 +1,42 @@ +--- +- name: Apply default deny NetworkPolicy for EDA operand pods + kubernetes.core.k8s: + state: present + apply: true + definition: "{{ lookup('template', 'default-deny.networkpolicy.yaml.j2') }}" + +- name: Apply EDA API NetworkPolicy + kubernetes.core.k8s: + state: present + apply: true + definition: "{{ lookup('template', 'eda-api.networkpolicy.yaml.j2') }}" + +- name: Apply EDA UI NetworkPolicy + kubernetes.core.k8s: + state: "{{ 'present' if not ui_disabled else 'absent' }}" + apply: true + definition: "{{ lookup('template', 'eda-ui.networkpolicy.yaml.j2') }}" + +- name: Apply EDA default-worker NetworkPolicy + kubernetes.core.k8s: + state: present + apply: true + definition: "{{ lookup('template', 'eda-default-worker.networkpolicy.yaml.j2') }}" + +- name: Apply EDA activation-worker NetworkPolicy + kubernetes.core.k8s: + state: present + apply: true + definition: "{{ lookup('template', 'eda-activation-worker.networkpolicy.yaml.j2') }}" + +- name: Apply EDA event-stream NetworkPolicy + kubernetes.core.k8s: + state: present + apply: true + definition: "{{ lookup('template', 'eda-event-stream.networkpolicy.yaml.j2') }}" + +- name: Apply db-management NetworkPolicy for backup and restore pods + kubernetes.core.k8s: + state: present + apply: true + definition: "{{ lookup('template', 'db-management.networkpolicy.yaml.j2') }}" diff --git a/roles/eda/templates/db-management.networkpolicy.yaml.j2 b/roles/eda/templates/db-management.networkpolicy.yaml.j2 new file mode 100644 index 00000000..d4769502 --- /dev/null +++ b/roles/eda/templates/db-management.networkpolicy.yaml.j2 @@ -0,0 +1,42 @@ +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: '{{ ansible_operator_meta.name }}-db-management' + namespace: '{{ ansible_operator_meta.namespace }}' + labels: + {{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(4) | trim }} +spec: + podSelector: + matchLabels: + app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' + app.kubernetes.io/component: '{{ deployment_type }}' + policyTypes: + - Egress + egress: + # Allow DNS resolution via OpenShift DNS + - to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: openshift-dns + ports: + - protocol: TCP + port: 5353 + - protocol: UDP + port: 5353 + # Allow egress to managed PostgreSQL for pg_dump / pg_restore +{% if managed_database | bool %} + - to: + - podSelector: + matchLabels: + app.kubernetes.io/component: database + app.kubernetes.io/instance: 'postgres-{{ supported_pg_version }}-{{ ansible_operator_meta.name }}' + ports: + - protocol: TCP + port: {{ eda_postgres_port | default('5432') }} +{% else %} + # External database — destination is user-configured, allow on the port + - ports: + - protocol: TCP + port: {{ eda_postgres_port | default('5432') }} +{% endif %} diff --git a/roles/eda/templates/default-deny.networkpolicy.yaml.j2 b/roles/eda/templates/default-deny.networkpolicy.yaml.j2 new file mode 100644 index 00000000..ae120930 --- /dev/null +++ b/roles/eda/templates/default-deny.networkpolicy.yaml.j2 @@ -0,0 +1,18 @@ +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: '{{ ansible_operator_meta.name }}-default-deny' + namespace: '{{ ansible_operator_meta.namespace }}' + labels: + {{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(4) | trim }} +spec: + podSelector: + matchExpressions: + - key: app.kubernetes.io/managed-by + operator: In + values: + - '{{ deployment_type }}-operator' + policyTypes: + - Ingress + - Egress diff --git a/roles/eda/templates/eda-activation-worker.networkpolicy.yaml.j2 b/roles/eda/templates/eda-activation-worker.networkpolicy.yaml.j2 new file mode 100644 index 00000000..218932c1 --- /dev/null +++ b/roles/eda/templates/eda-activation-worker.networkpolicy.yaml.j2 @@ -0,0 +1,65 @@ +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: '{{ ansible_operator_meta.name }}-activation-worker' + namespace: '{{ ansible_operator_meta.namespace }}' + labels: + {{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(4) | trim }} + app.kubernetes.io/component: '{{ deployment_type }}-activation-worker' +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: '{{ ansible_operator_meta.name }}' + app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' + app.kubernetes.io/component: '{{ deployment_type }}-activation-worker' + policyTypes: + - Ingress + - Egress + ingress: + # Allow traffic only from other EDA operator-managed pods (task dispatch) + - from: + - podSelector: + matchLabels: + app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' + egress: + # Allow DNS resolution via OpenShift DNS + - to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: openshift-dns + ports: + - protocol: TCP + port: 5353 + - protocol: UDP + port: 5353 + # Allow egress to managed PostgreSQL (task queue and pg_notify) +{% if managed_database | bool %} + - to: + - podSelector: + matchLabels: + app.kubernetes.io/component: database + app.kubernetes.io/instance: 'postgres-{{ supported_pg_version }}-{{ ansible_operator_meta.name }}' + ports: + - protocol: TCP + port: {{ eda_postgres_port | default('5432') }} +{% else %} + # External database — destination is user-configured, allow on the port + - ports: + - protocol: TCP + port: {{ eda_postgres_port | default('5432') }} +{% endif %} + # Allow egress to the gateway API for JWT validation + - to: + - podSelector: + matchLabels: + app.kubernetes.io/managed-by: 'aap-gateway-operator' + ports: + - protocol: TCP + port: 8000 + - protocol: TCP + port: 8080 + # Activation workers create and manage Job pods via the Kubernetes API. + # Unrestricted egress is required because event source destinations are + # user-defined and cannot be enumerated at policy creation time. + - {} diff --git a/roles/eda/templates/eda-api.networkpolicy.yaml.j2 b/roles/eda/templates/eda-api.networkpolicy.yaml.j2 new file mode 100644 index 00000000..1cb89eb7 --- /dev/null +++ b/roles/eda/templates/eda-api.networkpolicy.yaml.j2 @@ -0,0 +1,85 @@ +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: '{{ ansible_operator_meta.name }}-api' + namespace: '{{ ansible_operator_meta.namespace }}' + labels: + {{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(4) | trim }} + app.kubernetes.io/component: '{{ deployment_type }}-api' +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: '{{ ansible_operator_meta.name }}' + app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' + app.kubernetes.io/component: '{{ deployment_type }}-api' + policyTypes: + - Ingress + - Egress + ingress: + # Allow traffic from the OpenShift router (Routes) and ingress controllers + - from: + - namespaceSelector: + matchLabels: + policy-group.network.openshift.io/ingress: "" + - namespaceSelector: + matchLabels: + network.openshift.io/policy-group: ingress + ports: + - protocol: TCP + port: {{ api_nginx_port }} + # Allow traffic from the gateway operator (reverse proxy) and other AAP + # component pods that call the EDA API + - from: + - podSelector: + matchExpressions: + - key: app.kubernetes.io/managed-by + operator: In + values: + - '{{ deployment_type }}-operator' + - 'aap-gateway-operator' + - podSelector: + matchLabels: + control-plane: controller-manager + ports: + - protocol: TCP + port: {{ api_nginx_port }} + - protocol: TCP + port: {{ websocket_port }} + egress: + # Allow DNS resolution via OpenShift DNS + - to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: openshift-dns + ports: + - protocol: TCP + port: 5353 + - protocol: UDP + port: 5353 + # Allow egress to managed PostgreSQL +{% if managed_database | bool %} + - to: + - podSelector: + matchLabels: + app.kubernetes.io/component: database + app.kubernetes.io/instance: 'postgres-{{ supported_pg_version }}-{{ ansible_operator_meta.name }}' + ports: + - protocol: TCP + port: {{ eda_postgres_port | default('5432') }} +{% else %} + # External database — destination is user-configured, allow on the port + - ports: + - protocol: TCP + port: {{ eda_postgres_port | default('5432') }} +{% endif %} + # Allow egress to the gateway API for JWT validation and service mesh calls + - to: + - podSelector: + matchLabels: + app.kubernetes.io/managed-by: 'aap-gateway-operator' + ports: + - protocol: TCP + port: 8000 + - protocol: TCP + port: 8080 diff --git a/roles/eda/templates/eda-default-worker.networkpolicy.yaml.j2 b/roles/eda/templates/eda-default-worker.networkpolicy.yaml.j2 new file mode 100644 index 00000000..6aec3a44 --- /dev/null +++ b/roles/eda/templates/eda-default-worker.networkpolicy.yaml.j2 @@ -0,0 +1,62 @@ +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: '{{ ansible_operator_meta.name }}-default-worker' + namespace: '{{ ansible_operator_meta.namespace }}' + labels: + {{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(4) | trim }} + app.kubernetes.io/component: '{{ deployment_type }}-default-worker' +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: '{{ ansible_operator_meta.name }}' + app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' + app.kubernetes.io/component: '{{ deployment_type }}-default-worker' + policyTypes: + - Ingress + - Egress + ingress: + # Allow traffic only from other EDA operator-managed pods (e.g. api pod + # dispatching tasks to the worker via dispatcherd/postgres LISTEN/NOTIFY) + - from: + - podSelector: + matchLabels: + app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' + egress: + # Allow DNS resolution via OpenShift DNS + - to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: openshift-dns + ports: + - protocol: TCP + port: 5353 + - protocol: UDP + port: 5353 + # Allow egress to managed PostgreSQL (dispatcherd task queue) +{% if managed_database | bool %} + - to: + - podSelector: + matchLabels: + app.kubernetes.io/component: database + app.kubernetes.io/instance: 'postgres-{{ supported_pg_version }}-{{ ansible_operator_meta.name }}' + ports: + - protocol: TCP + port: {{ eda_postgres_port | default('5432') }} +{% else %} + # External database — destination is user-configured, allow on the port + - ports: + - protocol: TCP + port: {{ eda_postgres_port | default('5432') }} +{% endif %} + # Allow egress to the gateway API for JWT validation and service calls + - to: + - podSelector: + matchLabels: + app.kubernetes.io/managed-by: 'aap-gateway-operator' + ports: + - protocol: TCP + port: 8000 + - protocol: TCP + port: 8080 diff --git a/roles/eda/templates/eda-event-stream.networkpolicy.yaml.j2 b/roles/eda/templates/eda-event-stream.networkpolicy.yaml.j2 new file mode 100644 index 00000000..dadd7ab6 --- /dev/null +++ b/roles/eda/templates/eda-event-stream.networkpolicy.yaml.j2 @@ -0,0 +1,74 @@ +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: '{{ ansible_operator_meta.name }}-event-stream' + namespace: '{{ ansible_operator_meta.namespace }}' + labels: + {{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(4) | trim }} + app.kubernetes.io/component: '{{ deployment_type }}-event-stream' +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: '{{ ansible_operator_meta.name }}' + app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' + app.kubernetes.io/component: '{{ deployment_type }}-event-stream' + policyTypes: + - Ingress + - Egress + ingress: + # Allow traffic from the OpenShift router (Routes) and ingress controllers. + # Event stream receives external webhook/event traffic via the router. + - from: + - namespaceSelector: + matchLabels: + policy-group.network.openshift.io/ingress: "" + - namespaceSelector: + matchLabels: + network.openshift.io/policy-group: ingress + ports: + - protocol: TCP + port: {{ event_stream_nginx_port }} + # Allow traffic from the gateway operator and other EDA pods + - from: + - podSelector: + matchExpressions: + - key: app.kubernetes.io/managed-by + operator: In + values: + - '{{ deployment_type }}-operator' + - 'aap-gateway-operator' + - podSelector: + matchLabels: + control-plane: controller-manager + ports: + - protocol: TCP + port: {{ event_stream_nginx_port }} + egress: + # Allow DNS resolution via OpenShift DNS + - to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: openshift-dns + ports: + - protocol: TCP + port: 5353 + - protocol: UDP + port: 5353 + # Allow egress to managed PostgreSQL (event stream uses a dedicated DB user + # and pg_notify for event dispatch) +{% if managed_database | bool %} + - to: + - podSelector: + matchLabels: + app.kubernetes.io/component: database + app.kubernetes.io/instance: 'postgres-{{ supported_pg_version }}-{{ ansible_operator_meta.name }}' + ports: + - protocol: TCP + port: {{ eda_postgres_port | default('5432') }} +{% else %} + # External database — destination is user-configured, allow on the port + - ports: + - protocol: TCP + port: {{ eda_postgres_port | default('5432') }} +{% endif %} diff --git a/roles/eda/templates/eda-ui.networkpolicy.yaml.j2 b/roles/eda/templates/eda-ui.networkpolicy.yaml.j2 new file mode 100644 index 00000000..d18adeed --- /dev/null +++ b/roles/eda/templates/eda-ui.networkpolicy.yaml.j2 @@ -0,0 +1,66 @@ +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: '{{ ansible_operator_meta.name }}-ui' + namespace: '{{ ansible_operator_meta.namespace }}' + labels: + {{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(4) | trim }} + app.kubernetes.io/component: '{{ deployment_type }}-ui' +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: '{{ ansible_operator_meta.name }}' + app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' + app.kubernetes.io/component: '{{ deployment_type }}-ui' + policyTypes: + - Ingress + - Egress + ingress: + # Allow traffic from the OpenShift router (Routes) and ingress controllers + - from: + - namespaceSelector: + matchLabels: + policy-group.network.openshift.io/ingress: "" + - namespaceSelector: + matchLabels: + network.openshift.io/policy-group: ingress + ports: + - protocol: TCP + port: {{ api_nginx_port }} + # Allow traffic from the gateway operator (reverse proxy) + - from: + - podSelector: + matchExpressions: + - key: app.kubernetes.io/managed-by + operator: In + values: + - '{{ deployment_type }}-operator' + - 'aap-gateway-operator' + - podSelector: + matchLabels: + control-plane: controller-manager + ports: + - protocol: TCP + port: {{ api_nginx_port }} + egress: + # Allow DNS resolution via OpenShift DNS + - to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: openshift-dns + ports: + - protocol: TCP + port: 5353 + - protocol: UDP + port: 5353 + # Allow egress to the EDA API pod (UI proxies API requests to the api pod) + - to: + - podSelector: + matchLabels: + app.kubernetes.io/name: '{{ ansible_operator_meta.name }}' + app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' + app.kubernetes.io/component: '{{ deployment_type }}-api' + ports: + - protocol: TCP + port: {{ api_nginx_port }} diff --git a/roles/postgres/tasks/main.yml b/roles/postgres/tasks/main.yml index c11f5f1b..3583a0c7 100644 --- a/roles/postgres/tasks/main.yml +++ b/roles/postgres/tasks/main.yml @@ -13,6 +13,12 @@ - name: Set variables to be used in Postgres templates import_tasks: set_variables.yml +# NetworkPolicy must be applied before the StatefulSet so deny is in place +# before any database pod starts +- name: Apply PostgreSQL NetworkPolicy + import_tasks: networkpolicy.yml + when: managed_database + # Managed Database block - name: Check PostgreSQL version to determine if an upgrade is needed import_tasks: check_postgres_version.yml diff --git a/roles/postgres/tasks/networkpolicy.yml b/roles/postgres/tasks/networkpolicy.yml new file mode 100644 index 00000000..ddb88760 --- /dev/null +++ b/roles/postgres/tasks/networkpolicy.yml @@ -0,0 +1,6 @@ +--- +- name: Apply PostgreSQL NetworkPolicy + kubernetes.core.k8s: + state: present + apply: true + definition: "{{ lookup('template', 'postgres.networkpolicy.yaml.j2') }}" diff --git a/roles/postgres/templates/postgres.networkpolicy.yaml.j2 b/roles/postgres/templates/postgres.networkpolicy.yaml.j2 new file mode 100644 index 00000000..e35065e3 --- /dev/null +++ b/roles/postgres/templates/postgres.networkpolicy.yaml.j2 @@ -0,0 +1,55 @@ +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: '{{ ansible_operator_meta.name }}-postgres-{{ supported_pg_version }}' + namespace: '{{ ansible_operator_meta.namespace }}' + labels: + app.kubernetes.io/name: 'postgres-{{ supported_pg_version }}' + app.kubernetes.io/instance: 'postgres-{{ supported_pg_version }}-{{ ansible_operator_meta.name }}' + app.kubernetes.io/component: database + app.kubernetes.io/part-of: '{{ ansible_operator_meta.name }}' + app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: 'postgres-{{ supported_pg_version }}' + app.kubernetes.io/instance: 'postgres-{{ supported_pg_version }}-{{ ansible_operator_meta.name }}' + app.kubernetes.io/component: database + policyTypes: + - Ingress + - Egress + ingress: + # Allow traffic from EDA operator-managed pods and the operator + # controller-manager (for DB initialization) + - from: + - podSelector: + matchExpressions: + - key: app.kubernetes.io/managed-by + operator: In + values: + - '{{ deployment_type }}-operator' + - 'aap-gateway-operator' + - podSelector: + matchLabels: + control-plane: controller-manager + # EDA activation Job pods (created at runtime by activation workers, + # not operator-managed — carry only app: eda, no managed-by label). + # Event stream activations connect to Postgres via pg_notify. + - podSelector: + matchLabels: + app: eda + ports: + - protocol: TCP + port: {{ eda_postgres_port | default('5432') }} + egress: + # Allow DNS resolution via OpenShift DNS + - to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: openshift-dns + ports: + - protocol: TCP + port: 5353 + - protocol: UDP + port: 5353 From d38d8cde13f10449d49e0f2523a7e122bbee007c Mon Sep 17 00:00:00 2001 From: Thomas Tuffin Date: Fri, 7 Aug 2026 15:06:47 +0200 Subject: [PATCH 02/14] add default value for supported_pg_version --- roles/eda/defaults/main.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/roles/eda/defaults/main.yml b/roles/eda/defaults/main.yml index 21b8c3f3..78953f17 100644 --- a/roles/eda/defaults/main.yml +++ b/roles/eda/defaults/main.yml @@ -8,6 +8,9 @@ api_version: '{{ deployment_type }}.ansible.com/v1alpha1' no_log: true ui_disabled: false +# Keep in sync with roles/postgres/vars/main.yml +supported_pg_version: 15 + image_pull_policy: IfNotPresent image_pull_secrets: [] From 1c48894d6f49ee1154d7a7361612ee58f6c3c9c6 Mon Sep 17 00:00:00 2001 From: Thomas Tuffin Date: Fri, 7 Aug 2026 16:18:14 +0200 Subject: [PATCH 03/14] Address coderabbit concerns --- molecule/default/tasks/eda_test.yml | 35 +++++++++++++------ roles/eda/tasks/networkpolicy.yml | 1 - .../templates/eda-ui.networkpolicy.yaml.j2 | 7 ++-- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/molecule/default/tasks/eda_test.yml b/molecule/default/tasks/eda_test.yml index 4135cc9c..722aa1a2 100644 --- a/molecule/default/tasks/eda_test.yml +++ b/molecule/default/tasks/eda_test.yml @@ -12,12 +12,16 @@ vars: cr_file: 'eda_v1alpha1_eda.yaml' +- name: Set EDA resource name from sample CR + ansible.builtin.set_fact: + eda_cr_name: "{{ lookup('file', samples_dir + '/eda_v1alpha1_eda.yaml') | from_yaml | json_query('metadata.name') }}" + - name: Verify default-deny NetworkPolicy exists kubernetes.core.k8s_info: api_version: networking.k8s.io/v1 kind: NetworkPolicy namespace: '{{ namespace }}' - name: '{{ namespace }}-default-deny' + name: '{{ eda_cr_name }}-default-deny' register: np_default_deny until: np_default_deny.resources | length > 0 retries: 10 @@ -28,6 +32,7 @@ that: - np_default_deny.resources[0].spec.podSelector.matchExpressions | length == 1 - np_default_deny.resources[0].spec.podSelector.matchExpressions[0].key == 'app.kubernetes.io/managed-by' + - np_default_deny.resources[0].spec.podSelector.matchExpressions[0].operator == 'In' - "'eda-operator' in np_default_deny.resources[0].spec.podSelector.matchExpressions[0].values" - "'Ingress' in np_default_deny.resources[0].spec.policyTypes" - "'Egress' in np_default_deny.resources[0].spec.policyTypes" @@ -40,7 +45,7 @@ api_version: networking.k8s.io/v1 kind: NetworkPolicy namespace: '{{ namespace }}' - name: '{{ namespace }}-postgres-15' + name: '{{ eda_cr_name }}-postgres-{{ supported_pg_version }}' register: np_postgres until: np_postgres.resources | length > 0 retries: 10 @@ -59,15 +64,25 @@ api_version: networking.k8s.io/v1 kind: NetworkPolicy namespace: '{{ namespace }}' - name: '{{ namespace }}-{{ item }}' + name: '{{ eda_cr_name }}-{{ item }}' register: np_workload until: np_workload.resources | length > 0 retries: 10 delay: 5 - loop: - - api - - ui - - default-worker - - activation-worker - - event-stream - - db-management + loop: "{{ ['api', 'default-worker', 'activation-worker', 'event-stream', 'db-management'] + ([] if ui_disabled | bool else ['ui']) }}" + +- name: Assert UI NetworkPolicy is absent when ui_disabled + kubernetes.core.k8s_info: + api_version: networking.k8s.io/v1 + kind: NetworkPolicy + namespace: '{{ namespace }}' + name: '{{ eda_cr_name }}-ui' + register: np_ui_absent + when: ui_disabled | bool + +- name: Assert UI NetworkPolicy is absent when ui_disabled + ansible.builtin.assert: + that: + - np_ui_absent.resources | length == 0 + fail_msg: "UI NetworkPolicy should be absent when ui_disabled=true" + when: ui_disabled | bool diff --git a/roles/eda/tasks/networkpolicy.yml b/roles/eda/tasks/networkpolicy.yml index 90da2fb0..61d200ea 100644 --- a/roles/eda/tasks/networkpolicy.yml +++ b/roles/eda/tasks/networkpolicy.yml @@ -14,7 +14,6 @@ - name: Apply EDA UI NetworkPolicy kubernetes.core.k8s: state: "{{ 'present' if not ui_disabled else 'absent' }}" - apply: true definition: "{{ lookup('template', 'eda-ui.networkpolicy.yaml.j2') }}" - name: Apply EDA default-worker NetworkPolicy diff --git a/roles/eda/templates/eda-ui.networkpolicy.yaml.j2 b/roles/eda/templates/eda-ui.networkpolicy.yaml.j2 index d18adeed..b2dbfd21 100644 --- a/roles/eda/templates/eda-ui.networkpolicy.yaml.j2 +++ b/roles/eda/templates/eda-ui.networkpolicy.yaml.j2 @@ -17,7 +17,8 @@ spec: - Ingress - Egress ingress: - # Allow traffic from the OpenShift router (Routes) and ingress controllers + # Allow traffic from the OpenShift router (Routes) and ingress controllers. + # The UI nginx container listens on 8080, not api_nginx_port (8000). - from: - namespaceSelector: matchLabels: @@ -27,7 +28,7 @@ spec: network.openshift.io/policy-group: ingress ports: - protocol: TCP - port: {{ api_nginx_port }} + port: 8080 # Allow traffic from the gateway operator (reverse proxy) - from: - podSelector: @@ -42,7 +43,7 @@ spec: control-plane: controller-manager ports: - protocol: TCP - port: {{ api_nginx_port }} + port: 8080 egress: # Allow DNS resolution via OpenShift DNS - to: From e3745dd30bb52369b6461afd0181d51e0d174fbc Mon Sep 17 00:00:00 2001 From: Thomas Tuffin Date: Mon, 10 Aug 2026 11:14:49 +0200 Subject: [PATCH 04/14] Fix GW operator label --- .../eda/templates/eda-activation-worker.networkpolicy.yaml.j2 | 2 +- roles/eda/templates/eda-api.networkpolicy.yaml.j2 | 4 ++-- roles/eda/templates/eda-default-worker.networkpolicy.yaml.j2 | 2 +- roles/eda/templates/eda-event-stream.networkpolicy.yaml.j2 | 2 +- roles/eda/templates/eda-ui.networkpolicy.yaml.j2 | 2 +- roles/postgres/templates/postgres.networkpolicy.yaml.j2 | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/roles/eda/templates/eda-activation-worker.networkpolicy.yaml.j2 b/roles/eda/templates/eda-activation-worker.networkpolicy.yaml.j2 index 218932c1..5e49317b 100644 --- a/roles/eda/templates/eda-activation-worker.networkpolicy.yaml.j2 +++ b/roles/eda/templates/eda-activation-worker.networkpolicy.yaml.j2 @@ -53,7 +53,7 @@ spec: - to: - podSelector: matchLabels: - app.kubernetes.io/managed-by: 'aap-gateway-operator' + app.kubernetes.io/managed-by: 'aap-operator' ports: - protocol: TCP port: 8000 diff --git a/roles/eda/templates/eda-api.networkpolicy.yaml.j2 b/roles/eda/templates/eda-api.networkpolicy.yaml.j2 index 1cb89eb7..e67a20cd 100644 --- a/roles/eda/templates/eda-api.networkpolicy.yaml.j2 +++ b/roles/eda/templates/eda-api.networkpolicy.yaml.j2 @@ -37,7 +37,7 @@ spec: operator: In values: - '{{ deployment_type }}-operator' - - 'aap-gateway-operator' + - 'aap-operator' - podSelector: matchLabels: control-plane: controller-manager @@ -77,7 +77,7 @@ spec: - to: - podSelector: matchLabels: - app.kubernetes.io/managed-by: 'aap-gateway-operator' + app.kubernetes.io/managed-by: 'aap-operator' ports: - protocol: TCP port: 8000 diff --git a/roles/eda/templates/eda-default-worker.networkpolicy.yaml.j2 b/roles/eda/templates/eda-default-worker.networkpolicy.yaml.j2 index 6aec3a44..47ae97cb 100644 --- a/roles/eda/templates/eda-default-worker.networkpolicy.yaml.j2 +++ b/roles/eda/templates/eda-default-worker.networkpolicy.yaml.j2 @@ -54,7 +54,7 @@ spec: - to: - podSelector: matchLabels: - app.kubernetes.io/managed-by: 'aap-gateway-operator' + app.kubernetes.io/managed-by: 'aap-operator' ports: - protocol: TCP port: 8000 diff --git a/roles/eda/templates/eda-event-stream.networkpolicy.yaml.j2 b/roles/eda/templates/eda-event-stream.networkpolicy.yaml.j2 index dadd7ab6..4f2dacf2 100644 --- a/roles/eda/templates/eda-event-stream.networkpolicy.yaml.j2 +++ b/roles/eda/templates/eda-event-stream.networkpolicy.yaml.j2 @@ -37,7 +37,7 @@ spec: operator: In values: - '{{ deployment_type }}-operator' - - 'aap-gateway-operator' + - 'aap-operator' - podSelector: matchLabels: control-plane: controller-manager diff --git a/roles/eda/templates/eda-ui.networkpolicy.yaml.j2 b/roles/eda/templates/eda-ui.networkpolicy.yaml.j2 index b2dbfd21..035e7327 100644 --- a/roles/eda/templates/eda-ui.networkpolicy.yaml.j2 +++ b/roles/eda/templates/eda-ui.networkpolicy.yaml.j2 @@ -37,7 +37,7 @@ spec: operator: In values: - '{{ deployment_type }}-operator' - - 'aap-gateway-operator' + - 'aap-operator' - podSelector: matchLabels: control-plane: controller-manager diff --git a/roles/postgres/templates/postgres.networkpolicy.yaml.j2 b/roles/postgres/templates/postgres.networkpolicy.yaml.j2 index e35065e3..fb0e2a8f 100644 --- a/roles/postgres/templates/postgres.networkpolicy.yaml.j2 +++ b/roles/postgres/templates/postgres.networkpolicy.yaml.j2 @@ -29,7 +29,7 @@ spec: operator: In values: - '{{ deployment_type }}-operator' - - 'aap-gateway-operator' + - 'aap-operator' - podSelector: matchLabels: control-plane: controller-manager From c1f4b0cb48ab664de03f3e9a59a654f6f57c60c7 Mon Sep 17 00:00:00 2001 From: Thomas Tuffin Date: Mon, 10 Aug 2026 12:24:54 +0200 Subject: [PATCH 05/14] Unrestricted egress for default worker --- roles/eda/templates/eda-default-worker.networkpolicy.yaml.j2 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/roles/eda/templates/eda-default-worker.networkpolicy.yaml.j2 b/roles/eda/templates/eda-default-worker.networkpolicy.yaml.j2 index 47ae97cb..0d89ba45 100644 --- a/roles/eda/templates/eda-default-worker.networkpolicy.yaml.j2 +++ b/roles/eda/templates/eda-default-worker.networkpolicy.yaml.j2 @@ -60,3 +60,7 @@ spec: port: 8000 - protocol: TCP port: 8080 + # Default-worker executes user-defined tasks (git clones, SCM syncs, + # webhook calls). Destinations are user-configured and cannot be + # enumerated at policy creation time. + - {} From b0299a7b719d6fe449dd1a848fcda3e1081f4207 Mon Sep 17 00:00:00 2001 From: Thomas Tuffin Date: Mon, 10 Aug 2026 13:22:50 +0200 Subject: [PATCH 06/14] Add policy for websocket connectivity --- roles/eda/templates/eda-api.networkpolicy.yaml.j2 | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/roles/eda/templates/eda-api.networkpolicy.yaml.j2 b/roles/eda/templates/eda-api.networkpolicy.yaml.j2 index e67a20cd..88bdd118 100644 --- a/roles/eda/templates/eda-api.networkpolicy.yaml.j2 +++ b/roles/eda/templates/eda-api.networkpolicy.yaml.j2 @@ -46,6 +46,16 @@ spec: port: {{ api_nginx_port }} - protocol: TCP port: {{ websocket_port }} + # Allow websocket connections from activation Job pods (created at runtime + # by activation workers, not operator-managed — carry only app: eda). + # ansible_rulebook connects to daphne via websocket for rulebook execution. + - from: + - podSelector: + matchLabels: + app: eda + ports: + - protocol: TCP + port: {{ websocket_port }} egress: # Allow DNS resolution via OpenShift DNS - to: From e6aaeb461328eeaf99313e069571560010ec8fee Mon Sep 17 00:00:00 2001 From: Thomas Tuffin Date: Mon, 10 Aug 2026 15:18:10 +0200 Subject: [PATCH 07/14] Add egress to GW API --- .../templates/eda-event-stream.networkpolicy.yaml.j2 | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/roles/eda/templates/eda-event-stream.networkpolicy.yaml.j2 b/roles/eda/templates/eda-event-stream.networkpolicy.yaml.j2 index 4f2dacf2..5cb5c194 100644 --- a/roles/eda/templates/eda-event-stream.networkpolicy.yaml.j2 +++ b/roles/eda/templates/eda-event-stream.networkpolicy.yaml.j2 @@ -55,6 +55,18 @@ spec: port: 5353 - protocol: UDP port: 5353 + # Allow egress to the gateway API for JWT public key fetch + # (ansible_base cert.py calls the gateway on every event stream POST to + # validate the X-Trusted-Proxy header) + - to: + - podSelector: + matchLabels: + app.kubernetes.io/managed-by: 'aap-operator' + ports: + - protocol: TCP + port: 8000 + - protocol: TCP + port: 8080 # Allow egress to managed PostgreSQL (event stream uses a dedicated DB user # and pg_notify for event dispatch) {% if managed_database | bool %} From 4ce037fb4cabaf82c90cb0e623b8b2c8fe24ca80 Mon Sep 17 00:00:00 2001 From: Thomas Tuffin Date: Mon, 10 Aug 2026 16:35:12 +0200 Subject: [PATCH 08/14] Fix websocket token refresh --- roles/eda/templates/eda-api.networkpolicy.yaml.j2 | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/roles/eda/templates/eda-api.networkpolicy.yaml.j2 b/roles/eda/templates/eda-api.networkpolicy.yaml.j2 index 88bdd118..821998de 100644 --- a/roles/eda/templates/eda-api.networkpolicy.yaml.j2 +++ b/roles/eda/templates/eda-api.networkpolicy.yaml.j2 @@ -46,9 +46,10 @@ spec: port: {{ api_nginx_port }} - protocol: TCP port: {{ websocket_port }} - # Allow websocket connections from activation Job pods (created at runtime - # by activation workers, not operator-managed — carry only app: eda). - # ansible_rulebook connects to daphne via websocket for rulebook execution. + # Allow traffic from activation Job pods (created at runtime by activation + # workers, not operator-managed — carry only app: eda). + # ansible_rulebook connects to daphne (websocket_port) for rulebook + # execution and to the API (api_nginx_port) for token refresh. - from: - podSelector: matchLabels: @@ -56,6 +57,8 @@ spec: ports: - protocol: TCP port: {{ websocket_port }} + - protocol: TCP + port: {{ api_nginx_port }} egress: # Allow DNS resolution via OpenShift DNS - to: From 02559026f1ee78ed0816953ede203d2e66543bea Mon Sep 17 00:00:00 2001 From: Thomas Tuffin Date: Mon, 10 Aug 2026 17:06:38 +0200 Subject: [PATCH 09/14] Allow egress for external sms --- roles/eda/templates/eda-api.networkpolicy.yaml.j2 | 5 +++++ roles/eda/templates/eda-event-stream.networkpolicy.yaml.j2 | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/roles/eda/templates/eda-api.networkpolicy.yaml.j2 b/roles/eda/templates/eda-api.networkpolicy.yaml.j2 index 821998de..46d26de8 100644 --- a/roles/eda/templates/eda-api.networkpolicy.yaml.j2 +++ b/roles/eda/templates/eda-api.networkpolicy.yaml.j2 @@ -96,3 +96,8 @@ spec: port: 8000 - protocol: TCP port: 8080 + # Allow egress to user-configured external services (external secret + # managers such as HashiCorp Vault, fetched by daphne during websocket + # handshake when credentials use external SMS). Destinations are + # user-defined and cannot be enumerated at policy creation time. + - {} diff --git a/roles/eda/templates/eda-event-stream.networkpolicy.yaml.j2 b/roles/eda/templates/eda-event-stream.networkpolicy.yaml.j2 index 5cb5c194..81bd6447 100644 --- a/roles/eda/templates/eda-event-stream.networkpolicy.yaml.j2 +++ b/roles/eda/templates/eda-event-stream.networkpolicy.yaml.j2 @@ -67,6 +67,11 @@ spec: port: 8000 - protocol: TCP port: 8080 + # Allow egress to user-configured external services (external secret + # managers such as HashiCorp Vault, fetched when event stream credentials + # use external SMS). Destinations are user-defined and cannot be + # enumerated at policy creation time. + - {} # Allow egress to managed PostgreSQL (event stream uses a dedicated DB user # and pg_notify for event dispatch) {% if managed_database | bool %} From 8065a2ccc7148a60741b4fefcba91068655cd642 Mon Sep 17 00:00:00 2001 From: Thomas Tuffin Date: Mon, 10 Aug 2026 17:26:10 +0200 Subject: [PATCH 10/14] Consolidate egress rules --- ...da-activation-worker.networkpolicy.yaml.j2 | 33 ++-------------- .../templates/eda-api.networkpolicy.yaml.j2 | 32 ++-------------- .../eda-default-worker.networkpolicy.yaml.j2 | 33 ++-------------- .../eda-event-stream.networkpolicy.yaml.j2 | 38 +++---------------- 4 files changed, 16 insertions(+), 120 deletions(-) diff --git a/roles/eda/templates/eda-activation-worker.networkpolicy.yaml.j2 b/roles/eda/templates/eda-activation-worker.networkpolicy.yaml.j2 index 5e49317b..d2f09ec6 100644 --- a/roles/eda/templates/eda-activation-worker.networkpolicy.yaml.j2 +++ b/roles/eda/templates/eda-activation-worker.networkpolicy.yaml.j2 @@ -33,33 +33,8 @@ spec: port: 5353 - protocol: UDP port: 5353 - # Allow egress to managed PostgreSQL (task queue and pg_notify) -{% if managed_database | bool %} - - to: - - podSelector: - matchLabels: - app.kubernetes.io/component: database - app.kubernetes.io/instance: 'postgres-{{ supported_pg_version }}-{{ ansible_operator_meta.name }}' - ports: - - protocol: TCP - port: {{ eda_postgres_port | default('5432') }} -{% else %} - # External database — destination is user-configured, allow on the port - - ports: - - protocol: TCP - port: {{ eda_postgres_port | default('5432') }} -{% endif %} - # Allow egress to the gateway API for JWT validation - - to: - - podSelector: - matchLabels: - app.kubernetes.io/managed-by: 'aap-operator' - ports: - - protocol: TCP - port: 8000 - - protocol: TCP - port: 8080 - # Activation workers create and manage Job pods via the Kubernetes API. - # Unrestricted egress is required because event source destinations are - # user-defined and cannot be enumerated at policy creation time. + # Unrestricted egress required: activation workers create and manage Job + # pods via the Kubernetes API, connect to user-defined event source + # destinations, and may call external SMS. Destinations cannot be + # enumerated at policy creation time. - {} diff --git a/roles/eda/templates/eda-api.networkpolicy.yaml.j2 b/roles/eda/templates/eda-api.networkpolicy.yaml.j2 index 46d26de8..97764c31 100644 --- a/roles/eda/templates/eda-api.networkpolicy.yaml.j2 +++ b/roles/eda/templates/eda-api.networkpolicy.yaml.j2 @@ -70,34 +70,8 @@ spec: port: 5353 - protocol: UDP port: 5353 - # Allow egress to managed PostgreSQL -{% if managed_database | bool %} - - to: - - podSelector: - matchLabels: - app.kubernetes.io/component: database - app.kubernetes.io/instance: 'postgres-{{ supported_pg_version }}-{{ ansible_operator_meta.name }}' - ports: - - protocol: TCP - port: {{ eda_postgres_port | default('5432') }} -{% else %} - # External database — destination is user-configured, allow on the port - - ports: - - protocol: TCP - port: {{ eda_postgres_port | default('5432') }} -{% endif %} - # Allow egress to the gateway API for JWT validation and service mesh calls - - to: - - podSelector: - matchLabels: - app.kubernetes.io/managed-by: 'aap-operator' - ports: - - protocol: TCP - port: 8000 - - protocol: TCP - port: 8080 - # Allow egress to user-configured external services (external secret - # managers such as HashiCorp Vault, fetched by daphne during websocket - # handshake when credentials use external SMS). Destinations are + # Unrestricted egress required for user-configured external services + # (external SMS such as HashiCorp Vault, fetched by daphne during websocket + # handshake; gateway JWT key fetch; postgres). Destinations are # user-defined and cannot be enumerated at policy creation time. - {} diff --git a/roles/eda/templates/eda-default-worker.networkpolicy.yaml.j2 b/roles/eda/templates/eda-default-worker.networkpolicy.yaml.j2 index 0d89ba45..5cc98cd7 100644 --- a/roles/eda/templates/eda-default-worker.networkpolicy.yaml.j2 +++ b/roles/eda/templates/eda-default-worker.networkpolicy.yaml.j2 @@ -34,33 +34,8 @@ spec: port: 5353 - protocol: UDP port: 5353 - # Allow egress to managed PostgreSQL (dispatcherd task queue) -{% if managed_database | bool %} - - to: - - podSelector: - matchLabels: - app.kubernetes.io/component: database - app.kubernetes.io/instance: 'postgres-{{ supported_pg_version }}-{{ ansible_operator_meta.name }}' - ports: - - protocol: TCP - port: {{ eda_postgres_port | default('5432') }} -{% else %} - # External database — destination is user-configured, allow on the port - - ports: - - protocol: TCP - port: {{ eda_postgres_port | default('5432') }} -{% endif %} - # Allow egress to the gateway API for JWT validation and service calls - - to: - - podSelector: - matchLabels: - app.kubernetes.io/managed-by: 'aap-operator' - ports: - - protocol: TCP - port: 8000 - - protocol: TCP - port: 8080 - # Default-worker executes user-defined tasks (git clones, SCM syncs, - # webhook calls). Destinations are user-configured and cannot be - # enumerated at policy creation time. + # Unrestricted egress required: default-worker executes user-defined tasks + # (git clones, SCM syncs, webhook calls) and may call external SMS. + # Destinations are user-configured and cannot be enumerated at policy + # creation time. - {} diff --git a/roles/eda/templates/eda-event-stream.networkpolicy.yaml.j2 b/roles/eda/templates/eda-event-stream.networkpolicy.yaml.j2 index 81bd6447..f853c346 100644 --- a/roles/eda/templates/eda-event-stream.networkpolicy.yaml.j2 +++ b/roles/eda/templates/eda-event-stream.networkpolicy.yaml.j2 @@ -55,37 +55,9 @@ spec: port: 5353 - protocol: UDP port: 5353 - # Allow egress to the gateway API for JWT public key fetch - # (ansible_base cert.py calls the gateway on every event stream POST to - # validate the X-Trusted-Proxy header) - - to: - - podSelector: - matchLabels: - app.kubernetes.io/managed-by: 'aap-operator' - ports: - - protocol: TCP - port: 8000 - - protocol: TCP - port: 8080 - # Allow egress to user-configured external services (external secret - # managers such as HashiCorp Vault, fetched when event stream credentials - # use external SMS). Destinations are user-defined and cannot be - # enumerated at policy creation time. + # Unrestricted egress required: event stream validates the X-Trusted-Proxy + # header by fetching the gateway JWT public key, connects to postgres via + # pg_notify, and may call external SMS when event stream credentials use + # an external secret manager. Destinations cannot be enumerated at policy + # creation time. - {} - # Allow egress to managed PostgreSQL (event stream uses a dedicated DB user - # and pg_notify for event dispatch) -{% if managed_database | bool %} - - to: - - podSelector: - matchLabels: - app.kubernetes.io/component: database - app.kubernetes.io/instance: 'postgres-{{ supported_pg_version }}-{{ ansible_operator_meta.name }}' - ports: - - protocol: TCP - port: {{ eda_postgres_port | default('5432') }} -{% else %} - # External database — destination is user-configured, allow on the port - - ports: - - protocol: TCP - port: {{ eda_postgres_port | default('5432') }} -{% endif %} From b1002d6384b9808040a7b5724b052c971afbf640 Mon Sep 17 00:00:00 2001 From: Thomas Tuffin Date: Tue, 11 Aug 2026 16:23:55 +0200 Subject: [PATCH 11/14] Test tightening --- molecule/default/tasks/eda_test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/molecule/default/tasks/eda_test.yml b/molecule/default/tasks/eda_test.yml index 722aa1a2..3827723f 100644 --- a/molecule/default/tasks/eda_test.yml +++ b/molecule/default/tasks/eda_test.yml @@ -33,7 +33,8 @@ - np_default_deny.resources[0].spec.podSelector.matchExpressions | length == 1 - np_default_deny.resources[0].spec.podSelector.matchExpressions[0].key == 'app.kubernetes.io/managed-by' - np_default_deny.resources[0].spec.podSelector.matchExpressions[0].operator == 'In' - - "'eda-operator' in np_default_deny.resources[0].spec.podSelector.matchExpressions[0].values" + - np_default_deny.resources[0].spec.podSelector.matchExpressions[0].values | length == 1 + - np_default_deny.resources[0].spec.podSelector.matchExpressions[0].values[0] == deployment_type + '-operator' - "'Ingress' in np_default_deny.resources[0].spec.policyTypes" - "'Egress' in np_default_deny.resources[0].spec.policyTypes" - np_default_deny.resources[0].spec.ingress is not defined or np_default_deny.resources[0].spec.ingress | length == 0 From 302107e5cc01818a6a8eb9568e298705c9fedc5d Mon Sep 17 00:00:00 2001 From: Thomas Tuffin Date: Mon, 17 Aug 2026 12:56:20 +0200 Subject: [PATCH 12/14] Load postgres role vars --- roles/eda/defaults/main.yml | 3 --- roles/eda/tasks/networkpolicy.yml | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/roles/eda/defaults/main.yml b/roles/eda/defaults/main.yml index 78953f17..21b8c3f3 100644 --- a/roles/eda/defaults/main.yml +++ b/roles/eda/defaults/main.yml @@ -8,9 +8,6 @@ api_version: '{{ deployment_type }}.ansible.com/v1alpha1' no_log: true ui_disabled: false -# Keep in sync with roles/postgres/vars/main.yml -supported_pg_version: 15 - image_pull_policy: IfNotPresent image_pull_secrets: [] diff --git a/roles/eda/tasks/networkpolicy.yml b/roles/eda/tasks/networkpolicy.yml index 61d200ea..98d1f7b9 100644 --- a/roles/eda/tasks/networkpolicy.yml +++ b/roles/eda/tasks/networkpolicy.yml @@ -1,4 +1,8 @@ --- +- name: Load postgres role vars for supported_pg_version + ansible.builtin.include_vars: + file: "{{ role_path }}/../postgres/vars/main.yml" + - name: Apply default deny NetworkPolicy for EDA operand pods kubernetes.core.k8s: state: present From f0ae75e882f7263a3d17845b802aeb3293df0b57 Mon Sep 17 00:00:00 2001 From: Thomas Tuffin Date: Wed, 19 Aug 2026 16:49:23 +0200 Subject: [PATCH 13/14] Address review comments --- roles/eda/tasks/networkpolicy.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/roles/eda/tasks/networkpolicy.yml b/roles/eda/tasks/networkpolicy.yml index 98d1f7b9..49a0cd5c 100644 --- a/roles/eda/tasks/networkpolicy.yml +++ b/roles/eda/tasks/networkpolicy.yml @@ -18,6 +18,7 @@ - name: Apply EDA UI NetworkPolicy kubernetes.core.k8s: state: "{{ 'present' if not ui_disabled else 'absent' }}" + apply: true definition: "{{ lookup('template', 'eda-ui.networkpolicy.yaml.j2') }}" - name: Apply EDA default-worker NetworkPolicy From 8f0ed3e9ece5ef0b1f98efc78cb9c62cea679ead Mon Sep 17 00:00:00 2001 From: Thomas Tuffin Date: Thu, 20 Aug 2026 20:31:33 +0200 Subject: [PATCH 14/14] Address review feedback --- config/rbac/role.yaml | 15 --------------- .../templates/db-management.networkpolicy.yaml.j2 | 1 + .../eda-activation-worker.networkpolicy.yaml.j2 | 7 ------- .../eda-default-worker.networkpolicy.yaml.j2 | 8 -------- 4 files changed, 1 insertion(+), 30 deletions(-) diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 48246988..756ee138 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -79,21 +79,6 @@ rules: - patch - update - watch - - apiGroups: - - apps - resources: - - deployments - - daemonsets - - replicasets - - statefulsets - verbs: - - get - - list - - create - - delete - - patch - - update - - watch - apiGroups: - networking.k8s.io resources: diff --git a/roles/eda/templates/db-management.networkpolicy.yaml.j2 b/roles/eda/templates/db-management.networkpolicy.yaml.j2 index d4769502..d8f1c9c1 100644 --- a/roles/eda/templates/db-management.networkpolicy.yaml.j2 +++ b/roles/eda/templates/db-management.networkpolicy.yaml.j2 @@ -12,6 +12,7 @@ spec: app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' app.kubernetes.io/component: '{{ deployment_type }}' policyTypes: + - Ingress - Egress egress: # Allow DNS resolution via OpenShift DNS diff --git a/roles/eda/templates/eda-activation-worker.networkpolicy.yaml.j2 b/roles/eda/templates/eda-activation-worker.networkpolicy.yaml.j2 index d2f09ec6..059b8fe2 100644 --- a/roles/eda/templates/eda-activation-worker.networkpolicy.yaml.j2 +++ b/roles/eda/templates/eda-activation-worker.networkpolicy.yaml.j2 @@ -14,14 +14,7 @@ spec: app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' app.kubernetes.io/component: '{{ deployment_type }}-activation-worker' policyTypes: - - Ingress - Egress - ingress: - # Allow traffic only from other EDA operator-managed pods (task dispatch) - - from: - - podSelector: - matchLabels: - app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' egress: # Allow DNS resolution via OpenShift DNS - to: diff --git a/roles/eda/templates/eda-default-worker.networkpolicy.yaml.j2 b/roles/eda/templates/eda-default-worker.networkpolicy.yaml.j2 index 5cc98cd7..7f37444a 100644 --- a/roles/eda/templates/eda-default-worker.networkpolicy.yaml.j2 +++ b/roles/eda/templates/eda-default-worker.networkpolicy.yaml.j2 @@ -14,15 +14,7 @@ spec: app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' app.kubernetes.io/component: '{{ deployment_type }}-default-worker' policyTypes: - - Ingress - Egress - ingress: - # Allow traffic only from other EDA operator-managed pods (e.g. api pod - # dispatching tasks to the worker via dispatcherd/postgres LISTEN/NOTIFY) - - from: - - podSelector: - matchLabels: - app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' egress: # Allow DNS resolution via OpenShift DNS - to: