diff --git a/Dockerfile b/Dockerfile index f1a59b7..300e5de 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,9 @@ RUN pip install --no-cache-dir -r requirements.txt COPY . . +COPY entrypoint.sh /app/entrypoint.sh +RUN chmod +x /app/entrypoint.sh + RUN mkdir -p /app/staticfiles /app/media EXPOSE 8000 @@ -25,4 +28,5 @@ EXPOSE 8000 HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ CMD curl -f http://localhost:8000/api/health/ || exit 1 +ENTRYPOINT ["/app/entrypoint.sh"] CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"] diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 4cd6ee1..fac009c 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -58,14 +58,15 @@ services: - rfiles_media:/app/media - rfiles_static:/app/staticfiles environment: + - INFISICAL_CLIENT_ID=${INFISICAL_CLIENT_ID} + - INFISICAL_CLIENT_SECRET=${INFISICAL_CLIENT_SECRET} + - INFISICAL_PROJECT_SLUG=rfiles + - INFISICAL_ENV=prod + - INFISICAL_URL=http://infisical:8080 - DATABASE_URL=postgresql://rfiles:${DB_PASSWORD}@postgres:5432/rfiles - CELERY_BROKER_URL=redis://:${REDIS_PASSWORD}@redis:6379/0 - CELERY_RESULT_BACKEND=redis://:${REDIS_PASSWORD}@redis:6379/0 - DJANGO_SETTINGS_MODULE=config.settings - - DEBUG=False - - ALLOWED_HOSTS=rfiles.online,www.rfiles.online,.rfiles.online,direct.rfiles.online,localhost - - SHARE_BASE_URL=https://rfiles.online - - SECRET_KEY=${SECRET_KEY} depends_on: postgres: condition: service_healthy @@ -114,11 +115,15 @@ services: volumes: - rfiles_media:/app/media environment: + - INFISICAL_CLIENT_ID=${INFISICAL_CLIENT_ID} + - INFISICAL_CLIENT_SECRET=${INFISICAL_CLIENT_SECRET} + - INFISICAL_PROJECT_SLUG=rfiles + - INFISICAL_ENV=prod + - INFISICAL_URL=http://infisical:8080 - DATABASE_URL=postgresql://rfiles:${DB_PASSWORD}@postgres:5432/rfiles - CELERY_BROKER_URL=redis://:${REDIS_PASSWORD}@redis:6379/0 - CELERY_RESULT_BACKEND=redis://:${REDIS_PASSWORD}@redis:6379/0 - DJANGO_SETTINGS_MODULE=config.settings - - SECRET_KEY=${SECRET_KEY} depends_on: postgres: condition: service_healthy @@ -148,11 +153,15 @@ services: container_name: rfiles-celery-beat restart: unless-stopped environment: + - INFISICAL_CLIENT_ID=${INFISICAL_CLIENT_ID} + - INFISICAL_CLIENT_SECRET=${INFISICAL_CLIENT_SECRET} + - INFISICAL_PROJECT_SLUG=rfiles + - INFISICAL_ENV=prod + - INFISICAL_URL=http://infisical:8080 - DATABASE_URL=postgresql://rfiles:${DB_PASSWORD}@postgres:5432/rfiles - CELERY_BROKER_URL=redis://:${REDIS_PASSWORD}@redis:6379/0 - CELERY_RESULT_BACKEND=redis://:${REDIS_PASSWORD}@redis:6379/0 - DJANGO_SETTINGS_MODULE=config.settings - - SECRET_KEY=${SECRET_KEY} depends_on: - celery-worker healthcheck: diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100755 index 0000000..dfe9204 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,63 @@ +#!/bin/sh +# Infisical secret injection entrypoint (Python version) +set -e + +INFISICAL_URL="${INFISICAL_URL:-http://infisical:8080}" +INFISICAL_ENV="${INFISICAL_ENV:-prod}" +INFISICAL_PROJECT_SLUG="${INFISICAL_PROJECT_SLUG:-rfiles}" + +if [ -z "$INFISICAL_CLIENT_ID" ] || [ -z "$INFISICAL_CLIENT_SECRET" ]; then + echo "[infisical] No credentials set, starting without secret injection" + exec "$@" +fi + +echo "[infisical] Fetching secrets from ${INFISICAL_PROJECT_SLUG}/${INFISICAL_ENV}..." + +EXPORTS=$(python3 -c " +import urllib.request, json, os, sys + +base = os.environ['INFISICAL_URL'] +slug = os.environ['INFISICAL_PROJECT_SLUG'] +env = os.environ['INFISICAL_ENV'] + +try: + # Auth + data = json.dumps({'clientId': os.environ['INFISICAL_CLIENT_ID'], 'clientSecret': os.environ['INFISICAL_CLIENT_SECRET']}).encode() + req = urllib.request.Request(f'{base}/api/v1/auth/universal-auth/login', data=data, headers={'Content-Type': 'application/json'}) + auth = json.loads(urllib.request.urlopen(req).read()) + token = auth.get('accessToken') + if not token: + print('[infisical] Auth failed', file=sys.stderr) + sys.exit(1) + + # Fetch secrets + req = urllib.request.Request(f'{base}/api/v3/secrets/raw?workspaceSlug={slug}&environment={env}&secretPath=/&recursive=true') + req.add_header('Authorization', f'Bearer {token}') + secrets = json.loads(urllib.request.urlopen(req).read()) + + if 'secrets' not in secrets: + print('[infisical] No secrets returned', file=sys.stderr) + sys.exit(1) + + for s in secrets['secrets']: + key = s['secretKey'] + val = s['secretValue'].replace(\"'\", \"'\\\\\\\\''\") + print(f\"export {key}='{val}'\") +except Exception as e: + print(f'[infisical] Error: {e}', file=sys.stderr) + sys.exit(1) +" 2>&1) || { + echo "[infisical] WARNING: Failed to fetch secrets, starting with existing env vars" + exec "$@" +} + +if echo "$EXPORTS" | grep -q "^export "; then + COUNT=$(echo "$EXPORTS" | grep -c "^export ") + eval "$EXPORTS" + echo "[infisical] Injected ${COUNT} secrets" +else + echo "[infisical] WARNING: $EXPORTS" + echo "[infisical] Starting with existing env vars" +fi + +exec "$@"