# 07 - Private Network Port Matrix

This file defines the ports that must be opened inside the Hetzner private network for test and prod environments. Ports open to the public internet will only be `22/tcp`, `80/tcp`, and `443/tcp`. Vault `8200/tcp` will not be opened publicly.

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

## Network Plan

### Test

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

### Prod

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

## Public Ingress Standard

Public ingress for all environments:

| 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` (DB node) | 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 |

`docker-stack-infra.yml` has been updated so that only the SWAG service publishes ports 80/443 in host mode. All other services contain no published ports; access is provided only through the `iklimco-net` overlay. This table remains the source for private ingress decisions.

## DB Node Ports

Because DB infrastructure will be installed manually, the exact cluster technology is outside this document. Still, the default ports for firewall purposes 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`, `6379/tcp`, `5672/tcp`, `61613/tcp`, `15674/tcp`, `2379/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 inside the private app/swarm subnet.
- 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.
