# 01 - Private Network Port Matrix

This file defines the ports that must be opened inside the Hetzner private network for test and prod environments. Public ingress is limited to `22/tcp`, `80/tcp`, and `443/tcp`, with one current test-only exception: `51820/udp` is public on the test DB node for WireGuard. Vault `8200/tcp` will not be opened publicly.

This matrix must be treated as the source for Terraform Hetzner firewall and Ansible `firewalld` rules.

## Network Plan

### Test

| Subnet | CIDR | Purpose |
| --- | --- | --- |
| App/Swarm | `10.10.10.0/24` | `iklim-app-01` |
| DB | `10.10.20.0/24` | `iklim-db-01` |

### Prod

| Subnet | CIDR | Purpose |
| --- | --- | --- |
| App/Swarm | `10.20.10.0/24` | `iklim-app-01/02/03` |
| DB | `10.20.20.0/24` | `iklim-db-01/02/03` |

## Public Ingress Standard

Public ingress:

| Port | Protocol | Source | Target | Requirement |
| --- | --- | --- | --- | --- |
| `22` | TCP | Admin IP/CIDR | All nodes | SSH management |
| `80` | TCP | Internet | `iklim-app-01` (gateway) | HTTP / ACME redirect |
| `443` | TCP | Internet | `iklim-app-01` (gateway) | HTTPS |
| `51820` | UDP | `0.0.0.0/0`, `::/0` | `iklim-db-01` in test only | WireGuard VPN — authentication with cryptographic key |

Critical ports that will not be opened publicly:

| Port | Service |
| --- | --- |
| `8200/tcp` | Vault |
| `5432/tcp` | PostgreSQL |
| `27017/tcp` | MongoDB |
| `6379/tcp` | Redis |
| `5672/tcp`, `15672/tcp`, `61613/tcp`, `15674/tcp` | RabbitMQ |
| `2377/tcp`, `7946/tcp`, `7946/udp`, `4789/udp` | Docker Swarm |
| `9180/tcp` | APISIX Admin API |
| `9090/tcp` | Prometheus |
| `3000/tcp` | Grafana |

## Docker Swarm Private Ports

Required ports between Docker Swarm nodes:

| Port | Protocol | Source | Target | Description |
| --- | --- | --- | --- | --- |
| `2377` | TCP | Swarm nodes | Swarm manager nodes | Swarm control plane / join |
| `7946` | TCP | All Swarm nodes | All Swarm nodes | Node discovery / gossip |
| `7946` | UDP | All Swarm nodes | All Swarm nodes | Node discovery / gossip |
| `4789` | UDP | All Swarm nodes | All Swarm nodes | Overlay VXLAN data path |

In test, these ports are effectively required for a single Swarm node, but they can be defined inside the app subnet to make adding workers easier later.

In prod, these ports must be open between all `iklim-app-*` nodes inside the `10.20.10.0/24` app/swarm subnet.

Source: Docker overlay network documentation, https://docs.docker.com/engine/network/drivers/overlay/

## Application and Infra Service Private Ports

These ports will not be opened publicly. Access will be allowed only from required sources inside the private network or Docker overlay.

| Port | Protocol | Service | Source | Target | Note |
| --- | --- | --- | --- | --- | --- |
| `8200` | TCP | Vault API/UI | Swarm app nodes / runner | Vault service/node | Public closed. Runtime services must reach Vault through private/overlay |
| `6379` | TCP | Redis | Swarm app nodes | Redis service/node | Public closed |
| `5672` | TCP | RabbitMQ AMQP | Swarm app nodes | RabbitMQ service/node | Public closed |
| `15672` | TCP | RabbitMQ Management | Admin CIDR or private ops | RabbitMQ service/node | Public closed; preferably VPN/bastion |
| `61613` | TCP | RabbitMQ STOMP | Required app nodes | RabbitMQ service/node | Public closed |
| `15674` | TCP | RabbitMQ Web STOMP | Required app/gateway nodes | RabbitMQ service/node | Public closed |
| `2379` | TCP | etcd client | APISIX service/node | etcd service/node | Public closed |
| `2380` | TCP | etcd peer | etcd cluster nodes | etcd cluster nodes | May not be needed for a single replica; required if clustered |
| `9180` | TCP | APISIX Admin API | Admin CIDR or private ops | APISIX service/node | Public closed |
| `9090` | TCP | Prometheus UI/API | Admin CIDR or private ops | Prometheus service/node | Public closed |
| `3000` | TCP | Grafana UI | Admin CIDR or private ops | Grafana service/node | Public closed |

The current prod root stack is `docker-stack-infra_db-prod.yml`; Vault is deployed separately with `docker-stack-vault.yml` through `vault-bootstrap.sh`. Public traffic is expected to enter through SWAG on 80/443. Private service reachability is provided by the `iklimco-net` overlay and by the explicit host-mode DB/cluster ports listed below.

## DB Node Ports

DB runtime services are deployed as Docker Swarm services. Prod currently uses Patroni/PostgreSQL, etcd, and a MongoDB replica set in `docker-stack-infra_db-prod.yml`; the required firewall ports are below.

### PostgreSQL / PostGIS (Patroni + etcd)

The prod environment uses PostgreSQL managed with Patroni + etcd. In the test environment, replication and HA ports are not required because there is a single node.

| Port | Protocol | Source | Target | Note |
| --- | --- | --- | --- | --- |
| `5432` | TCP | App/Swarm subnet | PostgreSQL nodes (Patroni-managed) | Application JDBC — connects to all nodes, driver finds the primary |
| `5432` | TCP | DB subnet | PostgreSQL nodes | Patroni replication (pg_basebackup and WAL streaming) |
| `8008` | TCP | DB subnet | PostgreSQL nodes | Patroni REST API — leader election, health check |
| `2379` | TCP | DB subnet | etcd nodes | etcd client — Patroni -> etcd access |
| `2380` | TCP | DB subnet | etcd nodes | etcd peer — raft protocol inside the etcd cluster |

### MongoDB

| Port | Protocol | Source | Target | Note |
| --- | --- | --- | --- | --- |
| `27017` | TCP | App/Swarm subnet | MongoDB node/replica set endpoint | Application DB connection |
| `27017` | TCP | DB subnet | MongoDB replica set nodes | Replica set internal traffic |

If sharding is added later, additional MongoDB roles such as `27018/27019` may come up; they will not be opened at this stage.

## Test Private Rules

Minimum for the test environment:

| Source | Target | Ports |
| --- | --- | --- |
| `10.10.10.0/24` | `10.10.10.0/24` | `2377/tcp`, `7946/tcp`, `7946/udp`, `4789/udp` |
| `10.10.10.0/24` | `10.10.20.0/24` | `5432/tcp`, `27017/tcp` |
| `10.10.10.0/24` | `10.10.10.0/24` | `8200/tcp`, `6379/tcp`, `5672/tcp`, `61613/tcp`, `15674/tcp` |
| Admin CIDR or VPN | `10.10.10.0/24` | `9000/tcp`, `15672/tcp`, `9180/tcp`, `9090/tcp`, `3000/tcp` |

Because the DB node is single-node in test, PostgreSQL/MongoDB replication ports inside the DB subnet may not be actively used.

## Prod Private Rules

Minimum for the prod environment, including Patroni + etcd:

App subnet (swarm firewall) — traffic inside itself:

| Source | Target | Ports |
| --- | --- | --- |
| `10.20.10.0/24` | `10.20.10.0/24` | `2377/tcp`, `7946/tcp`, `7946/udp`, `4789/udp` (Swarm) |
| `10.20.10.0/24` | `10.20.10.0/24` | `8200/tcp`, `5672/tcp`, `61613/tcp`, `15674/tcp` (application services) |
| Admin CIDR or VPN | `10.20.10.0/24` | `15672/tcp`, `9180/tcp`, `9090/tcp`, `3000/tcp` |

App -> DB traffic (there is no related rule in the swarm firewall; it is allowed in the db firewall):

| Source | Target | Ports |
| --- | --- | --- |
| `10.20.10.0/24` | `10.20.20.0/24` | `5432/tcp`, `27017/tcp` (DB access) |
| `10.20.10.0/24` | `10.20.20.0/24` | `2377/tcp`, `7946/tcp`, `7946/udp`, `4789/udp` (Swarm — DB worker join) |

DB subnet (db firewall) — traffic between DB nodes:

| Source | Target | Ports |
| --- | --- | --- |
| `10.20.20.0/24` | `10.20.20.0/24` | `5432/tcp`, `27017/tcp` (DB replication) |
| `10.20.20.0/24` | `10.20.20.0/24` | `2379/tcp`, `2380/tcp` (etcd client/peer) |
| `10.20.20.0/24` | `10.20.20.0/24` | `8008/tcp` (Patroni REST API) |

DB -> App traffic (allowed in the swarm firewall):

| Source | Target | Ports |
| --- | --- | --- |
| `10.20.20.0/24` | `10.20.10.0/24` | `2377/tcp`, `7946/tcp`, `7946/udp`, `4789/udp` (Swarm — manager ports) |

## Acceptance Criteria

- The public firewall does not open `8200/tcp`.
- DB ports are not open publicly.
- Swarm ports are open only between Swarm app and DB subnets.
- The App/Swarm subnet reaches the DB subnet only through required DB ports.
- The DB subnet is not opened to the app subnet with broad permissions.
- Admin UI ports are restricted through admin CIDR/VPN/private ops instead of public access.
