Put an internal web application behind Korthveil. Browsers connect to Korthveil over HTTPS, Korthveil checks every request with the web application firewall (WAF), and only clean requests reach the server. This guide publishes the SentinelOps web console (https://192.168.2.11) under the name sentinelops.home.arpa, for PCs inside the house only. Nothing is opened to the internet, and nothing changes on the upstream router.

What you need:

The screenshots come from a test firewall with a test site shop.test behind listener e2e. Your names and values are the ones in the tables.

How it works

 PC in the house                  Korthveil firewall                      SentinelOps server
 ┌────────────────────┐  HTTPS   ┌──────────────────────────────────┐  HTTPS  ┌──────────────────┐
 │ browser            │ ───────▶ │ reverse proxy, port 8443         │ ──────▶ │ 192.168.2.11     │
 │ sentinelops.       │          │  certificate: sentinelops        │         │ web console      │
 │ home.arpa:8443     │          │  asks the WAF about each request │         └──────────────────┘
 │ trusts your CA     │ ◀─────── │ WAF: OWASP Core Rule Set 4.25.2  │
 └────────────────────┘ 403 page │  detect, or block                │
                                 └──────────────────────────────────┘

Step 1: A certificate for the name, from Korthveil's own CA

Korthveil makes and renews the certificate itself (System › Certificates). The reverse proxy takes it from there, and gets each renewal automatically.

Go to System › Certificates, panel Add a certificate:

  1. A CA, once: if the store has no local CA yet, choose Create a local CA: Name in the store home-ca, Common name Home web CA. Click Create. (A CA made for NAC, such as nac-ca, also works.)
  2. The site's certificate: choose Issue from a local CA:
Field Set to
Name in the store sentinelops
Issuing CA home-ca
Use Server (web sites, VPN servers)
Common name sentinelops.home.arpa
DNS names sentinelops.home.arpa
Renew automatically on

Click Create.

Issue the site's server certificate from the local CA
Issue the site's server certificate from the local CA

The key never leaves the firewall. While the site uses it, the store lists the reverse proxy under Used by, and the certificate can't be deleted by mistake. When it renews, the site serves the new one within seconds, without a commit.

The name home.arpa is reserved for home networks, so it never clashes with a real internet name.

Your CA's certificate for the PCs: in the Store, click Download on home-ca. You get home-ca.pem: the CA's public certificate, which each PC that opens the site must trust (Step 7). It holds no key.

A certificate from elsewhere (a public CA, or one you made yourself): import it on System › Certificates (Import from your registrar or CA) and choose it the same way, or upload it in the reverse proxy's Certificates tab (Uploaded certificates).

Step 2: The server pool

Tab Server pools, click Add server pool:

Field Set to Meaning
Name sentinelops
Servers https://192.168.2.11 One row per server. Use Add server for a second one
Load balancing Round robin Only matters with two or more servers
Check servers over HTTP on, Path / Checked every 10 seconds; 3 failures mark a server down, 2 answers bring it back
Trusted CA (PEM, optional) The SentinelOps console's own certificate (see below) Korthveil checks the server's certificate against this instead of the public CAs
Expected server name The name in that certificate, if 192.168.2.11 is not in it
Example: the test pool on the test firewall
Example: the test pool on the test firewall

Getting the console's certificate: on the PC, run echo | openssl s_client -connect 192.168.2.11:443 -showcerts and copy the last -----BEGIN CERTIFICATE----- … -----END CERTIFICATE----- block (the root of its chain, or the only one if it is self-signed). Or open https://192.168.2.11 in a browser and export the certificate from the padlock.

Leave Do not verify server certificates (insecure) off. It is there only for servers whose certificate can't be checked at all.

Step 3: The listener

Tab Listeners. A new installation has a listener public with HTTP 80 and HTTPS 443 and no zone. Change it, or click Add listener:

Field Set to Why
Listener name inside
Zones Only the zones of the PCs that may open the console: MGMT (home LAN), and WIFI if you like Never tick WAN: then nothing is reachable from outside
HTTP port empty HTTPS only
HTTPS port 8443 (the default for a new listener) Port 443 in a zone takes the Korthveil web console away from that zone
Example: the test firewall's listener
Example: the test firewall's listener

Keep Access log on: one record per request, without cookies, credentials, or query strings.

No firewall rule is needed: the add-in opens the port in the chosen zones by itself.

Step 4: The site

Tab Sites, click Publish a site:

Field Set to
Name sentinelops
Host names sentinelops.home.arpa
Server pool sentinelops
Listeners tick inside only
Certificate sentinelops, under From System › Certificates (renewed automatically)
Redirect HTTP to HTTPS off (the listener has no HTTP port; left on, the commit shows a warning)
HSTS (browsers use HTTPS only) on, HSTS max-age (days) 365
Largest request body (MiB) 10 (the default)
Allow WebSocket connections Turn it on if live parts of the console stay empty (see Troubleshooting)
Example: the test site shop.test
Example: the test site shop.test

Click Save to pending changes. Errors and warnings appear above the button, each with the setting it is about.

Step 5: Protect the site with the WAF

Go to Security › Web application firewall and click Protect sentinelops. A new policy starts in detect mode.

Field Set to Meaning
Virtual host sentinelops (sentinelops.home.arpa)
Mode Detect only (log, never block) for now Every request is checked and logged; nothing is stopped yet
Application Generic web application
OWASP Core Rule Set on Shows the version, 4.25.2
Paranoia level 1 — few false positives (recommended) Higher levels add rules and false positives
Anomaly score that blocks 5 (the default) 5 blocks on one critical match; Step 8 starts blocking at 20
Inspect up to (MiB) 13 (the default) Request bodies larger than this are rejected (413), or partly inspected if you choose so under Larger bodies

Allowlist (trusted clients): click Add trusted client for your admin PC, so a false positive never locks you out:

Name Addresses Only under path Action
Admin PC its address, for example 192.168.2.50/32 empty (all paths) Inspect, never block

Inspect, never block still logs what the PC triggers (labelled with the entry's name). Skip inspection doesn't look at its requests at all; use it rarely.

Example: the policy of the test site shop.test, with a trusted client added
Example: the policy of the test site shop.test, with a trusted client added

Click Save to pending changes.

Step 6: Commit

Click Review and commit in the bar at the top of the page. Check the Configuration changes, type a Comment such as Publish SentinelOps behind the WAF, keep Safety rollback (5 minutes) on, and click Commit. Confirm the change within five minutes, or the firewall goes back by itself.

Both add-ins start with the commit. The commit is all-or-nothing: if the certificate, the pool, or the WAF policy is wrong, nothing changes and the reason is shown.

Step 7: Prepare a PC inside the house

The name. The PC must find sentinelops.home.arpa at the firewall's address in its zone. Use one of:

192.168.2.13 sentinelops.home.arpa On the Wi-Fi, use 192.168.20.1 instead.

Don't change the upstream router for this; the hosts file or Korthveil's resolver is all it takes.

Trust your CA. Install home-ca.pem (from Step 1) as a trusted root. On Windows, rename it to home-ca.crt first:

Open https://sentinelops.home.arpa:8443/. The SentinelOps console appears with a padlock and no warning.

Step 8: Detect first, then block

Let the site run in detect mode while you use the console normally, for a few days if you can (the guide WAF learning mode explains every step). Then, on the WAF page, panel Learning and readiness:

Learning and readiness: verdict, requests inspected, blocks, and the attack self-test
Learning and readiness: verdict, requests inspected, blocks, and the attack self-test
  1. Click Run attack self-test. It sends 15 attacks and 7 normal requests through the committed policy; it must show passed.
  2. Read Likely false positives. Accept adds a narrow rule exclusion (one rule, one path, one parameter) to the policy; save and commit it.
  3. When the Verdict is READY or REVIEW, click Block at threshold 20, then Save to pending changes and commit. Only requests with several strong detections are stopped at first.
  4. After a clean week, Lower threshold to 10, and a week later to 5. Back to detect mode undoes it at any time.

The verdict needs some traffic and days of observation. For a quick test you can set Mode to Block attacks in the policy directly, or type ENABLE at Block anyway, but you then skip the checks that protect you from false positives.

False positives by hand: under Rule exclusions, Add exclusion with the Rule IDs from Recent blocks, limited to a Path prefix and, better, to the one variable (for example ARGS:q), so the rule keeps protecting everything else. Write Why for the next person.

Check it

  1. Normal use: the console works at https://sentinelops.home.arpa:8443/.
  2. Test a request (WAF page): it runs a request through the committed policy without sending it to SentinelOps. The default /?id=1' OR '1'='1 is a classic SQL injection. Click Test: the answer says whether it would be blocked and by which rule.
Test a request without touching the application
Test a request without touching the application
  1. A real attack from the browser: open https://sentinelops.home.arpa:8443/?id=1' OR '1'='1. In block mode the page says Request blocked with a reference: that is the event ID in Recent blocks. At threshold 20 a single simple attack may still pass; at 5 it is blocked. In detect mode the page loads and the request is logged as detected.
  2. Live activity (WAF page): requests inspected, blocked, and detected per second, with a marker for each block. Point at a line to read the value at that moment; the rule behind each block is in Recent blocks.
Live activity on the WAF page: inspected, blocked, and detected requests per second with block markers
Live activity on the WAF page: inspected, blocked, and detected requests per second with block markers
  1. Recent blocks (WAF page): newest first, with the action (blocked, detected, or allowlisted), the attack type and rule, the client, and the request. Details shows what was sent; passwords, tokens, cookies, and authorization values are masked.
Recent blocks, one opened with Details
Recent blocks, one opened with Details
  1. Reverse proxy, tab Status: Live activity shows HTTP requests, server errors (5xx), and WAF blocks per second for all sites. Sites counts requests, 2xx, 4xx, 5xx, WAF blocked, and No server per site since the add-in started. Servers shows the health of https://192.168.2.11.
Live activity on the reverse proxy page
Live activity on the reverse proxy page
The Sites counters (example: the test site shop.test)
The Sites counters (example: the test site shop.test)

The dashboard's threat activity also shows WAF blocks per second, once a policy is running.

Change it later

What Where
Mode, paranoia level, anomaly threshold Security › Web application firewall, the site's policy
Trusted clients and rule exclusions The same policy: Allowlist (trusted clients), Rule exclusions
Every other Core Rule Set option (methods, content types, limits) The same policy: Advanced engine and Core Rule Set settings
Your own blocking rules (virtual patches) The same policy: Custom rules (SecLang, each rule with an id)
Who can reach the site Security › Reverse proxy › Listeners: the zones
A renewed certificate Nothing to do: Korthveil renews sentinelops before it expires, and the site serves the new one at once. Renew now on System › Certificates forces it
More servers, health checks Security › Reverse proxy › Server pools
Switch an add-in off or remove it The switch and Remove at the top of its page (applies on commit)

Troubleshooting

Symptom Check
The browser warns that the connection is not private home-ca.pem isn't trusted on this PC, or only in the system while you use Firefox (Step 7). Or the name in the address bar differs from the certificate's: it must be exactly sentinelops.home.arpa.
The site can't be reached The hosts entry points at the firewall's address in the PC's zone. The listener has that zone ticked and the commit is done. Status › Listeners and add-ins shows the listener's bound ports, or why it is not listening.
Site not found The name the browser used isn't in the site's Host names, or the site isn't ticked on that listener.
Temporarily unavailable: No application server is available, or 502 Bad Gateway Status › Servers: the server is down and shows why. certificate signed by unknown authority: paste the console's certificate into Trusted CA. certificate is valid for …, not 192.168.2.11: put that name in Expected server name. A health check failing on /: choose a path that answers 200 or a redirect.
The console loads, but live parts stay empty, or the browser console shows WebSocket errors Tick Allow WebSocket connections on the site. Without it, upgrade requests get Not allowed.
Normal pages are blocked after switching to block mode Recent blocks › Details shows the rule and the value. Add a narrow exclusion, raise Anomaly score that blocks for now, or go Back to detect mode. Allowlist your admin PC so you can always get in.
Every request gets Temporarily unavailable: Request inspection is temporarily unavailable Block mode fails closed when the WAF isn't answering. Status › Listeners and add-ins shows Web application firewall … not answering. Check System › Add-ins; detect mode lets requests through in that case.
Request too large on uploads Raise Largest request body (MiB) on the site, and Inspect up to (MiB) in the WAF policy (or Larger bodies: Inspect the first part, pass the rest).
Test a request says the virtual host has no committed WAF policy Save the policy and commit first; the test uses the committed policy.
The Korthveil web console is gone from a zone A listener uses HTTPS port 443 in that zone. Use 8443, or move the web console's port (System › Certificates › Web console › HTTPS port).
Live activity is empty The graphs fill from the add-in's counters every few seconds after the page opens; they stay flat while nobody uses the site.

More: modules/reverse-proxy.md and modules/waf.md describe both add-ins in depth.