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

10 KiB

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)

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

# AFTER line 10 (after existing imports), ADD:

from ssl_config import SSLConfig

Change 2b: Modify init method (lines 20-30)

BEFORE:

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:

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:

response = requests.post(auth_url, json=payload, timeout=timeout)

AFTER:

response = requests.post(auth_url, json=payload, timeout=timeout, verify=self.verify_ssl)

Change 2d: Modify verify_auth() method (line 157)

BEFORE:

response = requests.post(verify_url, json=payload, timeout=timeout)

AFTER:

response = requests.post(verify_url, json=payload, timeout=timeout, verify=self.verify_ssl)

Change 2e: Modify get_playlist() method (line 178)

BEFORE:

response = requests.get(playlist_url, headers=headers, timeout=timeout)

AFTER:

response = requests.get(playlist_url, headers=headers, timeout=timeout, verify=self.verify_ssl)

Change 2f: Modify send_heartbeat() method (line 227-228)

BEFORE:

response = requests.post(heartbeat_url, headers=headers, 
                       json=payload, timeout=timeout)

AFTER:

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:

response = requests.post(feedback_url, headers=headers, 
                       json=payload, timeout=timeout)

AFTER:

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)

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

response = requests.get(file_url, timeout=30)

AFTER:

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:

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

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

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

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

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:

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