feat: wire rfiles to pull secrets from Infisical at startup

Add entrypoint.sh that authenticates with Infisical via universal auth
and injects secrets as environment variables before the main process
starts. Update Dockerfile with ENTRYPOINT directive and add Infisical
connection vars to all services in both compose files (dev and prod).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-02-23 19:36:55 -08:00
parent 88674debd4
commit 28aafb73fe
3 changed files with 82 additions and 6 deletions

View File

@ -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"]

View File

@ -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:

63
entrypoint.sh Executable file
View File

@ -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 "$@"