- TLS Refactoring
This commit is contained in:
19
common/utils/cache/regions.py
vendored
19
common/utils/cache/regions.py
vendored
@@ -42,24 +42,15 @@ def get_redis_config(app):
|
|||||||
'password': redis_uri.password
|
'password': redis_uri.password
|
||||||
})
|
})
|
||||||
|
|
||||||
# SSL support using Dogpile's built-in mechanism
|
# SSL support using centralized config
|
||||||
cert_data = app.config.get('REDIS_CERT_DATA')
|
cert_path = app.config.get('REDIS_CA_CERT_PATH')
|
||||||
if cert_data and redis_uri.scheme == 'rediss':
|
if cert_path and redis_uri.scheme == 'rediss':
|
||||||
import ssl
|
import ssl
|
||||||
import tempfile
|
|
||||||
|
|
||||||
# Create SSL context
|
# Create SSL context
|
||||||
ssl_context = ssl.create_default_context()
|
ssl_context = ssl.create_default_context()
|
||||||
ssl_context.verify_mode = ssl.CERT_REQUIRED
|
ssl_context.verify_mode = ssl.CERT_REQUIRED
|
||||||
ssl_context.check_hostname = True
|
ssl_context.check_hostname = app.config.get('REDIS_SSL_CHECK_HOSTNAME', True)
|
||||||
|
ssl_context.load_verify_locations(cert_path)
|
||||||
# Write cert to temp file
|
|
||||||
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.pem') as f:
|
|
||||||
f.write(cert_data)
|
|
||||||
ssl_cert_path = f.name
|
|
||||||
|
|
||||||
ssl_context.load_verify_locations(ssl_cert_path)
|
|
||||||
|
|
||||||
# Add SSL to connection pool kwargs
|
# Add SSL to connection pool kwargs
|
||||||
config['connection_pool_class_kwargs']['ssl'] = ssl_context
|
config['connection_pool_class_kwargs']['ssl'] = ssl_context
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
import atexit
|
|
||||||
import os
|
|
||||||
import ssl
|
import ssl
|
||||||
import tempfile
|
|
||||||
|
|
||||||
from celery import Celery
|
from celery import Celery
|
||||||
from kombu import Queue
|
from kombu import Queue
|
||||||
@@ -9,26 +6,6 @@ from werkzeug.local import LocalProxy
|
|||||||
from redbeat import RedBeatScheduler
|
from redbeat import RedBeatScheduler
|
||||||
|
|
||||||
celery_app = Celery()
|
celery_app = Celery()
|
||||||
_tmp_paths = []
|
|
||||||
|
|
||||||
def _create_ssl_cert_file(cert_data: str) -> str:
|
|
||||||
"""Create temporary certificate file for Celery SSL"""
|
|
||||||
if not cert_data:
|
|
||||||
return None
|
|
||||||
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.pem') as cert_file:
|
|
||||||
cert_file.write(cert_data)
|
|
||||||
path = cert_file.name
|
|
||||||
_tmp_paths.append(path) # track for cleanup
|
|
||||||
return path
|
|
||||||
|
|
||||||
def _cleanup_tmp():
|
|
||||||
for p in _tmp_paths:
|
|
||||||
try:
|
|
||||||
os.remove(p)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
atexit.register(_cleanup_tmp)
|
|
||||||
|
|
||||||
|
|
||||||
def init_celery(celery, app, is_beat=False):
|
def init_celery(celery, app, is_beat=False):
|
||||||
@@ -67,25 +44,20 @@ def init_celery(celery, app, is_beat=False):
|
|||||||
# celery_config['result_backend_transport_options'] = result_backend_transport_options
|
# celery_config['result_backend_transport_options'] = result_backend_transport_options
|
||||||
|
|
||||||
# TLS (only when cert is provided or your URLs are rediss://)
|
# TLS (only when cert is provided or your URLs are rediss://)
|
||||||
cert_data = app.config.get('REDIS_CERT_DATA')
|
|
||||||
ssl_opts = None
|
ssl_opts = None
|
||||||
if cert_data:
|
cert_path = app.config.get('REDIS_CA_CERT_PATH')
|
||||||
try:
|
if cert_path:
|
||||||
ca_path = _create_ssl_cert_file(cert_data)
|
ssl_opts = {
|
||||||
if ca_path:
|
'ssl_cert_reqs': ssl.CERT_REQUIRED,
|
||||||
ssl_opts = {
|
'ssl_ca_certs': cert_path,
|
||||||
'ssl_cert_reqs': ssl.CERT_REQUIRED, # <— constant, not string
|
'ssl_check_hostname': app.config.get('REDIS_SSL_CHECK_HOSTNAME', True),
|
||||||
'ssl_ca_certs': ca_path,
|
}
|
||||||
# 'ssl_check_hostname': True, # kombu/redis doesn’t consistently honor this; CERT_REQUIRED is the key
|
app.logger.info(
|
||||||
}
|
"SSL configured for Celery Redis connection (CA: %s, hostname-check: %s)",
|
||||||
app.logger.info("SSL configured for Celery Redis connection (CA provided)")
|
cert_path,
|
||||||
except Exception as e:
|
'enabled' if app.config.get('REDIS_SSL_CHECK_HOSTNAME', True) else 'disabled (IP)'
|
||||||
app.logger.error(f"Failed to configure SSL for Celery: {e}")
|
)
|
||||||
|
|
||||||
if ssl_opts is None:
|
|
||||||
ssl_opts = {'ssl_cert_reqs': ssl.CERT_REQUIRED}
|
|
||||||
celery_config['broker_use_ssl'] = ssl_opts
|
celery_config['broker_use_ssl'] = ssl_opts
|
||||||
# Redis result backend needs its own key:
|
|
||||||
celery_config['redis_backend_use_ssl'] = ssl_opts
|
celery_config['redis_backend_use_ssl'] = ssl_opts
|
||||||
|
|
||||||
# Beat/RedBeat
|
# Beat/RedBeat
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ import os
|
|||||||
from os import environ, path
|
from os import environ, path
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import redis
|
import redis
|
||||||
|
import ssl
|
||||||
|
import tempfile
|
||||||
|
from ipaddress import ip_address
|
||||||
|
|
||||||
from common.utils.prompt_loader import load_prompt_templates
|
from common.utils.prompt_loader import load_prompt_templates
|
||||||
|
|
||||||
@@ -30,11 +33,38 @@ class Config(object):
|
|||||||
REDIS_PASS = environ.get('REDIS_PASS')
|
REDIS_PASS = environ.get('REDIS_PASS')
|
||||||
REDIS_CERT_DATA = environ.get('REDIS_CERT')
|
REDIS_CERT_DATA = environ.get('REDIS_CERT')
|
||||||
|
|
||||||
|
# Determine if REDIS_URL is an IP; use it to control hostname checking
|
||||||
|
REDIS_IS_IP = False
|
||||||
|
try:
|
||||||
|
ip_address(REDIS_URL)
|
||||||
|
REDIS_IS_IP = True
|
||||||
|
except Exception:
|
||||||
|
REDIS_IS_IP = False
|
||||||
|
REDIS_SSL_CHECK_HOSTNAME = not REDIS_IS_IP
|
||||||
|
|
||||||
|
# Write CA once to a file, expose path
|
||||||
|
REDIS_CA_CERT_PATH = None
|
||||||
|
if REDIS_CERT_DATA:
|
||||||
|
_tmp = tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.pem')
|
||||||
|
_tmp.write(REDIS_CERT_DATA)
|
||||||
|
_tmp.flush()
|
||||||
|
_tmp.close()
|
||||||
|
REDIS_CA_CERT_PATH = _tmp.name
|
||||||
|
|
||||||
if not REDIS_CERT_DATA: # We are in a simple dev/test environment
|
if not REDIS_CERT_DATA: # We are in a simple dev/test environment
|
||||||
REDIS_BASE_URI = f'redis://{REDIS_URL}:{REDIS_PORT}'
|
REDIS_BASE_URI = f'redis://{REDIS_URL}:{REDIS_PORT}'
|
||||||
else: # We are in a scaleway environment, providing name, user and certificate
|
else: # We are in a scaleway environment, providing name, user and certificate
|
||||||
REDIS_BASE_URI = f'rediss://{REDIS_USER}:{REDIS_PASS}@{REDIS_URL}:{REDIS_PORT}'
|
REDIS_BASE_URI = f'rediss://{REDIS_USER}:{REDIS_PASS}@{REDIS_URL}:{REDIS_PORT}'
|
||||||
|
|
||||||
|
# Central SSL options dict for reuse (Celery/Dogpile/etc.)
|
||||||
|
REDIS_SSL_OPTIONS = None
|
||||||
|
if REDIS_CERT_DATA and REDIS_CA_CERT_PATH:
|
||||||
|
REDIS_SSL_OPTIONS = {
|
||||||
|
'ssl_cert_reqs': ssl.CERT_REQUIRED,
|
||||||
|
'ssl_ca_certs': REDIS_CA_CERT_PATH,
|
||||||
|
'ssl_check_hostname': REDIS_SSL_CHECK_HOSTNAME,
|
||||||
|
}
|
||||||
|
|
||||||
REDIS_PREFIXES = {
|
REDIS_PREFIXES = {
|
||||||
'celery_app': 'celery:app:',
|
'celery_app': 'celery:app:',
|
||||||
'celery_chat': 'celery:chat:',
|
'celery_chat': 'celery:chat:',
|
||||||
@@ -62,7 +92,20 @@ class Config(object):
|
|||||||
SESSION_USE_SIGNER = True
|
SESSION_USE_SIGNER = True
|
||||||
PERMANENT_SESSION_LIFETIME = timedelta(minutes=60)
|
PERMANENT_SESSION_LIFETIME = timedelta(minutes=60)
|
||||||
SESSION_REFRESH_EACH_REQUEST = True
|
SESSION_REFRESH_EACH_REQUEST = True
|
||||||
SESSION_REDIS = redis.from_url(f'{REDIS_BASE_URI}/0')
|
# Configure SESSION_REDIS with SSL when cert is provided
|
||||||
|
if REDIS_CERT_DATA and REDIS_CA_CERT_PATH:
|
||||||
|
SESSION_REDIS = redis.Redis(
|
||||||
|
host=REDIS_URL,
|
||||||
|
port=int(REDIS_PORT or 6379),
|
||||||
|
username=REDIS_USER,
|
||||||
|
password=REDIS_PASS,
|
||||||
|
ssl=True,
|
||||||
|
ssl_cert_reqs=ssl.CERT_REQUIRED,
|
||||||
|
ssl_ca_certs=REDIS_CA_CERT_PATH,
|
||||||
|
ssl_check_hostname=REDIS_SSL_CHECK_HOSTNAME,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
SESSION_REDIS = redis.from_url(f'{REDIS_BASE_URI}/0')
|
||||||
SESSION_KEY_PREFIX = f'session_{COMPONENT_NAME}:'
|
SESSION_KEY_PREFIX = f'session_{COMPONENT_NAME}:'
|
||||||
SESSION_COOKIE_NAME = f'{COMPONENT_NAME}_session'
|
SESSION_COOKIE_NAME = f'{COMPONENT_NAME}_session'
|
||||||
SESSION_COOKIE_DOMAIN = None # Laat Flask dit automatisch bepalen
|
SESSION_COOKIE_DOMAIN = None # Laat Flask dit automatisch bepalen
|
||||||
|
|||||||
@@ -467,20 +467,14 @@ kubectl -n tools port-forward svc/pgadmin-pgadmin4 8080:80
|
|||||||
|
|
||||||
### Phase 9: Enable Scaleway Registry
|
### Phase 9: Enable Scaleway Registry
|
||||||
|
|
||||||
1) Create docker pull secret via External Secrets (once):
|
#### Create Scaleway Registry Secret
|
||||||
|
Create docker pull secret via External Secrets (once):
|
||||||
```bash
|
```bash
|
||||||
kubectl apply -f scaleway/manifests/base/secrets/scaleway-registry-secret.yaml
|
kubectl apply -f scaleway/manifests/base/secrets/scaleway-registry-secret.yaml
|
||||||
kubectl -n eveai-staging get secret scaleway-registry-cred -o yaml | grep "type: kubernetes.io/dockerconfigjson"
|
kubectl -n eveai-staging get secret scaleway-registry-cred -o yaml | grep "type: kubernetes.io/dockerconfigjson"
|
||||||
```
|
```
|
||||||
2) Use the staging overlay to deploy apps with registry rewrite and imagePullSecrets:
|
|
||||||
```bash
|
|
||||||
kubectl apply -k scaleway/manifests/overlays/staging/
|
|
||||||
```
|
|
||||||
Notes:
|
|
||||||
- Base manifests keep generic images (josakola/...). The overlay rewrites them to rg.fr-par.scw.cloud/eveai-staging/josakola/...:staging and adds imagePullSecrets to all Pods.
|
|
||||||
- Staging uses imagePullPolicy: Always, so new pushes to :staging are pulled automatically.
|
|
||||||
|
|
||||||
### Phase 10: Ops Jobs Invocation (if required)
|
#### Ops Jobs Invocation (if required)
|
||||||
|
|
||||||
Run the DB ops scripts manually in order. Each manifest uses generateName; use kubectl create.
|
Run the DB ops scripts manually in order. Each manifest uses generateName; use kubectl create.
|
||||||
|
|
||||||
@@ -510,9 +504,14 @@ kubectl -n eveai-staging get jobs
|
|||||||
kubectl -n eveai-staging logs job/<created-job-name>
|
kubectl -n eveai-staging logs job/<created-job-name>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Phase 11: Application Services Deployment
|
#### Application Services Deployment
|
||||||
|
Use the staging overlay to deploy apps with registry rewrite and imagePullSecrets:
|
||||||
|
```bash
|
||||||
|
kubectl apply -k scaleway/manifests/overlays/staging/
|
||||||
|
```
|
||||||
|
Notes:
|
||||||
|
- Base manifests keep generic images (josakola/...). The overlay rewrites them to rg.fr-par.scw.cloud/eveai-staging/josakola/...:staging and adds imagePullSecrets to all Pods.
|
||||||
|
- Staging uses imagePullPolicy: Always, so new pushes to :staging are pulled automatically.
|
||||||
|
|
||||||
## Verification and Testing
|
## Verification and Testing
|
||||||
|
|
||||||
|
|||||||
8
documentation/generating_codes.md
Normal file
8
documentation/generating_codes.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Generating codes
|
||||||
|
|
||||||
|
## API_ENCRYPTION_KEY
|
||||||
|
|
||||||
|
```python
|
||||||
|
from cryptography.fernet import Fernet;
|
||||||
|
print(Fernet.generate_key().decode())
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user