Security tutorial
Self-signed mTLS with OpenSSL
Create separate local certificate authorities for RESP clients and Raft peers, then configure Nexir and redis-cli.
Prerequisites
- OpenSSL 1.1.1 or newer.
- A private hostname and IP for the node. The examples cover
localhost,nexir-node1,127.0.0.1, and10.0.0.11. - A protected working directory.
umask 077
mkdir -p nexir-tls/{client,cluster}
cd nexir-tls1. Create the client CA
openssl genrsa -out client/client-ca.key 4096
openssl req -x509 -new -key client/client-ca.key -sha256 -days 3650 \
-subj "/CN=nexir-development-client-ca" \
-out client/client-ca.pem2. Create the RESP listener certificate
openssl genrsa -out client/redis-listener.key 2048
openssl req -new -key client/redis-listener.key \
-subj "/CN=nexir-node1" \
-out client/redis-listener.csr
cat > client/redis-listener.ext <<'EOF'
basicConstraints=CA:FALSE
keyUsage=digitalSignature,keyEncipherment
extendedKeyUsage=serverAuth
subjectAltName=DNS:localhost,DNS:nexir-node1,IP:127.0.0.1,IP:10.0.0.11
EOF
openssl x509 -req -in client/redis-listener.csr \
-CA client/client-ca.pem -CAkey client/client-ca.key -CAcreateserial \
-out client/redis-listener.pem -days 825 -sha256 \
-extfile client/redis-listener.extReplace SAN values with every hostname or IP clients actually use. A hostname mismatch causes the client to reject the server certificate.
3. Create a RESP client certificate
openssl genrsa -out client/redis-client.key 2048
openssl req -new -key client/redis-client.key \
-subj "/CN=nexir-development-client" \
-out client/redis-client.csr
cat > client/redis-client.ext <<'EOF'
basicConstraints=CA:FALSE
keyUsage=digitalSignature,keyEncipherment
extendedKeyUsage=clientAuth
EOF
openssl x509 -req -in client/redis-client.csr \
-CA client/client-ca.pem -CAkey client/client-ca.key -CAcreateserial \
-out client/redis-client.pem -days 825 -sha256 \
-extfile client/redis-client.ext4. Create a separate cluster CA
openssl genrsa -out cluster/cluster-ca.key 4096
openssl req -x509 -new -key cluster/cluster-ca.key -sha256 -days 3650 \
-subj "/CN=nexir-development-cluster-ca" \
-out cluster/cluster-ca.pem5. Create a Raft peer certificate
The same peer credential is used inbound and outbound, so include both extended key usages.
openssl genrsa -out cluster/cluster-node.key 2048
openssl req -new -key cluster/cluster-node.key \
-subj "/CN=nexir-node1" \
-out cluster/cluster-node.csr
cat > cluster/cluster-node.ext <<'EOF'
basicConstraints=CA:FALSE
keyUsage=digitalSignature,keyEncipherment
extendedKeyUsage=serverAuth,clientAuth
subjectAltName=DNS:localhost,DNS:nexir-node1,IP:127.0.0.1,IP:10.0.0.11
EOF
openssl x509 -req -in cluster/cluster-node.csr \
-CA cluster/cluster-ca.pem -CAkey cluster/cluster-ca.key -CAcreateserial \
-out cluster/cluster-node.pem -days 825 -sha256 \
-extfile cluster/cluster-node.ext6. Inspect and protect files
openssl verify -CAfile client/client-ca.pem \
client/redis-listener.pem client/redis-client.pem
openssl verify -CAfile cluster/cluster-ca.pem cluster/cluster-node.pem
openssl x509 -in client/redis-listener.pem -noout -text
chmod 0600 client/*.key cluster/*.key
chmod 0644 client/*.pem cluster/*.pemStore CA private keys away from database hosts after issuance. Copy only the required leaf keys and trust bundles to each host.
7. Configure and start Nexir
NEXIR__RAFT__NODE_ID=1 \
NEXIR__SERVER__REDIS_BIND=127.0.0.1:16379 \
NEXIR__SERVER__GRPC_BIND=127.0.0.1:15051 \
NEXIR__STORAGE__PATH=./data/tls-node1 \
NEXIR__SERVER__TLS__CLIENT__MODE=require \
NEXIR__SERVER__TLS__CLIENT__CERT_FILE="$PWD/client/redis-listener.pem" \
NEXIR__SERVER__TLS__CLIENT__KEY_FILE="$PWD/client/redis-listener.key" \
NEXIR__SERVER__TLS__CLIENT__CA_BUNDLE="$PWD/client/client-ca.pem" \
NEXIR__SERVER__TLS__CLUSTER__MODE=require \
NEXIR__SERVER__TLS__CLUSTER__CERT_FILE="$PWD/cluster/cluster-node.pem" \
NEXIR__SERVER__TLS__CLUSTER__KEY_FILE="$PWD/cluster/cluster-node.key" \
NEXIR__SERVER__TLS__CLUSTER__CA_BUNDLE="$PWD/cluster/cluster-ca.pem" \
nexir8. Connect with redis-cli
redis-cli --tls -h localhost -p 16379 \
--cacert "$PWD/client/client-ca.pem" \
--cert "$PWD/client/redis-client.pem" \
--key "$PWD/client/redis-client.key" \
--raw PING
# PONGInitialize using the same TLS client flags and an https:// peer address:
redis-cli --tls -h localhost -p 16379 \
--cacert "$PWD/client/client-ca.pem" \
--cert "$PWD/client/redis-client.pem" \
--key "$PWD/client/redis-client.key" \
--raw INIT 1 https://127.0.0.1:15051 127.0.0.1:16379Common TLS errors
| Symptom | Check |
|---|---|
| Unknown CA | Correct CA bundle on both sides and complete certificate chain |
| Hostname mismatch | Connect with a DNS/IP value present in the server certificate SAN |
| Peer sent no certificates | Client certificate and key were supplied; a one-off warning may occur during membership TLS preflight |
| Invalid private key | PEM encoding, readable permissions, and public key match |
| Peer connection refused | https:// address, private firewall rules, and peer listener state |
| Startup rejects shared CA | Use distinct client and cluster CA bundle files |
Rotation
Nexir does not hot reload TLS. Use overlapping trust bundles and rolling restarts, one node at a time. Monitor nexir_tls_cert_not_after_timestamp_seconds{surface="client"} and {surface="cluster"} and alert before expiry.