initial commit
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Emergency Sunshine pairing bypass — extract client cert from log and inject into state file.
|
||||
|
||||
Sunshine v2026.516.143833 has a broken PIN validation pipeline. The /api/pin endpoint
|
||||
deadlocks internally even when IP addresses match. This script:
|
||||
1. Reads the most recent /pair request's clientcert from sunshine.log
|
||||
2. Decodes the hex-encoded PEM cert
|
||||
3. Injects it directly into sunshine_state.json as a pre-paired named_device
|
||||
4. Restarts Sunshine
|
||||
|
||||
After running, Moonlight must be FULLY CLOSED AND REOPENED — it will see the pre-paired
|
||||
cert and skip the PIN screen.
|
||||
|
||||
Usage:
|
||||
python3 pairing-bypass-inject-cert.py [--restart]
|
||||
|
||||
Requirements:
|
||||
- Root not needed (Sunshine runs as the same user)
|
||||
- sunshine_state.json must be writable
|
||||
- Moonlight must have sent at least one /pair request (visible in sunshine.log)
|
||||
"""
|
||||
|
||||
import json
|
||||
import re
|
||||
import uuid
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
SUNSHINE_LOG = os.path.expanduser("~/.config/sunshine/sunshine.log")
|
||||
SUNSHINE_STATE = os.path.expanduser("~/.config/sunshine/sunshine_state.json")
|
||||
SUNSHINE_CONF = os.path.expanduser("~/.config/sunshine/sunshine.conf")
|
||||
|
||||
def extract_latest_cert():
|
||||
with open(SUNSHINE_LOG, "r", encoding="utf-8", errors="replace") as f:
|
||||
content = f.read()
|
||||
matches = re.findall(r'clientcert -- ([0-9a-fA-F]+)', content)
|
||||
if not matches:
|
||||
print("ERROR: No clientcert found in sunshine.log")
|
||||
print("Make sure Moonlight has tried to pair (it should show a PIN screen)")
|
||||
sys.exit(1)
|
||||
hex_cert = matches[-1]
|
||||
pem_cert = bytes.fromhex(hex_cert).decode("utf-8")
|
||||
print(f"Extracted certificate ({len(pem_cert)} bytes)")
|
||||
print(f"Preview: {pem_cert[:60]}...")
|
||||
return pem_cert
|
||||
|
||||
def inject_cert(pem_cert, client_name="RE-PC"):
|
||||
try:
|
||||
with open(SUNSHINE_STATE, "r") as f:
|
||||
state = json.load(f)
|
||||
except (FileNotFoundError, json.JSONDecodeError):
|
||||
state = {}
|
||||
|
||||
if "root" not in state:
|
||||
state["root"] = {"uniqueid": "0123456789ABCDEF", "named_devices": []}
|
||||
if "named_devices" not in state["root"]:
|
||||
state["root"]["named_devices"] = []
|
||||
|
||||
# Check if client already exists — update cert
|
||||
updated = False
|
||||
for dev in state["root"]["named_devices"]:
|
||||
if dev.get("name") == client_name:
|
||||
dev["cert"] = pem_cert
|
||||
dev["enabled"] = "true"
|
||||
updated = True
|
||||
print(f"Updated existing client '{client_name}'")
|
||||
break
|
||||
|
||||
if not updated:
|
||||
state["root"]["named_devices"].append({
|
||||
"name": client_name,
|
||||
"cert": pem_cert,
|
||||
"uuid": str(uuid.uuid4()).upper(),
|
||||
"enabled": "true"
|
||||
})
|
||||
print(f"Added new client '{client_name}'")
|
||||
|
||||
# Ensure origin_pin_allowed = pc for easier future pairing
|
||||
try:
|
||||
with open(SUNSHINE_CONF, "r") as f:
|
||||
conf = f.read()
|
||||
if "origin_pin_allowed" not in conf:
|
||||
with open(SUNSHINE_CONF, "a") as f:
|
||||
f.write("\norigin_pin_allowed = pc\n")
|
||||
print("Added origin_pin_allowed = pc to sunshine.conf")
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
with open(SUNSHINE_STATE, "w") as f:
|
||||
json.dump(state, f, indent=4)
|
||||
print(f"Injected into {SUNSHINE_STATE}")
|
||||
|
||||
def restart_sunshine():
|
||||
print("Restarting Sunshine...")
|
||||
subprocess.run(["systemctl", "--user", "restart", "sunshine"], check=False)
|
||||
time.sleep(3)
|
||||
result = subprocess.run(
|
||||
["systemctl", "--user", "status", "sunshine", "--no-pager"],
|
||||
capture_output=True, text=True
|
||||
)
|
||||
if "active (running)" in result.stdout:
|
||||
print("Sunshine is running.")
|
||||
else:
|
||||
print("WARNING: Sunshine may not have started. Check: systemctl --user status sunshine")
|
||||
|
||||
def main():
|
||||
restore = "--restart" in sys.argv
|
||||
cert = extract_latest_cert()
|
||||
inject_cert(cert, client_name=os.environ.get("MOONLIGHT_CLIENT_NAME", "RE-PC"))
|
||||
if restore:
|
||||
restart_sunshine()
|
||||
else:
|
||||
print("\nNow restart Sunshine: systemctl --user restart sunshine")
|
||||
print("\nIMPORTANT: Fully CLOSE Moonlight on the client, then reopen it.")
|
||||
print("It should connect directly to apps without asking for a PIN.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user