Every client on loopback shares the 127.0.0.1 identity, so counting them against 'max connections per host' or the default-on auth lockout lets one local client deny service to all the others (and makes a shared-NAT/proxy address a natural DoS vector for remote clients). Use utils_fd_peer_is_local (fail-closed) in the daemon gate to exempt a provably local peer from the per-source cap and the auth lockout while keeping the per-module and global caps. Remote peers are unchanged. Document the shared-NAT/proxy identity limitation and the loopback exemption in README/RSYNC_COMPAT/CHANGELOG, update the integration test to assert the exemption, and fix the README 'auth failure delay' cap (5000, not 60000).
5.6 KiB
5.6 KiB
Changelog
All notable changes to FastSync are documented here. Versions match
PROTOCOL_VERSION (printed by fastsync --version); the client and server must
run the same version because the handshake is strict.
[Unreleased]
Security
- Enforce the daemon's per-module
max connectionscap and add a globalmax connections per hostcap plus a cross-processauth lockout(auth lockout threshold/auth lockout duration). Because the listener forks one child per connection, the counters live in an anonymous shared mapping created before the accept loop and reclaimed by the parent'sSIGCHLDhandler, so the per-module, per-source and auth-failure state is shared across every child (including afterSIGKILL). The per-source table now has a bounded lifetime (expired-lockout/idle entries are reclaimed, with a rate-limited warning when it is genuinely full), and the occupancy counters are re-derived from the shared slot table on every child exit so a child killed mid-registration cannot leak a count. Trusted loopback peers are exempt from the per-host cap and the auth lockout (they share one address); clients behind a shared NAT/proxy still share a single per-host budget and lockout, which is documented.
[2.20.0] - 2026-09-13
Security
- Cap cumulative
DirTimeListgrowth and bound pre-auth config-string memory (remote memory-exhaustion DoS). - Daemon host access control (
hosts allow/hosts deny, IPv4/IPv6/CIDR), configurable globalmax connections, connection audit logging, and a boundedauth failure delaythrottle. IPv4-mapped peers are normalized and invalid patterns are rejected at parse time (no silent fail-open). - Honor
--timeoutfor protocol I/O and bound idle/session time to defeat keepalive slowloris; child-safe signal handling in the forked daemon. - Compiler/linker hardening (
_FORTIFY_SOURCE, stack protector, PIE, RELRO) and pinned build dependencies.
Fixed
- Use-after-free in the basis-dir oversize preflight.
- Placeholder
Dataleaks,missing_argsleak, scanner chunk leak. - Thread-safe logging; single fd owner and cleanup epilogue in the server handler.
Performance
- Metadata now crosses the wire as one packed frame (protocol 2.20.0).
- Delete keep-set and
--files-fromlookups indexed (O(n*m) → O(n)). - Reused per-thread zstd contexts;
TCP_NODELAYby default. - Byte-bounded sender queues; removed a redundant scanner
stat().
[2.19.0] - 2026-09-12
Security
- Daemon authentication rewritten as SCRAM-SHA-256 challenge/response
(
STATUS_AUTH_CHALLENGE→STATUS_AUTH_RESPONSE→STATUS_AUTH_OK/STATUS_AUTH_FAILED), replacing the old replayable staticSHA-256(password)bearer credential. Each proof is bound to a fresh per-connection server nonce plus a client nonce, so a captured response can never be reused. - Salted verifier store.
--password-file/--early-inputnow holduser:$fastsync$1$pbkdf2-sha256$<iters>$<salt>$<stored_key>$<server_key>(PBKDF2-HMAC-SHA256, default 600000 iterations, range 100000–10000000). The legacyuser:SHA256HEXform is hard-rejected; there is no auto-upgrade. Generate stores offline withfastsync-server --hash-credentials FILE [--iterations N]. - Username-enumeration hardening. Unknown/off-list users are answered with a
dummy verifier whose salt is a deterministic per-username value
(
HMAC-SHA256(dummy_key, username)), using the store-wide uniform iteration count and a constant-time full-length membership scan. The dummy key is persisted in an owner-only<store>.dummykeysidecar (atomic publish, exact mode 0600) so challenges are stable across restarts. - Verified transport for auth-required modules. A module with
auth usersaccepts credentials only over verified TLS whose client certificate matches--client-cn, or — when--allow-unauthenticatedis explicitly set — plaintext from a loopback peer. Remote plaintext is refused before any challenge. Clients must use--tlsto send--password-filecredentials to a non-loopback daemon;--client-cnis mandatory with--tls. - Secret hygiene. The plaintext password, derived keys, nonces/proofs and the dummy key are wiped from memory on every path and never logged.
- Carried-over hardening:
-KTOCTOU-safe directory walk (openat(O_NOFOLLOW)per component), always shell-quoted SSH remote path, TLS compression/renegotiation disabled, race-free (open-then-fstat)--password-file/--early-inputchecks, log-injection escaping, and lazy protocol debug escaping.
Added
fastsync-server --hash-credentials FILE [--iterations N]offline tool.<store>.dummykeysidecar (auto-created, owner-only, 0600).- Integration tests for auth replay rejection, malformed frames, legacy-store refusal, and the loopback/TLS transport policy; fuzz targets for config receive and daemon-auth parsing.
Changed
- Protocol version 2.18.0 → 2.19.0 (breaking). The config-frame auth block
is now
[present][username](digest removed) and the auth challenge/response frames are interleaved between the config frame and itsSTATUS_OK. A 2.19.0 client and a 2.18.0 server (or vice versa) fail cleanly at the handshake. - Daemon modules declaring
auth usersrequire a configured credential store at startup (fail closed); operators regenerate stores from plaintext with--hash-credentials.
Notes
- First tagged release. FastSync implements rsync-compatible file synchronization over TCP and SSH with TLS (OpenSSL), streaming zstd compression, multithreaded transfers, and incremental sync. See RSYNC_COMPAT.md for the flag-parity matrix.