Files
digiserver-v2/old_code_documentation/player_analisis/PLAYER_HTTPS_CONNECTION_ANALYSIS.md

11 KiB

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:

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:

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:

# 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

Issue: HTTP requests to "/" are redirected to HTTPS:

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

Issue:

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:

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

Issue:

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

from flask_cors import CORS

# Add after other extensions

File: app/app.py - In create_app() function

# 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)

# Generate proper certificates with certbot
certbot certonly --standalone -d yourdomain.com --email your@email.com

Option B: Generate Self-Signed Certs with Longer Validity

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

# 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

Change:

# Line 28 - Currently set to False for development
SESSION_COOKIE_SECURE = False  # Set to True in production with HTTPS

To:

class ProductionConfig(Config):
    SESSION_COOKIE_SECURE = True  # HTTPS only
    SESSION_COOKIE_SAMESITE = 'Lax'

🟡 PRIORITY 4: Fix nginx Configuration

Verify in nginx.conf:

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

# 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 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

# This should include Access-Control-Allow-Origin
curl -v https://192.168.0.121/api/health -H "Origin: *"

Test 2: Check SSL Certificate

# 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

# 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

# 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:
docker exec digiserver-v2 python -c "
from app import create_app
from flask_cors import CORS
app = create_app('production')
CORS(app)
"
  1. Test player connection:
curl -k https://192.168.0.121/api/health
  1. If works, the issue is CORS + SSL certificates

  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