A firewall is a reusable set of rules, inbound and outbound, that you attach to your machines. It is enforced by the hypervisor, before traffic reaches the system: nothing to install, it works the same on Linux, Windows and BSD, and a mistake inside the machine cannot switch it off.
How it filters
- A machine without a firewall accepts all inbound traffic, as before.
- As soon as a machine has at least one firewall, inbound traffic no rule opens is dropped. With several firewalls, their rules add up.
- Replies to the machine's own connections always get through: updates, downloads and outgoing API calls need no rule.
- Its private networks are never filtered, and neither is the web console of the panel, which reaches the machine even if every rule is closed.
- Outbound traffic is free until one of the machine's firewalls has an outbound rule.
Create a firewall
A rule lets in one protocol (tcp, udp, icmp for
ping in IPv4 and IPv6, or any), on ports ("22",
"80,443", a range "8000-8100"; omitted: all ports), from
sources (IPv4 and IPv6 addresses or ranges; omitted: anywhere).
curl -s -X POST https://api.ffxf.net/v1/firewalls \
-H "Authorization: Bearer $FFXF_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "web",
"rules": [
{"protocol": "tcp", "ports": "22", "sources": ["203.0.113.0/24", "2001:db8::/32"], "description": "SSH from the office"},
{"protocol": "tcp", "ports": "80,443", "description": "Web"},
{"protocol": "icmp", "description": "Ping"}
]
}'
{
"data": {
"id": 7,
"name": "web",
"rules": [
{ "protocol": "tcp", "ports": "22", "sources": ["203.0.113.0/24", "2001:db8::/32"], "description": "SSH from the office" },
{ "protocol": "tcp", "ports": "80,443", "sources": ["0.0.0.0/0", "::/0"], "description": "Web" },
{ "protocol": "icmp", "ports": null, "sources": ["0.0.0.0/0", "::/0"], "description": "Ping" }
],
"members": []
}
}
Without rules, the firewall starts with SSH (TCP 22) and ping open from
anywhere; send "rules": [] for none. Addresses are brought back to their
network address (203.0.113.7/24 becomes 203.0.113.0/24).
Ten firewalls per account, fifty rules per firewall, twenty sources per rule.
Keep a way in. A firewall that does not open TCP 22 (or 3389 for Windows) cuts SSH (or remote desktop) to every machine it protects. The web console still works, but a script or an agent working over SSH loses its machine.
Protect a machine
curl -s -X POST https://api.ffxf.net/v1/firewalls/7/members \
-H "Authorization: Bearer $FFXF_TOKEN" \
-H "Content-Type: application/json" \
-d '{"vm": 4821}'
{ "data": { "vm": 4821, "hostname": "web-1", "status": "applied" } }
The rules are in place when the call returns. Attaching a firewall already attached
returns 200: a script can replay its configuration safely. Up to five
firewalls per machine. A machine without a public address cannot get one: it has
nothing to filter (409).
Change the rules
PUT /firewalls/{id} with rules replaces the whole
list: describe the state you want, the API applies it to every attached
machine before answering. Send every rule you keep, not only the new one.
curl -s -X PUT https://api.ffxf.net/v1/firewalls/7 \
-H "Authorization: Bearer $FFXF_TOKEN" \
-H "Content-Type: application/json" \
-d '{"rules": [{"protocol": "tcp", "ports": "22"}, {"protocol": "tcp", "ports": "443"}]}'
pending_machines counts the machines the new rules have not reached yet
(hypervisor unreachable); they show "status": "error" in
members and are retried automatically within minutes.
Detach, delete
| Action | Call | Good to know |
|---|---|---|
| Detach | DELETE /firewalls/{id}/members/{vm} | The firewall's rules leave the machine. With no other firewall, it accepts all inbound traffic again. |
| Rename | PUT /firewalls/{id} with name | Rules untouched when rules is omitted. |
| Delete | DELETE /firewalls/{id} | Refused (409) while it protects a machine: deleting it would open them all at once. |
Outbound rules
A rule with "direction": "out" lets traffic out, to
destinations instead of coming from sources. As long as none of
a machine's firewalls has an outbound rule, everything may go out. As soon as one does,
only what outbound rules open goes out, plus what is always allowed:
- DNS (port 53) to the resolvers FFxF configures on the machine;
- replies to inbound connections the firewall let in;
- the private network.
{
"rules": [
{"protocol": "tcp", "ports": "22"},
{"direction": "out", "protocol": "tcp", "ports": "80,443", "description": "Updates and HTTPS"},
{"direction": "out", "protocol": "tcp", "ports": "5432", "destinations": ["198.51.100.20"], "description": "External database"}
]
}
The first outbound rule closes everything else: without the 80,443 rule
above, package updates and downloads stop. Sending sources on an outbound
rule, or destinations on an inbound one, is refused
(validation_failed) rather than silently ignored. On a machine without IPv4,
an IPv4 destination also allows its NAT64 address (64:ff9b::/96), the way such
a machine reaches IPv4 hosts. Port 25 stays closed whatever the rules say.
Useful details
Read calls need the vms.read scope; creating, changing, attaching and
deleting need network.write. The MCP server exposes the same actions: see
MCP tools.