# Firewall and ports

VPS port forwards, SSH with expiry and allowed or excluded IP lists in the host firewall.

Source: https://ardumaker.com/en/docs/firewall-y-puertos/

- Category: Servers and infrastructure
- Status: Available
- Requirements: A company VPS with a host assigned; To view and create port forwards: being a company member with access to the VPS; To change the IP policy: being a company admin
- MCP tools (Read only): `listar_redirecciones_vps`, `ver_politica_ip_puertos_vps`
- MCP tools (Write or modify): `crear_redireccion_vps`, `cambiar_politica_ip_puertos_vps`, `cambiar_lista_ip_app`
- MCP tools (Destructive): `eliminar_redireccion_vps`

## What can you do with it?

- Expose a TCP or UDP port from inside the VPS on a public host port, assigned automatically.
- Open SSH (port 22) for a number of hours, closing automatically when it expires.
- Decide which IPs reach the forwarded ports: everyone, only a list, or everyone except a list; for the whole VPS or for a single port.
- See which forwards belong to the platform (they are not touched) and which ones are yours.

## When should you use it?

- A non-HTTP service (a game, an MQTT broker, a VPN server) that needs its own port.
- SSH-ing in from your computer during a work window.
- Making a port reachable only from the office or from a customer's IP.

> **INFO:** A website does not need open ports: domains come in through the host's port 443 and reach the VPS nginx. See [Nginx](https://ardumaker.com/en/docs/nginx).

## How does it work in ArduMaker?

### Port forwards

1. You give the port inside the VPS (`container_port`) and the protocol (`tcp` by default, or `udp`).
2. If a forward to that port and protocol already exists, the same one comes back: it is not duplicated.
3. The host picks a free port (`host_port`) and creates the forward to the VPS.
4. In the host firewall (`ufw`) that port is opened to the whole internet, unless the port is restricted or there is a global IP policy, which the new forward inherits.
5. From outside it is reached at `<VPS dnsip>:<host_port>`.

### Ports that are never public

- PostgreSQL (5432), MySQL/MariaDB (3306) and Redis (6379, or the port it was installed on, and its TLS port): the forward is created, but it is only reachable over the platform's internal network. To open a database to the outside, use Apps external access with its IP list.
- The ArduMaker agent port is reserved: creation rejects it.
- The forward to the VPS port 443 belongs to the VPS nginx: it is never opened to the internet nor to other VPS.

### SSH with expiry

A forward to port 22 accepts `closed_in_hs`: once those hours pass, the platform deletes it and writes it to the VPS log. The listing shows `is_ssh: true` and the remaining hours. Without `closed_in_hs`, SSH stays open until it is deleted.

### IP policy

| Mode | Rule in the host firewall | Use |
| --- | --- | --- |
| `todos` | `allow <port>` | Open to anyone (the default) |
| `solo` | One `allow from <ip>` per IP; the rest falls into the default `deny` | Allow list; access from other VPS on the host is also closed |
| `excluir` | A prepended `deny from <ip>` plus the general `allow` | Block specific IPs |

- `alcance: "global"` applies to every forward of the VPS; `alcance: "puerto"` to one `host_port` and wins over the global one.
- Lists accept IPv4 and IPv6, with or without a prefix. By default, up to 20 entries and prefixes no wider than /24 (IPv4) or /48 (IPv6). Never `0.0.0.0/0` or `::/0`, nor loopback, multicast or link-local.
- It is applied immediately: new rules are added first and old ones are removed afterwards, so there is never an extra open gap nor a sudden cut for everyone.
- Platform ports are not touched. The port of an Apps app is changed through Apps (same list; `excluir` is not available there).

## What actually changes?

- On the host: the forward (a proxy device towards the VPS) and its `ufw` rules. Nothing changes inside the VPS.
- When a forward is deleted, its IP rules are restored and its policies forgotten first, and then the general rule is removed.
- The policy is stored with its status (`aplicada`, `pendiente` or `fallida`) and shown by `ver_politica_ip_puertos_vps`.
- Every creation, deletion or policy change is written to the VPS activity log.

## Real IP and platform ports

The IP filter lives on the host, **before** the forward: that is where the client's real IP is visible. The service inside the VPS, on the other hand, sees connections from a TCP forward as if they came from `127.0.0.1`. If your app needs the client IP, publish it as a website (through nginx), where it arrives in `X-Real-IP`.

| Classification (`motivo_plataforma`) | What it is | Editable? |
| --- | --- | --- |
| `agente` | ArduMaker agent | No (not listed) |
| `nginx_del_vps` | Web entry to the VPS port 443 | No |
| `tailnet` | Databases and Redis, internal network only | No |
| `apps` | Created by Apps (external access or an app URL) | From Apps |
| (empty) | Yours | Yes |

## Diagram

**A TCP connection to a forwarded port**

```text
Client (<dnsip>:<host_port>)
  ↓
Host firewall (ufw) (todos / solo / excluir; sees the real IP)
  ↓
Port forward (host_port → container_port)
  ↓
Service in the VPS (sees the connection from 127.0.0.1)
```

## Expected result

- `crear_redireccion_vps` returns `name` (the `device_name` to delete it), `host_port`, `container_port`, `protocol`, `is_public_for_ip` and `solo_tailscale` (true if the port stayed on the internal network only).
- `listar_redirecciones_vps` adds `de_plataforma`, `editable`, `gestionado_por` and `motivo_plataforma` to each one, plus the `ip_host` (the `dnsip`) to connect to.
- `ver_politica_ip_puertos_vps` returns the global policy and, per port, its own one and the `efectiva` one.

```bash
nc -vz <dnsip> <host_port>
ssh -p <host_port> usuario@<dnsip>
```

## Errors and troubleshooting

| Answer | Cause | What to do |
| --- | --- | --- |
| `container_port <n> is reserved` | Reserved agent port | Use another port |
| 400 on create | Non-positive port, protocol other than tcp/udp, non-positive `closed_in_hs`, or the host out of free ports | Fix the argument |
| 409 `redireccion_de_plataforma` | The forward was created by Apps | Remove it from the app |
| 404 `redirect_not_found` | That `host_port` or `device_name` does not belong to this VPS | List first |
| 400 `ips_requeridas` | `solo` or `excluir` with no IP | Add at least one |
| 400 `lista_ip_invalida` / `lista_ip_demasiado_amplia` | Entry that is not an IP, or a range that is too wide; `data.invalid` says which | Fix the list |
| 409 `modo_no_soportado` | `excluir` on an app port | Use `solo` or `todos` |
| 424 `regla_no_aplicada` | The host firewall failed; the policy is `fallida` | Retry the same call |
| 403 | A member tried to change the policy | Ask an admin |

Creating a forward is safe to repeat: if one already exists for that port and protocol, the same one comes back. Changing the policy is safe too (applying the same list changes nothing). Deleting a forward drops the connections using it on the spot.

## MCP access

Yes, in the `/mcp` and `/mcp/vps` profiles.

| Tool | Type | Permission |
| --- | --- | --- |
| `listar_redirecciones_vps` | Read | Member |
| `ver_politica_ip_puertos_vps` | Read | Member |
| `crear_redireccion_vps` | Write (accepts `idempotency_key`) | Member |
| `cambiar_politica_ip_puertos_vps` | Write | Company admin |
| `eliminar_redireccion_vps` | Destructive | Member |
| `cambiar_lista_ip_app` | Write | IP list of an app's external access; requires the IPs written out |

Reference at [/mcp/docs](https://ardumaker.com/en/mcp/docs) and examples at [/mcp/vps](https://ardumaker.com/en/mcp/vps).

## Related features

- [Nginx](https://ardumaker.com/en/docs/nginx/)
- [SSH tunnels](https://ardumaker.com/en/docs/tuneles-ssh/)
- [Linux users](https://ardumaker.com/en/docs/usuarios-linux/)
- [PostgreSQL](https://ardumaker.com/en/docs/postgresql/)
- [VPS servers](https://ardumaker.com/en/docs/vps/)
