# Kiwy-Signage Self-Signed Certificate Support - Code Patches This file contains exact code patches ready to apply to enable self-signed certificate support. ## PATCH 1: Create ssl_config.py **File:** `Kiwy-Signage/src/ssl_config.py` (NEW FILE) ```python """ SSL Configuration Module for Kiwy-Signage Handles certificate verification for self-signed and custom CA certificates """ import os import logging logger = logging.getLogger(__name__) class SSLConfig: """Manage SSL certificate verification settings""" # Default to True (use system CA certificates) _custom_ca_path = None _verify_ssl = True @classmethod def get_ca_bundle(cls): """Get path to CA certificate bundle for verification Priority order: 1. Custom CA bundle path specified via set_ca_bundle() 2. CA bundle path from REQUESTS_CA_BUNDLE environment variable 3. CA bundle in config/ca_bundle.crt 4. System default CA bundle (True = use system certs) Returns: str or bool: Path to CA bundle file or True for system default """ # Check if custom CA was explicitly set if cls._custom_ca_path: if os.path.exists(cls._custom_ca_path): logger.info(f"Using custom CA bundle: {cls._custom_ca_path}") return cls._custom_ca_path else: logger.warning(f"Custom CA bundle not found: {cls._custom_ca_path}, falling back to system") # Check environment variable env_ca = os.environ.get('REQUESTS_CA_BUNDLE') if env_ca and os.path.exists(env_ca): logger.info(f"Using CA bundle from REQUESTS_CA_BUNDLE: {env_ca}") return env_ca # Check config directory config_ca = 'config/ca_bundle.crt' if os.path.exists(config_ca): logger.info(f"Using CA bundle from config: {config_ca}") return config_ca # Use system default logger.debug("Using system default CA certificates") return True @classmethod def get_verify_setting(cls): """Get the 'verify' parameter for requests calls Returns: bool or str: Value to pass as 'verify=' parameter to requests """ if not cls._verify_ssl: logger.warning("SSL verification is DISABLED - this is insecure!") return False return cls.get_ca_bundle() @classmethod def set_ca_bundle(cls, ca_path): """Manually set custom CA bundle path Args: ca_path (str): Path to CA certificate file """ if os.path.exists(ca_path): cls._custom_ca_path = ca_path logger.info(f"CA bundle set to: {ca_path}") else: logger.error(f"CA bundle file not found: {ca_path}") @classmethod def disable_verification(cls): """DANGER: Disable SSL certificate verification ⚠️ WARNING: Only use for development/testing! This makes the application vulnerable to MITM attacks. """ cls._verify_ssl = False logger.critical("⚠️ SSL VERIFICATION DISABLED - This is insecure!") @classmethod def enable_verification(cls): """Enable SSL certificate verification (default)""" cls._verify_ssl = True logger.info("SSL verification enabled") @classmethod def is_verification_enabled(cls): """Check if SSL verification is enabled Returns: bool: True if verification is enabled, False if disabled """ return cls._verify_ssl ``` --- ## PATCH 2: Modify src/player_auth.py **Location:** `Kiwy-Signage/src/player_auth.py` ### Change 2a: Add import at top of file ```python # AFTER line 10 (after existing imports), ADD: from ssl_config import SSLConfig ``` ### Change 2b: Modify __init__ method (lines 20-30) **BEFORE:** ```python def __init__(self, config_file: str = 'player_auth.json'): """Initialize player authentication. Args: config_file: Path to authentication config file """ self.config_file = config_file self.auth_data = self._load_auth_data() ``` **AFTER:** ```python def __init__(self, config_file: str = 'player_auth.json'): """Initialize player authentication. Args: config_file: Path to authentication config file """ self.config_file = config_file self.auth_data = self._load_auth_data() self.verify_ssl = SSLConfig.get_verify_setting() ``` ### Change 2c: Modify authenticate() method (line 95) **BEFORE:** ```python response = requests.post(auth_url, json=payload, timeout=timeout) ``` **AFTER:** ```python response = requests.post(auth_url, json=payload, timeout=timeout, verify=self.verify_ssl) ``` ### Change 2d: Modify verify_auth() method (line 157) **BEFORE:** ```python response = requests.post(verify_url, json=payload, timeout=timeout) ``` **AFTER:** ```python response = requests.post(verify_url, json=payload, timeout=timeout, verify=self.verify_ssl) ``` ### Change 2e: Modify get_playlist() method (line 178) **BEFORE:** ```python response = requests.get(playlist_url, headers=headers, timeout=timeout) ``` **AFTER:** ```python response = requests.get(playlist_url, headers=headers, timeout=timeout, verify=self.verify_ssl) ``` ### Change 2f: Modify send_heartbeat() method (line 227-228) **BEFORE:** ```python response = requests.post(heartbeat_url, headers=headers, json=payload, timeout=timeout) ``` **AFTER:** ```python response = requests.post(heartbeat_url, headers=headers, json=payload, timeout=timeout, verify=self.verify_ssl) ``` ### Change 2g: Modify send_feedback() method (line 254-255) **BEFORE:** ```python response = requests.post(feedback_url, headers=headers, json=payload, timeout=timeout) ``` **AFTER:** ```python response = requests.post(feedback_url, headers=headers, json=payload, timeout=timeout, verify=self.verify_ssl) ``` --- ## PATCH 3: Modify src/get_playlists_v2.py **Location:** `Kiwy-Signage/src/get_playlists_v2.py` ### Change 3a: Add import (after line 6) ```python # AFTER line 6 (after "from player_auth import PlayerAuth"), ADD: from ssl_config import SSLConfig ``` ### Change 3b: Modify download_media_files() function (line 159) **BEFORE:** ```python response = requests.get(file_url, timeout=30) ``` **AFTER:** ```python verify_ssl = SSLConfig.get_verify_setting() response = requests.get(file_url, timeout=30, verify=verify_ssl) ``` --- ## PATCH 4: Extract Server Certificate **Steps to follow on the DigiServer:** ```bash #!/bin/bash # Run this on the DigiServer with self-signed certificate # Export the certificate openssl s_client -connect localhost:443 -showcerts < /dev/null | \ openssl x509 -outform PEM > /tmp/server_cert.crt # Copy to player configuration directory # (transfer via SSH, USB, or other secure method) cp /tmp/server_cert.crt /path/to/Kiwy-Signage/config/ca_bundle.crt # Verify it was copied correctly ls -la /path/to/Kiwy-Signage/config/ca_bundle.crt ``` --- ## PATCH 5: Alternative - Use Environment Variable Instead of placing cert in config directory, you can use environment variable: ```bash #!/bin/bash # Before running the player: export REQUESTS_CA_BUNDLE=/etc/ssl/certs/custom-ca.crt cd /path/to/Kiwy-Signage ./start.sh ``` --- ## Testing After Patches ### Test 1: Verify patches applied correctly ```bash cd /tmp/Kiwy-Signage/src # Check imports added grep "from ssl_config import SSLConfig" player_auth.py grep "from ssl_config import SSLConfig" get_playlists_v2.py # Check verify parameter added grep "verify=self.verify_ssl" player_auth.py | wc -l # Should output: 5 # Check new file exists test -f ssl_config.py && echo "ssl_config.py exists" || echo "MISSING" ``` ### Test 2: Test with self-signed server ```bash cd /tmp/Kiwy-Signage # 1. Export server cert (run on server) openssl s_client -connect server.local:443 -showcerts < /dev/null | \ openssl x509 -outform PEM > config/ca_bundle.crt # 2. Test player connection python3 -c " import sys sys.path.insert(0, 'src') from player_auth import PlayerAuth from ssl_config import SSLConfig # Check what certificate will be used cert_path = SSLConfig.get_ca_bundle() print(f'Using certificate: {cert_path}') # Try authentication auth = PlayerAuth() success, error = auth.authenticate( server_url='https://server.local:443', hostname='test-player', quickconnect_code='TEST123' ) print(f'Connection result: {\"SUCCESS\" if success else \"FAILED\"}') if error: print(f'Error: {error}') " ``` ### Test 3: Verify backward compatibility ```bash cd /tmp/Kiwy-Signage # Test connection to production server (valid CA cert) python3 -c " import sys sys.path.insert(0, 'src') from player_auth import PlayerAuth auth = PlayerAuth() success, error = auth.authenticate( server_url='https://digi-signage.moto-adv.com', hostname='test-player', quickconnect_code='TEST123' ) print(f'Production server: {\"OK\" if success else \"FAILED\"}') " ``` --- ## Summary of Changes | File | Type | Changes | Complexity | |------|------|---------|------------| | `src/ssl_config.py` | NEW | Full file (~60 lines) | Low | | `src/player_auth.py` | MODIFY | 7 small changes | Low | | `src/get_playlists_v2.py` | MODIFY | 2 small changes | Low | | `config/ca_bundle.crt` | NEW | Certificate file | N/A | **Total lines of code modified:** ~8 lines **New code added:** ~60 lines **Breaking changes:** None **Backward compatible:** Yes --- ## Rollback Instructions If you need to revert the changes: ```bash cd /tmp/Kiwy-Signage # Restore original files from git git checkout src/player_auth.py git checkout src/get_playlists_v2.py # Remove new file rm src/ssl_config.py # Remove certificate file (optional) rm config/ca_bundle.crt ``` --- ## Implementation Checklist - [ ] Read the full analysis (KIWY_PLAYER_HTTPS_ANALYSIS.md) - [ ] Review this patch file - [ ] Create `src/ssl_config.py` (PATCH 1) - [ ] Apply changes to `src/player_auth.py` (PATCH 2) - [ ] Apply changes to `src/get_playlists_v2.py` (PATCH 3) - [ ] Export server certificate (PATCH 4) - [ ] Place certificate in `config/ca_bundle.crt` - [ ] Run Test 1: Verify patches applied - [ ] Run Test 2: Test with self-signed server - [ ] Run Test 3: Test with production server - [ ] Update player documentation - [ ] Deploy to test player - [ ] Monitor player logs for SSL errors