The Relay Proxy supports Redis for caching flag data. This guide covers all Redis authentication methods and setup.
- Authentication Methods
- Password Authentication
- mTLS Authentication
- Certificate Generation
- Configuration Reference
- Architecture Overview
- Helm Chart Deployment
- Testing
- Troubleshooting
- FAQs
- Security Notes
The Relay Proxy supports two Redis authentication methods:
- Password Authentication (default) - Username/password with optional TLS encryption
- mTLS Authentication - Mutual TLS with client certificates
Password authentication is the default method. It supports both plain and TLS-encrypted connections.
export REDIS_ADDRESS=localhost:6379
export REDIS_PASSWORD=your_password
export REDIS_USERNAME=your_username # Optional
export REDIS_DB=0 # Optional, default: 0Using rediss:// automatically enables TLS encryption:
export REDIS_ADDRESS=rediss://localhost:6379
export REDIS_PASSWORD=your_passwordThe rediss:// protocol prefix automatically enables TLS encryption with server verification.
mTLS (Mutual TLS) requires client certificates for authentication. This provides the highest level of security.
- Redis server configured with mTLS - Redis must be running with TLS and require client certificates
- CA certificate - To verify the Redis server
- Client certificate - For mTLS authentication
- Client private key - For mTLS authentication
export REDIS_ADDRESS=rediss://localhost:6380
export REDIS_MTLS_CA_CERT=/path/to/ca.crt
export REDIS_MTLS_CLIENT_CERT=/path/to/client.crt
export REDIS_MTLS_CLIENT_KEY=/path/to/client.key
# Optional: Additional TLS settings
export REDIS_TLS_INSECURE_SKIP_VERIFY=false # Skip server verification (testing only)
export REDIS_TLS_SERVER_NAME=localhost # SNI server nameYou can use both password and mTLS together if your Redis requires both:
export REDIS_ADDRESS=rediss://localhost:6380
export REDIS_USERNAME=myuser
export REDIS_PASSWORD=mypassword
export REDIS_MTLS_CA_CERT=/path/to/ca.crt
export REDIS_MTLS_CLIENT_CERT=/path/to/client.crt
export REDIS_MTLS_CLIENT_KEY=/path/to/client.keyFor local testing, you can generate self-signed certificates using OpenSSL.
# Generate all certificates automatically
./examples/redis_mtls/generate-certs.shThis creates:
./certs/redis/ca.crt- CA certificate./certs/redis/ca.key- CA private key./certs/redis/server.crt- Redis server certificate./certs/redis/server.key- Redis server private key./certs/redis/client.crt- Client certificate (for mTLS)./certs/redis/client.key- Client private key (for mTLS)
If you prefer to generate certificates manually:
# Create certificates directory
mkdir -p ./certs/redis
cd ./certs/redis
# 1. Generate CA private key
openssl genrsa -out ca.key 2048
# 2. Generate CA certificate
openssl req -new -x509 -days 365 -key ca.key -out ca.crt \
-subj "/C=US/ST=State/L=City/O=Test/CN=Redis-CA"
# 3. Generate server private key
openssl genrsa -out server.key 2048
# 4. Generate server certificate signing request
openssl req -new -key server.key -out server.csr \
-subj "/C=US/ST=State/L=City/O=Test/CN=localhost"
# 5. Generate server certificate (signed by CA)
openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out server.crt \
-extensions v3_req -extfile <(echo "[v3_req]"; echo "subjectAltName=IP:127.0.0.1,DNS:localhost")
# 6. Generate client private key (for mTLS)
openssl genrsa -out client.key 2048
# 7. Generate client certificate signing request
openssl req -new -key client.key -out client.csr \
-subj "/C=US/ST=State/L=City/O=Test/CN=ff-proxy-client"
# 8. Generate client certificate (signed by CA)
openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out client.crt
# 9. Clean up temporary files
rm server.csr client.csr ca.srl- Format: All certificates must be in PEM format
- Permissions: Private keys (
.keyfiles) should have restricted permissions (600) - Paths: Use absolute paths or paths relative to the working directory
For local testing, you need a Redis instance configured with TLS and mTLS.
A Docker Compose file is provided for local testing:
# Start Redis with TLS/mTLS
docker-compose -f docker-compose.redis-tls.yml up -d redis-tlsThis starts Redis on port 6380 with TLS enabled and requires client certificates.
If you have Redis installed locally, create redis-tls.conf:
port 0
tls-port 6380
tls-cert-file /path/to/certs/redis/server.crt
tls-key-file /path/to/certs/redis/server.key
tls-ca-cert-file /path/to/certs/redis/ca.crt
tls-auth-clients yes
tls-protocols "TLSv1.2 TLSv1.3"
Start Redis:
redis-server redis-tls.conf# Using Docker
docker exec ff-proxy-redis-tls redis-cli --tls \
--cert /certs/client.crt \
--key /certs/client.key \
--cacert /certs/ca.crt \
-p 6380 pingExpected: PONG
| Variable | Flag | Description | Default | Required |
|---|---|---|---|---|
REDIS_ADDRESS |
redis-address |
Redis host:port address | - | Yes |
REDIS_USERNAME |
redis-username |
Redis username (ACL) | - | No |
REDIS_PASSWORD |
redis-password |
Redis password | - | No |
REDIS_DB |
redis-db |
Database number | 0 | No |
REDIS_MTLS_CA_CERT |
redis-mtls-ca-cert |
Path to CA certificate | "" | Yes (if mTLS) |
REDIS_MTLS_CLIENT_CERT |
redis-mtls-client-cert |
Path to client certificate | "" | Yes (if mTLS) |
REDIS_MTLS_CLIENT_KEY |
redis-mtls-client-key |
Path to client private key | "" | Yes (if mTLS) |
REDIS_TLS_INSECURE_SKIP_VERIFY |
redis-tls-insecure-skip-verify |
Skip server verification | false | No |
REDIS_TLS_SERVER_NAME |
redis-tls-server-name |
SNI server name | "" | No |
Important Notes:
- Mount Path is Configurable: The default mount path is
/etc/redis/tls, but this can be customized - Path Construction: Certificate paths are constructed as
mountPath + "/" + secretKey - Example: If
mountPath="/etc/redis/tls"andsecretKey="ca.crt", the full path is/etc/redis/tls/ca.crt - Override: You can override the mount path if needed (e.g., for custom security policies)
Local Development vs Production:
- Local/Docker Compose: For local testing (e.g.,
docker-compose.yml), certificates are mounted via bind mounts (e.g.,./certs:/certs) - Production/Helm: In Kubernetes, certificates come from Secrets mounted at
/etc/redis/tls(configurable viaredis.tls.mountPath) - Container Paths: The application reads certificates from the paths specified in
REDIS_MTLS_*environment variables, regardless of how they're mounted
| Variable | Flag | Description | Default |
|---|---|---|---|
REDIS_POOL_SIZE |
redis-pool-size |
Pool size multiplier (× CPU cores) | 10 |
REDIS_POOL_SIZE_LITERAL |
redis-pool-size-literal |
Fixed pool size (overrides multiplier) | 0 |
REDIS_DIAL_TIMEOUT_SECONDS |
redis-dial-timeout-seconds |
Connection timeout | 5 |
REDIS_READ_TIMEOUT_SECONDS |
redis-read-timeout-seconds |
Read timeout | 3 |
REDIS_WRITE_TIMEOUT_SECONDS |
redis-write-timeout-seconds |
Write timeout | 3 |
REDIS_POOL_TIMEOUT_SECONDS |
redis-pool-timeout-seconds |
Pool timeout | 4 |
See Configuration for complete list of options.
The following diagram illustrates how Redis client initialization works with different authentication methods:
graph TB
Start([Application Start]) --> Main[main.go: main]
Main --> BuildConfig[main.go: buildRedisConfig]
BuildConfig --> ConfigStruct[redis/config.go: Config Struct]
ConfigStruct --> AutoDetect[redis/config.go: AutoDetectTLS]
AutoDetect --> Validate[redis/config.go: Validate]
Validate --> GetAuthMode[redis/config.go: AuthMode]
GetAuthMode --> AuthDecision{All mTLS certs provided?}
AuthDecision -->|Yes| ReturnMTLS[Return 'mtls']
AuthDecision -->|No| ReturnPassword[Return 'password']
ReturnMTLS --> ValidateMTLS[Validate: mtlsCertificateValidator]
ReturnPassword --> ValidatePassword[Validate: return nil]
ValidateMTLS --> ValidateCA[util/file_handler.go: ValidateFileExists<br/>ValidateFileReadable]
ValidateMTLS --> ValidateClientCert[util/file_handler.go: ValidateFileExists<br/>ValidateFileReadable]
ValidateMTLS --> ValidateClientKey[util/file_handler.go: ValidateFileExists<br/>ValidateFileReadable]
ValidateCA --> NewClient[redis/client.go: NewClient]
ValidateClientCert --> NewClient
ValidateClientKey --> NewClient
ValidatePassword --> NewClient
NewClient --> AuthSwitch{Switch AuthMode}
AuthSwitch -->|password| BuildPassword[redis/client.go: buildOptionsWithPasswordAuth]
AuthSwitch -->|mtls| BuildMTLS[redis/client.go: buildOptionsWithMTLSAuth]
BuildPassword --> ParseURL[redis/client.go: parseRedisURL]
ParseURL --> ParseAddresses[redis/client.go: parseAddresses]
ParseAddresses --> BuildOptions1[Build UniversalOptions<br/>TLSConfig from URL]
BuildMTLS --> BuildTLS[redis/tls.go: BuildTLSConfig]
BuildTLS --> LoadCA[Load CA Certificate]
LoadCA --> LoadClientCert[Load Client Certificate]
LoadClientCert --> LoadClientKey[Load Client Key]
LoadClientKey --> CreateTLSConfig[Create tls.Config<br/>with Certificates]
CreateTLSConfig --> ParseAddresses2[redis/client.go: parseAddresses]
ParseAddresses2 --> CalculateTimeouts[redis/client.go: calculateTimeouts]
CalculateTimeouts --> CalculatePoolSize[redis/client.go: calculatePoolSize]
CalculatePoolSize --> BuildOptions2[Build UniversalOptions<br/>TLSConfig from BuildTLSConfig]
BuildOptions1 --> CreateClient[redis.NewUniversalClient]
BuildOptions2 --> CreateClient
CreateClient --> End([Redis Client Ready])
style Start fill:#e1f5ff
style End fill:#c8e6c9
style AuthDecision fill:#fff9c4
style AuthSwitch fill:#fff9c4
style BuildPassword fill:#bbdefb
style BuildMTLS fill:#c5e1a5
style ValidateMTLS fill:#ffccbc
The AuthMode() function determines authentication type:
- Default: Always returns
"password"(backward compatible) - mTLS: Automatically enabled when all three certificate paths are provided (CA cert, client cert, client key)
- TLS via URL:
rediss://protocol auto-enables TLS encryption but uses password auth (not mTLS)
cmd/ff-proxy/
└── main.go
└── buildRedisConfig() → redis.Config
redis/
├── config.go
│ ├── Config struct
│ ├── AuthMode() → "password" | "mtls"
│ ├── AutoDetectTLS()
│ └── Validate()
│
├── client.go
│ ├── NewClient()
│ ├── buildOptionsWithPasswordAuth()
│ └── buildOptionsWithMTLSAuth()
│
└── tls.go
└── BuildTLSConfig() → *tls.Config
util/
└── file_handler.go
├── ValidateFileExists()
└── ValidateFileReadable()
The Helm chart supports Redis mTLS authentication through Kubernetes Secrets. TLS is disabled by default and must be explicitly enabled by clients.
- Kubernetes Secret containing Redis TLS certificates
- Helm chart with Redis TLS configuration enabled
- Certificates in PEM format (CA, client cert, client key)
# Create secret with certificates
kubectl create secret generic redis-tls-secret \
--from-file=ca.crt=/path/to/ca.crt \
--from-file=client.crt=/path/to/client.crt \
--from-file=client.key=/path/to/client.key \
-n <namespace>helm upgrade -i ff-proxy --namespace ff-proxy . \
--set proxyKey=xxxx-xxx-xxx-xxxx \
--set authSecret=xxxx-xxx-xxx-xxxx \
--set global.database.redis.protocol=rediss \
--set global.database.redis.hosts[0]=redis.example.com:6380 \
--set global.database.redis.tls.enabled=true \
--set global.database.redis.tls.secret.name=redis-tls-secretCreate my-values.yaml:
global:
database:
redis:
protocol: "rediss"
hosts:
- redis.example.com:6380
tls:
enabled: true
secret:
name: "redis-tls-secret"
caCert: "ca.crt"
clientCert: "client.crt"
clientKey: "client.key"
mountPath: "/etc/redis/tls"
serverName: "redis" # Optional SNIDeploy:
helm upgrade -i ff-proxy --namespace ff-proxy . -f my-values.yamlIf you need a custom mount path:
global:
database:
redis:
protocol: "rediss"
hosts:
- redis.example.com:6380
tls:
enabled: true
secret:
name: "redis-tls-secret"
mountPath: "/custom/certs" # Override default /etc/redis/tlsNote: Certificate paths are automatically constructed as mountPath + "/" + secretKey.
| Field | Type | Default | Description |
|---|---|---|---|
global.database.redis.protocol |
string | "redis" |
Protocol (redis or rediss for TLS) |
global.database.redis.hosts |
list | [] |
Redis host:port list |
global.database.redis.tls.enabled |
bool | false |
Enable mTLS (opt-in, required for mTLS) |
global.database.redis.tls.secret.name |
string | "" |
Kubernetes secret name (required if enabled) |
global.database.redis.tls.secret.caCert |
string | "ca.crt" |
Secret key for CA certificate |
global.database.redis.tls.secret.clientCert |
string | "client.crt" |
Secret key for client certificate |
global.database.redis.tls.secret.clientKey |
string | "client.key" |
Secret key for client private key |
global.database.redis.tls.mountPath |
string | "/etc/redis/tls" |
Mount path for certificates (configurable) |
global.database.redis.tls.insecureSkipVerify |
bool | false |
Skip server verification (testing only) |
global.database.redis.tls.serverName |
string | "" |
SNI server name (optional) |
-
Secret Mounting: When
redis.tls.enabled: true, the Helm chart:- Mounts the Kubernetes Secret as a volume at
redis.tls.mountPath - Sets environment variables with certificate file paths
- Constructs paths as
mountPath + "/" + secretKey
- Mounts the Kubernetes Secret as a volume at
-
Environment Variables: The chart sets:
REDIS_MTLS_CA_CERT=/etc/redis/tls/ca.crt(or custom path)REDIS_MTLS_CLIENT_CERT=/etc/redis/tls/client.crtREDIS_MTLS_CLIENT_KEY=/etc/redis/tls/client.key
-
Application: The application reads certificates from the mounted paths and establishes mTLS connection.
- Default Behavior:
redis.tls.enabled: false(TLS disabled) - Existing Deployments: Continue working without any changes
- No Action Required: Clients who don't need TLS don't need to do anything
- Opt-in: Only clients who need mTLS must explicitly enable it
After deployment, verify TLS configuration:
# Check environment variables
kubectl exec -n <namespace> <pod-name> -- env | grep REDIS_TLS
# Check mounted certificates
kubectl exec -n <namespace> <pod-name> -- ls -la /etc/redis/tls
# Check logs for authentication mode
kubectl logs -n <namespace> <pod-name> | grep "connecting to redis"Expected Logs:
INFO connecting to redis address=rediss://redis.example.com:6380 db=0 authMode=mtls tlsEnabled=true poolSize=20
Symptoms: Pod crashes with errors about missing certificates
Solutions:
- Verify secret exists:
kubectl get secret redis-tls-secret -n <namespace> - Verify secret contains required keys:
kubectl describe secret redis-tls-secret -n <namespace> - Check secret key names match
values.yamlconfiguration - Verify certificates are base64-encoded in secret
Symptoms: Application doesn't detect TLS configuration
Solutions:
- Verify
redis.tls.enabled: truein values.yaml - Check deployment template has conditional blocks
- Verify environment variables in pod:
kubectl exec <pod> -- env | grep REDIS_TLS - Check Helm template rendering:
helm template . --debug
Symptoms: Certificate files not found at expected paths
Solutions:
- Verify volume mount exists:
kubectl describe pod <pod-name> | grep -A 5 "Mounts" - Check volume definition references correct secret
- Verify mount path matches environment variable paths
- Check secret key names match mount path construction
export REDIS_ADDRESS=localhost:6379
export REDIS_PASSWORD=your_password
export AUTH_SECRET="dummy-secret"
export PROXY_KEY="dummy-key"
export BYPASS_AUTH=true
export OFFLINE=true
./ff-proxyExpected Logs:
INFO connecting to redis address=localhost:6379 db=0 authMode=password tlsEnabled=false poolSize=20
export REDIS_ADDRESS=rediss://localhost:6379
export REDIS_PASSWORD=your_password
export AUTH_SECRET="dummy-secret"
export PROXY_KEY="dummy-key"
export BYPASS_AUTH=true
export OFFLINE=true
./ff-proxyExpected Logs:
INFO connecting to redis address=rediss://localhost:6379 db=0 authMode=password tlsEnabled=true poolSize=20
# 1. Generate certificates (if not done)
./examples/redis_mtls/generate-certs.sh
# 2. Start Redis with TLS (if not running)
docker-compose -f docker-compose.redis-tls.yml up -d redis-tls
# 3. Configure and run proxy
export REDIS_ADDRESS=rediss://localhost:6380
export REDIS_MTLS_CA_CERT=./certs/redis/ca.crt
export REDIS_MTLS_CLIENT_CERT=./certs/redis/client.crt
export REDIS_MTLS_CLIENT_KEY=./certs/redis/client.key
export AUTH_SECRET="dummy-secret"
export PROXY_KEY="dummy-key"
export BYPASS_AUTH=true
export OFFLINE=true
./ff-proxyExpected Logs:
INFO connecting to redis address=rediss://localhost:6380 db=0 authMode=mtls poolSize=20
Check the health endpoint:
curl http://localhost:8000/healthExpected Response:
{
"cacheStatus": "healthy",
...
}Error:
failed to read CA certificate: open ./certs/redis/ca.crt: no such file or directory
Solution:
- Verify certificate file paths are correct
- Use absolute paths if relative paths don't work
- Check file permissions (certificates should be readable)
Error:
certificate verify failed
Solution:
- Ensure CA certificate matches the one used by Redis server
- Check certificate expiration dates
- Verify certificate format (must be PEM)
Error:
handshake failure
Solution:
- Verify
tls-auth-clients yesis set in Redis configuration - Ensure client certificate is signed by the same CA as server
- Check Redis logs for authentication errors
Error:
dial tcp [::1]:6380: connect: connection refused
Solution:
- Verify Redis is running:
docker ps | grep redis-tls - Check Redis is listening on correct port
- Verify firewall/network settings
Error:
incomplete mTLS configuration: all three certificate paths are required when any are set
Solution:
- mTLS requires all three certificate paths:
REDIS_MTLS_CA_CERT,REDIS_MTLS_CLIENT_CERT,REDIS_MTLS_CLIENT_KEY - If any one is set, all three must be provided
- Use
rediss://protocol for TLS connections
Yes. The Relay Proxy can connect to AWS ElastiCache Redis instances as long as cluster mode is disabled.
If connection is lost to Harness servers, the Relay Proxy will continue to serve cached values to connected SDKs. It will attempt to reconnect and sync the latest data once connection is restored.
Once connection is reestablished with SaaS, all flag data is pulled down again. Any changes made on SaaS (e.g., flag toggles) during the outage will be picked up once connection is restored.
If the Relay Proxy has previously stored flag data in Redis, it will startup successfully using the cached data and serve connected SDKs. This essentially launches the Relay Proxy in offline mode.
Yes. If your Redis requires both password authentication and mTLS, you can configure both:
- Set
REDIS_PASSWORDandREDIS_USERNAME - Provide all three mTLS certificate paths:
REDIS_MTLS_CA_CERT,REDIS_MTLS_CLIENT_CERT,REDIS_MTLS_CLIENT_KEY
- Self-signed certificates are for testing only
- Use proper CA-signed certificates in production
- Keep private keys (
.keyfiles) secure - Never commit certificates or private keys to version control
- Use appropriate file permissions (600 for private keys)
- Rotate certificates regularly in production
- Configuration Guide - Complete configuration reference
- Testing Guide - Comprehensive testing instructions
- Helm Chart README - Helm deployment guide