# Player HTTPS Connection Issues - Analysis & Solutions ## Problem Summary Players can successfully connect to the DigiServer when using **HTTP on port 80**, but connections are **refused/blocked when the server is on HTTPS**. --- ## Root Causes Identified ### 1. **Missing CORS Headers on API Endpoints** ⚠️ CRITICAL **Issue:** The app imports `Flask-Cors` (requirements.txt line 31) but **never initializes it** in the application. **Location:** - [app/extensions.py](app/extensions.py) - CORS not initialized - [app/app.py](app/app.py#L1-L80) - No CORS initialization in create_app() **Impact:** Players making cross-origin requests (from device IP to server domain/IP) get CORS errors and connections are refused at the browser/HTTP client level. **Affected Endpoints:** - `/api/playlists` - GET (primary endpoint for player playlist fetch) - `/api/auth/player` - POST (authentication) - `/api/auth/verify` - POST (token verification) - `/api/player-feedback` - POST (player status updates) - All endpoints prefixed with `/api/*` --- ### 2. **SSL Certificate Trust Issues** ⚠️ CRITICAL for Device-to-Server Communication **Issue:** Players are likely receiving **self-signed certificates** from nginx. **Location:** - [docker-compose.yml](docker-compose.yml#L22-L35) - Nginx container with SSL - [nginx.conf](nginx.conf#L54-L67) - SSL certificate paths point to self-signed certs - [data/nginx-ssl/](data/nginx-ssl/) - Contains `cert.pem` and `key.pem` **Details:** ``` ssl_certificate /etc/nginx/ssl/cert.pem; ssl_certificate_key /etc/nginx/ssl/key.pem; ``` **Impact:** - Players using standard HTTP clients (Python `requests`, JavaScript `fetch`, Kivy's HTTP module) will **reject self-signed certificates by default** - This causes connection refusal with SSL certificate verification errors - The player might be using hardcoded certificate verification (certificate pinning) --- ### 3. **No Certificate Validation Bypass in Player API** ⚠️ HIGH **Issue:** The API endpoints don't provide a way for players to bypass SSL verification or explicitly trust the certificate. **What's Missing:** ```python # Players likely need: # - Endpoint to fetch and validate server certificate # - API response with certificate fingerprint # - Configuration to disable cert verification for self-signed setups # - Or: Generate proper certificates with Let's Encrypt ``` --- ### 4. **Potential HTTP/HTTPS Redirect Issues** **Location:** [nginx.conf](nginx.conf#L40-L50) **Issue:** HTTP requests to "/" are redirected to HTTPS: ```nginx location / { return 301 https://$host$request_uri; # Forces HTTPS } ``` **Impact:** - If player tries to connect via HTTP, it gets a 301 redirect to HTTPS - If the player doesn't follow redirects or isn't configured for HTTPS, it fails - The redirect URL depends on the `$host` variable, which might not match player's expectations --- ### 5. **ProxyFix Middleware May Lose Protocol Info** **Location:** [app/app.py](app/app.py#L37) **Issue:** ```python app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_port=1) ``` **Detail:** If nginx doesn't properly set `X-Forwarded-Proto: https`, the app might generate HTTP URLs in responses instead of HTTPS. **Config Check:** ```nginx proxy_set_header X-Forwarded-Proto $scheme; # Should be in nginx.conf ``` ✓ **This is present in nginx.conf**, so ProxyFix should work correctly. --- ### 6. **Security Headers Might Block Requests** **Location:** [nginx.conf](nginx.conf#L70-L74) **Issue:** ```nginx add_header X-Frame-Options "SAMEORIGIN" always; add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always; ``` **Impact:** Overly restrictive CSP could block embedded resource loading from players. --- ### 7. **Missing Player Certificate Configuration** ⚠️ CRITICAL **Issue:** Players (especially embedded devices) often have: - Limited certificate stores - Self-signed cert validation disabled by default in some frameworks - No built-in mechanism to trust new certificates **What's Not Addressed:** - No endpoint to retrieve server certificate for device installation - No configuration for certificate thumbprint verification - No setup guide for device SSL configuration --- ## Solutions by Priority ### 🔴 **PRIORITY 1: Enable CORS for API Endpoints** **Fix:** Initialize Flask-CORS in the application. **File:** [app/extensions.py](app/extensions.py) ```python from flask_cors import CORS # Add after other extensions ``` **File:** [app/app.py](app/app.py) - In `create_app()` function ```python # After initializing extensions, add: CORS(app, resources={ r"/api/*": { "origins": ["*"], # Or specific origins: ["http://...", "https://..."] "methods": ["GET", "POST", "OPTIONS"], "allow_headers": ["Content-Type", "Authorization"], "supports_credentials": True, "max_age": 3600 } }) ``` --- ### 🔴 **PRIORITY 2: Fix SSL Certificate Issues** **Option A: Use Let's Encrypt (Recommended for production)** ```bash # Generate proper certificates with certbot certbot certonly --standalone -d yourdomain.com --email your@email.com ``` **Option B: Generate Self-Signed Certs with Longer Validity** ```bash # Current certs might be expired or have trust issues openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 \ -subj "/CN=digiserver/O=Organization/C=US" ``` **Option C: Allow Players to Trust Self-Signed Cert** Add endpoint to serve certificate: ```python # In app/blueprints/api.py @api_bp.route('/certificate', methods=['GET']) def get_server_certificate(): """Return server certificate for player installation.""" try: with open('/etc/nginx/ssl/cert.pem', 'r') as f: cert_content = f.read() return jsonify({ 'certificate': cert_content, 'certificate_format': 'PEM' }), 200 except Exception as e: return jsonify({'error': str(e)}), 500 ``` --- ### 🟡 **PRIORITY 3: Update Configuration** **File:** [app/config.py](app/config.py) **Change:** ```python # Line 28 - Currently set to False for development SESSION_COOKIE_SECURE = False # Set to True in production with HTTPS ``` **To:** ```python class ProductionConfig(Config): SESSION_COOKIE_SECURE = True # HTTPS only SESSION_COOKIE_SAMESITE = 'Lax' ``` --- ### 🟡 **PRIORITY 4: Fix nginx Configuration** **Verify in [nginx.conf](nginx.conf):** ```nginx # Line 86-95: Ensure these headers are present proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $server_name; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # Add this for player connections: proxy_set_header X-Forwarded-Port 443; ``` **Consider relaxing CORS headers at nginx level:** ```nginx # Add to location / block in HTTPS server: add_header 'Access-Control-Allow-Origin' '*' always; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always; add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always; ``` --- ### 🟡 **PRIORITY 5: Update Player Connection Code** **If you control the player code, add:** ```python # Python example for player connecting to server import requests from requests.adapters import HTTPAdapter from requests.packages.urllib3.util.retry import Retry class PlayerClient: def __init__(self, server_url, hostname, quickconnect_code, verify_ssl=False): self.server_url = server_url self.session = requests.Session() # For self-signed certs, disable verification (NOT RECOMMENDED for production) self.session.verify = verify_ssl # Or: Trust specific certificate # self.session.verify = '/path/to/server-cert.pem' self.hostname = hostname self.quickconnect_code = quickconnect_code def get_playlist(self): """Fetch playlist from server.""" try: response = self.session.get( f"{self.server_url}/api/playlists", params={ 'hostname': self.hostname, 'quickconnect_code': self.quickconnect_code }, headers={'Authorization': f'Bearer {self.auth_code}'} ) response.raise_for_status() return response.json() except requests.exceptions.SSLError as e: print(f"SSL Error: {e}") # Retry without SSL verification if configured if not self.session.verify: raise # Fall back to unverified connection self.session.verify = False return self.get_playlist() ``` --- ## Verification Steps ### Test 1: Check CORS Headers ```bash # This should include Access-Control-Allow-Origin curl -v https://192.168.0.121/api/health -H "Origin: *" ``` ### Test 2: Check SSL Certificate ```bash # View certificate details openssl s_client -connect 192.168.0.121:443 -showcerts # Check expiration openssl x509 -in /srv/digiserver-v2/data/nginx-ssl/cert.pem -text -noout | grep -i valid ``` ### Test 3: Test API Endpoint ```bash # Try fetching playlist (should fail with SSL error or CORS error initially) curl -k https://192.168.0.121/api/playlists \ -G --data-urlencode "hostname=test" \ --data-urlencode "quickconnect_code=test123" \ -H "Origin: http://192.168.0.121" ``` ### Test 4: Player Connection Simulation ```python # From player device import requests session = requests.Session() session.verify = False # Temp for testing response = session.get( 'https://192.168.0.121/api/playlists', params={'hostname': 'player1', 'quickconnect_code': 'abc123'} ) print(response.json()) ``` --- ## Summary of Changes Needed | Issue | Fix | Priority | File | |-------|-----|----------|------| | No CORS Headers | Initialize Flask-CORS | 🔴 HIGH | app/extensions.py, app/app.py | | Self-Signed SSL Cert | Get Let's Encrypt cert or add trust endpoint | 🔴 HIGH | data/nginx-ssl/ | | Certificate Validation | Add /certificate endpoint | 🟡 MEDIUM | app/blueprints/api.py | | SESSION_COOKIE_SECURE | Update in ProductionConfig | 🟡 MEDIUM | app/config.py | | X-Forwarded Headers | Verify nginx.conf | 🟡 MEDIUM | nginx.conf | | CSP Too Restrictive | Relax CSP for player requests | 🟢 LOW | nginx.conf | --- ## Quick Fix for Immediate Testing To quickly test if CORS is the issue: 1. **Enable CORS temporarily:** ```bash docker exec digiserver-v2 python -c " from app import create_app from flask_cors import CORS app = create_app('production') CORS(app) " ``` 2. **Test player connection:** ```bash curl -k https://192.168.0.121/api/health ``` 3. **If works, the issue is CORS + SSL certificates** --- ## Recommended Next Steps 1. ✅ Enable Flask-CORS in the application 2. ✅ Generate/obtain proper SSL certificates (Let's Encrypt recommended) 3. ✅ Add certificate trust endpoint for devices 4. ✅ Update nginx configuration for player device compatibility 5. ✅ Create player connection guide documenting HTTPS setup 6. ✅ Test with actual player device