This commit is contained in:
2026-06-12 17:19:26 +08:00
parent a3ebb28ce0
commit 8d2e18c5a4
20 changed files with 3011 additions and 411 deletions
+475
View File
@@ -0,0 +1,475 @@
# 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 "<command>"`. 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/<name>` 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 "<downloadUrl>"
```
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.