A VPN alert that fires on a new country feels conclusive, and it usually is not. Authentication says an attempt was accepted; the session says which connection carried it; resource records say what happened inside that connection. Each answers a different question. A rule that merges all three produces thirty tickets on a Monday and misses the real case on a Friday.

A foreign address is a question, not an answer

Travel, mobile carriers that egress in another country, a corporate web proxy and a reassigned carrier-grade NAT pool all move the apparent source without moving the attacker. The sign-in record reflects where the connection entered the network, not who was holding the device.

The reverse failure is worse because it is quiet. A stolen password used from the user's own city produces a login that matches every previous login on the geography check.

Treating geography as the verdict has a measurable cost. Roaming laptops generate a steady stream of reviews, reviewers learn to close them without reading, and the habit survives until a real case lands in the same queue.

Three record types answer three different questions

Start from what the deployed products emit, not from what a product brochure suggests they could emit. The authentication decision names the asserted identity, the method and the result. The session lifecycle names the connection, the gateway and the assigned address, from start to stop.

RecordQuestion it answersWhat it cannot prove
Authentication decisionWhich identity was accepted or rejected, by which method, at what timeThat the person operating the device was the account owner
Session lifecycleWhich connection, gateway and assigned address were active, and for how longWhat the session did with that access
Resource eventWhich read, write or denied operation a service observedWhich human behind the session triggered it
Policy changeWhether an administrator altered the rule that governs the accessWhether the change was reviewed or intended

An empty cell is a collection gap, not an acquittal. If resource events never arrive, the absence of a suspicious read means the pipeline has nothing to report, and a quiet dashboard describes the pipeline rather than the network. Write down which records the deployment actually produces before promising a rule that depends on them, and name the owner of each missing feed.

Accounting detail is where sessions become checkable. RADIUS accounting as described in RFC 2866 carries start, stop and interim updates keyed to a session identifier, so a session with a start and no stop is a gap you can name instead of a mystery.

Join on the session, not on the username

One account legitimately holds several sessions at once: a laptop, a phone, a lab machine left connected overnight. Joining every event for that username merges three devices into one story and then reads the combination as compromise. Assigned addresses get recycled as well, so a join that keeps matching an address after the session ended drags last month's device into today's incident. Retain the session key next to the address and bound the match by the time window the session actually covered.

Where translation strips the original source before a service sees it, use the identity that survives at that boundary, such as a mutual TLS subject or a single sign-on assertion. Inventing a one-to-one mapping between an address and a person is the same mistake with extra steps.

An alert without an owner is a dashboard

Each rule needs a condition, the benign explanations that produce it, the evidence to pull, and the person who acts. A repeated denied administrative operation, a burst of failed passwords and a silent collector are three different incidents with three different clocks.

Notification is not containment, and a distribution list does not restrict anything. Changing a live session requires the component that can do it, whether that is the gateway dropping the connection or the identity provider revoking the session. Stale feeds and missing stop records belong in the rule text as well, because a collector that has sent nothing for a day looks exactly like a quiet network from the dashboard. The difference is only visible in the pipeline itself.

Check the collection path before you trust a quiet week

Prove the pipeline end to end before tuning a single rule. The commands below confirm that the authentication and accounting listeners are up, that a test credential is rejected for the right reason, and that session events are still arriving:

ss -uanp | grep -E ':(1812|1813)\b'
radtest alice 'not-the-password' 127.0.0.1 0 testing123
journalctl -u openvpn-server@server --since '-30 min' | grep -c 'Peer Connection Initiated'
timedatectl | grep -E 'System clock|synchronized'

An Access-Reject from the test proves the request reached the server and the shared secret matched, which is the part people skip. A timeout instead points at the secret, the listener or a filter in front of it, and every alert downstream is unverifiable until that is fixed.

Clock state matters because correlation windows assume agreement. A gateway minutes off makes an otherwise clean session join look impossible, and reviewers compensate by widening the window until the join stops discriminating. Check the offset before editing the rule.

Then exercise one rule under a harmless test: generate a login from an unexpected address, confirm the alert fires, confirm the responder can pull the session and its resource events, and confirm the ticket carries the session identifier. A rule that produces a coherent story passes. A rule that produces a pile of log lines is still a dashboard. Record the result against the rule so the next review starts from evidence instead of intuition.

ተዛማጅ ጽሑፎች