- Enhanced install.sh with comprehensive autostart workflow: * XDG autostart entry (desktop environment) * systemd user service (most reliable) * LXDE autostart support (Raspberry Pi OS) * Cron fallback (@reboot) * Terminal mode enabled for debugging - Added Raspberry Pi power management features: * Disable HDMI screen blanking * Prevent CPU power saving (performance mode) * Disable system sleep/suspend * X11 screensaver disabled * Display power management (DPMS) disabled - Fixed sudo compatibility: * Properly detects actual user when run with sudo * Correct file ownership for user configs * systemctl --user works correctly - Player launches in terminal for error visibility - Autostart configured to use start.sh (watchdog with auto-restart)
11 KiB
11 KiB
Player Code HTTPS Integration Guide
Server-Side Improvements Implemented
All critical and medium improvements have been implemented on the server:
✅ CORS Support Enabled
- File:
app/extensions.py- CORS extension initialized - File:
app/app.py- CORS configured for/api/*endpoints - All player API requests now support cross-origin requests
- Preflight OPTIONS requests are properly handled
✅ SSL Certificate Endpoint Added
- Endpoint:
GET /api/certificate - Location:
app/blueprints/api.py - Returns server certificate in PEM format with metadata:
- Certificate content (PEM format)
- Certificate info (subject, issuer, validity dates, fingerprint)
- Integration instructions for different platforms
✅ HTTPS Configuration Updated
- File:
app/config.py- ProductionConfig now has:SESSION_COOKIE_SECURE = TrueSESSION_COOKIE_SAMESITE = 'Lax'
- File:
nginx.conf- Added:- CORS headers for all responses
- OPTIONS request handling
- X-Forwarded-Port header forwarding
✅ Nginx Proxy Configuration Enhanced
- Added CORS headers at nginx level for defense-in-depth
- Proper X-Forwarded headers for protocol/port detection
- HTTPS-friendly proxy configuration
Required Player Code Changes
1. For Python/Kivy Players Using Requests Library
Update: Import and use certificate handling:
import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
import os
class DigiServerClient:
def __init__(self, server_url, hostname, quickconnect_code, use_https=True):
self.server_url = server_url
self.hostname = hostname
self.quickconnect_code = quickconnect_code
self.session = requests.Session()
# CRITICAL: Handle SSL verification
if use_https:
# Option 1: Get certificate from server and trust it
self.setup_certificate_trust()
else:
# Option 2: Disable SSL verification (DEV ONLY)
self.session.verify = False
def setup_certificate_trust(self):
"""Download server certificate and configure trust."""
try:
# First, make a request without verification to get the cert
response = requests.get(
f"{self.server_url}/api/certificate",
verify=False,
timeout=5
)
if response.status_code == 200:
cert_data = response.json()
# Save certificate locally
cert_path = os.path.expanduser('~/.digiserver/server_cert.pem')
os.makedirs(os.path.dirname(cert_path), exist_ok=True)
with open(cert_path, 'w') as f:
f.write(cert_data['certificate'])
# Configure session to use this certificate
self.session.verify = cert_path
print(f"✓ Server certificate installed from {cert_data['certificate_info']['issuer']}")
print(f" Valid until: {cert_data['certificate_info']['valid_until']}")
except Exception as e:
print(f"⚠️ Failed to setup certificate trust: {e}")
print(" Falling back to unverified connection (not recommended for production)")
self.session.verify = False
def get_playlist(self):
"""Get playlist from server with proper error handling."""
try:
response = self.session.get(
f"{self.server_url}/api/playlists",
params={
'hostname': self.hostname,
'quickconnect_code': self.quickconnect_code
},
timeout=10
)
response.raise_for_status()
return response.json()
except requests.exceptions.SSLError as e:
print(f"❌ SSL Error: {e}")
# Log error for debugging
print(" This usually means the server certificate is not trusted.")
print(" Try running: DigiServerClient.setup_certificate_trust()")
raise
except requests.exceptions.ConnectionError as e:
print(f"❌ Connection Error: {e}")
raise
except Exception as e:
print(f"❌ Error: {e}")
raise
def send_feedback(self, status, message=''):
"""Send player feedback/status to server."""
try:
response = self.session.post(
f"{self.server_url}/api/player-feedback",
json={
'hostname': self.hostname,
'quickconnect_code': self.quickconnect_code,
'status': status,
'message': message,
'timestamp': datetime.utcnow().isoformat()
},
timeout=10
)
response.raise_for_status()
return response.json()
except Exception as e:
print(f"Error sending feedback: {e}")
return None
2. For Kivy Framework Specifically
Update: In your Kivy HTTP client configuration:
from kivy.network.urlrequest import UrlRequest
from kivy.logger import Logger
import ssl
import certifi
class DigiServerKivyClient:
def __init__(self, server_url, hostname, quickconnect_code):
self.server_url = server_url
self.hostname = hostname
self.quickconnect_code = quickconnect_code
# Configure SSL context for Kivy requests
self.ssl_context = self._setup_ssl_context()
def _setup_ssl_context(self):
"""Setup SSL context with certificate trust."""
try:
# Try to get server certificate
import requests
response = requests.get(
f"{self.server_url}/api/certificate",
verify=False,
timeout=5
)
if response.status_code == 200:
cert_data = response.json()
cert_path = os._get_cert_path()
with open(cert_path, 'w') as f:
f.write(cert_data['certificate'])
# Create SSL context
context = ssl.create_default_context()
context.load_verify_locations(cert_path)
Logger.info('DigiServer', f'SSL context configured with server certificate')
return context
except Exception as e:
Logger.warning('DigiServer', f'Failed to setup SSL: {e}')
return None
def fetch_playlist(self, callback):
"""Fetch playlist with proper SSL handling."""
url = f"{self.server_url}/api/playlists"
params = f"?hostname={self.hostname}&quickconnect_code={self.quickconnect_code}"
headers = {
'Content-Type': 'application/json',
'User-Agent': 'Kiwy-Signage-Player/1.0'
}
request = UrlRequest(
url + params,
on_success=callback,
on_error=self._on_error,
on_failure=self._on_failure,
headers=headers
)
return request
def _on_error(self, request, error):
Logger.error('DigiServer', f'Request error: {error}')
def _on_failure(self, request, result):
Logger.error('DigiServer', f'Request failed: {result}')
3. Environment Configuration
Add to player app_config.json or environment:
{
"server": {
"url": "https://192.168.0.121",
"hostname": "player1",
"quickconnect_code": "ABC123XYZ",
"verify_ssl": false,
"use_server_certificate": true,
"certificate_path": "~/.digiserver/server_cert.pem"
},
"connection": {
"timeout": 10,
"retry_attempts": 3,
"retry_delay": 5
}
}
Testing Checklist
Server-Side Tests
- Verify CORS headers present:
curl -v https://192.168.0.121/api/health - Check certificate endpoint:
curl -k https://192.168.0.121/api/certificate - Test OPTIONS preflight:
curl -X OPTIONS https://192.168.0.121/api/playlists - Verify X-Forwarded headers:
curl -v https://192.168.0.121/
Player Connection Tests
- Player connects with HTTPS successfully
- Player fetches playlist without SSL errors
- Player receives status update confirmation
- Player sends feedback/heartbeat correctly
Integration Tests
# Test certificate retrieval
curl -k https://192.168.0.121/api/certificate | jq '.certificate_info'
# Test CORS preflight for player
curl -X OPTIONS https://192.168.0.121/api/playlists \
-H "Origin: http://192.168.0.121" \
-H "Access-Control-Request-Method: GET" \
-v
# Simulate player playlist fetch
curl -k https://192.168.0.121/api/playlists \
--data-urlencode "hostname=test-player" \
--data-urlencode "quickconnect_code=test123" \
-H "Origin: *"
Migration Steps
For Existing Players
- Update player code with new SSL handling from this guide
- Restart player application to pick up changes
- Verify connection works with HTTPS server
- Monitor logs for any SSL-related errors
For New Players
- Deploy updated player code with SSL support from the start
- Configure with HTTPS server URL
- Run initialization to fetch and trust server certificate
Troubleshooting
"SSL: CERTIFICATE_VERIFY_FAILED"
- Player is rejecting the self-signed certificate
- Solution: Run certificate trust setup or disable SSL verification
"Connection Refused"
- Server HTTPS port not accessible
- Solution: Check nginx is running, port 443 is open, firewall rules
"CORS error"
- Browser/HTTP client blocking cross-origin request
- Solution: Verify CORS headers in response, check Origin header
"Certificate not found at endpoint"
- Server certificate file missing
- Solution: Verify cert.pem exists at
/etc/nginx/ssl/cert.pem
Security Recommendations
-
For Development/Testing: Disable SSL verification temporarily
session.verify = False -
For Production:
- Use proper certificates (Let's Encrypt recommended)
- Deploy certificate trust setup at player initialization
- Monitor SSL certificate expiration
- Implement certificate pinning for critical deployments
-
For Self-Signed Certificates:
- Use
/api/certificateendpoint to distribute certificates - Store certificates in secure location on device
- Implement certificate update mechanism
- Log certificate trust changes for auditing
- Use
Next Steps
- Implement SSL handling in player code using examples above
- Test with HTTP first to ensure API works
- Enable HTTPS and test with certificate handling
- Deploy to production with proper SSL setup
- Monitor player connections and SSL errors