-
Notifications
You must be signed in to change notification settings - Fork 29
added defaultazurecredential for azure redis #272
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2,19 +2,45 @@ | |||||||||||||||||||||||||||||||||||||||
| import time | ||||||||||||||||||||||||||||||||||||||||
| from typing import Any, List, Optional, Union | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| from azure.core.exceptions import ClientAuthenticationError | ||||||||||||||||||||||||||||||||||||||||
| from azure.identity import DefaultAzureCredential | ||||||||||||||||||||||||||||||||||||||||
| from common_module.common_cache import CommonCache | ||||||||||||||||||||||||||||||||||||||||
| from common_module.log.logger import logger | ||||||||||||||||||||||||||||||||||||||||
| from redis import Connection | ||||||||||||||||||||||||||||||||||||||||
| from redis import ConnectionError | ||||||||||||||||||||||||||||||||||||||||
| from redis import ConnectionPool | ||||||||||||||||||||||||||||||||||||||||
| from redis import Redis | ||||||||||||||||||||||||||||||||||||||||
| from redis import RedisError | ||||||||||||||||||||||||||||||||||||||||
| from redis import SSLConnection | ||||||||||||||||||||||||||||||||||||||||
| from redis import TimeoutError | ||||||||||||||||||||||||||||||||||||||||
| from redis.credentials import CredentialProvider | ||||||||||||||||||||||||||||||||||||||||
| from tenacity import retry | ||||||||||||||||||||||||||||||||||||||||
| from tenacity import retry_if_exception_type | ||||||||||||||||||||||||||||||||||||||||
| from tenacity import stop_after_attempt | ||||||||||||||||||||||||||||||||||||||||
| from tenacity import wait_exponential | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| class AzureManagedRedisProvider(CredentialProvider): | ||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||
| Adapter to bridge Azure Identity with Redis CredentialProvider. | ||||||||||||||||||||||||||||||||||||||||
| Azure Managed Redis requires 'default' as the username and the | ||||||||||||||||||||||||||||||||||||||||
| Entra ID access token as the password. | ||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| def __init__(self): | ||||||||||||||||||||||||||||||||||||||||
| self.credential = DefaultAzureCredential() | ||||||||||||||||||||||||||||||||||||||||
| self.scope = 'https://redis.azure.com/.default' | ||||||||||||||||||||||||||||||||||||||||
| self.username = os.getenv('REDIS_USERNAME', 'default') | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| def get_credentials(self): | ||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||
| token = self.credential.get_token(self.scope) | ||||||||||||||||||||||||||||||||||||||||
| return (self.username, token.token) | ||||||||||||||||||||||||||||||||||||||||
| except ClientAuthenticationError as e: | ||||||||||||||||||||||||||||||||||||||||
| logger.error(f'Azure authentication failed: {e}') | ||||||||||||||||||||||||||||||||||||||||
| raise | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| class CacheManager(CommonCache): | ||||||||||||||||||||||||||||||||||||||||
| def __init__( | ||||||||||||||||||||||||||||||||||||||||
| self, | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -58,19 +84,41 @@ def _create_connection_pool( | |||||||||||||||||||||||||||||||||||||||
| pool_size: int, | ||||||||||||||||||||||||||||||||||||||||
| ) -> ConnectionPool: | ||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||
| return ConnectionPool( | ||||||||||||||||||||||||||||||||||||||||
| host=str(os.getenv('REDIS_HOST', 'localhost')), | ||||||||||||||||||||||||||||||||||||||||
| port=int(os.getenv('REDIS_PORT', 6379)), | ||||||||||||||||||||||||||||||||||||||||
| db=int(os.getenv('REDIS_DB', 0)), | ||||||||||||||||||||||||||||||||||||||||
| max_connections=pool_size, | ||||||||||||||||||||||||||||||||||||||||
| socket_timeout=socket_timeout, | ||||||||||||||||||||||||||||||||||||||||
| socket_keepalive=socket_keepalive, | ||||||||||||||||||||||||||||||||||||||||
| socket_connect_timeout=connection_timeout, | ||||||||||||||||||||||||||||||||||||||||
| retry_on_timeout=True, | ||||||||||||||||||||||||||||||||||||||||
| health_check_interval=30, | ||||||||||||||||||||||||||||||||||||||||
| encoding='utf-8', | ||||||||||||||||||||||||||||||||||||||||
| decode_responses=True, | ||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||
| host = os.getenv('REDIS_HOST', 'localhost') | ||||||||||||||||||||||||||||||||||||||||
| port = int(os.getenv('REDIS_PORT', 6379)) | ||||||||||||||||||||||||||||||||||||||||
| protocol = os.getenv('REDIS_PROTOCOL', 'redis') | ||||||||||||||||||||||||||||||||||||||||
| password = os.getenv('REDIS_PASSWORD') | ||||||||||||||||||||||||||||||||||||||||
| cloud_provider = os.getenv('CLOUD_PROVIDER', '').lower() | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| connection_class = Connection | ||||||||||||||||||||||||||||||||||||||||
| if protocol == 'rediss' or port == 10000: | ||||||||||||||||||||||||||||||||||||||||
| logger.info(f'Using SSLConnection for Redis (Port: {port})') | ||||||||||||||||||||||||||||||||||||||||
| connection_class = SSLConnection | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| pool_kwargs = { | ||||||||||||||||||||||||||||||||||||||||
| 'connection_class': connection_class, | ||||||||||||||||||||||||||||||||||||||||
| 'host': host, | ||||||||||||||||||||||||||||||||||||||||
| 'port': port, | ||||||||||||||||||||||||||||||||||||||||
| 'db': int(os.getenv('REDIS_DB', 0)), | ||||||||||||||||||||||||||||||||||||||||
| 'max_connections': pool_size, | ||||||||||||||||||||||||||||||||||||||||
| 'socket_timeout': socket_timeout, | ||||||||||||||||||||||||||||||||||||||||
| 'socket_keepalive': socket_keepalive, | ||||||||||||||||||||||||||||||||||||||||
| 'socket_connect_timeout': connection_timeout, | ||||||||||||||||||||||||||||||||||||||||
| 'retry_on_timeout': True, | ||||||||||||||||||||||||||||||||||||||||
| 'health_check_interval': 30, | ||||||||||||||||||||||||||||||||||||||||
| 'encoding': 'utf-8', | ||||||||||||||||||||||||||||||||||||||||
| 'decode_responses': True, | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| if cloud_provider == 'azure' and not password: | ||||||||||||||||||||||||||||||||||||||||
| logger.info( | ||||||||||||||||||||||||||||||||||||||||
| 'Configuring Azure Entra ID (Workload Identity) authentication' | ||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||
| pool_kwargs['credential_provider'] = AzureManagedRedisProvider() | ||||||||||||||||||||||||||||||||||||||||
| elif password: | ||||||||||||||||||||||||||||||||||||||||
| pool_kwargs['password'] = password | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+113
to
+119
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Silent fallback to unauthenticated Redis when credentials are misconfigured. Same concern as the 🛡️ Suggested improvement if cloud_provider == 'azure' and not password:
logger.info(
'Configuring Azure Entra ID (Workload Identity) authentication'
)
pool_kwargs['credential_provider'] = AzureManagedRedisProvider()
elif password:
pool_kwargs['password'] = password
+ else:
+ logger.warning(
+ 'No Redis authentication configured. '
+ 'Set REDIS_PASSWORD or use CLOUD_PROVIDER=azure for managed identity.'
+ )📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| return ConnectionPool(**pool_kwargs) | ||||||||||||||||||||||||||||||||||||||||
| except Exception as e: | ||||||||||||||||||||||||||||||||||||||||
| logger.error(f'Failed to create connection pool: {e}s') | ||||||||||||||||||||||||||||||||||||||||
| raise | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Silent fallback to unauthenticated Redis when credentials are misconfigured.
When
CLOUD_PROVIDERis notazureandREDIS_PASSWORDis unset, the pool is created without any authentication. This may be intentional for local development, but in production it risks connecting to Redis without auth if environment variables are misconfigured.Consider adding a warning log or, for non-local environments, requiring explicit opt-in for unauthenticated connections:
🛡️ Suggested improvement
if cloud_provider == 'azure' and not password: logger.info( 'Configuring Azure Entra ID (Workload Identity) authentication' ) pool_kwargs['credential_provider'] = AzureManagedRedisProvider() elif password: pool_kwargs['password'] = password + else: + logger.warning( + 'No Redis authentication configured. ' + 'Set REDIS_PASSWORD or use CLOUD_PROVIDER=azure for managed identity.' + )🤖 Prompt for AI Agents