// Matrix: what the rules do to new connections between zones, and to and // from the firewall itself. import { h, mount, modal, badge } from "../ui.js"; import { get, state } from "../api.js"; export async function render(el) { const m = await get("/matrix"); const cols = [...m.zones, "firewall"]; const cells = new Map(m.cells.map((c) => [c.from + "\u0000" + c.to, c])); const pending = state.draft && !state.immediate && (state.draft.ops || []).length; const name = (z) => z === "firewall" ? h("em", null, "firewall") : z; mount(el, h("div", { class: "page-head" }, h("div", { class: "grow" }, h("h1", null, "Zone matrix"), h("div", { class: "muted" }, "What happens to a new connection from a zone (rows) to a zone (columns), or to and from the firewall itself, worked out from the rules in order: all allowed, all blocked, or partly (some services, addresses or times let through, the rest not). Click a cell for the rules."))), pending ? h("div", { class: "msg info" }, "Shown with your pending changes.") : null, !m.zones.length ? h("div", { class: "panel" }, h("div", { class: "empty" }, "No zones yet: add them under Objects → Zones.")) : h("div", { class: "panel" }, h("div", { class: "table-wrap" }, h("table", { class: "matrix" }, h("thead", null, h("tr", null, h("th", { class: "corner" }, "from ↓ to →"), cols.map((c) => h("th", null, name(c))))), h("tbody", null, cols.map((from) => h("tr", null, h("th", null, name(from)), cols.map((to) => { const c = cells.get(from + "\u0000" + to); if (!c) return h("td", { class: "matrix-none" }, "–"); const open = c.open || []; const counts = [c.allowed ? "+" + c.allowed : "", c.blocked ? "−" + c.blocked : ""].filter(Boolean).join(" "); const title = (c.same_zone ? c.note + ". " : "") + "The rest: " + c.verdict + " by " + c.decided_by + (c.allowed ? "; " + c.allowed + " rule(s) let part of it through" : "") + (c.blocked ? "; " + c.blocked + " block part of it" : ""); return h("td", { class: "matrix-" + c.summary + (c.same_zone ? " matrix-same" : ""), title }, h("button", { type: "button", class: "matrix-cell", onclick: () => detail(c) }, c.summary === "partly" ? "partly" : c.verdict, counts ? h("span", { class: "matrix-n" }, counts) : null, open.length && !c.same_zone ? h("span", { class: "matrix-open" }, open.slice(0, 3).join(" · ") + (open.length > 3 ? " · +" + (open.length - 3) + " more" : "")) : null)); })))))), h("p", { class: "muted small" }, "Zone to zone is the forward chain; to the firewall, input; from it, output. +N: rules (and built-ins such as ping or port forwards) that let part of the traffic through before the rest falls to the deciding rule or the policy; −N: those that block part of it. Under each cell, what is let through. A zone to itself (faded): only traffic routed between its interfaces passes the firewall. Replies and established connections aren't shown: the built-in lets those through."))); } function detail(c) { const ruleLink = (id, chain, label) => id ? h("a", { href: "#/rules?chain=" + chain + "&rule=" + id, onclick: () => document.querySelector(".modal-back") && document.querySelector(".modal-back").remove() }, label) : label; const v = (verdict) => badge(verdict, { allowed: "ok", blocked: "err", logged: "", unknown: "warn" }[verdict]); const hits = (n) => h("td", { class: "small num" }, n === undefined || n === null ? "–" : n.toLocaleString()); const summary = { allowed: "all allowed", blocked: "all blocked", partly: "partly allowed" }[c.summary]; modal(c.from + " → " + c.to, h("div", null, h("p", null, h("strong", null, summary), h("span", { class: "muted" }, " · new connections, checked in this order (the " + c.chain + " chain):")), c.note ? h("div", { class: "msg info" }, c.note[0].toUpperCase() + c.note.slice(1) + ".") : null, h("div", { class: "table-wrap" }, h("table", null, h("thead", null, h("tr", null, h("th", null, ""), h("th", null, "What"), h("th", null, "By"), h("th", { class: "num" }, "Hits"))), h("tbody", null, c.exceptions.map((e) => h("tr", null, h("td", null, v(e.verdict)), h("td", null, e.what), h("td", { class: "small" }, ruleLink(e.rule_id, e.chain, e.label)), hits(e.hits))), h("tr", { class: "matrix-rest" }, h("td", null, v(c.verdict)), h("td", null, h("strong", null, c.exceptions.length ? "the rest" : "everything")), h("td", { class: "small" }, ruleLink(c.rule_id, c.chain, c.decided_by)), hits(c.hits))))), h("p", { class: "muted small" }, "Hits: each rule's packets since housecarld started, in all its traffic: a rule can cover several pairs.")), [{ label: "Close", value: null }], { wide: true }); }