From dc15b03156afff31241d9f2ce4d37c6672a4a4ef Mon Sep 17 00:00:00 2001 From: gusadmin Date: Tue, 14 Jul 2026 18:23:56 +0300 Subject: [PATCH] =?UTF-8?q?Custom-client:=20=D1=81=D0=B5=D1=80=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=20=D0=B2=D0=B8=D0=B4=D0=B5=D0=BD+=D0=B7=D0=B0=D0=BB?= =?UTF-8?q?=D0=BE=D1=87=D0=B5=D0=BD=20+=20preset-=D0=BF=D0=B0=D1=80=D0=BE?= =?UTF-8?q?=D0=BB=D1=8C=20(Win+Android)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit preconfigure.sh генерит src/custom_client.json (override-settings server/relay/key + verification-method + disable-change-permanent-password; preset-пароль salt+00b64(sha256(pw+salt)) в HARD_SETTINGS) и патчит common.rs встроенным неподписанным загрузчиком (обход ED25519-подписи RustDesk). Пароль из RUSTDESK_PRESET_PASSWORD, plaintext не хранится. Co-Authored-By: Claude Opus 4.8 --- docs/CODEMAP.md | 18 +++++++ scripts/preconfigure.sh | 108 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 121 insertions(+), 5 deletions(-) diff --git a/docs/CODEMAP.md b/docs/CODEMAP.md index 85b173c..b5b574f 100644 --- a/docs/CODEMAP.md +++ b/docs/CODEMAP.md @@ -92,3 +92,21 @@ Rust 1.75 (rustup override в клоне) · MSVC 14.44 из vcvars64. ## Секреты в CI (gitea Actions) `RUSTDESK_SERVER`, `RUSTDESK_PUB_KEY`, (опц.) `RUSTDESK_API_SERVER`. Префикс имён — не `GITHUB_`/`GITEA_` (зарезервированы), при необходимости `GT_`. + +## Custom-client: сервер виден+залочен + preset-пароль +Требование: сервер показан в Настройках и НЕизменяем + вшитый постоянный пароль (+ временный). +Механизм RustDesk (изучен по коду 1.4.9): +- `src/common.rs::load_custom_client()` (зовётся из core_main) читает `custom.txt` рядом с exe — + base64 + **ED25519-подпись** по вшитому pubkey `5Qbwsde3…` (приватник у генератора RustDesk Pro, у нас нет). +- Он заполняет `OVERWRITE_SETTINGS` (override — залоченные опции), `DEFAULT_SETTINGS`, `BUILTIN_SETTINGS` (локи), + `HARD_SETTINGS` (top-level, вкл. preset-пароль). +- `custom-rendezvous-server`/`relay-server`/`key`/`api-server`/`verification-method` ∈ `KEYS_SETTINGS` → + в `override-settings` показываются И залочены. `disable-change-permanent-password` ∈ BUILDIN. +- Preset-пароль (`hbb_common/src/config/permanent_password.rs`): `HARD_SETTINGS.password="00"+base64(sha256(pw+salt))`, + `HARD_SETTINGS.salt=<любая непустая соль>`. Формат «00»+b64(h1), h1=sha256(pw+salt)[..32]. + +Наша реализация (`preconfigure.sh`, вкл. по умолчанию; выкл. `RUSTDESK_NO_CUSTOM_CLIENT=1`): +- Пишет `src/custom_client.json` (override-settings + password/salt из `RUSTDESK_PRESET_PASSWORD`). +- Патчит `common.rs`: добавляет `read_custom_client_unsigned()` (парсит JSON БЕЗ подписи, тот же apply-код) + + `include_str!("custom_client.json")` в начало `load_custom_client()`. Работает Windows+Android (без внешнего файла). +- Пароль — только из env/secret `RUSTDESK_PRESET_PASSWORD`, солёный хэш пишется в gitignored-клон, plaintext нигде. diff --git a/scripts/preconfigure.sh b/scripts/preconfigure.sh index 303c158..5bda9e6 100644 --- a/scripts/preconfigure.sh +++ b/scripts/preconfigure.sh @@ -47,9 +47,107 @@ fi echo "[preconfigure] RENDEZVOUS_SERVERS -> ${#SRV[@]} сервер(ов), RS_PUB_KEY вшит." -# Опциональный API-сервер (RustDesk Server Pro). По умолчанию у hbbs/hbbr его нет. -if [ -n "${RUSTDESK_API_SERVER:-}" ]; then - # В 1.4.9 отдельной const API_SERVER нет; дефолт задаётся через overrideConfig/filename. - # Оставляем маркер для ручной доработки, если понадобится Pro-API. - echo "[preconfigure] RUSTDESK_API_SERVER задан ($RUSTDESK_API_SERVER) — вшивание API требует доп. патча (см. docs/CODEMAP.md)." +# ───────────────────────────────────────────────────────────────────────────── +# Custom-client: сервер/ключ ВИДНЫ и ЗАЛОЧЕНЫ в настройках (override-settings) + +# опц. preset (постоянный) пароль в HARD_SETTINGS. Механизм RustDesk load_custom_client() +# читает ПОДПИСАННЫЙ custom.txt (приватного ключа их генератора у нас нет) -> патчим +# загрузчик под встроенный НЕподписанный конфиг (src/custom_client.json, include_str!), +# что работает и на Windows, и на Android. Отключить: RUSTDESK_NO_CUSTOM_CLIENT=1. +# ───────────────────────────────────────────────────────────────────────────── +if [ "${RUSTDESK_NO_CUSTOM_CLIENT:-}" != "1" ]; then + # Рабочий python (на Windows git-bash python3 = Store-заглушка -> берём python). + PY=""; for p in python3 python; do "$p" -c 'import hashlib,base64,json,secrets' 2>/dev/null && { PY="$p"; break; }; done + [ -n "$PY" ] || { echo "ОШИБКА: не найден рабочий python для custom-client" >&2; exit 1; } + + FIRST_SRV="$(echo -n "${SRV[0]}" | tr -d '[:space:]')" + LOCK_PW="${RUSTDESK_LOCK_PASSWORD:-Y}" # залочить смену preset-пароля (по умолчанию да) + "$PY" - "$SRC/src/custom_client.json" "$FIRST_SRV" "$RUSTDESK_PUB_KEY" "${RUSTDESK_API_SERVER:-}" "${RUSTDESK_PRESET_PASSWORD:-}" "$LOCK_PW" <<'PYEOF' +import sys, json, hashlib, base64, secrets +out, server, key, api, preset_pw, lock_pw = sys.argv[1:7] +override = {"custom-rendezvous-server": server, "relay-server": server, "key": key} +if api: + override["api-server"] = api +cfg = {"override-settings": override} +if preset_pw: + override["verification-method"] = "use-both-passwords" # постоянный + временный пароль + if lock_pw == "Y": + override["disable-change-permanent-password"] = "Y" + salt = secrets.token_hex(16) + h1 = hashlib.sha256(preset_pw.encode() + salt.encode()).digest() # = compute_permanent_password_h1 + cfg["password"] = "00" + base64.b64encode(h1).decode() # HBBS preset storage: "00"+b64(h1) + cfg["salt"] = salt +with open(out, "w", encoding="utf-8") as f: + json.dump(cfg, f, ensure_ascii=False) +print("[preconfigure] custom_client.json: сервер виден+залочен, preset-пароль=" + ("да" if preset_pw else "нет")) +PYEOF + + # Патч common.rs: встроенный неподписанный загрузчик (идемпотентно; клон сбрасывается перед каждой сборкой). + CM="$SRC/src/common.rs" + if ! grep -q "read_custom_client_unsigned" "$CM"; then + sed -i 's|pub fn load_custom_client() {|pub fn load_custom_client() {\n read_custom_client_unsigned(include_str!("custom_client.json"));|' "$CM" + grep -q 'read_custom_client_unsigned(include_str' "$CM" || { echo "ОШИБКА: не удалось внедрить вызов в load_custom_client" >&2; exit 1; } + cat >> "$CM" <<'RUSTEOF' + +// [rustdesk-custom] Встроенный НЕподписанный custom-client конфиг (src/custom_client.json). +pub fn read_custom_client_unsigned(json: &str) { + let json = json.trim(); + if json.is_empty() || json == "{}" { + return; + } + let Ok(mut data) = + serde_json::from_str::>(json) + else { + log::error!("Failed to parse embedded custom client config"); + return; + }; + if let Some(app_name) = data.remove("app-name") { + if let Some(app_name) = app_name.as_str() { + *config::APP_NAME.write().unwrap() = app_name.to_owned(); + } + } + let mut map_display_settings = HashMap::new(); + for s in keys::KEYS_DISPLAY_SETTINGS { + map_display_settings.insert(s.replace("_", "-"), s); + } + let mut map_local_settings = HashMap::new(); + for s in keys::KEYS_LOCAL_SETTINGS { + map_local_settings.insert(s.replace("_", "-"), s); + } + let mut map_settings = HashMap::new(); + for s in keys::KEYS_SETTINGS { + map_settings.insert(s.replace("_", "-"), s); + } + let mut buildin_settings = HashMap::new(); + for s in keys::KEYS_BUILDIN_SETTINGS { + buildin_settings.insert(s.replace("_", "-"), s); + } + if let Some(default_settings) = data.remove("default-settings") { + read_custom_client_advanced_settings( + default_settings, + &map_display_settings, + &map_local_settings, + &map_settings, + &buildin_settings, + false, + ); + } + if let Some(overwrite_settings) = data.remove("override-settings") { + read_custom_client_advanced_settings( + overwrite_settings, + &map_display_settings, + &map_local_settings, + &map_settings, + &buildin_settings, + true, + ); + } + for (k, v) in data { + if let Some(v) = v.as_str() { + config::HARD_SETTINGS.write().unwrap().insert(k, v.to_owned()); + }; + } +} +RUSTEOF + echo "[preconfigure] common.rs пропатчен (встроенный custom-client загрузчик)." + fi fi