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 aiohttp==3.9.1
# Redis # Redis
redis==5.0.1 redis==6.4.0
hiredis==2.2.3 hiredis==2.2.3
# Utilities # Utilities

View File

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

View File

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

View File

@@ -1,9 +1,11 @@
""" """
Redis client initialization and connection management Redis client initialization and connection management
Provides standardized Redis connection for all services 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 import redis.asyncio as redis
from typing import Optional, Dict, Any from typing import Optional, Dict, Any
import structlog 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 Handles self-signed certificates by disabling certificate verification
when using rediss:// (TLS-enabled) URLs. when using rediss:// (TLS-enabled) URLs.
For redis-py 6.4.0+, ssl_cert_reqs accepts string values: "none", "optional", "required"
Args: Args:
redis_url: Redis connection URL (redis:// or rediss://) 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://"): if redis_url and redis_url.startswith("rediss://"):
return { return {
"ssl_cert_reqs": ssl.CERT_NONE, # Disable certificate verification "ssl_cert_reqs": "none", # Don't verify server certificate (self-signed)
"ssl_ca_certs": None, # Don't require CA certificates "ssl_check_hostname": False, # Don't verify hostname for internal services
"ssl_certfile": None, # Don't require client cert
"ssl_keyfile": None, # Don't require client key
} }
return {} return {}
@@ -133,112 +135,7 @@ class RedisConnectionManager:
try: try:
self._redis_url = redis_url self._redis_url = redis_url
# Create connection pool with SSL handling for self-signed certificates # Common connection kwargs for both TLS and non-TLS
# For Redis 6.4.0+, we need to handle SSL parameters correctly
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
}
# 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 = { connection_kwargs = {
'db': db, 'db': db,
'max_connections': max_connections, 'max_connections': max_connections,
@@ -248,9 +145,27 @@ class RedisConnectionManager:
'health_check_interval': health_check_interval 'health_check_interval': health_check_interval
} }
# Add SSL kwargs for self-signed certificates (using shared helper) # For redis-py 6.4.0+, SSL parameters must be passed to ConnectionPool.from_url()
connection_kwargs.update(get_ssl_kwargs_for_url(redis_url)) # The library handles SSL internally when using rediss:// URLs
if redis_url.startswith("rediss://"):
# 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)
}
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')
)
# Use from_url for both TLS and non-TLS - it handles SSL automatically for rediss://
self._pool = redis.ConnectionPool.from_url( self._pool = redis.ConnectionPool.from_url(
redis_url, redis_url,
**connection_kwargs **connection_kwargs