# Pyxsoft Firewall CLI Reference

> Complete reference for the Pyxsoft Firewall CLI (PXF) including command usage, examples, and typical workflows.

Source: https://www.pyxsoft.com/docs/firewall-cli/pxf-cli-command-reference
Updated: 2026-11-10

---

## Output formats (global flags)

* `--json` → JSON envelope: `{ ok, code, data }` or `{ ok:false, code, error }`
* `--yaml` → YAML envelope (same fields)
* (Human mode is default if neither flag is present)

Examples:

```bash
pxf status --yaml
pxf list ports --json
```

---

## Command reference

> **Journal-first model**: most commands append to the journal and are enforced on the next `pxf apply`. `pxf ip allow/deny/remove-*` are exceptions—they update the firewall immediately **and** record the change so the next apply stays in sync (the killswitch `enable/disable` also acts immediately).

### Status & inspection

* `pxf status` — Backend info, desired policies, counts, killswitch flag.
* `pxf is-enabled [--quiet]` — Script-friendly predicate. Prints `enabled` / `disabled` and exits **0** if the firewall is enabled, **1** otherwise. Use this in shell scripts and package triggers instead of parsing `pxf status --json`.
* `pxf list ports` — Desired open ports/ranges (proto, any/CIDR scope).
* `pxf list allowed-ip` / `pxf list blocked-ip` — Bastions and global blocks.  
  `pxf list blocked-ip` shows reverse DNS and ISO country codes (where resolvable) in separate columns; `--json/--yaml` include `reverse` and `country` fields in each item.
* `pxf list redirects` — `redirect`, `redirect-open`, `tredirect`, `lredirect`.
* `pxf journal show [--offset N --limit M]` — Paginated raw journal.
* `pxf journal prune` — Compact journal by logical key (“last wins”).
* `pxf show-rules` — Dump raw rules (`nft list ruleset` or `iptables-save`) + summary.
* `pxf validate` — Binaries, kernel modules, `firewalld`, root permission.
* `pxf verify` — Drift check: desired snapshot vs actual rules (policies + redirects).
* `pxf version` — Version/commit/build date.

### Lifecycle

* `pxf apply [--no-hooks]` — Recompute snapshot from journal, restore persisted IP blocks (TTL=0), and apply (idempotent).
    * Permanent blocks come from `/var/lib/pxf/blocked_ips.jsonl`
    * Temporary blocks (TTL>0) are preserved across `apply` by snapshotting the live firewall set before each flush, but they are **not** persisted to disk (lost on reboot or service restart)
    * After applying, pxf runs the post-apply [extension hooks](https://www.pyxsoft.com/docs/firewall-cli/pxf-hooks) under `/etc/pxf/post-apply.d/`. Pass `--no-hooks` to skip them (advanced; reserved for emergency situations).
* `pxf enable` — Turn off killswitch and apply the rules.
* `pxf disable [--no-hooks]` — Killswitch: runtime **ALLOW ALL** until re-enabled.
    * After flushing and switching to ALLOW ALL, pxf runs the post-disable [extension hooks](https://www.pyxsoft.com/docs/firewall-cli/pxf-hooks) under `/etc/pxf/post-disable.d/`. This lets external services (e.g. **pxShield**) keep their kernel rules (such as the 80→19080 / 443→19443 WAF redirects) active even when pxf itself is disabled.
    * Use `--no-hooks` for an absolute killswitch that bypasses extension hooks. This opens the firewall completely, including dropping any rules contributed by external services.
* `pxf reset --allow-all` — Clear journal, set `Disabled=false`, leave backend **ALLOW ALL**.

### Policies & ICMP

* `pxf policy deny-all` — Desired baseline `DROP` for INPUT/FORWARD (OUTPUT stays ACCEPT).
  On apply, essential ICMPv4/ICMPv6 are added automatically.
* `pxf icmp allow-essential [--v v4|v6|both]` — Record minimal ICMP rules (echo, unreachable, ND/PMTU…).

### Ports

* `pxf ports allow-port --port P --proto tcp|udp [--cidr A/B ...] [--comment "web app"]`
* `pxf ports allow-port-range --from A --to B --proto tcp|udp [--cidr ...] [--comment "..."]`
* `pxf ports close-port --port P --proto tcp|udp [--cidr ...] [--comment "sunset"]`
* `pxf ports close-port-range --from A --to B --proto tcp|udp [--cidr ...] [--comment "..."]`
* **Selective per-port denies (new)**

    * `pxf ports deny-from --port P --proto tcp|udp --cidr A/B [--cidr C/D ...]`
      Deny traffic **from** specific source CIDR(s) **to** the given **port/proto**.
      Applied **before NAT** and **before allow/bastion rules**, and reinforced in filter chains for local traffic.
      Effective even when the port is globally open.
    * `pxf ports remove-deny-from --port P --proto tcp|udp --cidr A/B [--cidr C/D ...]`
      Remove previously declared per-port denies.

> With **ACCEPT** baseline, closing ports only removes explicit allows; it doesn’t block traffic. To truly restrict inbound traffic, switch to `pxf policy deny-all` and then allow/deny as needed.

Port comments are saved in the journal and surface in `pxf list ports --json` to explain why a rule exists.

### IP allow/deny (before NAT)

All IP subcommands take effect immediately on the live firewall; `pxf apply` later keeps the declarative snapshot aligned with those changes.

* `pxf ip allow <CIDR>` / `pxf ip remove-allowed <CIDR>` — Bastion hosts/networks (always bypass blocks and closed ports).
* `pxf ip deny <CIDR> [--ttl SECONDS] [--comment "REASON"]` — Global block (affects redirected flows too).
    * `--ttl 0` (default): Permanent block, journalled and persisted to disk
    * `--ttl N`: Temporary block, stored only in the in-memory runtime sets (survives `pxf apply`, but **not** a reboot/systemd restart)
    * `--comment`: Reason for blocking (optional)
    * Listing (`pxf list blocked-ip`) enriches each IP with reverse DNS (`reverse`) and ISO country (`country`) when the target represents a single host and lookups succeed.
* `pxf ip remove-denied <CIDR>` — Remove a blocked IP/CIDR.
* `pxf ip blocked-status --cidr <CIDR> [--cidr <CIDR> ...]` — Membership probe: report which of the given IPs/CIDRs are currently in the deny set, with each match's remaining TTL.
    * A cheap lookup — it parses the live deny set once with **no** reverse-DNS or country enrichment, so cost is independent of the deny list's size. Use it to check a handful of IPs instead of paging the whole list with `pxf list blocked-ip`.
    * Repeat `--cidr` for each address; bare IPs are canonicalised (`/32`/`/128`).
    * Returns only the matches: `{"items": [{"cidr": "...", "ttl": <seconds>, "expires_at": "<ISO>"}]}` (`ttl`/`expires_at` omitted for a permanent block). An IP absent from the result is not blocked.
* `pxf ip clear-denied --force` — Remove every blocked IP (changes apply immediately).
* `pxf ip purge-permanent --keep N` — Remove the oldest permanent blocks that exceed the given cap.
    * Reads `/var/lib/pxf/blocked_ips.jsonl`, keeps the **N** most recent permanent (TTL=0) entries, and removes the rest from both persistent storage and the live firewall.
    * If the current permanent count is already at or below `--keep`, the command is a no-op.
    * Returns `{"purged": <count>, "remaining": <count>}` in JSON mode.
    * Typically called by LFD after each detection cycle when `max_permanent_blocks` is configured in `/etc/lfd.ini`.

### Redirects

* `pxf redirect add --from X --to Y --proto tcp|udp [--ipver 0|4|6] [--open-dst] [--comment "note"]`
  Classic PREROUTING redirect. `--open-dst` also opens destination port (handy for tests). `--comment` stores a short note that shows up in `pxf list redirects`.
* `pxf redirect rm --from X --to Y --proto tcp|udp [--ipver 0|4|6]`
* `pxf tredirect add --from X --to Y --proto tcp|udp [--ipver 0|4|6] [--comment "note"]`
  Transparent redirect: mark + allow only marked flows; destination port stays hidden externally. Comments here are also persisted and listed.
* `pxf tredirect rm --from X --to Y --proto tcp|udp [--ipver 0|4|6]`
* `pxf local-redirect add --from X --to Y --proto tcp|udp [--ipver 0|4|6]`
  OUTPUT (local) redirect — e.g., `curl localhost:80` → `:19080`.
  *(Removal depends on backend support and can be added later.)*

### firewalld (best-effort helpers)

* `pxf firewalld status` — `active|inactive|failed` + `enabled|disabled|masked`.
* `pxf firewalld disable [--mask]` — Stop + disable (optionally mask) the unit.
* `pxf firewalld enable` — Unmask + enable + start.

### Rule management

* `pxf rule delete <UUID>` — Remove a specific rule (apply required to enforce)

---

### Presets

* `pxf preset cpanel` — Applies a predefined configuration for cPanel/WHM servers:
    - clears state (reset --allow-all),
    - sets a secure baseline (policy deny-all + icmp allow-essential --v both),
    - opens web ports (80/443),
    - cPanel/WHM/Webmail/WebDAV (2077, 2078, 2082, 2083, 2086, 2087, 2095, 2096),
    - SSH (detect from sshd_config, default 22),
    - DNS (53 tcp/udp),
    - Mail (25, 465, 587, 110, 995, 143, 993, 4190),
    - FTP (21)

  The preset does not apply the changes; run `pxf apply` to enforce.
