Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/src/content/docs/reference/api-compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ that only need scan metadata should use `/summary`, then request paginated
results or host evidence separately when needed. This avoids loading large
snapshots just to show scan status and timestamps.

## Per-address port changes

A change in scan `changes`, an incident, a pending change, or an event can
have the kind `port-address`: a port of a DNS target opened or closed on one of
the target's resolved addresses while another address exposed it. `target` is
the DNS name and the new `address` key is the resolved address. `old` and `new`
are a positive port state or `not-open`, and `key` has the form
`port-address|<target>|<protocol>|<port>|<address>`. Other change kinds have no
`address`. Clients that handle change kinds individually should treat an
unknown kind as a generic change.

## Business units

v0.20.0 adds business units to every installation. The routes and response
Expand Down
47 changes: 38 additions & 9 deletions docs/src/content/docs/user-guide/jobs-baselines-incidents.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,39 @@ logical targets while each resolved effective address is shown separately in
host evidence. If a DNS target cannot be resolved, EdgeWatch still scans other
targets it could resolve, marks the overall scan incomplete, and protects the
unresolved target's baseline from false removals until a complete scan succeeds.
By default, DNS answer membership and each resolved host's reachability are
part of the monitored baseline. Jobs can opt into **Aggregate port and service
surface** in the job editor when DNS answers rotate routinely. Aggregate mode
continues comparing the logical DNS target's positive ports and service
fingerprints, but intentionally ignores answer additions/removals and individual
backend reachability; IP and CIDR targets remain address-sensitive. Per-IP scan
evidence is retained for investigation. This is a security-relevant change and
requires an explicit new baseline.
By default, in **Address-sensitive** mode, DNS answer membership, each resolved
host's reachability, and the resolved addresses that expose each port are part
of the monitored baseline. A port that opens on one address while another
address of the name already exposes it, or that closes on one address while
another keeps it open, is reported as a change on that address, for example
`edge.example tcp/22 on 2001:db8::10: not-open -> open`. A port that no
address exposed before, or that no address exposes any more, is a port change,
and an address that joins or leaves the DNS answer is a DNS change; the ports
of an address that joined the answer are compared after you accept that DNS
change. A port missing from an address
whose host is down is reported as that host's state change. While an address's
scan coverage is incomplete, a port that opened on another, complete address
is still reported, and closures wait for a complete scan. Baseline samples
converge only when they agree on which addresses expose each port. A baseline
port without recorded addresses, such as one accepted from an incident, takes
its addresses from the next complete scan without a report.

Jobs can opt into **Aggregate port and service surface** in the job editor when
DNS answers rotate routinely. Aggregate mode continues comparing the logical DNS
target's positive ports and service fingerprints, but intentionally ignores
answer additions/removals, individual backend reachability, and which address
exposes a port. IP and CIDR targets remain address-sensitive, so list addresses
as IP targets when they need per-address monitoring under a name whose answers
rotate. Per-IP scan evidence is retained for investigation. This is a
security-relevant change and requires an explicit new baseline.

Releases before per-address port comparison merged a DNS target's ports
across its addresses. After an upgrade, the first scans of an
address-sensitive job can report per-address changes that built up before the
upgrade; review them and accept the ones that are expected. A DNS target's
baseline that is still being learned during the upgrade may need one more
sample.

Schedules use five-field cron syntax in the selected IANA timezone. New jobs
default to the deployment `timezone` from `config.yaml`, or to the browser's
timezone when it is omitted. New jobs receive an optional 30-minute
Expand Down Expand Up @@ -57,7 +82,11 @@ From **Incidents**, administrators and operators can:
accepts that port; accepting the port alone leaves its service for a
separate decision. Until you accept a service for that port, its
fingerprint is reported as a change, also after a suppression or a scan
without a fingerprint, and never enters the baseline on its own.
without a fingerprint, and never enters the baseline on its own. Accepting
a change on one address of a DNS target updates which addresses the
baseline expects to expose the port. Accepting a closure also recomputes
the port's expected service from the remaining addresses and accepts a
reported service change that matches it.
- **Suppress 1 scan** to defer the alert for the next successful scan. If the
change remains, it is reported again afterward.

Expand Down
7 changes: 5 additions & 2 deletions internal/engine/dns_comparison_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@ func TestAggregateDNSComparisonIgnoresAnswerRotationButKeepsPortAndServiceChange
if baseline.Hash() == rotated.Hash() {
t.Fatal("historical snapshot hash unexpectedly ignored DNS and effective-host changes")
}
if got := snapshotHashForDNSMode(baseline, config.DNSComparisonAddressSensitive); got != baseline.Hash() {
t.Fatal("address-sensitive mode no longer uses the historical snapshot hash")
if got := snapshotHashForDNSMode(baseline, config.DNSComparisonAddressSensitive); got == baseline.Hash() {
t.Fatal("address-sensitive hash ignores which addresses expose a DNS target's port")
}
if got := snapshotHashForDNSMode(snapshot("open"), config.DNSComparisonAddressSensitive); got != snapshot("open").Hash() {
t.Fatal("address-sensitive hash of a snapshot without DNS port evidence differs from the snapshot hash")
}
if got, want := snapshotHashForDNSMode(rotated, job.DNSComparisonMode), snapshotHashForDNSMode(baseline, job.DNSComparisonMode); got != want {
t.Fatalf("aggregate hash changed after DNS answer rotation: %s != %s", got, want)
Expand Down
Loading
Loading