# Host Firewall

> Default-deny inbound filtering on firewalld: the deterministic seed, port rules, expiring blocks and the bastion allowlist — plus the escape hatch that always works.

Source: https://www.corepanel.net/docs/security/host-firewall/
Last updated: 2026-08-15
Part of the CorePanel documentation — https://www.corepanel.net/docs

---

The web application firewall inspects HTTP requests. The **host firewall** works one
layer below it, on the network itself: it decides which ports of this server are
reachable at all, and from where.

CorePanel manages **firewalld** directly — no agent, no second firewall competing with
the first. What it builds is a dedicated `corepanel` zone whose policy is to drop
inbound traffic, with the ports of your installed stack opened explicitly, plus two
address lists: one that blocks and one that always lets through.

![The Host firewall section of the panel: a status header with an Enabled chip, the firewalld engine, an "Inbound: default-deny" chip and the corepanel zone, above counters reading 16 open ports, 3 blocked and 1 allowed addresses. Below it the Port rules tab lists each open port with its protocol, its allowed sources — most reading "Anyone" — and a comment naming the service, with edit and close actions per row.](https://www.corepanel.net/_astro/firewall-ports-dark.Bt8RYL60.png)

Everything below is in **Security → Firewall** in the panel, and in
`corepanel firewall` on the command line. Both drive the same rules; the CLI is what
you reach for over SSH, which is also why it is the way out if something goes wrong.

> **Requires CorePanel Pro or Business**
>
> Managing the firewall is a paid feature. On Personal the section is still in the
> sidebar — the administrator who needs a firewall is exactly the one who should know the
> panel has one — and shows what Pro unlocks.
>
> Two operations are deliberately available on every edition: reading the status and
> **turning the firewall off**. A server whose licence lapsed while the firewall was
> enforcing keeps its status header and its Pause button, because a firewall that
> outlived its licence and could no longer be switched off would be a hostage situation,
> not an upgrade prompt.
## What a new server starts with

The installer leaves firewalld **running**, with the ports of the stack it just
installed open in the server's default zone. A new CorePanel server therefore has a
firewall from the first boot, on every edition, without anyone deciding anything.

That is not the same as CorePanel *managing* it. Adopting the firewall — building the
`corepanel` zone and taking over the rules — is an explicit action, and it is what
unlocks port rules, blocking and the allowlist:

```bash
corepanel firewall status
```

```
Firewall:              running
Managed by CorePanel:  no
Default zone:          public
...
firewalld is running but CorePanel does not manage it. Run `corepanel firewall enable` to take it over.
```

Servers installed before this feature existed have no firewall at all: the installer
runs once, and it is not re-run by an update. `corepanel firewall status` says so
plainly.

## Adopting the firewall

Switching a server you reach over the network to default-deny is the operation that
deserves a look before it runs. So the port list is deterministic, and the panel shows
it *before* anything changes — an unprotected server lands on this card rather than on
a button:

![The panel on a server that is not filtering: a card headed "The firewall is paused" explaining that every rule is still stored, followed by a grid of the ports that will stay open — 22/tcp SSH, 80/tcp HTTP, 443/tcp HTTPS, 443/udp HTTP/3, 53 tcp and udp DNS, 21/tcp FTP control, 42000-50000/tcp FTP passive data and the mail ports — a "Start the firewall" button, and a note that if anything goes wrong you can SSH in and run corepanel firewall disable.](https://www.corepanel.net/_astro/firewall-enable-dark.DULMuTMT.png)

The same list from the shell:

```bash
corepanel firewall seed
```

```
PORT         PROTO  SERVICE
22           tcp    SSH
80           tcp    HTTP (corehttpd)
443          tcp    HTTPS — sites, panel and webmail
443          udp    HTTP/3 (QUIC)
53           tcp    DNS (authoritative)
53           udp    DNS (authoritative)
21           tcp    FTP control
42000-50000  tcp    FTP passive data
25           tcp    SMTP
465          tcp    SMTPS
587          tcp    Submission
143          tcp    IMAP
993          tcp    IMAPS
110          tcp    POP3
995          tcp    POP3S
```

The passive FTP range is read from the FTP server's own configuration, so a server
tuned to a different range seeds that one.

The list is derived from what CorePanel installed, not from a template. In particular
**the SSH port is read from the running sshd**, not guessed from a configuration file
that may resolve `Include` directives differently — a server reachable only on 2222
seeds 2222. That is the one mistake that would end the session giving the order.

Then **Enable the firewall** in the panel, or:

```bash
corepanel firewall enable
```

Either way adoption works in an order chosen so the dangerous step is last: build the zone, create the address lists, apply the seed,
and *only then* make the zone default. Traffic starts being dropped at the final step,
with every rule already in place. Running it again on an adopted server repairs
anything missing.

Three conditions are checked first, and refused with an explanation rather than worked
around:

| Condition | Why |
|---|---|
| firewalld is installed | Minimal cloud images ship without it — including AlmaLinux 10's. Install it with `dnf install firewalld`; CorePanel will not install a firewall behind your back |
| The backend is nftables | The deprecated iptables backend cannot hold the address lists as native kernel sets |
| No direct rules are present | Direct rules push firewalld back onto the legacy path; the ones found are listed so you can remove them |

## How a packet is judged

Rules are not a flat list — they are evaluated in a fixed order, and the first decision
wins. This is what makes the allowlist as powerful as it is:

![How the firewall judges an inbound packet: it enters the corepanel zone and meets four checks in order. First the bastion allowlist, which accepts immediately and skips everything else. Then the blocklist, which drops the packet. Then the port rules, which accept traffic on an open port, optionally only from certain source networks. Anything that reaches the end is dropped by the zone's default-deny policy.](https://www.corepanel.net/_astro/firewall-rule-order.pmK5riZR.svg)

Two consequences worth keeping in mind:

- **An allowlisted address bypasses the port rules too**, not just the blocklist. It is
  not "never block this", it is "always accept this". Use it for the office or the jump
  host you must never lose access from, and for nothing else.
- **Outbound traffic is never filtered.** The panel's own calls out — licence check-in,
  ACME challenges, package updates — cannot be broken by anything in this section.

### IPv4 and IPv6

Both, everywhere, without a switch to find.

- **Port rules cover both families.** Opening 8443 opens it over IPv4 and IPv6 alike —
  firewalld's port rules are family-agnostic, and so is the zone's default-deny target.
  A rule restricted to source networks is the one exception, and only because a network
  belongs to one family: `--source 203.0.113.0/24` restricts the IPv4 side, and you add
  `--source 2001:db8::/32` for the IPv6 one. A rule with both listed accepts from both.
- **The address lists are twin sets.** Blocking or allowing a v6 address puts it in the
  v6 set and a v4 address in the v4 one; the panel and `deny list` merge them into a
  single list, with the family shown per entry. Automatic blocking works the same way.
- **A bare address becomes a host prefix** — `/32` for IPv4, `/128` for IPv6.
- **A prefix with host bits set is refused, not masked.** `2001:db8::1/32` is an error
  naming both fixes, because silently widening it to `2001:db8::/32` would block a great
  deal more than you asked for.
- **IPv4-mapped addresses (`::ffff:198.51.100.10`) are unmapped to plain IPv4**, so they
  land in the set that actually matches the traffic instead of sitting inert in the other.
- **Zoned addresses (`fe80::1%eth0`) are refused**: a firewall rule cannot use a zone.

## Port rules

The **Port rules** tab lists every open port with its sources and its comment; "Open a
port" adds one, and each row can be edited or closed. From the shell:

```bash
corepanel firewall ports list
corepanel firewall ports open 8443 --source 203.0.113.0/24 --comment "staging"
corepanel firewall ports close 8443
```

A port is a single number or a range (`40000-50000`), TCP unless `--udp` is passed, and
optionally restricted to source networks. A port with no restriction is reachable by the
whole internet — which is what you want for 80 and 443, and rarely for anything else.

Changing who may reach an open port does not close it in between — the pencil icon on
a row, or:

```bash
corepanel firewall ports edit 8443 --source 198.51.100.0/24
```

The new sources are in place before the old ones are withdrawn, so an address that keeps
access never loses it for an instant. The port and protocol are the rule's identity and
cannot be changed — close the rule and open the new one instead.

> **SSH and 443 refuse to close**
>
> Closing the port sshd listens on, or the panel's own 443, is refused unless it is
> forced. The panel names the service behind the port in the confirmation and turns the
> button into "Close it anyway"; on the command line it is `--force`. Those are the two
> ports whose loss ends your access to the server, including the access you would need to
> undo the mistake.
Comments are CorePanel's, not firewalld's — the firewall has nowhere to store them. They
are kept per port and protocol, which is why editing a rule's sources keeps its comment
while closing and reopening the port does not inherit the old one.

## Blocking addresses

The **Blocked IPs** tab, or:

```bash
corepanel firewall deny 203.0.113.7 --ttl 24h --comment "ssh brute force"
corepanel firewall deny 198.51.100.0/24
corepanel firewall deny list
```

A block is permanent unless it is given a deadline — the panel offers 1 hour, 24 hours,
7 days or permanent (and a free-form value in hours); the CLI takes `--ttl`, from one
minute to seven days. The list shows what is blocked, how long each entry has left, and
why:

```
ADDRESS          EXPIRES    ORIGIN   COMMENT
203.0.113.7/32   in 23h     manual   ssh brute force
198.51.100.0/24  permanent  manual   scanner
192.0.2.4/32     in 41m     manual   -
```

> **The deadline is CorePanel's, not the kernel's**
>
> firewalld can hold entries that expire by themselves, but on both RHEL 8 and RHEL 10
> such an entry cannot be read back or removed through its API — it is write-only, and on
> EL8 the timeout is ignored outright. Blocks are therefore stored flat, and CorePanel
> releases the expired ones once a minute and once at startup.
>
> The practical consequence is a good one: a block **survives a reboot** and still expires
> on time, even if the deadline passed while the machine was off.
An entry the kernel is blocking that CorePanel has no record for is listed as
`untracked` rather than hidden. It usually means the metadata was lost; the address is
genuinely blocked either way, and you can remove it like any other.

Unblocking an address that is not blocked succeeds, which makes `deny remove` safe to
run from a script that does not check first.

## Blocking attackers automatically

The **Automatic blocking** tab, or `corepanel firewall autoblock`.

CorePanel watches its own login paths. When an address crosses the threshold of one of
them it is reported, and this policy decides what happens next.

![The Automatic blocking tab: a master switch, three source toggles for panel, mail/FTP and SSH logins, selects for the first ban, the longest ban and the cooldown, and a list of what is never blocked automatically.](https://www.corepanel.net/_astro/firewall-autoblock-dark.BXEdPBsI.png)

The ban gets longer every time the same address comes back:

| Offence | Ban |
|---|---|
| 1st | 30 minutes (`--ttl`) |
| 2nd | 1 hour |
| 3rd | 2 hours |
| … | doubling |
| — | stops at 7 days (`--max-ttl`) |

Escalation is an update of the deadline, never an unblock followed by a re-block, so the
address stays blocked throughout — there is no window in which it is let back in.

**Cooldown** (10 minutes by default) is how long an address is left alone before a new
report can lengthen its ban. Without it the counter would measure how fast an attacker
knocks rather than how often they come back, and a determined bot would reach the
seven-day ceiling in under a minute.

**Signal sources** are the login paths allowed to trigger a block. Each can be switched
off on its own:

| Source | Watches | Status |
|---|---|---|
| `panel` | Panel logins, via the API's brute-force guard | Reporting |
| `mail` | IMAP, POP3, SMTP and FTP logins, via the authentication broker | Reporting |
| `ssh` | SSH logins | Detector not shipped yet |

The pending source can already be switched on — the policy outlives the phase that builds
each detector, so leaving it enabled means "use it the moment it works" — and the panel and
the CLI both say which source is not watching anything today.

Only an address that crossed the threshold is ever reported. A *username* being locked
out is not evidence against whichever address happened to be last: on a shared or
NAT'd source that would ban a gateway full of innocent people, at the attacker's
choosing.

The `mail` source carries an extra rule that this page enforces regardless of what the
detector sends: **an address that has failed against fewer than three different accounts
is never blocked**, however many times it failed. One mailbox failing over and over is a
stale password on somebody's device, and banning the address it comes from cuts off
whoever owns it. See [Access protection](https://www.corepanel.net/docs/security/access-protection) for the rest of what
the mail detector requires before it will report anything at all.

```bash
corepanel firewall autoblock
corepanel firewall autoblock set --enable --sources panel,ssh --ttl 1h --max-ttl 48h
corepanel firewall autoblock set --disable
```

> **Four things are never blocked automatically**
>
> - **This server's own addresses**, and the loopback.
> - **Anything on the bastion allowlist** — the office and the jump host cannot be locked
>   out by their own typos, whatever any detector reports.
> - **Addresses you blocked by hand.** A deliberate permanent block is never rewritten
>   into a shorter automatic one.
> - **Addresses that are not globally routable**: private ranges, link-local, and
>   carrier-grade NAT (`100.64.0.0/10`). The first two are your own plumbing — the container
>   network, the provider's LAN — and the third can be tens of thousands of mobile
>   subscribers sharing one address.
>
> If the allowlist cannot be read at that moment, nothing is blocked. Not knowing whether
> an address is a bastion is not a reason to ban it — the attack stays visible and will be
> reported again a minute later.
Automatic blocking is a **Pro and Business** feature. On Personal the detection keeps
running and the attacks stay visible; nothing is blocked. Turning the switch off does the
same thing on any edition: you keep seeing the attacks, and nothing is blocked without
you.

## The bastion allowlist

The **Allowed IPs** tab, or:

```bash
corepanel firewall allow 203.0.113.10 --comment "office"
corepanel firewall allow list
```

Addresses here are accepted before every other rule is evaluated: they cannot be blocked,
and they reach ports that are closed to everyone else. Powerful, and worth keeping short.

## When something goes wrong

The panel has a **Pause** button in the status header, and it is never edition-gated.
But the firewall is the one feature whose failure mode is losing access to the machine
that runs it — including access to the panel. So the way out does not depend on the
panel, on a licence, or on anything that could itself be unreachable: SSH in and run

```bash
corepanel firewall disable
```

firewalld stops, the server accepts everything again immediately, and **nothing is
destroyed**: every rule stays in firewalld's permanent configuration, and
`corepanel firewall enable` puts it all back. This command asks no questions and no
edition may refuse it.

To go further and remove CorePanel's configuration entirely — handing the default zone
back to what it was before adoption and deleting the zone, its rules and both address
lists:

```bash
corepanel firewall factory-reset
```

That one is destructive: the blocked and allowed addresses go with the lists. It is not
how you turn the firewall off; `disable` is, and `disable` is reversible.

## Related

- [Web Application Firewall](https://www.corepanel.net/docs/security/waf) — the layer above, inspecting HTTP requests
- [CorePanel CLI](https://www.corepanel.net/docs/cli) — the full `corepanel firewall` command reference
- [Licensing](https://www.corepanel.net/docs/licensing) — what happens to paid features when a licence lapses
