fix: 포트 충돌 회피 — note_bridge 8098, intent_service 8099

Jellyfin(8096), OrbStack(8097) 포트 충돌으로 변경.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-03-19 13:53:55 +09:00
parent dc08d29509
commit c2257d3a86
2709 changed files with 619549 additions and 10 deletions

View File

@@ -0,0 +1,30 @@
from __future__ import annotations
import platform
import sys
# Platform detection
IS_WINDOWS = sys.platform == "win32"
IS_MACOS = sys.platform == "darwin"
IS_LINUX = sys.platform.startswith("linux")
IS_BSD = sys.platform.startswith(("freebsd", "openbsd", "netbsd"))
# macOS version detection
MACOS_VERSION: tuple[int, ...] | None = None
if IS_MACOS:
version_str = platform.mac_ver()[0]
MACOS_VERSION = tuple(map(int, version_str.split(".")))
if IS_WINDOWS:
from ._windows import root_der_certificates
elif IS_MACOS and MACOS_VERSION >= (10, 15): # type: ignore[operator]
from ._macos import root_der_certificates
elif IS_LINUX or IS_BSD:
from ._linux import root_der_certificates
else:
from ._embed import root_der_certificates
__all__ = ("root_der_certificates",)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,95 @@
from __future__ import annotations
import os
from pathlib import Path
from ssl import PEM_cert_to_DER_cert
# source: http://gagravarr.org/writing/openssl-certs/others.shtml
BUNDLE_TRUST_STORE_DIRECTORIES: list[str] = [
"/var/ssl",
"/usr/share/ssl",
"/usr/local/ssl",
"/usr/local/openssl",
"/usr/local/etc/openssl",
"/usr/local/share/certs",
"/usr/lib/ssl",
"/usr/ssl",
"/etc/openssl",
"/etc/pki/ca-trust/extracted/pem",
"/etc/pki/tls",
"/etc/ssl",
"/etc/certs",
"/opt/etc/ssl",
"/system/etc/security/cacerts",
"/boot/system/data/ssl",
]
KNOWN_TRUST_STORE_EXTENSIONS: list[str] = [
"pem",
"crt",
]
BANNED_KEYWORD_NOT_TLS: set[str] = {
"email",
"objsign",
"trust",
"timestamp",
"codesign",
"ocsp",
"untrusted",
}
def root_der_certificates() -> list[bytes]:
certificates: list[bytes] = []
for directory in BUNDLE_TRUST_STORE_DIRECTORIES:
if not os.path.exists(directory):
continue
# Use rglob to recursively search all files in directory and subdirectories
for filepath in Path(directory).rglob("*"):
try:
if not filepath.is_file(): # Skip directories
continue
extension = filepath.suffix.lstrip(".").lower()
if extension not in KNOWN_TRUST_STORE_EXTENSIONS and extension.isdigit() is False:
continue
if any(kw in str(filepath).lower() for kw in BANNED_KEYWORD_NOT_TLS):
continue
with open(filepath, encoding="utf-8") as f:
bundle = f.read()
if not bundle.strip(): # Skip empty files
continue # Defensive:
line_ending = "\n" if "-----END CERTIFICATE-----\r\n" not in bundle else "\r\n"
boundary = "-----END CERTIFICATE-----" + line_ending
for chunk in bundle.split(boundary):
if chunk:
start_marker = chunk.find("-----BEGIN CERTIFICATE-----" + line_ending)
if start_marker == -1:
break # Defensive: file that aren't PEM encoded in target directories(...)
pem_reconstructed = "".join([chunk[start_marker:], boundary])
try:
der_certificate = PEM_cert_to_DER_cert(pem_reconstructed)
except ValueError: # Defensive: malformed cert/base64?
continue
if der_certificate not in certificates:
certificates.append(der_certificate)
except (OSError, UnicodeDecodeError): # Defensive: Skip files we can't read
# OSError -> e.g. PermissionError
# UnicodeDecodeError -> DER ASN.1 encoded
continue
return certificates

View File

@@ -0,0 +1,142 @@
from __future__ import annotations
import ctypes
from ctypes import POINTER, byref, c_int32, c_uint32, c_void_p
# Load frameworks
_core = ctypes.CDLL("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")
_sec = ctypes.CDLL("/System/Library/Frameworks/Security.framework/Security")
# Type aliases
CFTypeRef = c_void_p
CFArrayRef = c_void_p
CFDataRef = c_void_p
CFDictionaryRef = c_void_p
OSStatus = c_int32
# CoreFoundation function prototypes
_CFDictionaryCreate = _core.CFDictionaryCreate
_CFDictionaryCreate.argtypes = [c_void_p, POINTER(c_void_p), POINTER(c_void_p), c_uint32, c_void_p, c_void_p]
_CFDictionaryCreate.restype = CFDictionaryRef
_CFArrayGetCount = _core.CFArrayGetCount
_CFArrayGetCount.argtypes = [CFArrayRef]
_CFArrayGetCount.restype = c_uint32
_CFArrayGetValueAtIndex = _core.CFArrayGetValueAtIndex
_CFArrayGetValueAtIndex.argtypes = [CFArrayRef, c_uint32]
_CFArrayGetValueAtIndex.restype = CFTypeRef
_CFDataGetLength = _core.CFDataGetLength
_CFDataGetLength.argtypes = [CFDataRef]
_CFDataGetLength.restype = c_uint32
_CFDataGetBytePtr = _core.CFDataGetBytePtr
_CFDataGetBytePtr.argtypes = [CFDataRef]
_CFDataGetBytePtr.restype = ctypes.POINTER(ctypes.c_ubyte)
_CFRelease = _core.CFRelease
_CFRelease.argtypes = [c_void_p]
_CFRelease.restype = None
# Security function prototypes
_SecItemCopyMatching = _sec.SecItemCopyMatching
_SecItemCopyMatching.argtypes = [CFDictionaryRef, POINTER(CFTypeRef)]
_SecItemCopyMatching.restype = OSStatus
_SecCertificateCopyData = _sec.SecCertificateCopyData
_SecCertificateCopyData.argtypes = [CFTypeRef]
_SecCertificateCopyData.restype = CFDataRef
_SecTrustSettingsCopyCertificates = _sec.SecTrustSettingsCopyCertificates
_SecTrustSettingsCopyCertificates.argtypes = [c_int32, POINTER(CFArrayRef)]
_SecTrustSettingsCopyCertificates.restype = OSStatus
# CF callbacks & boolean constants
_kCFTypeDictKeyCallBacks = c_void_p.in_dll(_core, "kCFTypeDictionaryKeyCallBacks")
_kCFTypeDictValueCallBacks = c_void_p.in_dll(_core, "kCFTypeDictionaryValueCallBacks")
_kCFBooleanTrue = c_void_p.in_dll(_core, "kCFBooleanTrue")
_kSecBooleanTrue = _kCFBooleanTrue
# SecItem constants
_kSecClass = c_void_p.in_dll(_sec, "kSecClass")
_kSecClassCertificate = c_void_p.in_dll(_sec, "kSecClassCertificate")
_kSecMatchLimit = c_void_p.in_dll(_sec, "kSecMatchLimit")
_kSecMatchLimitAll = c_void_p.in_dll(_sec, "kSecMatchLimitAll")
_kSecMatchTrustedOnly = c_void_p.in_dll(_sec, "kSecMatchTrustedOnly")
_kSecReturnRef = c_void_p.in_dll(_sec, "kSecReturnRef")
# Helper: build a CFDictionary for SecItem queries
def _make_query(keys: list[c_void_p], values: list[c_void_p]) -> CFDictionaryRef:
count = len(keys)
KeyArr = (c_void_p * count)(*keys)
ValArr = (c_void_p * count)(*values)
return _CFDictionaryCreate( # type: ignore[no-any-return]
None,
KeyArr,
ValArr,
count,
_kCFTypeDictKeyCallBacks,
_kCFTypeDictValueCallBacks,
)
# Helper: perform SecItemCopyMatching and return CFTypeRef list
def _query_refs(query: CFDictionaryRef) -> list[CFTypeRef]:
result = CFTypeRef()
status = _SecItemCopyMatching(query, byref(result))
if status != 0:
raise OSError(f"SecItemCopyMatching failed with status={status}") # Defensive: OOM?
array_ref = CFArrayRef(result.value)
count = _CFArrayGetCount(array_ref)
items = [_CFArrayGetValueAtIndex(array_ref, i) for i in range(count)]
# Note: No CFRelease() calls to avoid premature deallocation
return items
# Convert CFDataRef to Python bytes
def _data_to_bytes(data_ref: c_void_p) -> bytes:
length = _CFDataGetLength(data_ref)
ptr = _CFDataGetBytePtr(data_ref)
data = bytes(ctypes.string_at(ptr, length))
_CFRelease(data_ref)
return data
# Public: retrieve DER-encoded trusted certificates
def root_der_certificates() -> list[bytes]:
"""
Returns a list of DER-encoded certificates trusted for TLS server auth,
covering system roots, admin, user trust settings, and personal CAs.
"""
certificates: list[bytes] = []
# 1) System/user/admin trust settings
for domain in (0, 1, 2):
cert_array = CFArrayRef()
status = _SecTrustSettingsCopyCertificates(domain, byref(cert_array))
if status == 0:
count = _CFArrayGetCount(cert_array)
for i in range(count):
cert_ref = _CFArrayGetValueAtIndex(cert_array, i)
certificates.append(_data_to_bytes(_SecCertificateCopyData(cert_ref)))
# 2) Personal CA certificates from keychain marked trusted
query = _make_query(
keys=[_kSecClass, _kSecMatchLimit, _kSecMatchTrustedOnly, _kSecReturnRef],
values=[_kSecClassCertificate, _kSecMatchLimitAll, _kSecBooleanTrue, _kSecReturnRef],
)
try:
cert_refs = _query_refs(query)
for c in cert_refs:
certificates.append(_data_to_bytes(_SecCertificateCopyData(c)))
except OSError: # Defensive: OOM?
pass
return certificates

View File

@@ -0,0 +1,40 @@
from __future__ import annotations
from ssl import enum_certificates # type: ignore[attr-defined]
# ROOT: Highest level of trust. Trust anchors. Self-Signed.
# MY: User installed/custom trust anchors. Self-Signed.
# CA: Intermediates CA. Not trusted directly, not self-signed.
WINDOWS_STORES: list[str] = [
"ROOT",
"MY",
"CA",
]
SERVER_AUTH_OID: str = "1.3.6.1.5.5.7.3.1"
def root_der_certificates() -> list[bytes]:
certificates = []
for system_store in WINDOWS_STORES:
try:
for cert_bytes, encoding_type, trust in enum_certificates(system_store):
if not trust:
continue # Defensive: edge case, rare one.
# if not True, then, we MUST LOOK for SERVER_AUTH oid EKU
if not isinstance(trust, bool) and SERVER_AUTH_OID not in trust:
continue
# Check it's in X.509 ASN.1 format and is trusted
if (
encoding_type == "x509_asn" # X.509 ASN.1 data
):
certificates.append(cert_bytes)
except PermissionError: # Defensive: we can't cover that scenario in CI.
continue
return certificates
__all__ = ("root_der_certificates",)