m11p7 — secure the cluster, all opt-in (pre-m11p7 byte-for-byte):
- gRPC replication mTLS by default via a custom tokio-rustls acceptor +
DynamicCertResolver; zero-drop content-hash cert rotation (k8s ..data swap,
no pod restart, no inotify)
- inter-node HTTP TLS sharing the same resolver (one rotation, both planes) +
per-node keyed-BLAKE3 signed x-tidal-node-token; marker-without-token -> 403
- admin audit log (operator-leg only) + per-principal rate limit (engine
RateLimiter; sibling nodes exempt)
- k8s cert-manager manifest (certs.yaml) + scripts/gen-cluster-certs.sh fallback;
secret.example.yaml gains TIDAL_CLUSTER_KEY (file-mounted, hot-rotatable)
- exit gate verified real: mtls.rs (gRPC foreign-pod), cluster_security.rs
(HTTP foreign + zero-drop rotation under load), 7 security unit tests
perf — instrument floor (sweep Wave 1):
- new tidal/benches/wal.rs + tidal-server/benches/scatter.rs
- p99->mean honesty relabel; sweep manifest at docs/reviews/perf-sweep-2026-06-13.md
- add @tidal-performance agent (Martin Thompson)
new: cluster/{audit,http_tls,security}.rs, tests/cluster_security.rs,
docs/planning/milestone-11/phase-7.md
58 lines
2.5 KiB
Bash
Executable File
58 lines
2.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Generate the tidalDB inter-node TLS material (m11p7) for clusters NOT using
|
|
# cert-manager: a self-signed cluster CA + ONE shared node cert whose SANs cover
|
|
# every pod's stable DNS (the StatefulSet pattern). Output matches the cert-manager
|
|
# Secret shape — tls.crt / tls.key / ca.crt — so the topology grpc_tls paths and
|
|
# the k8s mount are identical either way.
|
|
#
|
|
# Usage:
|
|
# scripts/gen-cluster-certs.sh [OUT_DIR] [NAMESPACE] [STATEFULSET] [HEADLESS_SVC] [N]
|
|
# Defaults reproduce k8s/cluster/: out=./cluster-certs ns=tidaldb-cluster
|
|
# sts=tidaldb headless=tidaldb-peers N=3 (pods tidaldb-0..2).
|
|
#
|
|
# Then either mount the files at /etc/tidaldb/tls, or load them as the Secret the
|
|
# StatefulSet expects:
|
|
# kubectl -n tidaldb-cluster create secret generic tidaldb-cluster-tls \
|
|
# --from-file=tls.crt=cluster-certs/tls.crt \
|
|
# --from-file=tls.key=cluster-certs/tls.key \
|
|
# --from-file=ca.crt=cluster-certs/ca.crt
|
|
#
|
|
# Local (non-k8s) clusters point each region's grpc_tls at these files directly
|
|
# (ca_cert=ca.crt, server_cert=client_cert=tls.crt, server_key=client_key=tls.key).
|
|
set -euo pipefail
|
|
|
|
OUT_DIR="${1:-./cluster-certs}"
|
|
NAMESPACE="${2:-tidaldb-cluster}"
|
|
STS="${3:-tidaldb}"
|
|
HEADLESS="${4:-tidaldb-peers}"
|
|
N="${5:-3}"
|
|
|
|
mkdir -p "$OUT_DIR"
|
|
cd "$OUT_DIR"
|
|
|
|
# Build the SAN list: every pod's stable headless-Service DNS + the headless and
|
|
# client Services + loopback (so a local test cluster on 127.0.0.1 also verifies).
|
|
SANS="DNS:${HEADLESS}.${NAMESPACE}.svc.cluster.local,DNS:${STS}.${NAMESPACE}.svc.cluster.local,DNS:localhost,IP:127.0.0.1"
|
|
for i in $(seq 0 $((N - 1))); do
|
|
SANS="${SANS},DNS:${STS}-${i}.${HEADLESS}.${NAMESPACE}.svc.cluster.local"
|
|
done
|
|
|
|
echo "==> cluster CA"
|
|
openssl genrsa -out ca.key 4096 2>/dev/null
|
|
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \
|
|
-subj "/CN=tidaldb-cluster-ca" -out ca.crt
|
|
|
|
echo "==> node leaf (SANs: ${SANS})"
|
|
openssl genrsa -out tls.key 4096 2>/dev/null
|
|
openssl req -new -key tls.key -subj "/CN=tidaldb-cluster" -out node.csr
|
|
# serverAuth + clientAuth so the same leaf is the gRPC mTLS client identity AND
|
|
# the gRPC/HTTP server identity.
|
|
openssl x509 -req -in node.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
|
|
-days 825 -sha256 -out tls.crt \
|
|
-extfile <(printf 'subjectAltName=%s\nextendedKeyUsage=serverAuth,clientAuth\n' "$SANS")
|
|
rm -f node.csr ca.srl
|
|
|
|
chmod 600 ca.key tls.key
|
|
echo "==> wrote ${OUT_DIR}/{ca.crt,tls.crt,tls.key} (+ ca.key — keep offline)"
|
|
echo " grpc_tls: ca_cert=ca.crt server_cert=client_cert=tls.crt server_key=client_key=tls.key"
|