Commit Graph
28 Commits
Author SHA1 Message Date
wweir 0b27a34f3e fix(install): generate the client password instead of the placeholder
`sowerd -i` wrote config.ExampleSowerdConfigTOML verbatim, so a fresh
install started with `password = "change_me"` — a publicly known credential
— while Validate rejects only an empty password. The admin console already
generates a random password when none is configured; do the same here.

- renderDefaultConfig replaces the template's top-level `password = ` line
  with a random 128-bit hex value (the commented `# password = ...` sample
  inside site_routes.auth is left alone) and errors when the template has no
  such line, instead of silently keeping a known value.
- The password is printed once during install for the client configs, and an
  existing /etc/sower/sowerd.toml is still never rewritten.
- docs: security.md states this under "What the Transport Password Gates"
  and moves the risk out of Residual Accepted Signals — a fixable
  operational defect is not an inherent signal — recording the declined
  alternative (a placeholder blocklist in Validate) with its rationale;
  ARCHITECTURE/README describe the generated password.

Gate: gofmt, go vet ./..., go test ./... green; install_e2e_test PASS.
2026-10-12 10:44:47 +08:00
wweir 2894a15510 docs(security): state what the transport password does and does not gate
The threat model scoped the adversary as "does not know the sower
password" but never said what holding it grants, which is the question an
operator asks before pointing a public domain at an intranet service:

- one shared secret, no per-client identity and no revocation short of
  rotating it for everyone (transport/sower's Sower holds only the password);
- a passing frame reaches any target the host can dial — Sower.Unwrap
  rejects only empty hosts and control characters, so there is no allowlist
  and a leaked password exposes the whole network position;
- no attempt throttling on the transport path (a wrong checksum just falls
  back to the website path); only the site_routes gate is throttled;
- password handling is the operator's: validation rejects only an empty
  value, the HMAC key is the raw password, and the shipped default config
  still carries the change_me placeholder (now flagged in Residual Accepted
  Signals and marked in the example config as requiring replacement).

ARCHITECTURE.md step 11 points at the authority for the no-allowlist fact.
2026-10-12 10:35:33 +08:00
wweir 8a3670d50e feat(sowerd): add an optional HTTP Basic gate for site routes
Site-route domains become public once autocert issues for them
(Certificate Transparency), and the edge answers any SNI. An upstream that
cannot authenticate its own visitors is therefore exposed, and sowerd owns
80/443 on the host, so nothing can be placed in front of it.

Add an opt-in per-route visitor gate:

- config: site_routes[].auth {username, password}; both fields required,
  empty means ungated, ':' rejected in the username (RFC 7617 splits there).
- runtime: the gate runs in the reverse-proxy handler, so it covers path
  overrides and WebSocket upgrades too; the sower transport path is
  dispatched earlier and stays untouched. Credentials are compared in
  constant time, failed attempts accumulate per source IP and lock it out for
  15 minutes (bounded map, mirroring the admin login throttle), and a passing
  request has its Authorization header removed so the edge credential never
  reaches the upstream. The fake-site decoy is deliberately never gated.
- fix: upstream URL userinfo was silently dropped. ReverseProxy sends
  through a Transport, and only net/http.Client turns userinfo into Basic
  credentials, so sowerd now sets that header itself; the upstream password
  is redacted in proxy error logs (url.URL.Redacted).
- docs: security.md records the mechanism and its limits (not a substitute
  for upstream auth, no session/logout, CORS preflight blind spot, and the
  browser WebSocket handshake: script cannot set Authorization, cached-
  credential replay is browser-specific and unverified, and a reconnecting
  client's failed handshakes feed the same per-IP counter, so it can lock its
  own source IP out of the whole route) plus the rejected alternatives
  (session login, gating the fake site).

Gate: gofmt, go vet ./..., go test ./... green; install_e2e_test PASS.
2026-10-10 19:15:32 +08:00
wweir ea243feb00 docs(architecture): drop the duplicated custom certificate SAN note
The Operational Notes bullet repeated the Design Decisions rule that a custom
certificate must cover every routed domain through SANs -- and config.Validate
already enforces exactly that through validateSiteRouteCertificate. Keep the
normative sentence in one place.
2026-10-10 14:56:52 +08:00
wweir 5dc57bc234 docs: security model for the sowerd edge; document the unknown-SNI cert fallback
Review of the anti-probing hardening found no code defects; the fixes
are documentation:

- docs/security.md: authoritative threat model, the three camouflage
  layers, unknown-SNI certificate fallback rules (whitelist source,
  error surfacing, capability propagation, concrete first entry), the
  client_hello fingerprint knob, rejected directions (QUIC, h2) with
  evidence, and residual accepted signals.
- Promote the plan doc to a non-authoritative historical record per
  AGENTS.md docs rules; index security.md from docs/README.md and
  ARCHITECTURE.md.
- config/sowerd.toml: document cert.domains; the first entry answers
  unknown-SNI handshakes.
2026-09-18 18:05:02 +08:00
wweir c4129c06ef docs: describe fake-site HTTP reverse proxy and forwarding headers
Correct the sowerd data flow and the stale claim that fallback rejects
HTTP upgrades. README notes X-Forwarded-* on unmatched browsing.
2026-08-28 13:37:14 +08:00
wweir bfad951fb0 docs: reflect hand-maintained shadcn-style UI without bits-ui 2026-08-20 18:15:23 +08:00
wweir f3894ef110 docs: reflect cert whitelist, probe handshake, frame checksum, DHCP validation 2026-08-12 17:34:37 +08:00
wweir a0f627621b fix(router,admin): trust full-length ECS only, gate PTR on selected upstream, harden hostname cache
Review findings on the DNS attribution and hostname series:

- ClientIPOf only trusts EDNS Client Subnet addresses with a full-length
  source prefix (32/128); a truncated prefix like dnsmasq --add-subnet=24
  carries only the subnet base and is ignored, so attribution stays on the
  real client instead of the /24 network address.
- The internal PTR gate now judges the currently selected upstream rather
  than the whole pool, so a degraded mixed pool cannot forward internal
  reverse lookups to a public fallback.
- Failed hostname lookups are cached for five minutes instead of the full
  hour, so a resolver blip stops suppressing names quickly while keeping
  retries rare.
- Duplicate IPs in one decoration batch resolve once instead of once per
  entry.
- Traffic-view hostname decoration runs on a private copy of the cached
  snapshot outside trafficMu, so a stalled resolver neither blocks other
  readers nor races on the shared backing array.
- Local DNS replies (failures and empty answers) echo an OPT record when
  the query carried EDNS0, per RFC 6891.
2026-08-12 14:53:13 +08:00
wweir dda120c8ca feat(admin): show client hostnames via reverse DNS in traffic console 2026-08-11 22:02:57 +08:00
wweir 5a57267512 feat(router): attribute DNS stats via EDNS client subnet, strip ECS upstream 2026-08-11 21:34:11 +08:00
wweir b4d92703ce feat(router): answer internal PTR lookups locally, keep external DNS unexposed
Parse in-addr.arpa/ip6.arpa query names back to IPs and answer
NXDOMAIN locally when the target is an internal range (RFC1918,
CGNAT 100.64/10, link-local, loopback, IPv6 ULA) and no internal
DNS server is among the upstreams. Reverse lookups still forward
when an internal DNS server can answer them, and public reverse
names keep the existing forwarding path.
2026-08-11 21:02:41 +08:00
wweir 033a070db6 feat(admin): temporary password fallback with login hint and password masking 2026-08-10 16:17:57 +08:00
wweir 52f9205e6a refactor(config): drop YAML support, keep TOML only 2026-08-10 16:17:11 +08:00
wweir 9ef545b189 docs: complete admin configuration and plan status updates
README gains an admin configuration table (state_file, session_file,
disable_session_persistence, cookie_secure); sower.toml documents
cookie_secure; ARCHITECTURE.md records best-effort session persistence;
both admin plans note the evolved whitelist, restart, stats, and SSE
implementation.
2026-08-09 21:18:40 +08:00
wweir 49710d9151 docs: sync admin console docs with implemented behavior
ARCHITECTURE.md and README.md now describe editable config fields,
in-place restart, rule hit/miss stats, and session persistence; the
admin console plan is marked implemented.
2026-08-09 19:21:02 +08:00
wweir 9104370d52 docs: sync architecture and admin console plan
- Document batched byte attribution and the soft memory limit
- Update admin console plan to the current stats implementation
2026-08-07 12:02:31 +08:00
wweir 1981c61f0c feat(admin): share port 80 with http proxy via host-based dispatch 2026-08-06 13:55:55 +08:00
wweir 1e87298d15 feat(admin): embedded admin console for rule management and traffic stats
- internal/admin: session-cookie auth server, rule CRUD API, bounded
  per-domain traffic stats with pre-parse byte accounting
- cmd/sower: wrap proxied connections and DNS queries into stats, serve the
  admin console on its own listener
- web/: Svelte 5 + Vite admin console with shadcn-svelte UI and an advanced
  breadcrumb header (section/context dropdowns, favorites), embedded via
  go:embed and committed to web/dist
- Makefile: sower build depends on the frontend build (bun); sowerd does not
- docs: ARCHITECTURE, README, and plan updated for the admin surface
2026-08-06 08:56:33 +08:00
wweir 62ca487087 docs: update docs and config examples for trojan removal
- ARCHITECTURE.md: remove trojan transport references, update sowerd flow
- README.md: remove trojan usage instructions, simplify remote type docs
- docs/plans/site-routing.md: update to reflect sower-only probe model
- config/*.toml,*.yaml: remove trojan from example config type options
2026-06-25 09:20:37 +08:00
wweir 0c05d04dec docs: codify DNS/transparent/smart routing layer separation in ARCHITECTURE.md
Document that DNS routing intentionally does not mirror smart TCP
routing, transparent HTTP/HTTPS listeners are proxy-only second-stage
handlers, and SOCKS5/explicit HTTP proxy entries use smart routing.
2026-06-24 13:30:06 +08:00
wweir 0547502748 feat(sowerd): route fallback sites by SNI 2026-06-24 09:24:25 +08:00
wweir f23f7f6950 docs: document access probe cache 2026-05-14 21:52:58 +08:00
wweir ad338598ae fix: harden dns proxy routing 2026-04-25 04:12:42 +08:00
wweir 09e4fc49fc docs: update client routing notes 2026-04-25 03:21:59 +08:00
root d514a52497 docs: clarify transparent https routing flow 2026-03-31 11:53:10 +08:00
wweir 549ab2a990 fix: fallback to system cache for sowerd service 2026-03-31 07:46:32 +08:00
wweir 6d410b104b docs: add architecture and deployment notes 2026-03-30 23:03:07 +08:00