summaryrefslogtreecommitdiffstats
path: root/opt/obsproxy
diff options
context:
space:
mode:
authoryum <yum.food.vr@gmail.com>2025-10-16 14:51:18 -0700
committeryum <yum.food.vr@gmail.com>2025-10-28 17:19:38 -0700
commita91dd1d55550d36cda17f8acd777d07d7e8d83ee (patch)
treee66fcd9a8aa61ef4eb11d06f07554797c8cf0cfe /opt/obsproxy
parentbecdbeeefac4fbf4b75ff5052502945f5843ddfb (diff)
add basic encryption. note that keys are publicly available
the intent is just to prevent dragnet snooping
Diffstat (limited to 'opt/obsproxy')
-rwxr-xr-xopt/obsproxy/server.py40
1 files changed, 40 insertions, 0 deletions
diff --git a/opt/obsproxy/server.py b/opt/obsproxy/server.py
index df6edb3..e4a2750 100755
--- a/opt/obsproxy/server.py
+++ b/opt/obsproxy/server.py
@@ -7,6 +7,7 @@ import threading
import shutil
import time
from pathlib import Path
+from typing import Optional
from flask import Flask, request
import logging
import atexit
@@ -34,6 +35,9 @@ SERVER_DOMAIN = os.environ.get('SERVER_DOMAIN', 'yummers.b-cdn.net')
STREAM_HEX = secrets.token_hex(16)
STREAM_PATH = BASE_DIR / 'live' / STREAM_HEX
HLS_ROUTE_PREFIX = f"/hls/{STREAM_HEX}"
+SESSION_KEY_NAME = 'session.key'
+SESSION_KEYINFO_NAME = 'session.keyinfo'
+SESSION_KEY_URI: Optional[str] = None
# Media output settings tuned for VRChat playback
AUDIO_BITRATE = '256k'
AUDIO_CHANNELS = 2
@@ -65,10 +69,38 @@ if not INGEST_PSK:
BASE_DIR.mkdir(parents=True, exist_ok=True)
STREAM_PATH.mkdir(parents=True, exist_ok=True)
+
+def _session_key_path() -> Path:
+ return STREAM_PATH / SESSION_KEY_NAME
+
+
+def _session_keyinfo_path() -> Path:
+ return STREAM_PATH / SESSION_KEYINFO_NAME
+
+
+def _write_key_material() -> None:
+ """Generate and persist AES-128 key + keyinfo for the current session."""
+ global SESSION_KEY_URI
+
+ key_bytes = secrets.token_bytes(16)
+ iv_bytes = secrets.token_bytes(16)
+ key_path = _session_key_path()
+ key_path.write_bytes(key_bytes)
+
+ key_uri = f"https://{SERVER_DOMAIN}{HLS_ROUTE_PREFIX}/{SESSION_KEY_NAME}"
+ keyinfo_path = _session_keyinfo_path()
+ iv_hex = format(int.from_bytes(iv_bytes, 'big'), '032x')
+ keyinfo_path.write_text(
+ f"{key_uri}\n{key_path}\n{iv_hex}\n",
+ encoding="utf-8",
+ )
+ SESSION_KEY_URI = key_uri
+
def reset_stream_path():
"""Ensure the live stream directory is empty and ready."""
shutil.rmtree(STREAM_PATH, ignore_errors=True)
STREAM_PATH.mkdir(parents=True, exist_ok=True)
+ _write_key_material()
def _safe_reset_stream_path(context: str) -> bool:
@@ -160,6 +192,11 @@ def _terminate_ffmpeg_process(process: subprocess.Popen[str], *, timeout: float
def _build_ffmpeg_command() -> list[str]:
"""Construct the ffmpeg command line we execute for each attempt."""
+ keyinfo_path = _session_keyinfo_path()
+ if not keyinfo_path.exists():
+ _write_key_material()
+
+ keyinfo_path = _session_keyinfo_path()
return [
'ffmpeg',
'-nostdin',
@@ -180,6 +217,7 @@ def _build_ffmpeg_command() -> list[str]:
'-hls_list_size', str(HLS_PLAYLIST_SIZE),
'-hls_flags', 'delete_segments+independent_segments',
'-hls_delete_threshold', str(HLS_DELETE_THRESHOLD),
+ '-hls_key_info_file', str(keyinfo_path),
'-hls_segment_filename', str(STREAM_PATH / 'segment-%05d.ts'),
str(STREAM_PATH / 'stream.m3u8'),
]
@@ -381,6 +419,8 @@ def print_instructions():
print("\n[URLS]")
print(f" OBS ingest: {obs_url}")
print(f" HLS: {hls_url}")
+ if SESSION_KEY_URI:
+ print(f" HLS key: {SESSION_KEY_URI}")
print("\n[STATUS]")
print(f" Stream is {'ACTIVE' if ffmpeg_process else 'INACTIVE'}")