Fix redis ssl issues 5

This commit is contained in:
2026-01-25 08:43:24 +01:00
parent b44d7c8c1b
commit c8dc021e13
4 changed files with 43 additions and 128 deletions

View File

@@ -18,7 +18,7 @@ httpx==0.25.1
aiohttp==3.9.1
# Redis
redis==5.0.1
redis==6.4.0
hiredis==2.2.3
# Utilities

View File

@@ -15,7 +15,7 @@ psycopg2-binary==2.9.9
aio-pika==9.3.0
# Redis
redis[hiredis]==5.0.1
redis==6.4.0
# HTTP client
httpx==0.25.1

View File

@@ -9,7 +9,7 @@ httpx==0.25.2
pydantic==2.5.0
pydantic-settings==2.1.0
structlog==24.1.0
redis==5.0.1
redis==6.4.0
ortools==9.8.3296 # Google OR-Tools for VRP optimization
# Message queuing

View File

@@ -1,9 +1,11 @@
"""
Redis client initialization and connection management
Provides standardized Redis connection for all services
Compatible with redis-py 6.4.0+
SSL/TLS support for self-signed certificates in Kubernetes environments
"""
import ssl
import redis.asyncio as redis
from typing import Optional, Dict, Any
import structlog
@@ -19,6 +21,8 @@ def get_ssl_kwargs_for_url(redis_url: str) -> Dict[str, Any]:
Handles self-signed certificates by disabling certificate verification
when using rediss:// (TLS-enabled) URLs.
For redis-py 6.4.0+, ssl_cert_reqs accepts string values: "none", "optional", "required"
Args:
redis_url: Redis connection URL (redis:// or rediss://)
@@ -27,10 +31,8 @@ def get_ssl_kwargs_for_url(redis_url: str) -> Dict[str, Any]:
"""
if redis_url and redis_url.startswith("rediss://"):
return {
"ssl_cert_reqs": ssl.CERT_NONE, # Disable certificate verification
"ssl_ca_certs": None, # Don't require CA certificates
"ssl_certfile": None, # Don't require client cert
"ssl_keyfile": None, # Don't require client key
"ssl_cert_reqs": "none", # Don't verify server certificate (self-signed)
"ssl_check_hostname": False, # Don't verify hostname for internal services
}
return {}
@@ -133,130 +135,43 @@ class RedisConnectionManager:
try:
self._redis_url = redis_url
# Create connection pool with SSL handling for self-signed certificates
# For Redis 6.4.0+, we need to handle SSL parameters correctly
# Common connection kwargs for both TLS and non-TLS
connection_kwargs = {
'db': db,
'max_connections': max_connections,
'decode_responses': decode_responses,
'retry_on_timeout': retry_on_timeout,
'socket_keepalive': socket_keepalive,
'health_check_interval': health_check_interval
}
# For redis-py 6.4.0+, SSL parameters must be passed to ConnectionPool.from_url()
# The library handles SSL internally when using rediss:// URLs
if redis_url.startswith("rediss://"):
# Extract connection parameters from URL
from urllib.parse import urlparse
parsed_url = urlparse(redis_url)
# Build connection parameters for ConnectionPool
connection_params = {
'db': db,
'max_connections': max_connections,
'retry_on_timeout': retry_on_timeout,
'socket_keepalive': socket_keepalive,
'health_check_interval': health_check_interval
# SSL configuration for self-signed certificates (internal K8s traffic)
# Redis server has --tls-auth-clients no, so client certs are NOT required
# We use ssl_cert_reqs="none" to skip verifying the self-signed server cert
ssl_kwargs = {
'ssl_cert_reqs': "none", # Don't verify server certificate (self-signed)
'ssl_check_hostname': False, # Don't verify hostname (internal service names)
}
# Add password if present
if parsed_url.password:
connection_params['password'] = parsed_url.password
# Create connection pool (without SSL parameters - they go to the client)
self._pool = redis.ConnectionPool(
host=parsed_url.hostname,
port=parsed_url.port or 6379,
**connection_params
)
# Get SSL configuration for self-signed certificates
ssl_kwargs = get_ssl_kwargs_for_url(redis_url)
# Create Redis client with SSL parameters
client_params = {
'connection_pool': self._pool,
'decode_responses': decode_responses
}
if ssl_kwargs:
client_params['ssl'] = True
client_params['ssl_cert_reqs'] = ssl_kwargs.get('ssl_cert_reqs', ssl.CERT_NONE)
# For Kubernetes environments, try to use mounted TLS certificates
# These are typically mounted at /tls/redis-cert.pem, /tls/redis-key.pem, /tls/ca-cert.pem
import os
ca_certs_path = os.getenv('REDIS_CA_CERTS_PATH', '/tls/ca-cert.pem')
certfile_path = os.getenv('REDIS_CERTFILE_PATH', '/tls/redis-cert.pem')
keyfile_path = os.getenv('REDIS_KEYFILE_PATH', '/tls/redis-key.pem')
# Use environment variables or mounted files if they exist
if os.path.exists(ca_certs_path):
client_params['ssl_ca_certs'] = ca_certs_path
elif ssl_kwargs.get('ssl_ca_certs'):
client_params['ssl_ca_certs'] = ssl_kwargs.get('ssl_ca_certs')
if os.path.exists(certfile_path):
client_params['ssl_certfile'] = certfile_path
elif ssl_kwargs.get('ssl_certfile'):
client_params['ssl_certfile'] = ssl_kwargs.get('ssl_certfile')
if os.path.exists(keyfile_path):
client_params['ssl_keyfile'] = keyfile_path
elif ssl_kwargs.get('ssl_keyfile'):
client_params['ssl_keyfile'] = ssl_kwargs.get('ssl_keyfile')
# Add additional SSL context parameters for better compatibility
# These help with SSL handshake issues and protocol compatibility
client_params['ssl_check_hostname'] = False # Disable hostname verification for self-signed certs
# Add SSL context with specific protocol versions for better compatibility
# This helps with "wrong version number" and "unexpected eof" SSL errors
import ssl as ssl_module
ssl_context = ssl_module.create_default_context(
purpose=ssl_module.Purpose.SERVER_AUTH,
cafile=client_params.get('ssl_ca_certs')
)
ssl_context.check_hostname = False
ssl_context.verify_mode = client_params.get('ssl_cert_reqs', ssl_module.CERT_NONE)
# Set minimum TLS version for better security and compatibility
# TLS 1.2 is widely supported and secure enough for internal cluster communication
ssl_context.minimum_version = ssl_module.TLSVersion.TLSv1_2
# If client certificates are provided, load them
if client_params.get('ssl_certfile') and client_params.get('ssl_keyfile'):
ssl_context.load_cert_chain(
certfile=client_params.get('ssl_certfile'),
keyfile=client_params.get('ssl_keyfile')
)
client_params['ssl_context'] = ssl_context
# Debug: Log the SSL configuration being used
self.logger.debug(
"redis_ssl_config",
ssl_enabled=True,
ssl_cert_reqs=client_params.get('ssl_cert_reqs'),
ssl_ca_certs=client_params.get('ssl_ca_certs'),
ssl_certfile=client_params.get('ssl_certfile'),
ssl_keyfile=client_params.get('ssl_keyfile'),
ssl_check_hostname=False,
ssl_minimum_version="TLSv1_2"
)
self._client = redis.Redis(**client_params)
else:
# For non-TLS connections, use the original approach
connection_kwargs = {
'db': db,
'max_connections': max_connections,
'decode_responses': decode_responses,
'retry_on_timeout': retry_on_timeout,
'socket_keepalive': socket_keepalive,
'health_check_interval': health_check_interval
}
# Add SSL kwargs for self-signed certificates (using shared helper)
connection_kwargs.update(get_ssl_kwargs_for_url(redis_url))
self._pool = redis.ConnectionPool.from_url(
redis_url,
**connection_kwargs
connection_kwargs.update(ssl_kwargs)
self.logger.debug(
"redis_ssl_config",
ssl_enabled=True,
ssl_cert_reqs=ssl_kwargs.get('ssl_cert_reqs'),
ssl_check_hostname=ssl_kwargs.get('ssl_check_hostname')
)
self._client = redis.Redis(connection_pool=self._pool)
# Use from_url for both TLS and non-TLS - it handles SSL automatically for rediss://
self._pool = redis.ConnectionPool.from_url(
redis_url,
**connection_kwargs
)
self._client = redis.Redis(connection_pool=self._pool)
# Test connection
await self._client.ping()