# Kylin Remote Control & Monitoring Setup Complete reference for controlling and monitoring a Kylin Linux machine from a Windows 11 PC over a local phone-hotspot network. No VPN is used for the SSH connection itself — only the local Wi-Fi/hotspot. --- ## 0. Bootstrap — START HERE in a new session **The connection is already fully set up** (SSH key login + passwordless sudo). A new session does **NOT** need to repeat the setup in §2–§3 — those sections are records of what was already done. Just connect and go. **Verify everything with one command** (run from the Windows machine, e.g. Claude Code's Bash tool): ```bash ssh -o BatchMode=yes -o ConnectTimeout=10 kylin 'echo SSH_OK; whoami; hostname; sudo -n true && echo SUDO_OK' ``` Expected: `SSH_OK` / `caichl` / `caichl-HUAWEIQINGYUNL420` / `SUDO_OK`. - ✅ **`SSH_OK` + `SUDO_OK`** → full control. Run anything (incl. `sudo`) via `ssh -o BatchMode=yes kylin ""`. No password needed. - ❌ **asks for password / times out** → most likely the **hotspot IP changed** (`192.168.43.248` is DHCP-assigned and can change between sessions). Ask the user to re-check the Kylin IP (`hostname -I` on that machine), update `C:\Users\adusu\.ssh\config`, then re-verify. If it asks for a password, the key may have been removed — see §2.3 / §2.4 to re-establish. - ℹ️ SSH prints a harmless **post-quantum warning** to stderr — ignore it (the examples in this doc filter it out). **Connect manually:** `ssh kylin` (alias in `C:\Users\adusu\.ssh\config` → `caichl@192.168.43.248`, with the pinned cipher/MAC already configured — see §2). > A fresh Claude Code session also auto-loads memory pointers (`MEMORY.md` → > `kylin-remote-machine`, `kylin-command-logging`, `kylin-mihomo-vpn`) that link > back here. This file is the authoritative, self-contained reference. --- ## 1. The two machines | Role | Details | |------|---------| | **Controller** | Windows 11 (`C:\Users\adusu`), where Claude Code + VPN run | | **Target** | Kylin V10 SP1, Huawei QINGYUN L420, **ARM64 / aarch64** | | **Network** | Shared phone hotspot, subnet `192.168.43.x` | | **Target IP** | `192.168.43.248` | | **Target user** | `caichl` | > **Key idea:** Claude Code needs the VPN to reach Anthropic's servers, but the > SSH hop from Windows → Kylin is **local LAN traffic** and needs no VPN. The two > connections are independent. --- ## 2. SSH connection ### 2.1 Required cipher/MAC (important) The default cipher fails over this hotspot link with `Corrupted MAC on input`. The connection must pin a specific cipher and MAC: ``` -c aes256-ctr -m hmac-sha2-256 ``` ### 2.2 Windows SSH config File: `C:\Users\adusu\.ssh\config` ```sshconfig Host kylin kyln HostName 192.168.43.248 User caichl Ciphers aes256-ctr MACs hmac-sha2-256 ``` With this in place, simply run: ```powershell ssh kylin # or: ssh kyln (both aliases work) ``` The equivalent explicit command (no config) is: ```powershell ssh -m hmac-sha2-256 -c aes256-ctr caichl@192.168.43.248 ``` ### 2.3 Passwordless login (SSH key) The Windows key `~/.ssh/id_ed25519.pub` was copied to the Kylin machine: ```powershell Get-Content $env:USERPROFILE\.ssh\id_ed25519.pub | ` ssh kylin "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys" ``` Result: `ssh kylin` logs in with **no password**. ### 2.4 Passwordless sudo So admin commands can run non-interactively from Windows: ```bash # Run once on the Kylin machine (asks for password the one time) sudo bash -c 'echo "caichl ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/caichl-nopasswd; \ chmod 440 /etc/sudoers.d/caichl-nopasswd; \ visudo -cf /etc/sudoers.d/caichl-nopasswd' ``` > **Security note:** This makes `caichl` a passwordless-sudo account — acceptable > for a disposable machine only. To revert: `sudo rm /etc/sudoers.d/caichl-nopasswd`. After this, from Windows you can run, e.g.: ```powershell ssh -o BatchMode=yes kylin "sudo systemctl status ssh" ``` ### 2.5 Account credentials (recovery only) > ⚠️ **Plaintext password below — keep this file private; do NOT commit or share it.** | Item | Value | |------|-------| | User | `caichl` | | Password | `Caicai@2026` | **Normally not needed** — SSH login uses the key (§2.3) and sudo is passwordless (§2.4). Use this only to **recover** if the key auth or sudoers drop-in is ever lost (e.g. to log in interactively and re-copy the key, or to re-create `/etc/sudoers.d/caichl-nopasswd`). To change it: run `passwd` on the Kylin machine. --- ## 3. Initial SSH server setup (done on the Kylin machine) ```bash sudo apt update sudo apt install -y openssh-server sudo systemctl enable --now ssh hostname -I # find the IP (192.168.43.248) whoami # confirm the username (caichl) ``` --- ## 4. Monitoring ### 4.1 SSH logins & sessions (works out of the box) ```bash who # who is logged in + source w # sessions + current command last # login history with IPs lastb # FAILED login attempts (sudo) sudo grep "Accepted" /var/log/auth.log # successful SSH logins sudo grep "Failed" /var/log/auth.log # failed/suspicious attempts sudo tail -f /var/log/auth.log # live SSH activity ``` ### 4.2 Command logging — no reboot (WORKING) Logs every command typed in an interactive shell, with user, tty, source IP, and the command text. **`/etc/profile.d/cmdlog.sh`** ```bash # Log interactive shell commands to syslog (facility local6) -> /var/log/cmdlog.log if [ -n "$BASH" ] && [ -n "$PS1" ]; then export PROMPT_COMMAND='logger -p local6.notice -t cmdlog "user=$USER tty=$(tty 2>/dev/null) from=$(who am i 2>/dev/null | sed -n "s/.*(\(.*\)).*/\1/p") cmd=$(history 1 | sed "s/^ *[0-9]* *//")"' fi ``` **`/etc/rsyslog.d/30-cmdlog.conf`** ``` local6.* /var/log/cmdlog.log ``` **Setup commands** ```bash sudo touch /var/log/cmdlog.log sudo chown syslog:adm /var/log/cmdlog.log # MUST be syslog-owned (rsyslog drops privs) sudo chmod 640 /var/log/cmdlog.log sudo systemctl restart rsyslog ``` **View the logs** ```bash sudo cat /var/log/cmdlog.log # all logged commands sudo tail -f /var/log/cmdlog.log # live ``` Example line: ``` Jun 11 10:54:22 caichl-HUAWEIQINGYUNL420 cmdlog: user=caichl tty=/dev/pts/5 from=192.168.43.180 cmd=echo hello ``` > **Gotcha:** rsyslog runs as the `syslog` user. If `/var/log/cmdlog.log` is owned > by `root:root`, nothing is written. It must be `chown syslog:adm`. ### 4.3 System-wide auditing — auditd (QUEUED, UNVERIFIED) `auditd` is installed and enabled, with a rule logging all `execve` for real users: **`/etc/audit/rules.d/cmdlog.rules`** ``` -a exit,always -F arch=b64 -S execve -F auid>=1000 -F auid!=4294967295 -k cmdlog ``` It cannot run yet because the kernel booted with `audit=0`. This was changed to `audit=1`: ```bash sudo sed -i 's/audit=0/audit=1/' /etc/default/grub # /boot is read-only on this device — remount to rebuild grub sudo mount -o remount,rw /boot sudo update-grub sudo mount -o remount,ro /boot ``` > **Caveat:** This device appears to boot via **Huawei firmware, not standard > GRUB** (running kernel `-27` is not the one in `grub.cfg` which lists `-11`, and > the kernel cmdline has Huawei-specific params). So the `audit=1` change **may > not take effect** on reboot. The no-reboot logging in §4.2 already covers > interactive command logging, so auditd is a bonus. **Verify after any reboot** ```bash cat /proc/sys/kernel/audit_enabled # want: 1 sudo systemctl is-active auditd # want: active sudo ausearch -k cmdlog -i | tail -40 # human-readable command audit sudo aureport -x --summary # summary of executables run ``` --- ## 5. Device-specific quirks (Huawei Kylin ARM64) - **Architecture is ARM64 (aarch64).** Software must have an ARM64 build; x86-only proprietary apps will not install/run. - **`/boot` is a separate ext4 partition mounted read-only.** Remount rw to change GRUB, then back to ro (see §4.3). - **Boot likely controlled by Huawei firmware**, limiting effectiveness of GRUB cmdline edits. - **Hotspot link needs `aes256-ctr` / `hmac-sha2-256`** or SSH fails with "Corrupted MAC on input". --- ## 6. Quick reference ```powershell # Connect ssh kylin # Run a one-off command from Windows ssh kylin "uptime" # Run an admin command from Windows ssh kylin "sudo systemctl status ssh" # See logged commands ssh kylin "sudo tail -50 /var/log/cmdlog.log" # Watch live ssh kylin "sudo tail -f /var/log/cmdlog.log" # SSH login history ssh kylin "last" ``` --- ## 7. Troubleshooting | Symptom | Cause / Fix | |---------|-------------| | `Corrupted MAC on input` | Use `-c aes256-ctr -m hmac-sha2-256` (already in config) | | `Connection timed out` | Both devices not on same hotspot, or AP/client isolation on the phone — disable isolation | | `ssh kyln` "could not resolve" | Alias spelling — config now accepts both `kylin` and `kyln` | | Still asks for password | SSH key not copied (§2.3) | | `sudo` asks for password | sudoers drop-in empty/missing (§2.4) | | `audit support not in kernel` | Kernel booted `audit=0`; needs reboot with `audit=1` (§4.3) | | `cmdlog.log` empty | File not owned by `syslog:adm`; `sudo chown syslog:adm /var/log/cmdlog.log` then restart rsyslog | | `grub.cfg: Read-only file system` | `/boot` is read-only; `sudo mount -o remount,rw /boot` first | | VPN GUI app won't run (`GLIBC_2.34 not found`) | Kylin has glibc 2.31; modern Flutter/Electron clients need 2.34+. Use the Go-based mihomo core instead (see §8) | --- ## 8. VPN / proxy: mihomo + web dashboard **Why not FlClash / GUI clients:** Kylin V10 SP1 has **glibc 2.31**, but FlClash 0.8.93 and similar modern GUI clients require **glibc 2.34+** and fail to run. Upgrading glibc would risk breaking the OS. mihomo is a **statically-linked Go binary** with no glibc dependency, so it runs fine — controlled via a browser dashboard. ### Current state > **VPN is currently OFF.** The `mihomo` service is **stopped** and **disabled** > (auto-start removed), so it stays off across reboots until re-enabled. Ports > `7890`/`9090` are closed. The full configuration below is intact and ready to > resume. ### Turn the VPN on / off (run on Kylin, or via `ssh kylin`) ```bash # TURN ON (and restore auto-start on boot) sudo systemctl enable --now mihomo # TURN OFF for this session only (comes back on next reboot) sudo systemctl stop mihomo # TURN OFF and keep it off across reboots <-- current state sudo systemctl stop mihomo && sudo systemctl disable mihomo # Check state systemctl is-active mihomo # active / inactive systemctl is-enabled mihomo # enabled / disabled ``` > Reminder: if you had set the Kylin **system proxy** to `127.0.0.1:7890`, switch > it back to "None" while the VPN is off, or apps will fail to reach the network. ### Desktop access A launcher **"Mihomo VPN Dashboard"** was added to the app menu (Network category) at `/usr/share/applications/mihomo-dashboard.desktop`; it opens `http://127.0.0.1:9090/ui/`. (Only works while the service is running.) ### Installed components | Item | Location / value | |------|------------------| | mihomo core | `/usr/local/bin/mihomo` (Clash.Meta v1.19.27, arm64) | | Config | `/etc/mihomo/config.yaml` | | Web dashboard (zashboard) | `/etc/mihomo/ui/` | | systemd service | `/etc/systemd/system/mihomo.service` (auto-starts on boot) | | Proxy port (HTTP+SOCKS) | `7890` | | Dashboard API | `0.0.0.0:9090` | | Dashboard secret | `428968aebf7c301565c6b6d3a11826c4` | ### Install steps (done; for reference / rebuild) ```bash # binary (downloaded on Windows through VPN, scp'd over to dodge chicken-and-egg) gunzip mihomo-linux-arm64-vX.gz sudo install -m 755 mihomo-linux-arm64 /usr/local/bin/mihomo # dashboard sudo mkdir -p /etc/mihomo/ui && sudo unzip zashboard-dist.zip -d /etc/mihomo/ui # config.yaml: mixed-port 7890, external-controller 0.0.0.0:9090, secret, external-ui: ui, # proxy-providers (subscription URL), PROXY (select) + AUTO (url-test) groups sudo systemctl enable --now mihomo ``` > The subscription URL lives in `/etc/mihomo/config.yaml` under `proxy-providers:`. > `external-ui-name` must NOT be set (it makes mihomo look in a `ui/` subdir > and auto-download); serve `/etc/mihomo/ui` directly. ### Open the dashboard - **From Windows browser:** `http://192.168.43.248:9090/ui/` - backend/API: `http://192.168.43.248:9090` • secret: `428968aebf7c301565c6b6d3a11826c4` - **From Kylin desktop browser:** `http://127.0.0.1:9090/ui/` ### Use the proxy Point apps at `127.0.0.1:7890` (HTTP/SOCKS), or set the Kylin system proxy (Settings → Network → Proxy → Manual → `127.0.0.1:7890`). For transparent whole-system routing, enable mihomo **TUN mode** (`/dev/net/tun` is present). ### Manage via API (examples) ```bash S=428968aebf7c301565c6b6d3a11826c4 # switch PROXY group to a node / AUTO curl -X PUT -H "Authorization: Bearer $S" http://127.0.0.1:9090/proxies/PROXY -d '{"name":"AUTO"}' # verify exit IP goes through the proxy curl -x http://127.0.0.1:7890 -s https://api.ip.sb/geoip # update subscription / reload sudo systemctl restart mihomo ``` **Verified working:** exit IP resolved to Singapore and `google.com/generate_204` returned HTTP 204 through the proxy. --- ## 9. Browsers | Browser | App-menu name | Type / location | Notes | |---------|---------------|-----------------|-------| | **Chromium** | "Chromium" | snap `chromium` v149 (`/snap/bin/chromium`) | Installed as the Chrome/Edge equivalent — same engine, Chrome Web Store extensions work. **Set as the default browser.** | - **Chrome & Edge cannot be installed** — neither has an official ARM64 Linux build (same architecture wall as the VPN GUI clients). Chromium is the substitute. - Installing the chromium snap first required **updating snapd** (Kylin shipped 2.54, too old → error `assumes unsupported features: snapd2.55`). Fix: `sudo snap install snapd` (brought it to 2.75.2), then `sudo snap install chromium`. - Default browser is recorded in `~/.config/mimeapps.list` (`text/html`, `x-scheme-handler/http(s)` → `chromium_chromium.desktop`). If a double-click opens the wrong app, right-click the file → **Open with → Chromium → Set as default**. ### Desktop instruction files (for the user at the machine) - `~/桌面/VPN_Guide.html` + `~/桌面/VPN_Guide.md` (copies also in `~/Desktop/`): a user-facing "how to turn the VPN on/off" guide. Double-click the **`.html`** to read it rendered in a browser; the **`.md`** opens in **VS Code** (`code`, installed) with `Ctrl+Shift+V` preview. - App-menu launcher **"Mihomo VPN Dashboard"** → opens `http://127.0.0.1:9090/ui/` (only works while the mihomo service is running). --- ## 10. Inventory — everything set up on the Kylin machine | Area | What | State | |------|------|-------| | Access | SSH alias `kylin`/`kyln`, key login, passwordless sudo | ✅ active | | Monitoring | SSH login logs (`auth.log`); command logging → `/var/log/cmdlog.log` | ✅ active | | Monitoring | auditd execve rule | ⏳ queued (needs reboot + `audit=1`; may not take on Huawei firmware) | | VPN | mihomo core + zashboard dashboard, subscription configured | ⛔ installed, **OFF** (stopped + disabled) | | Browser | Chromium v149 (default), 360/CNOOC browser | ✅ installed | | Editors | VS Code (`code`), pluma, WPS Office | ✅ pre-installed | | IDE | Cursor 3.7.27 (`/opt/cursor`) via `env -i` wrapper | ✅ installed (see §11) | | Desktop docs | `VPN_Guide.html` / `.md`, dashboard launcher | ✅ on desktop | --- ## 11. Cursor IDE (Electron) — install + crash fixes Cursor (AI code editor, Electron-based) installed as an **extracted AppImage** at `/opt/cursor` (currently **v3.7.27**, ARM64). It will NOT run with a normal launcher on this Huawei/Kylin box — it needs a special wrapper. ### Why the special launcher (three Kylin/Huawei ARM64 problems) 1. **EFAULT machine-ID probe crash** — Cursor runs a command to generate a unique machine ID; the Kylin kernel blocks that memory `write` as suspicious → `Error: EFAULT: bad address in system call argument, write` → instant crash. **Fix:** `env -i` strips the environment (especially D-Bus) so Cursor skips the probe. 2. **Mali GPU crash (段错误 / segfault)** — hardware rendering conflicts with Huawei's Mali GPU driver (`dmesg`: `mali gpu: kctx ... create/destroyed` at crash time). **Fix:** `--disable-gpu` (software rendering). 3. **Blocked sandbox** — `--no-sandbox`. Also installed `xdg-desktop-portal` + `xdg-desktop-portal-gtk` (1.6.0) for the Wayland file picker (necessary but not sufficient alone). ### The launcher (already installed) - **Wrapper:** `/usr/local/bin/cursor-launch` - **Start-menu entry:** `/usr/share/applications/cursor.desktop` → `Exec=/usr/local/bin/cursor-launch %F` ```sh #!/bin/sh exec env -i \ HOME="$HOME" DISPLAY="${DISPLAY:-:0}" PATH="$PATH" \ /opt/cursor/usr/share/cursor/cursor --no-sandbox --disable-gpu "$@" ``` ### Updating Cursor to a newer version 1. Get the latest ARM64 AppImage URL (downloads on Windows through the VPN): ```bash curl -sL -A "Mozilla/5.0" -H "Accept: application/json" \ "https://www.cursor.com/api/download?platform=linux-arm64&releaseTrack=stable" # -> JSON with "downloadUrl" (…/Cursor-X.Y.Z-aarch64.AppImage) and "version" curl -L -C - -o Cursor-new.AppImage "" ``` 2. `scp` it to Kylin, then: ```bash chmod +x Cursor-new.AppImage cd /tmp && ./Cursor-new.AppImage --appimage-extract # -> /tmp/squashfs-root sudo rm -rf /opt/cursor && sudo cp -r /tmp/squashfs-root /opt/cursor sudo chown -R root:root /opt/cursor && rm -rf /tmp/squashfs-root ``` 3. **No change needed** to the wrapper or menu entry — paths stay the same. Verify glibc first (must need ≤ 2.31): `objdump -T /opt/cursor/usr/share/cursor/cursor | grep -oE 'GLIBC_[0-9.]+' | sort -V | tail`. ### Cleanup note Old AppImages in `~/software/` (`Cursor-3.2.11`, `Cursor-3.7.27`) and the user's manual extract `~/software/squashfs-root` (3.2.11) are redundant now that `/opt/cursor` is the install — safe to delete to reclaim space.