This document describes SoliKV's security model and the hardening shipped in the SEC-001…SEC-016 series.
Please report security issues privately to the maintainers rather than via public GitHub issues. Include reproduction steps and affected versions.
SoliKV is a Redis-protocol-compatible server. Two trust boundaries matter:
- Network → server: any party who can open a TCP connection to the RESP port (6379) or the REST port (5020).
- Local user → daemon: any local UNIX user who shares the host (e.g. multi-tenant deployments, shared CI runners).
Out of scope: an attacker with root on the same host, or with write access to the data directory after server startup.
- Protected mode (SEC-001): starting with
--bindset to a non-loopback address while--requirepassis unset is refused by default. Operators must either set a password or pass--protected-mode noto opt out. - TLS for RESP (SEC-002): pass
--tls-cert PATH --tls-key PATHto enable TLS termination on the RESP port. REST TLS is not yet wired (planned follow-up). The--tls-client-caflag is reserved for mTLS. - AUTH rate limiting (SEC-004): both the RESP server and the REST
middleware track failed AUTH attempts per peer IP and block the IP for
30 s after 10 failures, logging every failed attempt at
warn. The REST side usesinto_make_service_with_connect_info::<SocketAddr>()so the middleware sees the real peer address.
- REST denylist (SEC-005):
POST /cmdrejects destructive or DoS-prone commands (KEYS,SCAN/HSCAN/ZSCAN/SSCAN,RENAME,RENAMENX,RESET,LATENCY,MEMORY,WAIT,FAILOVER,CLIENT,INFO,DBSIZE,FLUSHDB,FLUSHALL,SHUTDOWN,EVAL,CONFIG, …).
- RDB length sanitization (SEC-006):
read_bytesrejects any chunk longer than 512 MB (MAX_VALUE_LEN); collection counts are clamped to 16 M (MAX_COLLECTION_LEN) beforewith_capacity. - AOF length sanitization (SEC-006): bulk lengths above 512 MB
(
MAX_AOF_BULK_LEN) abort the replay. - AOF replay denylist (SEC-007):
EVAL,EVALSHA,SCRIPT,DEBUG,SHUTDOWN,BGREWRITEAOF,BGSAVE,SLAVEOF/REPLICAOF,CLUSTER,MIGRATE,RESTORE,MODULEare skipped (with awarn) on replay. The data directory must still be on a filesystem only the SoliKV UID can write to — replay is not authenticated.
- PID file (SEC-003): moved from
/tmp/solikv.pidto<data-dir>/solikv.pid. The "kill old daemon" path resolves/proc/<pid>/exewithfs::read_link, canonicalizes both that andcurrent_exe(), and only signals the PID if the canonical paths match. Signalling usesnix::sys::signal::killdirectly, not a shell-out. - Cluster dump password (SEC-008):
solikv --cluster-dump/--cluster-restoreonly sends--cluster-passwordto the seed node by default. To AUTH against discovered nodes, pass--cluster-password-seed-only=false. - Password sources (SEC-009):
--requirepass/--cluster-passwordmay be supplied via--requirepass-file PATH/--cluster-password-file PATH(preferred) or theSOLIKV_REQUIREPASS/SOLIKV_CLUSTER_PASSWORDenv vars. Password files are opened withO_NOFOLLOWand rejected if the mode is looser than0600(any group/world bits set ⇒ startup fails). CLI flags remain for compatibility but log awarnbecause/proc/<pid>/cmdlineexposes them to other local users. - Installer (SEC-011):
install.shdownloadsSHA256SUMSalongside the release tarball, greps out exactly the line for the tarball it just downloaded, and verifies that single line withsha256sum -c --strict(orshasum -a 256). Missing-tarball, missing-checksum-line, and tag fetch failures all abort with a non-zero exit.tar --no-same-owneris used during extraction.
- EVAL/EVALSHA arithmetic (SEC-012):
numkeys + 2useschecked_add; overflow returnsERR Number of keys can't be greater than number of argsinstead of panicking the connection. Same fix applied toZUNIONSTOREandZINTERSTORE. - RESP decoder allocation (SEC-013):
decode_arrayno longer callsVec::with_capacity(N)from an untrusted*N\r\nheader — capacity grows on push. The decoder additionally takes anauthenticatedflag; an unauthenticated peer cannot declare arrays larger thanMAX_UNAUTH_ARRAY_LEN(32) or bulk strings larger thanMAX_UNAUTH_BULK_LEN(64 KiB). The cap applies recursively, so a bulk embedded inside an array is also bound. The RESP server passes the currentconn.authenticatedstate into every decode call. - FFI memory safety (SEC-010):
static mut KV_STOREreplaced withOnceLock<Mutex<HashMap>>. Everysolikv_*extern returns-1on null pointers and validates lengths via separatevalidate_key/validate_valuehelpers, so keys are bounded byMAX_KEY_LEN(4 KiB) and values byMAX_VALUE_LEN(64 KiB) without conflating the two. The crate is now in the workspace and covered bycargo test. - Lua VM hygiene (SEC-014): when a pooled VM is created, the post-setup
global key set is snapshotted. On reuse, every key on
_Gthat is not in the snapshot is wiped, thensandbox_luaandsetup_redis_moduleare re-applied — so a previous script cannot leak_G.foo = 'secret'to the next script that lands on this thread, nor permanently replaceredis.call. Mutations to the contents of pre-existing tables (e.g. clearingstring.upper) are still possible; full isolation requires per-script_ENV, which we hold for a follow-up if cross-tenant scoping ever lands. - Build artifact hygiene (SEC-015):
.gitignorenow excludes*.rmeta,*.rcgu.o,*.rcgu,*.o,*.rlib,*.dylib,*.so,*.dll,*.pdb,*.pdb.lockto prevent accidentally committing rustc artifacts. - Cluster bus / replication (SEC-016):
gossip.rsandreplication.rscarrySECURITY NOTE (SEC-016)blocks describing the auth/TLS/HMAC work required before the modules are wired intomain.rs.
- REST TLS is not implemented (SEC-002 follow-up): only the RESP port has TLS today. Front the REST port with a TLS-terminating reverse proxy if it is exposed beyond loopback.
- Cluster bus / replication TLS + HMAC (SEC-016): scaffolding only; do
not wire these modules to a non-loopback listener until the work
described in the in-file
SECURITY NOTE (SEC-016)is done. - Lua nested-table isolation (SEC-014 partial): top-level
_Gkeys and theredis.*module are restored between scripts, but mutations to the contents of inherited tables (string,math,table, …) are not. Adequate for the single-tenant model; revisit if per-tenant scopes land. - Argv password leakage:
--requirepass <pw>and--cluster-password <pw>remain visible in/proc/<pid>/cmdlineuntil the process exits. Prefer the file or env-var form.
- Run with
--requirepass-fileorSOLIKV_REQUIREPASSand a 0600 file. - Bind to loopback (
--bind 127.0.0.1) unless TLS is configured. - For multi-host deployments: front the RESP port with a TLS terminator
(or use
--tls-cert/--tls-key) and requirerequirepass. - Keep the data directory on a filesystem writable only by the SoliKV UID (AOF replay trusts the file).
- Do not use the cluster bus or replication modules until SEC-016 is closed.