Files
Kiwy-Signage/documentation/integration_guide.md
Kiwy Player 81432ac832 Add autostart functionality and power management for Raspberry Pi
- 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)
2026-01-17 18:50:47 +02:00

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 = True
    • SESSION_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

  1. Update player code with new SSL handling from this guide
  2. Restart player application to pick up changes
  3. Verify connection works with HTTPS server
  4. Monitor logs for any SSL-related errors

For New Players

  1. Deploy updated player code with SSL support from the start
  2. Configure with HTTPS server URL
  3. 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

  1. For Development/Testing: Disable SSL verification temporarily

    session.verify = False
    
  2. 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
  3. For Self-Signed Certificates:

    • Use /api/certificate endpoint to distribute certificates
    • Store certificates in secure location on device
    • Implement certificate update mechanism
    • Log certificate trust changes for auditing

Next Steps

  1. Implement SSL handling in player code using examples above
  2. Test with HTTP first to ensure API works
  3. Enable HTTPS and test with certificate handling
  4. Deploy to production with proper SSL setup
  5. Monitor player connections and SSL errors