Network and Port Reference

This reference lists every network connection the Axual Platform makes, the port each one reaches, the protocol it carries, and the port that carries it once TLS is in use.

Type

Reference

Goal

Write a network rule that permits exactly the traffic the platform needs, and no more

Audience

An operator who can create NetworkPolicies, or change firewall or service mesh rules, in the cluster hosting the platform

When to use

When writing or auditing least-privilege network rules for the platform

Network overview

The diagram below maps the platform: which components talk to each other, the protocol each connection carries, and its main port. Use it to orient, and the tables that follow to write a rule.

Axual Platform network overview

Port and TLS conventions

Four properties of the model decide what a rule has to say:

Every port is a container port

A port in this reference is the port the process listens on inside the pod. A Kubernetes NetworkPolicy matches that port, never the port on the Service object. Most Governance components publish a Service on port 80 that forwards to container port 8080. A policy written against 80 matches nothing, and silently drops the traffic it was meant to allow. Listen ports per component gives both numbers wherever they differ.

TLS does not work the same way on every component

Some components serve plain text or TLS on the one port, chosen by configuration. Some open a second port for TLS and keep both listening. Some do not serve TLS at all, and sit behind an Ingress or Route that terminates it. The TLS port column in Component flows gives the port that carries TLS for each connection, so a rule can be written for either state without working out which pattern applies.

Management ports are never TLS

The 8086 actuator ports, the Rest Proxy’s 8081, and the Prometheus endpoints on 9404, 5555 and 2112 all serve plain HTTP with no TLS option in the chart. The 5005 debug ports are plain TCP, and exist only when debugging is switched on. Scrape rules from the monitoring namespace stay plain regardless of how the rest of the platform is configured.

TLS is off by default where it is configurable

The shipped chart defaults leave tls.enabled at false for Platform Manager, and kafka.internalListenerTlsEnabled at false for the Kafka internal listener. A default install therefore carries plain HTTP and plain Kafka on connections that can carry TLS.

tls.enabled does not by itself make a component serve HTTPS. On Platform Manager it also needs tls.serverKeypairSecretName, which is what sets SERVER_SSL_KEY_STORE. The Metrics Exposer accepts the same two values and does build a server keystore from them, in an init container, but nothing then points its HTTP connector at that keystore: its templates set no SERVER_SSL_KEY_STORE, its configuration file sets no server.ssl properties, and the only TLS options it passes to the JVM are client-side. It goes on listening in plain text on 9080.

Component flows

Each row is one connection and translates to one policy rule. Port is the port on the receiving pod under the shipped chart defaults, and Protocol is what it carries there. TLS port is the port that carries the same connection once TLS is in use, which is often the same number and sometimes a different one. Component overview lists the component abbreviations.

Category Sender Receiver Port Protocol TLS port Data exchanged

Governance

AG

PM

8080

HTTP

8080

Self-Service API calls proxied to Platform Manager

AG

UI

8080

HTTP

2

Platform UI static assets

AG

TB

8080

HTTP

2

Topic Browse API calls

AG

ME

9080

HTTP

2

Metrics Exposer API calls

AG

KC

8080

HTTP

8443

Token introspection and authorisation

PM

KC

8080

HTTP

8443

User information and token validation

PM

DB

3306

MySQL

3306

Topic, application, environment, user and role metadata

PM

V

8200

HTTP

8200

Secrets, private keys and certificates

KC

DB

3306

MySQL

3306

Keycloak realm and user configuration

TB

PM

8080

HTTP

8080

Topic authorisation metadata

Streaming

PM

SR

8080

HTTP

8443

Schema registration and retrieval

PM

B

9093

Kafka

9093

Topic and Access Control List (ACL) configuration through the Kafka AdminClient

TB

SR

8080

HTTP

8443

Schemas used to deserialise topic data

TB

B

9093

Kafka

9093

Topic message content

SR

B

9093

Kafka

9093

Schema storage on a Kafka topic

RP

SR

8080

HTTP

8443

Schemas and schema IDs

RP

B

9093

Kafka

9093

Produced and consumed messages

SR

KC

8080

HTTP

8443

OIDC token validation, only when security.authentication.enabled is set

Kafka internal

B

C

9090

Kafka

9090 1

KRaft controller quorum

B

B

9091

Kafka

9091 1

Inter-broker replication

Run-time

PM

Ververica

4

HTTPS

4

Flink application lifecycle control requests, only when Flink support is enabled

PM

AC

11000

HTTP

11000

Connector lifecycle control requests

PM

RTP

8000

HTTP

2

KSML application lifecycle control and Kafka Connect log requests

AC

B

9093

Kafka

9093

Connector config, offset and status topics plus connector data

AC

V

8200

HTTP

8200

Connector secrets, private keys and certificates

KSML

B

9093

Kafka

9093

Topic message content

KSML

SR

8080

HTTP

8443

Schemas used to describe topic data

RTP

Kube API server

443

HTTPS

443 1

Creates and monitors KSML application deployments, reads Kafka Connect worker logs

Distribution

D

B

9096

Kafka

9096 1

Reads from the source cluster

D

Remote B

9096

Kafka

9096 1

Writes to the destination cluster

External ingress

Client

AG

8080

HTTP

2

All Self-Service and management traffic

Client

B

9094

Kafka

9094

External producer and consumer traffic

Client

SR

8443

HTTPS

8443 1

Schema lookups by client applications

Client

RP

18111

HTTPS

18111 1

Produce and consume over REST

MCP

AG

8080

HTTP

2

Platform operations issued from an AI agent

Identity

KC

Identity Provider

443

HTTPS

443 1

Identity verification over OIDC

Observability

Prometheus

AG

8086

HTTP

3

Scrapes /actuator/prometheus

Prometheus

PM

8086

HTTP

3

Scrapes /management/actuator/prometheus

Prometheus

TB

8086

HTTP

3

Scrapes /actuator/prometheus

Prometheus

ME

8086

HTTP

3

Scrapes /management/actuator/prometheus

Prometheus

KC

9000

HTTP

3

Scrapes the Keycloak management listener

Prometheus

SR

8080

HTTP

8443

Scrapes /metrics on the Registry API port; the shipped ServiceMonitor uses plain HTTP on 8080

Prometheus

RP

8081

HTTP

3

Scrapes /actuator/prometheus

Prometheus

B

9404

HTTP

3

Scrapes the broker JMX exporter

Prometheus

C

9404

HTTP

3

Scrapes the controller JMX exporter

Prometheus

AC

5555

HTTP

3

Scrapes Kafka Connect metrics

Prometheus

RTP

2112

HTTP

3

Scrapes Runtime Provisioner metrics

Prometheus

D

9404

HTTP

3

Scrapes the Axual Distributor JMX exporter, only when connect.metrics.enabled is set

ME

Prometheus

9090

HTTP

3

Queries the figures it exposes to dashboards

1 The port is TLS-only and has no plain-text mode, so the connection is already encrypted under the shipped defaults.

2 The pod always listens in plain text on this port, because the chart configures no server-side TLS for it. The ingress.tls and route.tls values hold the certificate that the Ingress or Route uses to terminate TLS in front of the pod; the pod behind it still receives plain HTTP. A rule for this hop therefore never becomes a TLS rule.

3 A management or metrics port. These serve plain traffic only and have no TLS option in the chart, whether or not the rest of the platform uses TLS, so a scrape rule never becomes a TLS rule.

4 The port of the Ververica Platform REST API, taken from the Flink URL configured on the Instance. It is operator-supplied, so the chart has no default for it.

Listen ports per component

Container is the port inside the pod and the value a NetworkPolicy matches. Service is the port published on the Kubernetes Service, used by clients addressing the component through DNS. TLS port is the port that carries this traffic once TLS is in use. A dash in Service means the port is not published on a Service.

Component Container Service TLS port Protocol Purpose

API Gateway (AG)

8080

80

2

HTTP

Self-Service and management API

8086

8086

3

HTTP

Actuator health and Prometheus metrics

5005

-

3

TCP

Remote JVM debug, only when debug is set

Platform Manager (PM)

8080

80

8080

HTTP

Platform Manager API

8086

8086

3

HTTP

Actuator health and Prometheus metrics

5005

-

3

TCP

Remote JVM debug, only when debug is set

Platform UI (UI)

8080

80

2

HTTP

Web interface

Topic Browse (TB)

8080

80

2

HTTP

Topic Browse API

8086

8086

3

HTTP

Actuator health and Prometheus metrics

5005

-

3

TCP

Remote JVM debug, only when debug is set

Metrics Exposer (ME)

9080

80

2

HTTP

Metrics Exposer API

8086

8086

3

HTTP

Actuator health and Prometheus metrics

5005

-

3

TCP

Remote JVM debug, only when debug is set

Keycloak (KC)

8080

80

8443

HTTP

Authentication

9000

9000

3

HTTP

Health and metrics

8443

8443

8443 1

HTTPS

Authentication over TLS

Platform Manager MySQL (DB)

3306

3306

3306

MySQL

Platform Manager database

Keycloak MySQL (DB)

3306

3306

3306

MySQL

Keycloak database

Vault (V)

8200

8200

8200

HTTP

Secrets API

8201

8201

8201 1

HTTPS

Cluster replication between Vault nodes

8202

-

3

HTTP

Replication traffic between Vault nodes

Kafka broker (B)

9093

9093

9093

Kafka

In-cluster client traffic

9094

9094

9094

Kafka

External client traffic

9095

9095

9095 1

Kafka

Simple Authentication and Security Layer (SASL) SCRAM-SHA-512 client traffic, only when kafka.scramsha512listener.enabled is set

9096

9096

9096 1

Kafka

Cross-cluster traffic used by Axual Distributor, only when kafka.interClusterListener.enabled is set

9097

9097

9097 1

Kafka

OAuth client traffic, only when kafka.oauthListener.enabled is set

9091

-

9091 1

Kafka

Inter-broker replication, also used by the Strimzi operator

9404

-

3

HTTP

JMX Prometheus exporter

8443

-

8443 1

HTTPS

Strimzi Kafka Agent readiness

KRaft controller (C)

9090

-

9090 1

Kafka

Controller quorum and metadata replication

9404

-

3

HTTP

JMX Prometheus exporter

8443

-

8443 1

HTTPS

Strimzi Kafka Agent readiness

Apicurio Registry (SR)

8080

20500

8443

HTTP

Registry API, and Prometheus metrics on /metrics

8443

21500

8443 1

HTTPS

Registry API over TLS

5005

-

3

TCP

Remote JVM debug, only when debug.enabled is set

Rest Proxy (RP)

18111

18111

18111 1

HTTPS

Rest Proxy API

8081

8081

3

HTTP

Actuator health and Prometheus metrics

5005

-

3

TCP

Remote JVM debug, only when debug.enabled is set

Axual Connect (AC)

11000

11000

11000

HTTP

Kafka Connect REST API, set by service.port

5555

5555

3

HTTP

Prometheus metrics, set by prometheusService.port

Axual Distributor (D)

8083

-

3

HTTP

Kafka Connect REST API

9404

-

3

HTTP

JMX Prometheus exporter, only when connect.metrics.enabled is set

Runtime Provisioner (RTP)

8000

80

2

HTTP

Provisioner API

2112

2112

3

HTTP

Prometheus metrics

1 The port is TLS-only and has no plain-text mode.

2 The pod always listens in plain text on this port, because the chart configures no server-side TLS for it. The ingress.tls and route.tls values hold the certificate that the Ingress or Route uses to terminate TLS in front of the pod; the pod behind it still receives plain HTTP.

3 A management, metrics or debug port. These serve plain traffic only and have no TLS option in the chart, whether or not the rest of the platform uses TLS.

The Apicurio Registry serves 8080 and 8443 at the same time, so choosing TLS here means choosing the other port rather than reconfiguring the same one. Its Service publishes them on 20500 and 21500, the widest gap on the platform between a Service port and the container port a NetworkPolicy has to match.

Two components carry no metrics port. The two MySQL deployments run without the Bitnami exporter, which the chart leaves disabled, and the Platform UI is nginx serving static files, so it has nothing to expose.

No Axual chart declares the Kafka broker’s 9090, 9091, 9404 and 8443, or either of Axual Distributor’s ports. Those belong to the Strimzi operator, so the numbers above come from the Strimzi contract instead.

Example network policies

These manifests implement the flows above for a default namespace layout, with the Governance components in a namespace called axual, the Streaming components in one called kafka, and the Run-time components in one called connect. Each file lists the placeholders to replace at the top.

How the policies fit together

There is one policy per component, carrying both its ingress and its egress rules, so the complete rule for a component can be read in one place. Metrics are the exception: every scrape rule lives in one file instead, so the ports Prometheus needs can be read together.

Both ends of every connection need a rule. A policy restricts egress on the pod that opens the connection and ingress on the pod that accepts it, and Kubernetes drops the traffic unless both permit it.

Apply them in this order:

  1. The baseline, in each namespace that holds platform components.

  2. The layer policies for the layers deployed, in any order.

  3. The metrics policy, which needs the Run-time policy applied with it.

Baseline: deny all, restore DNS

The baseline denies all traffic in its namespace and then restores DNS egress. Without that DNS rule every pod fails to resolve service names, and the platform appears to hang rather than fail.

It belongs in each namespace rather than once per cluster, so the same pair of policies is applied to axual, kafka and connect with the namespace changed.

Components that initiate nothing, such as the Platform UI, declare only Ingress from here on and let the baseline govern their egress.

Baseline deny with DNS egress restored
# Baseline: deny everything in the namespace, then allow DNS back.
#
# Apply this first. Without the DNS rule every pod fails to resolve service names and the
# platform appears to hang rather than fail, which is the single most common mistake when
# introducing NetworkPolicies.
#
# Replace `axual` with the namespace holding the platform.
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: axual
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns-egress
  namespace: axual
spec:
  podSelector: {}
  policyTypes:
    - Egress
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53

Governance layer

One policy per Governance component, each carrying its own ingress and egress. The two databases are separate deployments labelled platform-manager-mysql and keycloak-mysql; no pod is labelled mysql.

The baseline does not reach either of them. The Bitnami MySQL chart creates its own NetworkPolicy for the pods it manages, allowing all egress and ingress on 3306, and Kubernetes combines policies by union rather than by precedence. A namespace-wide default-deny therefore leaves those pods as permissive as the chart left them.

To bring them under the baseline, set networkPolicy.enabled to false on both platform-manager-mysql and keycloak-mysql, then write the rules for 3306 yourself as the policy below does.

Governance layer
# Governance layer. See "Governance layer" in the Network and Port Reference, which explains
# the Bitnami MySQL policy this one has to work around.
#
# Placeholders to replace before applying:
#
#   namespace: axual              the namespace holding the Governance components
#   app.kubernetes.io/instance    the Helm release name, `axual` below
#   kafka / connect               the namespaces holding the Streaming and Run-time layers
#   axual-kafka                   the Kafka resource name; strimzi.io/name is that plus `-kafka`
#   monitoring / ingress-nginx    the namespaces holding Prometheus and the ingress controller
---
# The API Gateway is the only entry point into the Governance layer, and it fans out to
# every other component.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-gateway
  namespace: axual
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: api-gateway
      app.kubernetes.io/instance: axual
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: ingress-nginx
      ports:
        - protocol: TCP
          port: 8080
  egress:
    - to:
        - podSelector:
            matchLabels:
              app.kubernetes.io/name: platform-manager
        - podSelector:
            matchLabels:
              app.kubernetes.io/name: platform-ui
        - podSelector:
            matchLabels:
              app.kubernetes.io/name: topic-browse
        - podSelector:
            matchLabels:
              app.kubernetes.io/name: keycloak
      ports:
        - protocol: TCP
          port: 8080
    - to:
        - podSelector:
            matchLabels:
              app.kubernetes.io/name: metrics-exposer
      ports:
        - protocol: TCP
          port: 9080
---
# Platform Manager holds the platform state and reaches every backing service.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: platform-manager
  namespace: axual
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: platform-manager
      app.kubernetes.io/instance: axual
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app.kubernetes.io/name: api-gateway
        - podSelector:
            matchLabels:
              app.kubernetes.io/name: topic-browse
      ports:
        - protocol: TCP
          port: 8080
  egress:
    - to:
        - podSelector:
            matchLabels:
              app.kubernetes.io/name: keycloak
      ports:
        - protocol: TCP
          port: 8080
    - to:
        - podSelector:
            matchLabels:
              app.kubernetes.io/name: platform-manager-mysql
      ports:
        - protocol: TCP
          port: 3306
    - to:
        - podSelector:
            matchLabels:
              app.kubernetes.io/name: platform-manager-vault
      ports:
        - protocol: TCP
          port: 8200
    # Cross-namespace peers: both selectors in one peer, so the rule is those pods only.
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kafka
          podSelector:
            matchLabels:
              app.kubernetes.io/name: apicurio-registry
      ports:
        - protocol: TCP
          port: 8080
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kafka
          podSelector:
            matchLabels:
              strimzi.io/cluster: axual-kafka
              strimzi.io/kind: Kafka
              strimzi.io/name: axual-kafka-kafka
      ports:
        - protocol: TCP
          port: 9093
    # Connector and KSML provisioning. Drop both if the Run-time layer is not deployed.
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: connect
          podSelector:
            matchLabels:
              app.kubernetes.io/name: axual-connect
      ports:
        - protocol: TCP
          port: 11000
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: connect
          podSelector:
            matchLabels:
              app.kubernetes.io/name: runtime-provisioner
      ports:
        - protocol: TCP
          port: 8000
---
# Topic Browse reads topic data from Kafka and schemas from the registry, and asks Platform
# Manager for the connection details it needs.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: topic-browse
  namespace: axual
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: topic-browse
      app.kubernetes.io/instance: axual
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app.kubernetes.io/name: api-gateway
      ports:
        - protocol: TCP
          port: 8080
  egress:
    - to:
        - podSelector:
            matchLabels:
              app.kubernetes.io/name: platform-manager
      ports:
        - protocol: TCP
          port: 8080
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kafka
          podSelector:
            matchLabels:
              app.kubernetes.io/name: apicurio-registry
      ports:
        - protocol: TCP
          port: 8080
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kafka
          podSelector:
            matchLabels:
              strimzi.io/cluster: axual-kafka
              strimzi.io/kind: Kafka
              strimzi.io/name: axual-kafka-kafka
      ports:
        - protocol: TCP
          port: 9093
---
# The Platform UI serves static assets and initiates nothing, so it declares no egress.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: platform-ui
  namespace: axual
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: platform-ui
      app.kubernetes.io/instance: axual
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app.kubernetes.io/name: api-gateway
      ports:
        - protocol: TCP
          port: 8080
---
# The Metrics Exposer answers the Gateway on 9080 and queries Prometheus for the figures.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: metrics-exposer
  namespace: axual
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: metrics-exposer
      app.kubernetes.io/instance: axual
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app.kubernetes.io/name: api-gateway
      ports:
        - protocol: TCP
          port: 9080
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: monitoring
      ports:
        - protocol: TCP
          port: 9090
---
# Keycloak. Browsers reach it directly through the ingress during the login redirect.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: keycloak
  namespace: axual
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: keycloak
      app.kubernetes.io/instance: axual
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: ingress-nginx
        - podSelector:
            matchLabels:
              app.kubernetes.io/name: api-gateway
        - podSelector:
            matchLabels:
              app.kubernetes.io/name: platform-manager
        # Apicurio Registry OIDC token validation, when its chart enables authentication.
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kafka
          podSelector:
            matchLabels:
              app.kubernetes.io/name: apicurio-registry
      ports:
        - protocol: TCP
          port: 8080
  egress:
    - to:
        - podSelector:
            matchLabels:
              app.kubernetes.io/name: keycloak-mysql
      ports:
        - protocol: TCP
          port: 3306
    # Upstream identity provider. Replace the CIDR, or drop the rule if none is configured.
    - to:
        - ipBlock:
            cidr: 203.0.113.0/24
      ports:
        - protocol: TCP
          port: 443
---
# Platform Manager's database. Needs `networkPolicy.enabled: false` on the subchart to take
# effect; see the Governance layer notes.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: platform-manager-mysql
  namespace: axual
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: platform-manager-mysql
      app.kubernetes.io/instance: axual
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app.kubernetes.io/name: platform-manager
      ports:
        - protocol: TCP
          port: 3306
---
# Keycloak's database, with the same caveat about the chart's own NetworkPolicy.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: keycloak-mysql
  namespace: axual
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: keycloak-mysql
      app.kubernetes.io/instance: axual
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app.kubernetes.io/name: keycloak
      ports:
        - protocol: TCP
          port: 3306
---
# Vault. Validates service account tokens through the Kubernetes API, hence the egress rule.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: platform-manager-vault
  namespace: axual
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: platform-manager-vault
      app.kubernetes.io/instance: axual
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app.kubernetes.io/name: platform-manager
      ports:
        - protocol: TCP
          port: 8200
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: connect
          podSelector:
            matchLabels:
              app.kubernetes.io/name: axual-connect
      ports:
        - protocol: TCP
          port: 8200
  egress:
    # Replace with the API server endpoint: the address behind the `kubernetes` Service.
    - to:
        - ipBlock:
            cidr: 198.51.100.0/32
      ports:
        - protocol: TCP
          port: 443

Streaming layer

The Kafka pods are the exception to one policy per component. Strimzi creates them, and the Cluster Operator generates its own NetworkPolicy for them. That policy confines the internal ports 9090, 9091 and 8443 to cluster pods and the operator, and leaves the client listeners and the metrics port reachable from every source. The policy below covers what it does not.

Selecting the Kafka pods needs care. Each of the obvious labels has a limit:

  • strimzi.io/name is <cluster>-kafka on both brokers and controllers, so it cannot separate the two roles.

  • strimzi.io/broker-role and strimzi.io/controller-role do separate them, but both labels are present on every Kafka pod, with a value of "true" or "false". Match on the value, not on whether the label is present.

  • strimzi.io/cluster with strimzi.io/kind: Kafka also matches the Kafka Exporter when it is enabled. A rule meant for the whole cluster needs strimzi.io/name as well, which is what Strimzi’s own generated policies use.

The Rest Proxy is a smaller trap. Its chart sets app.kubernetes.io/instance to <release>-rest-proxy, where every other chart on the platform sets it to the release name alone, so a selector copied from another component matches nothing.

Streaming layer
# Streaming layer. See "Streaming layer" in the Network and Port Reference.
#
# Placeholders to replace before applying:
#
#   namespace: kafka              the namespace holding the Streaming components
#   axual-kafka                   the Kafka resource name; strimzi.io/name is that plus `-kafka`
#   app.kubernetes.io/instance    the Helm release name, `axual` below
#   axual / connect               the namespaces holding the Governance and Run-time layers
#   ingress-nginx                 the namespace holding the ingress controller
#   10.0.0.0/8                    the address range reaching the external listeners
---
# Egress for the Kafka pods, which the Strimzi-generated policy does not cover. Both roles
# are selected together: a broker reaches the controller quorum on 9090 and its peers on
# 9091, and a controller reaches the other controllers on 9090.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: kafka-egress
  namespace: kafka
spec:
  podSelector:
    matchLabels:
      strimzi.io/cluster: axual-kafka
      strimzi.io/kind: Kafka
      strimzi.io/name: axual-kafka-kafka
  policyTypes:
    - Egress
  egress:
    - to:
        - podSelector:
            matchLabels:
              strimzi.io/cluster: axual-kafka
              strimzi.io/kind: Kafka
              strimzi.io/name: axual-kafka-kafka
      ports:
        - protocol: TCP
          port: 9090
        - protocol: TCP
          port: 9091
---
# The Apicurio Registry stores schemas on a Kafka topic, so it is a Kafka client as well as
# a server. It serves 8080 and 8443 at the same time; its Service publishes them on 20500
# and 21500, but a NetworkPolicy matches the container ports.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: apicurio-registry
  namespace: kafka
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: apicurio-registry
      app.kubernetes.io/instance: axual
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: axual
          podSelector:
            matchLabels:
              app.kubernetes.io/name: platform-manager
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: axual
          podSelector:
            matchLabels:
              app.kubernetes.io/name: topic-browse
        - podSelector:
            matchLabels:
              app.kubernetes.io/name: rest-proxy
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: connect
      ports:
        - protocol: TCP
          port: 8080
    # Schema lookups by client applications, terminating TLS at the pod on 8443.
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: ingress-nginx
      ports:
        - protocol: TCP
          port: 8443
  egress:
    - to:
        - podSelector:
            matchLabels:
              strimzi.io/cluster: axual-kafka
              strimzi.io/kind: Kafka
              strimzi.io/name: axual-kafka-kafka
      ports:
        - protocol: TCP
          port: 9093
    # OIDC token validation against Keycloak, which lives in the Governance namespace. Drop
    # this rule when `security.authentication.enabled` is left off.
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: axual
          podSelector:
            matchLabels:
              app.kubernetes.io/name: keycloak
      ports:
        - protocol: TCP
          port: 8080
---
# The Rest Proxy serves its API over TLS on 18111 and its management endpoints on 8081.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: rest-proxy
  namespace: kafka
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: rest-proxy
      # The Rest Proxy chart sets the instance label to `<release>-rest-proxy`, where every
      # other chart on the platform sets it to the release name alone.
      app.kubernetes.io/instance: axual-rest-proxy
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - ipBlock:
            cidr: 10.0.0.0/8
      ports:
        - protocol: TCP
          port: 18111
  egress:
    - to:
        - podSelector:
            matchLabels:
              strimzi.io/cluster: axual-kafka
              strimzi.io/kind: Kafka
              strimzi.io/name: axual-kafka-kafka
      ports:
        - protocol: TCP
          port: 9093
    - to:
        - podSelector:
            matchLabels:
              app.kubernetes.io/name: apicurio-registry
      ports:
        - protocol: TCP
          port: 8080

Run-time layer

These policies cover the pods the charts create: Axual Connect, the Runtime Provisioner and Axual Distributor. KSML application pods are not among them, because the Provisioner creates them at run time rather than a chart and their labels are not known ahead of time. Under a namespace default-deny those pods need rules of their own for the Kafka internal listener and the Apicurio Registry.

Axual Distributor runs as a Strimzi KafkaConnect resource, and Strimzi treats that differently from a Kafka resource: it generates no NetworkPolicy for it. Nothing therefore grants the Cluster Operator access to the Kafka Connect REST API on 8083, so the policy below does it explicitly. Without that rule the operator cannot manage connectors.

Run-time layer
# Run-time layer. See "Run-time layer" in the Network and Port Reference.
#
# Placeholders to replace before applying:
#
#   namespace: connect            the namespace holding the Run-time components
#   app.kubernetes.io/instance    the Helm release name, `axual` below
#   axual / kafka                 the namespaces holding the Governance and Streaming layers
#   axual-kafka                   the Kafka resource name; strimzi.io/name is that plus `-kafka`
#   strimzi                       the namespace running the Strimzi Cluster Operator
#   198.51.100.0/32               the Kubernetes API server endpoint
---
# Axual Connect accepts connector lifecycle calls from Platform Manager and reaches Kafka
# and Vault.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: axual-connect
  namespace: connect
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: axual-connect
      app.kubernetes.io/instance: axual
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: axual
          podSelector:
            matchLabels:
              app.kubernetes.io/name: platform-manager
      ports:
        - protocol: TCP
          port: 11000
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kafka
          podSelector:
            matchLabels:
              strimzi.io/cluster: axual-kafka
              strimzi.io/kind: Kafka
              strimzi.io/name: axual-kafka-kafka
      ports:
        - protocol: TCP
          port: 9093
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: axual
          podSelector:
            matchLabels:
              app.kubernetes.io/name: platform-manager-vault
      ports:
        - protocol: TCP
          port: 8200
---
# The Runtime Provisioner accepts application lifecycle calls from Platform Manager and creates
# KSML deployments through the Kubernetes API.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: runtime-provisioner
  namespace: connect
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: runtime-provisioner
      app.kubernetes.io/instance: axual
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: axual
          podSelector:
            matchLabels:
              app.kubernetes.io/name: platform-manager
      ports:
        - protocol: TCP
          port: 8000
  egress:
    # Replace the CIDR with the API server endpoint for the cluster. On a managed cluster
    # this is the address behind the `kubernetes` Service in the `default` namespace.
    - to:
        - ipBlock:
            cidr: 198.51.100.0/32
      ports:
        - protocol: TCP
          port: 443
---
# Axual Distributor runs as a Strimzi KafkaConnect resource, so Strimzi creates its pods and
# they carry Strimzi's labels. Unlike the Kafka resource, a KafkaConnect gets no generated
# NetworkPolicy, so the Cluster Operator's access to the REST API on 8083 has to be granted
# here. Without this rule the operator cannot manage connectors.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: distributor
  namespace: connect
spec:
  podSelector:
    matchLabels:
      strimzi.io/kind: KafkaConnect
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: strimzi
      ports:
        - protocol: TCP
          port: 8083
  egress:
    # The inter-cluster listener on the local cluster, present only when
    # `kafka.interClusterListener.enabled` is set.
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kafka
          podSelector:
            matchLabels:
              strimzi.io/cluster: axual-kafka
              strimzi.io/kind: Kafka
              strimzi.io/name: axual-kafka-kafka
      ports:
        - protocol: TCP
          port: 9096
    # The destination cluster, which is outside this cluster. Replace the CIDR with the
    # address range of the remote brokers.
    - to:
        - ipBlock:
            cidr: 203.0.113.0/24
      ports:
        - protocol: TCP
          port: 9096

Metrics scraping

Every scrape rule lives here rather than spread across the layer policies, so the ports Prometheus needs can be read in one place and applied as their own step. The file covers the Governance management ports, the Keycloak management listener, the Apicurio Registry metrics endpoint on its API port, the Rest Proxy actuator, and the Run-time metrics ports.

It leaves out the Kafka brokers and controllers. Their 9404 is already open to every source in the policy Strimzi generates, so a rule here would add nothing.

Apply the Run-time policy alongside this one. Selecting a pod with an Ingress policy closes every port that policy does not list, so these rules on their own cut Platform Manager off from Axual Connect on 11000 and the Runtime Provisioner on 8000, and connector and KSML provisioning stop.
Metrics scraping
# Metrics scraping. See "Metrics scraping" in the Network and Port Reference, which explains
# why the Run-time policy has to be applied alongside this one.
#
# Placeholders to replace before applying:
#
#   axual / kafka / connect       the namespaces holding the three layers
#   monitoring                    the namespace running Prometheus
#   app.kubernetes.io/instance    the Helm release name, `axual` below
---
# Governance: the Spring Boot components share one management port, and Keycloak keeps its
# own management listener.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: monitoring-scrape-governance
  namespace: axual
spec:
  podSelector:
    matchExpressions:
      - key: app.kubernetes.io/name
        operator: In
        values:
          - api-gateway
          - platform-manager
          - topic-browse
          - metrics-exposer
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: monitoring
      ports:
        - protocol: TCP
          port: 8086
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: monitoring-scrape-keycloak
  namespace: axual
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: keycloak
      app.kubernetes.io/instance: axual
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: monitoring
      ports:
        - protocol: TCP
          port: 9000
---
# Streaming: the Apicurio Registry serves metrics on its API port rather than a separate
# management port, so this rule opens 8080 to the monitoring namespace.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: monitoring-scrape-apicurio-registry
  namespace: kafka
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: apicurio-registry
      app.kubernetes.io/instance: axual
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: monitoring
      ports:
        - protocol: TCP
          port: 8080
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: monitoring-scrape-rest-proxy
  namespace: kafka
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: rest-proxy
      # The Rest Proxy chart sets the instance label to `<release>-rest-proxy`, where every
      # other chart on the platform sets it to the release name alone.
      app.kubernetes.io/instance: axual-rest-proxy
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: monitoring
      ports:
        - protocol: TCP
          port: 8081
---
# Run-time: Axual Connect and the Runtime Provisioner each expose their own metrics port.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: monitoring-scrape-axual-connect
  namespace: connect
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: axual-connect
      app.kubernetes.io/instance: axual
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: monitoring
      ports:
        - protocol: TCP
          port: 5555
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: monitoring-scrape-runtime-provisioner
  namespace: connect
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: runtime-provisioner
      app.kubernetes.io/instance: axual
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: monitoring
      ports:
        - protocol: TCP
          port: 2112
---
# Axual Distributor runs as a Strimzi KafkaConnect resource, so its pods carry Strimzi's
# labels. Selecting on the kind matches them without naming the tenant and instance the
# resource name is built from. The port exists only when `connect.metrics.enabled` is set.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: monitoring-scrape-distributor
  namespace: connect
spec:
  podSelector:
    matchLabels:
      strimzi.io/kind: KafkaConnect
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: monitoring
      ports:
        - protocol: TCP
          port: 9404