Claude commited on
Commit
54fdaaa
·
1 Parent(s): 34a60dc

Fix CAA production deployment with SSL/TLS configuration

Browse files

- Add nginx reverse proxy with SSL termination (nginx/nginx.conf)
- Create production docker-compose with certbot for auto-renewal
- Fix insecure CORS policy (wildcards -> configurable origins)
- Add security headers middleware (HSTS, CSP, X-Frame-Options, etc.)
- Add trusted host middleware to prevent host header attacks
- Create comprehensive SSL/CAA setup documentation
- Update .env.example with production security variables
- Add .gitignore entries to protect SSL certificates

.env.example CHANGED
@@ -41,6 +41,19 @@ VECTOR_DB_TYPE=pinecone
41
  API_HOST=0.0.0.0
42
  API_PORT=8000
43
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  # Disable telemetry and warnings
45
  TOKENIZERS_PARALLELISM=false
46
  ANONYMIZED_TELEMETRY=false
 
41
  API_HOST=0.0.0.0
42
  API_PORT=8000
43
 
44
+ # Production SSL/Security Configuration
45
+ # Set these for production deployment (see docs/markdowns/SSL_CAA_SETUP.md)
46
+ PRODUCTION=false
47
+ HTTPS_ONLY=false
48
+
49
+ # Domain configuration - comma-separated list of allowed hosts
50
+ # Example: TRUSTED_HOSTS=yourdomain.com,www.yourdomain.com
51
+ TRUSTED_HOSTS=*
52
+
53
+ # CORS Origins - comma-separated list of allowed origins
54
+ # Example: ALLOWED_ORIGINS=https://yourdomain.com,https://www.yourdomain.com
55
+ ALLOWED_ORIGINS=*
56
+
57
  # Disable telemetry and warnings
58
  TOKENIZERS_PARALLELISM=false
59
  ANONYMIZED_TELEMETRY=false
.gitignore CHANGED
@@ -33,4 +33,16 @@ venv/
33
  env/
34
  ENV/
35
  data/hackathon_data
36
- docs/beatbyteai_id_rsa
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  env/
34
  ENV/
35
  data/hackathon_data
36
+ docs/beatbyteai_id_rsa
37
+
38
+ # SSL Certificates (never commit actual certs)
39
+ nginx/ssl/*.pem
40
+ nginx/ssl/*.crt
41
+ nginx/ssl/*.key
42
+ nginx/ssl/live/
43
+ nginx/ssl/archive/
44
+ !nginx/ssl/.gitkeep
45
+
46
+ # Certbot data
47
+ certbot/conf/
48
+ !certbot/www/.gitkeep
app/main.py CHANGED
@@ -17,8 +17,11 @@ import fitz # PyMuPDF
17
  from PIL import Image
18
  from fastapi import FastAPI, HTTPException, File, UploadFile, Request
19
  from fastapi.middleware.cors import CORSMiddleware
 
20
  from fastapi.staticfiles import StaticFiles
21
  from fastapi.templating import Jinja2Templates
 
 
22
  from pydantic import BaseModel
23
  from dotenv import load_dotenv
24
  from openai import AzureOpenAI
@@ -35,13 +38,42 @@ app = FastAPI(
35
  version="1.0.0"
36
  )
37
 
38
- # CORS middleware
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  app.add_middleware(
40
  CORSMiddleware,
41
- allow_origins=["*"],
42
  allow_credentials=True,
43
- allow_methods=["*"],
44
- allow_headers=["*"],
45
  )
46
 
47
  # Mount static files and templates
 
17
  from PIL import Image
18
  from fastapi import FastAPI, HTTPException, File, UploadFile, Request
19
  from fastapi.middleware.cors import CORSMiddleware
20
+ from fastapi.middleware.trustedhost import TrustedHostMiddleware
21
  from fastapi.staticfiles import StaticFiles
22
  from fastapi.templating import Jinja2Templates
23
+ from fastapi.responses import Response
24
+ from starlette.middleware.base import BaseHTTPMiddleware
25
  from pydantic import BaseModel
26
  from dotenv import load_dotenv
27
  from openai import AzureOpenAI
 
38
  version="1.0.0"
39
  )
40
 
41
+ # Security Headers Middleware for production
42
+ class SecurityHeadersMiddleware(BaseHTTPMiddleware):
43
+ """Add security headers to all responses for production deployment."""
44
+
45
+ async def dispatch(self, request: Request, call_next):
46
+ response = await call_next(request)
47
+
48
+ # Only add security headers in production (when HTTPS is enabled)
49
+ if os.getenv("PRODUCTION", "false").lower() == "true":
50
+ response.headers["Strict-Transport-Security"] = "max-age=63072000; includeSubDomains; preload"
51
+ response.headers["X-Content-Type-Options"] = "nosniff"
52
+ response.headers["X-Frame-Options"] = "SAMEORIGIN"
53
+ response.headers["X-XSS-Protection"] = "1; mode=block"
54
+ response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
55
+ response.headers["Permissions-Policy"] = "geolocation=(), microphone=(), camera=()"
56
+
57
+ return response
58
+
59
+ # Add security headers middleware
60
+ app.add_middleware(SecurityHeadersMiddleware)
61
+
62
+ # Trusted Host Middleware for production (prevents host header attacks)
63
+ trusted_hosts = os.getenv("TRUSTED_HOSTS", "*").split(",")
64
+ if trusted_hosts != ["*"]:
65
+ app.add_middleware(TrustedHostMiddleware, allowed_hosts=trusted_hosts)
66
+
67
+ # CORS middleware - configurable for production
68
+ # In production, set ALLOWED_ORIGINS environment variable to your domain(s)
69
+ # Example: ALLOWED_ORIGINS=https://yourdomain.com,https://www.yourdomain.com
70
+ allowed_origins = os.getenv("ALLOWED_ORIGINS", "*").split(",")
71
  app.add_middleware(
72
  CORSMiddleware,
73
+ allow_origins=allowed_origins if allowed_origins != ["*"] else ["*"],
74
  allow_credentials=True,
75
+ allow_methods=["GET", "POST", "OPTIONS"],
76
+ allow_headers=["Content-Type", "Authorization", "X-Requested-With"],
77
  )
78
 
79
  # Mount static files and templates
certbot/www/.gitkeep ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ # Certbot webroot directory for ACME challenges
2
+ # This directory is used by Let's Encrypt for domain validation
docker-compose.prod.yml ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SOCAR AI System - Production Docker Compose with SSL/TLS Support
2
+ # This configuration includes nginx reverse proxy with SSL termination
3
+ #
4
+ # Prerequisites:
5
+ # 1. Set up CAA DNS records for your domain (see docs/markdowns/SSL_CAA_SETUP.md)
6
+ # 2. Generate SSL certificates using certbot or your CA
7
+ # 3. Copy certificates to ./nginx/ssl/
8
+ #
9
+ # Usage:
10
+ # docker-compose -f docker-compose.prod.yml up -d
11
+
12
+ version: '3.8'
13
+
14
+ services:
15
+ # Nginx reverse proxy with SSL termination
16
+ nginx:
17
+ image: nginx:alpine
18
+ container_name: socar-nginx
19
+ ports:
20
+ - "80:80"
21
+ - "443:443"
22
+ volumes:
23
+ - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
24
+ - ./nginx/ssl:/etc/nginx/ssl:ro
25
+ - ./certbot/www:/var/www/certbot:ro
26
+ depends_on:
27
+ socar-ai-system:
28
+ condition: service_healthy
29
+ restart: unless-stopped
30
+ healthcheck:
31
+ test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:80/"]
32
+ interval: 30s
33
+ timeout: 10s
34
+ retries: 3
35
+ networks:
36
+ - socar-network
37
+ labels:
38
+ - "com.socar.service=nginx-proxy"
39
+ - "com.socar.ssl=enabled"
40
+
41
+ # Certbot for automatic SSL certificate renewal (Let's Encrypt)
42
+ certbot:
43
+ image: certbot/certbot:latest
44
+ container_name: socar-certbot
45
+ volumes:
46
+ - ./nginx/ssl:/etc/letsencrypt:rw
47
+ - ./certbot/www:/var/www/certbot:rw
48
+ entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
49
+ networks:
50
+ - socar-network
51
+ labels:
52
+ - "com.socar.service=certbot"
53
+ - "com.socar.purpose=ssl-renewal"
54
+
55
+ # Main SOCAR AI System
56
+ socar-ai-system:
57
+ build:
58
+ context: .
59
+ dockerfile: Dockerfile
60
+ container_name: socar-ai-system
61
+ # Only expose internally to nginx (not to host)
62
+ expose:
63
+ - "8000"
64
+ env_file:
65
+ - .env
66
+ environment:
67
+ - PYTHONUNBUFFERED=1
68
+ - PRODUCTION=true
69
+ - HTTPS_ONLY=true
70
+ - TRUSTED_HOSTS=${TRUSTED_HOSTS:-localhost}
71
+ restart: unless-stopped
72
+ healthcheck:
73
+ test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
74
+ interval: 30s
75
+ timeout: 10s
76
+ retries: 3
77
+ start_period: 40s
78
+ networks:
79
+ - socar-network
80
+ labels:
81
+ - "com.socar.description=SOCAR Historical Documents AI System"
82
+ - "com.socar.features=OCR,LLM,Frontend"
83
+ - "com.socar.version=1.0.0"
84
+ - "com.socar.environment=production"
85
+
86
+ networks:
87
+ socar-network:
88
+ driver: bridge
89
+ ipam:
90
+ config:
91
+ - subnet: 172.28.0.0/16
92
+
93
+ # Volume for persistent SSL certificates
94
+ volumes:
95
+ ssl-certs:
96
+ driver: local
nginx/nginx.conf ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SOCAR AI System - Production Nginx Configuration with SSL/TLS
2
+ # This configuration provides SSL termination and reverse proxy for production
3
+
4
+ upstream socar_backend {
5
+ server socar-ai-system:8000;
6
+ keepalive 32;
7
+ }
8
+
9
+ # Redirect HTTP to HTTPS
10
+ server {
11
+ listen 80;
12
+ listen [::]:80;
13
+ server_name _;
14
+
15
+ # ACME challenge location for Let's Encrypt/certbot
16
+ location /.well-known/acme-challenge/ {
17
+ root /var/www/certbot;
18
+ }
19
+
20
+ # Redirect all other traffic to HTTPS
21
+ location / {
22
+ return 301 https://$host$request_uri;
23
+ }
24
+ }
25
+
26
+ # HTTPS server block
27
+ server {
28
+ listen 443 ssl http2;
29
+ listen [::]:443 ssl http2;
30
+ server_name _;
31
+
32
+ # SSL Certificate Configuration
33
+ # For production: Use Let's Encrypt or your CA certificates
34
+ ssl_certificate /etc/nginx/ssl/fullchain.pem;
35
+ ssl_certificate_key /etc/nginx/ssl/privkey.pem;
36
+
37
+ # SSL Configuration - Modern settings for security
38
+ ssl_protocols TLSv1.2 TLSv1.3;
39
+ ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
40
+ ssl_prefer_server_ciphers off;
41
+
42
+ # SSL Session settings
43
+ ssl_session_timeout 1d;
44
+ ssl_session_cache shared:SSL:50m;
45
+ ssl_session_tickets off;
46
+
47
+ # OCSP Stapling
48
+ ssl_stapling on;
49
+ ssl_stapling_verify on;
50
+ resolver 8.8.8.8 8.8.4.4 valid=300s;
51
+ resolver_timeout 5s;
52
+
53
+ # Security Headers
54
+ add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
55
+ add_header X-Frame-Options "SAMEORIGIN" always;
56
+ add_header X-Content-Type-Options "nosniff" always;
57
+ add_header X-XSS-Protection "1; mode=block" always;
58
+ add_header Referrer-Policy "strict-origin-when-cross-origin" always;
59
+ add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https:;" always;
60
+
61
+ # Logging
62
+ access_log /var/log/nginx/socar_access.log;
63
+ error_log /var/log/nginx/socar_error.log;
64
+
65
+ # Client settings
66
+ client_max_body_size 100M;
67
+ client_body_buffer_size 10M;
68
+
69
+ # Proxy settings for API
70
+ location / {
71
+ proxy_pass http://socar_backend;
72
+ proxy_http_version 1.1;
73
+ proxy_set_header Upgrade $http_upgrade;
74
+ proxy_set_header Connection 'upgrade';
75
+ proxy_set_header Host $host;
76
+ proxy_set_header X-Real-IP $remote_addr;
77
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
78
+ proxy_set_header X-Forwarded-Proto $scheme;
79
+ proxy_set_header X-Forwarded-Host $host;
80
+ proxy_set_header X-Forwarded-Port $server_port;
81
+ proxy_cache_bypass $http_upgrade;
82
+
83
+ # Timeouts for long-running LLM requests
84
+ proxy_connect_timeout 60s;
85
+ proxy_send_timeout 120s;
86
+ proxy_read_timeout 120s;
87
+ }
88
+
89
+ # Static files with caching
90
+ location /static/ {
91
+ proxy_pass http://socar_backend;
92
+ proxy_cache_valid 200 1d;
93
+ add_header Cache-Control "public, max-age=86400";
94
+ }
95
+
96
+ # Health check endpoint (no caching)
97
+ location /health {
98
+ proxy_pass http://socar_backend;
99
+ proxy_cache_bypass 1;
100
+ add_header Cache-Control "no-cache, no-store, must-revalidate";
101
+ }
102
+ }
nginx/ssl/.gitkeep ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ # SSL certificates directory
2
+ # Place your SSL certificates here:
3
+ # - fullchain.pem: Your full certificate chain
4
+ # - privkey.pem: Your private key
5
+ #
6
+ # See docs/markdowns/SSL_CAA_SETUP.md for setup instructions
7
+ #
8
+ # IMPORTANT: Never commit actual certificate files to git!