Compare commits
No commits in common. "948253c2b6909a1d299389ccf18d6a87620ad5c1" and "f49b679ce2648214943e41ec9feaf54e73394585" have entirely different histories.
948253c2b6
...
f49b679ce2
5 changed files with 11 additions and 203 deletions
152
README.md
152
README.md
|
|
@ -54,158 +54,6 @@ include `asn`. Unmapped countries use `ZZ`; unmapped ASNs use `0`.
|
||||||
- `dnstt_bytes_out_total`
|
- `dnstt_bytes_out_total`
|
||||||
- `dnstt_sessions_total`
|
- `dnstt_sessions_total`
|
||||||
|
|
||||||
## Grafana Queries
|
|
||||||
|
|
||||||
Use `$domain` as a Grafana variable for the DNSTT domain, for example
|
|
||||||
`t2.bypasscensorship.org`. Replace `$__rate_interval` with a fixed range such
|
|
||||||
as `5m` if you are not using Grafana's built-in interval variables.
|
|
||||||
|
|
||||||
- Title: Current active clients
|
|
||||||
- Description: Current number of active DNSTT sessions for the selected domain.
|
|
||||||
- Visualization: Stat or gauge.
|
|
||||||
|
|
||||||
```promql
|
|
||||||
sum(dnstt_active_clients{domain="$domain"})
|
|
||||||
```
|
|
||||||
|
|
||||||
- Title: Peak active clients
|
|
||||||
- Description: Highest active DNSTT session count observed since the exporter
|
|
||||||
started.
|
|
||||||
- Visualization: Stat.
|
|
||||||
|
|
||||||
```promql
|
|
||||||
sum(dnstt_peak_clients{domain="$domain"})
|
|
||||||
```
|
|
||||||
|
|
||||||
- Title: Active clients by country
|
|
||||||
- Description: Current active DNSTT sessions grouped by resolver country.
|
|
||||||
- Visualization: Geomap, bar chart, or table.
|
|
||||||
|
|
||||||
```promql
|
|
||||||
sum by (country) (dnstt_active_clients{domain="$domain"})
|
|
||||||
```
|
|
||||||
|
|
||||||
- Title: Top countries by active clients
|
|
||||||
- Description: Countries with the most active DNSTT sessions right now.
|
|
||||||
- Visualization: Bar chart.
|
|
||||||
|
|
||||||
```promql
|
|
||||||
topk(10, sum by (country) (dnstt_active_clients{domain="$domain"}))
|
|
||||||
```
|
|
||||||
|
|
||||||
- Title: Active clients by ASN
|
|
||||||
- Description: Current active DNSTT sessions grouped by resolver ASN.
|
|
||||||
- Visualization: Bar chart or table.
|
|
||||||
|
|
||||||
```promql
|
|
||||||
sum by (asn) (dnstt_active_clients{domain="$domain"})
|
|
||||||
```
|
|
||||||
|
|
||||||
- Title: Top ASNs by active clients
|
|
||||||
- Description: Resolver ASNs with the most active DNSTT sessions right now.
|
|
||||||
- Visualization: Bar chart.
|
|
||||||
|
|
||||||
```promql
|
|
||||||
topk(10, sum by (asn) (dnstt_active_clients{domain="$domain"}))
|
|
||||||
```
|
|
||||||
|
|
||||||
- Title: Active clients by country and ASN
|
|
||||||
- Description: Current active DNSTT sessions split by both resolver country and
|
|
||||||
resolver ASN.
|
|
||||||
- Visualization: Table.
|
|
||||||
|
|
||||||
```promql
|
|
||||||
sum by (country, asn) (dnstt_active_clients{domain="$domain"})
|
|
||||||
```
|
|
||||||
|
|
||||||
- Title: Top country/ASN pairs by active clients
|
|
||||||
- Description: Country and ASN combinations with the most active DNSTT sessions
|
|
||||||
right now.
|
|
||||||
- Visualization: Bar chart or table.
|
|
||||||
|
|
||||||
```promql
|
|
||||||
topk(20, sum by (country, asn) (dnstt_active_clients{domain="$domain"}))
|
|
||||||
```
|
|
||||||
|
|
||||||
- Title: DNS query rate
|
|
||||||
- Description: Total observed DNSTT DNS queries per second for the selected
|
|
||||||
domain.
|
|
||||||
- Visualization: Time series.
|
|
||||||
|
|
||||||
```promql
|
|
||||||
sum(rate(dnstt_queries_total{domain="$domain"}[$__rate_interval]))
|
|
||||||
```
|
|
||||||
|
|
||||||
- Title: DNS query rate by country
|
|
||||||
- Description: Observed DNSTT DNS queries per second grouped by resolver country.
|
|
||||||
- Visualization: Stacked time series or bar chart. Use stacked time series for
|
|
||||||
trends over time, and bar chart for current top countries.
|
|
||||||
|
|
||||||
```promql
|
|
||||||
sum by (country) (rate(dnstt_queries_total{domain="$domain"}[$__rate_interval]))
|
|
||||||
```
|
|
||||||
|
|
||||||
- Title: Top ASNs by DNS query rate
|
|
||||||
- Description: Resolver ASNs producing the highest DNSTT DNS query rates.
|
|
||||||
- Visualization: Bar chart.
|
|
||||||
|
|
||||||
```promql
|
|
||||||
topk(10, sum by (asn) (rate(dnstt_queries_total{domain="$domain"}[$__rate_interval])))
|
|
||||||
```
|
|
||||||
|
|
||||||
- Title: Inbound DNS traffic rate
|
|
||||||
- Description: Bytes per second received in DNSTT DNS queries.
|
|
||||||
- Visualization: Time series.
|
|
||||||
|
|
||||||
```promql
|
|
||||||
sum(rate(dnstt_bytes_in_total{domain="$domain"}[$__rate_interval]))
|
|
||||||
```
|
|
||||||
|
|
||||||
- Title: Outbound DNS traffic rate
|
|
||||||
- Description: Bytes per second sent in DNSTT DNS responses.
|
|
||||||
- Visualization: Time series.
|
|
||||||
|
|
||||||
```promql
|
|
||||||
sum(rate(dnstt_bytes_out_total{domain="$domain"}[$__rate_interval]))
|
|
||||||
```
|
|
||||||
|
|
||||||
- Title: DNS traffic rate by country
|
|
||||||
- Description: Combined inbound and outbound DNSTT DNS bytes per second grouped
|
|
||||||
by resolver country.
|
|
||||||
- Visualization: Geomap, stacked time series, or bar chart.
|
|
||||||
|
|
||||||
```promql
|
|
||||||
sum by (country) (
|
|
||||||
rate(dnstt_bytes_in_total{domain="$domain"}[$__rate_interval])
|
|
||||||
+
|
|
||||||
rate(dnstt_bytes_out_total{domain="$domain"}[$__rate_interval])
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
- Title: Total observed sessions
|
|
||||||
- Description: Total unique DNSTT sessions observed since the exporter started.
|
|
||||||
- Visualization: Stat.
|
|
||||||
|
|
||||||
```promql
|
|
||||||
sum(dnstt_sessions_total{domain="$domain"})
|
|
||||||
```
|
|
||||||
|
|
||||||
- Title: New session rate
|
|
||||||
- Description: New DNSTT sessions observed per second.
|
|
||||||
- Visualization: Time series.
|
|
||||||
|
|
||||||
```promql
|
|
||||||
sum(rate(dnstt_sessions_total{domain="$domain"}[$__rate_interval]))
|
|
||||||
```
|
|
||||||
|
|
||||||
- Title: New session rate by country
|
|
||||||
- Description: New DNSTT sessions per second grouped by resolver country.
|
|
||||||
- Visualization: Stacked time series, geomap, or bar chart.
|
|
||||||
|
|
||||||
```promql
|
|
||||||
sum by (country) (rate(dnstt_sessions_total{domain="$domain"}[$__rate_interval]))
|
|
||||||
```
|
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|
|
||||||
18
flake.lock
generated
18
flake.lock
generated
|
|
@ -2,18 +2,16 @@
|
||||||
"nodes": {
|
"nodes": {
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1779622335,
|
"lastModified": 1777578337,
|
||||||
"narHash": "sha256-ViA62qtL5za7V3d5I8OA9q9JcFhsVAiL5jVHwEclWqk=",
|
"narHash": "sha256-Ad49moKWeXtKBJNy2ebiTQUEgdLyvGmTeykAQ9xM+Z4=",
|
||||||
"owner": "nixos",
|
"rev": "15f4ee454b1dce334612fa6843b3e05cf546efab",
|
||||||
"repo": "nixpkgs",
|
"revCount": 990025,
|
||||||
"rev": "705e9929918b43bd7b715dc0a878ac870449bb03",
|
"type": "tarball",
|
||||||
"type": "github"
|
"url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.1.990025%2Brev-15f4ee454b1dce334612fa6843b3e05cf546efab/019de756-85a1-7400-84a3-d277a7ed191b/source.tar.gz"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "nixos",
|
"type": "tarball",
|
||||||
"ref": "nixos-26.05",
|
"url": "https://flakehub.com/f/NixOS/nixpkgs/0.1"
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-26.05";
|
inputs.nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.1";
|
||||||
outputs =
|
outputs =
|
||||||
{ self, nixpkgs }:
|
{ self, nixpkgs }:
|
||||||
let
|
let
|
||||||
|
|
|
||||||
|
|
@ -167,12 +167,9 @@ func (c *Collector) RecordQueryFrom(domain string, clientID string, resolverIP n
|
||||||
|
|
||||||
now := c.now()
|
now := c.now()
|
||||||
client, exists := tunnel.clients[clientID]
|
client, exists := tunnel.clients[clientID]
|
||||||
updatePeak := !exists
|
|
||||||
if !exists {
|
if !exists {
|
||||||
client = &clientState{firstSeen: now, firstKey: key}
|
client = &clientState{firstSeen: now, firstKey: key}
|
||||||
tunnel.clients[clientID] = client
|
tunnel.clients[clientID] = client
|
||||||
} else if now.Sub(client.lastSeen) >= ClientTimeout || client.lastKey != key {
|
|
||||||
updatePeak = true
|
|
||||||
}
|
}
|
||||||
client.lastSeen = now
|
client.lastSeen = now
|
||||||
client.lastKey = key
|
client.lastKey = key
|
||||||
|
|
@ -181,9 +178,7 @@ func (c *Collector) RecordQueryFrom(domain string, clientID string, resolverIP n
|
||||||
client.bytesIn += uint64(size)
|
client.bytesIn += uint64(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
if updatePeak {
|
|
||||||
updatePeaks(tunnel, now)
|
updatePeaks(tunnel, now)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RecordResponse records an observed DNSTT DNS response.
|
// RecordResponse records an observed DNSTT DNS response.
|
||||||
|
|
@ -216,6 +211,7 @@ func (c *Collector) Snapshot() Snapshot {
|
||||||
snapshot := Snapshot{Tunnels: make(map[string]TunnelSnapshot, len(c.tunnels))}
|
snapshot := Snapshot{Tunnels: make(map[string]TunnelSnapshot, len(c.tunnels))}
|
||||||
for _, domain := range c.domains {
|
for _, domain := range c.domains {
|
||||||
tunnel := c.tunnels[domain]
|
tunnel := c.tunnels[domain]
|
||||||
|
updatePeaks(tunnel, now)
|
||||||
series := c.seriesSnapshotsLocked(domain, tunnel, now)
|
series := c.seriesSnapshotsLocked(domain, tunnel, now)
|
||||||
|
|
||||||
tunnelSnapshot := TunnelSnapshot{Domain: domain, Series: series}
|
tunnelSnapshot := TunnelSnapshot{Domain: domain, Series: series}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package dnstt
|
package dnstt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/netip"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
@ -60,36 +59,3 @@ func TestCollectorMatchesSubdomainsToRegisteredTunnel(t *testing.T) {
|
||||||
t.Fatalf("active clients = %d, want 1", tunnel.ActiveClients)
|
t.Fatalf("active clients = %d, want 1", tunnel.ActiveClients)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCollectorUpdatesPeakWhenActiveClientChangesGeoKey(t *testing.T) {
|
|
||||||
now := time.Unix(1000, 0)
|
|
||||||
firstResolver := netip.MustParseAddr("192.0.2.53")
|
|
||||||
secondResolver := netip.MustParseAddr("198.51.100.53")
|
|
||||||
c := NewCollector(
|
|
||||||
[]string{"tunnel.example.com"},
|
|
||||||
WithNow(func() time.Time { return now }),
|
|
||||||
WithGeoResolver(fakeGeoResolver{
|
|
||||||
labelNames: []string{"asn"},
|
|
||||||
labels: map[netip.Addr]GeoLabels{
|
|
||||||
firstResolver: {ASN: "64500"},
|
|
||||||
secondResolver: {ASN: "64501"},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
c.RecordQueryFrom("tunnel.example.com", "client-a", firstResolver, 120)
|
|
||||||
c.RecordQueryFrom("tunnel.example.com", "client-a", secondResolver, 120)
|
|
||||||
|
|
||||||
foundChangedASN := false
|
|
||||||
for _, series := range c.Snapshot().Tunnels["tunnel.example.com"].Series {
|
|
||||||
if series.ASN == "64501" && series.PeakClients != 1 {
|
|
||||||
t.Fatalf("peak clients for changed ASN = %d, want 1", series.PeakClients)
|
|
||||||
}
|
|
||||||
if series.ASN == "64501" {
|
|
||||||
foundChangedASN = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !foundChangedASN {
|
|
||||||
t.Fatal("series for changed ASN not found")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue