Low-severity security hygiene #4

Closed
opened 2026-08-31 18:53:11 -05:00 by Ghost · 1 comment

Severity: Low (hygiene)

Practical nits that will not by themselves dump a database, but are worth tightening.

  1. Generated configs are world-readable. GenerateExampleConfig writes with 0644 and embeds a live encryption_key (pkg/config/config.go). Use 0600 (same idea as the log file already using 0600).

  2. PBKDF2 salt is derived from the password. DeriveKey salts with SHA256(password)[:16] (pkg/encryption/encryption.go). That is not a salt; it does not stop precomputation of common passphrases. Either treat --generate-key output as a raw 256-bit key (hex-decode, no PBKDF2) or store a random salt next to the key in the config.

  3. Key derivation on every encrypt/decrypt. DeriveKey (100k PBKDF2 iterations) is called per handshake and per UDP packet. Cache the derived key at startup. This is a CPU footgun under UDP load even with rate limits on TCP accepts.

  4. Open recursive DNS when enabled. Unknown names are forwarded to backup_server (default 8.8.8.8:53) with no client ACL. If DNS is bound on all interfaces, the box is an open resolver. Restrict clients or disable recursion except for configured records.

  5. Rate limit is process-global, not per source IP. One noisy peer burns the token bucket for everyone (pkg/ratelimit, server Allow()). Fine for a single-user homelab; misleading if you think it stops a single attacker from starving others.

  6. keep_alive is unused. It is in YAML and README but never applied to TCP conns. Long-lived forwards through NAT will die quietly; not a vuln, but operators will misread it as a liveness control.

## Severity: Low (hygiene) Practical nits that will not by themselves dump a database, but are worth tightening. 1. **Generated configs are world-readable.** `GenerateExampleConfig` writes with `0644` and embeds a live `encryption_key` (`pkg/config/config.go`). Use `0600` (same idea as the log file already using `0600`). 2. **PBKDF2 salt is derived from the password.** `DeriveKey` salts with `SHA256(password)[:16]` (`pkg/encryption/encryption.go`). That is not a salt; it does not stop precomputation of common passphrases. Either treat `--generate-key` output as a raw 256-bit key (hex-decode, no PBKDF2) or store a random salt next to the key in the config. 3. **Key derivation on every encrypt/decrypt.** `DeriveKey` (100k PBKDF2 iterations) is called per handshake and per UDP packet. Cache the derived key at startup. This is a CPU footgun under UDP load even with rate limits on TCP accepts. 4. **Open recursive DNS when enabled.** Unknown names are forwarded to `backup_server` (default `8.8.8.8:53`) with no client ACL. If DNS is bound on all interfaces, the box is an open resolver. Restrict clients or disable recursion except for configured records. 5. **Rate limit is process-global, not per source IP.** One noisy peer burns the token bucket for everyone (`pkg/ratelimit`, server `Allow()`). Fine for a single-user homelab; misleading if you think it stops a single attacker from starving others. 6. **`keep_alive` is unused.** It is in YAML and README but never applied to TCP conns. Long-lived forwards through NAT will die quietly; not a vuln, but operators will misread it as a liveness control.

Triaged as low hygiene. 0600 configs, real salt or raw key, derive-once, no open recursive DNS, document global rate limit, wire or drop keep_alive. After #2/#3.

Triaged as low hygiene. 0600 configs, real salt or raw key, derive-once, no open recursive DNS, document global rate limit, wire or drop keep_alive. After #2/#3.
Ghost closed this issue 2026-08-31 23:27:14 -05:00
Sign in to join this conversation.