InfraGuard

Firewall (Attack Surface)

An exposure map that cross-references each host's firewall rules against registered services and colors every open port by risk.

Overview — what it does

Security ▸ Firewall (/security/firewall) draws every port your server fleet leaves open into a single exposure map. It colors each open port by risk (crit / warn / ok / unknown) and distinguishes whether it is a registered service (managed) or a surprise opening (unmanaged), so you can immediately see "what is visible from outside, and why."

It joins three sources:

infr_instance_firewall   (per-instance firewall rules = open ports/services)
        ×  infr_instance  (host meta — public IP · criticality · env · status)
        ×  infr_service   (registered logical services — instance_id · port)
        →  classified by FirewallLore →  exposure map

Role boundary: this page looks at the physical open/exposure of ports (attack surface). Logical service reachability (health checks) belongs to Service ▸ Catalog. The same port is viewed differently in each place.

When to use

Layout

Header (HERO)

Title/subtitle (Ports your fleet leaves open — attack-surface exposure map), a last collected timestamp, a LIVE / PAUSED toggle (30s polling on/off), and a fullscreen button.

Empty state

If there are no firewall rules at all, an onboarding panel appears — a Go to Instances button sends you to /asset/instances. Import a firewall-cmd result from Instances ▸ [fw], or collect open ports via remote emulate, and the map fills in.

KPI band (5 tiles)

KPI Label Definition Computed as
ports Open ports Total open-port rows across all hosts count of classified rules
hosts Exposed hosts Machines that open at least one port distinct instance_id
crit Risky exposure Ports classified crit risk == 'crit'
unmanaged Unmanaged exposure Ports that are NOT a registered service AND whose risk is not ok !managed && risk != 'ok'
public_crit Public · crit Crit ports open on a host that has a public IP crit && public

Public · crit is a deliberate double-count. A crit port on a public IP already shows up under Risky exposure and (if it is not a registered service) under Unmanaged exposure too — but because that intersection (public × crit) is the single worst exposure in the fleet, it is surfaced again as its own KPI so nothing slips through. If this tile is non-zero, fix it first.

Exposure risk band

A four-color stacked bar (crit·warn·ok·unknown) shows the risk mix. The legend below is a click filter — click a risk level to keep only that level (click again to clear). Next to the band, coverage (e.g. 7 / 12 hosts collected) is the number of hosts that have firewall rules versus the total instance count.

Toolbar · filters

Port view

A grid of port cards. Each card shows port/protocol · service label · risk badge, the host chips that open this port (up to 8 + +N), and a footer with host count · public-exposure count · category. Click a card → port detail + risk lore modal. Sorted by risk → host count → port number.

Host view

A grid of host cards. Each card shows hostname · alias · env · criticality, a public badge, the port chips that machine opens (risk-colored, check-decagram marker if a registered service), and a footer with per-risk counts · total port count. Click a port chip → the same detail modal. Sorted by crit count → warn count → port count.

Port detail + risk lore modal

Port/protocol · service name · risk badge · category, plus the three-part fwlore explanation:

Below it, the list of hosts that open this port (env · criticality · zone · public/internal · managed/unmanaged badges) and a Manage in Instances ▸ [fw] link.

How to use

  1. If LIVE is on, the page refreshes every 30 seconds. If moving values distract you during an investigation, switch to PAUSED.
  2. Check the Public · crit KPI first — if it is non-zero, click crit in the risk legend to narrow the port view, then tackle hosts that carry a public chip (globe icon) first.
  3. Use Unmanaged only to isolate "surprise openings." If a DB (3306/5432/27017), cache (6379/11211), Docker (2375), or remote-admin (23/3389/VNC) that is not a registered service shows up here, narrow it immediately.
  4. Click a port card, read why/best in the lore modal, then follow Manage in Instances ▸ [fw] to tighten the rule or register it as a service.
  5. Actually closing a port or editing a rule happens not here but on the target host's firewall plus Instances ▸ [fw]. This page is a map and an audit tool.

Risk classification rules (FirewallLore)

A port string may be a single value (8080) or a range (6081-6100). A range is matched against the well-known map by its starting port.

risk rank meaning examples
crit 3 Immediate danger if exposed — datastore · remote-admin · file-share · container MySQL 3306, Redis 6379, RDP 3389, SMB 445, Docker 2375, k8s-api 6443
warn 2 Watch — management · legacy · non-standard SSH 22, SMTP 25, 8080/8443, SNMP 161, Grafana 3000
ok 1 Standard public service HTTP 80, HTTPS 443, DNS 53, NTP 123, WireGuard 51820
unknown 0 Unmapped — needs review ports/ranges not in the catalog

Managed determination

The registered-service map is keyed infr_service.instance_id : port. If the instance has a registered service on that port, the rule is managed (check-decagram marker, managed_name). Range ports match on their starting port (low). Without a registered service the rule is unmanaged — and if its risk is not ok, it lands in the Unmanaged exposure KPI.

Data sources

Table / source Role Populated by
infr_instance_firewall Per-instance open port/service rules (ledger) Assets ▸ Instances ▸ [fw] — manual CRUD / firewall-cmd --list-all raw-text import / scripts/infr_firewall_probe.cjs JSON import
infr_instance Host meta — name · alias · env · criticality · status · ip_public Assets ▸ Instances
infr_service Registered logical services (instance_id · port · name · kind) → managed check Service ▸ Catalog
App\Support\FirewallLore Port → {key,label,category,risk,icon} classification SOT (locale-independent) code
resources/lang/{ko,en}/fwlore.php Per risk-level and per category what/why/best lore lang pack

infr_instance_firewall rule shape (UNIQUE instance_id, zone, proto, port):

{
  "zone": "public",
  "ports": [
    { "port": "22",         "proto": "tcp", "service": "ssh" },
    { "port": "443",        "proto": "tcp", "service": "https" },
    { "port": "3306",       "proto": "tcp", "service": "mysql" },
    { "port": "6081-6100",  "proto": "tcp", "service": "" }
  ]
}

The [fw] importer also parses raw firewall-cmd --list-all text (zone header → services: mapping → ports: directly):

public (active)
  services: http https ssh
  ports: 8080/tcp 6081-6100/tcp 3306/tcp

The built dashboard is cached server-side for 30 seconds (infr:sec:firewall:dashboard). With no rules at all it returns an empty payload (empty=true) that renders the onboarding state.

Access

Troubleshooting & FAQ

I only see the empty screen ("No firewall data yet"). It means infr_instance_firewall has no rules. Paste a firewall-cmd --list-all result into Assets ▸ Instances ▸ [fw] to import, or collect open ports via remote emulate.

A port shows as Unknown service. The port is not in the FirewallLore catalog (risk unknown). If it is a legitimate service, register it in Service ▸ Catalog or add a label in the [fw] rule's service/note. If it is unneeded, close it.

It is a registered service but shows unmanaged. The managed check requires an infr_service on the same instance at that port. If the service lives on a different host or a different port, it will not match. Range ports match only on their starting port.

Risky exposure is non-zero but Public · crit is 0. It means all hosts with crit ports open are internal-only (no ip_public) — a small exposure radius, so relatively safer. Still, narrow them anyway to account for insider threats and lateral movement.

Values are not real-time. LIVE may be PAUSED or the tab may be backgrounded. The dashboard is cached for 30 seconds server-side, so for up to 30 seconds after you change a rule in [fw] you may still see the old values.

Can I close a port here? No. This page is a read-only map. Actual blocking is done on the target host's firewall, and rule-ledger updates in Instances ▸ [fw].

Related

Firewall — the open-port exposure map
Firewall — the open-port exposure map