lightweight-server/agents/codex/done/2026-10-23-vps-ru-de-setup.md
ochenstarik-ui 09c860463a
All checks were successful
Bash syntax / syntax (push) Successful in 18s
Bash syntax / syntax (pull_request) Successful in 18s
docs: resolve direct VPN hold with verified local proxy exception
2026-10-23 16:33:00 +07:00

272 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# RU/DE VPS — настройка 23.10.2026
**База и прямой VPN-вход из текущей сети: PASS.** Первоначальный HOLD устранён точечным исключением RU-IP из локального прозрачного прокси. Обычный REALITY-клиент без SSH выходит в зарубежный Интернет через DE, к российскому сервису через RU. Другая сеть и устройство семьи проверяются при переносе. Финляндия и Дания не затронуты.
## Основание и исходники
Прямое задание владельца: `kagent/agents/claude/done/2026-10-23-fixes/CODEX-vps-setup-ru-de.md`; повтор `kagent/kagent` в переданном пути исправлен. Только два адреса из разрешённых RU/DE файлов; иных SSH-хостов не проверял. Порядок: RU полностью, затем DE, затем связка.
Развёрнуты скрипты **main после PR19**, точный merge SHA `47d5e030dfa392a995649c0519088aae74f73935` (PR19 head `eb00e38a7515c6772535322d7b353c9c01718fcc`). Merge и main CI5/task7370 SUCCESS проверены до работы. Использован отдельный worktree существующего репозитория; скрипты переданы SCP, все SHA-256 сверены на серверах. С GitHub репозиторий на VPS не клонировался. Продуктовых прогонов KAgent/Telegram не запускал.
Первый парольный вход — один на каждый VPS через SSH_ASKPASS, без паролей в argv/history/логах/временных копиях. Далее — новый ключ оператора. Исходные `.env` и `.ppk` не копировались и не менялись, каталог700/файлы600 сохранены. У `.ppk` читались только заголовки и ровно Public-Lines, остановка до приватной части.
Host keys (Ed25519), зафиксированы при первом контакте, **TOFU**, не независимая внеполосная аттестация:
- RU: `SHA256:rYHjuLL2NjiHlImXA3ShWCeZBrdoGbGw0phQfxafJlE`
- DE: `SHA256:UeebWqUQoNx4FePQGV4P14iSpzZHClFDjndLm/9k3DI`
Постоянный known_hosts: `~/.ssh/kagent_vps_known_hosts`; новый порт привязан к тому же ранее проверенному host key. Создан `~/.ssh/kagent_vps_ed25519` (0600). Приватный ключ не передавался на VPS. В новом admin authorized_keys обоих — публичные ключи владельца и оператора. Полный вход оператора и sudo с паролем проверены. Сервер отдельно подтвердил приём публичного ключа владельца (SSH public-key probe); end-to-end подпись его приватным ключом не выполнялась, поскольку приватную часть PPK читать запрещено.
## Выполненные этапы
| Проверка | RU | DE |
| --- | --- | --- |
| ОС | Ubuntu24.04 LTS: проверка24.04.4, после обновления24.04.5 | то же |
| Часовой пояс | Europe/Moscow | Europe/Berlin |
| Этап1 | swap2GiB, текущая локаль сохранена, необязательные программы пропущены | существующий swap512MiB сохранён, локаль сохранена, программы пропущены |
| Этап2 | IPv4, выбранный SSH +443, временный22 затем удалён | то же |
| Этап3 | новый admin, два ключа, SSH root/password/kbdinteractive no, sudo с паролем | то же |
| Вторая независимая key-session | PASS до подтверждения удаления22 | PASS до подтверждения удаления22 |
| Этап6 | 3x-ui v3.10.2; панель и подписки loopback; WARP off | то же |
| Получение 3x-ui | GitHub, закреплённые SHA-256 main-helper | то же |
| Страница входа через SSH-L | HTTP200 | HTTP200 |
| Этап7 | initial snapshot +checksum PASS | initial snapshot +checksum PASS |
| Состояние после связки и финальной смены секретов | consistent SQLite checkpoint +ручные daily snapshots/checksum PASS | то же |
Автоматические расписания не включались; первичные снимки не перезаписывались. После API-ротации логина/пароля/секретного пути выполнен финальный согласованный checkpoint БД и новый ручной снимок, чтобы восстановление соответствовало актуальным env-файлам. Настройка выполнялась без изменения глобальной сети/брандмауэра этой машины. Этапы4,5,8 не выполнялись. Telegram-узел DE не ставился. WARP не установлен.
На Ubuntu с socket activation после обновления пакетов отсутствовал `/run/sshd`; standalone `sshd -t` завершился ошибкой до рестарта. Обратимо создан стандартный служебный каталог root:root755, затем неизменённый main-скрипт прошёл проверку и перенос SSH. Текущая административная сессия сохранялась до новой key-session и password-sudo проверки.
Перед контрольной перезагрузкой обнаружено: **RU ufw.service была masked**, хотя действующие kernel rules были активны. До перезагрузки сохранена исходная mask-ссылка в закрытом root-каталоге `codex-ufw-boot-before-*`, выполнены `systemctl unmask ufw.service` и `systemctl enable ufw.service`. DE служба уже была enabled. Это локализованное операционное исправление, не автоматическое снятие произвольных масок установщиком.
**Оба VPS последовательно перезагружены.** Boot ID изменился; новая независимая key-session admin, UFW, x-ui и fail2ban после загрузки PASS. Итоговые ядра: RU6.8.0-100-generic, DE6.8.0-147-generic; ниже приведены реальные post-boot снимки. Обе страницы панелей и оба egress-направления повторно проверены после загрузки.
Оба дефекта оформлены **отдельным [code PR20](https://git.ochenstarik.ru/ochenstarik/lightweight-server/pulls/20)**: head `e5b06793ec69dcbdf402b72b2e661453a7732569`; 14тестов PASS, pushCI9/task7381 и PRCI10/task7382 SUCCESS именно на этом SHA. Этап3 подготавливает runtime directory, этап2 требует автозапуск UFW до изменения правил и отказывает при маске без её автоматического снятия. Существующие тесты не изменены. Полный pushlog23954B без усечения проверен, оба новых теста и Job succeeded присутствуют; SHA256 `f74aee3ac1f4d4902145a6a42554073156ccf8bd40958c43d1c7f47fd8a25483`. На VPS выполнялись описанные операционные исправления; несмерженный код PR20 не разворачивался.
## Связка и сетевые проверки
Через аутентифицированный API 3x-ui по SSH-туннелям, с штатным CSRF-token/session cookie:
- DE: VLESS+Reality TCP на24443, inbound `codex-ru-de-link`, клиент `ru-link`; UFW разрешает этот порт **только с RU_IP**. В общий список глобально публикуемых managed ports он не добавлен, чтобы последующий рендер не расширил CIDR-правило до Anywhere.
- RU: VLESS+Reality443, один тестовый клиент `owner-test`, inbound `family-owner-test`; outbound `vpn-de` — в DE. Private IP и geoip:ru → direct; geosite:category-ru и домены ru/su/рф → direct; остальное по умолчанию → DE. Служебные API/stats и прежние правила сохранены.
- Native Xray из закреплённого 3x-ui:26.10.10 на обоих; временный клиент26.10.21 (SHA-256 проверен по pinned архиву). Также проверялся клиент26.10.10: наблюдение не является только различием версий.
- Начальный camouflage target RU Microsoft сменён на доступный Cloudflare; ключи/UUID не ротировались. DE target Microsoft. После изменения выполнен штатный reload Xray. Настройки в реальном config.json сверены с API-моделью. Подмены TLS/отключения проверок сертификата нет.
| Путь клиента | Зарубежный запрос api.ipify.org | Российский запрос к сервису Яндекса | Статус |
| --- | --- | --- | --- |
| Эта машина → публичный RU443, до исправления | TLS error35 | TLS error35 | исторический HOLD, устранён |
| Эта машина → публичный RU443, после исправления | фактический IP=DE_IP | фактический IP=RU_IP | PASS, без SSH |
| Эта машина → SSH-L к тому же RU443 → REALITY | фактический IP=DE_IP | фактический IP=RU_IP | PASS, это SSH-путь |
| Временный клиент на DE → публичный RU443 → DE | фактический IP=DE_IP | фактический IP=RU_IP | PASS, внешний контроль публичного входа |
Российская проверка: `https://ipv4-internet.yandex.net/api/v0/ip`; ответ сопоставлен с RU_IP в памяти, сам IP не журналировался. 2ip.ru вернул HTTP-ошибку и не использовался как положительное доказательство.
Первоначально REALITY получал маскирующий сертификат, хотя UUID, SNI, short ID, private/public pair и загруженные настройки совпадали. Через SSH и с DE ошибки не было. Перебор fingerprint/SNI/версии не устранил исходный отказ; это историческое наблюдение.
### Прямой HOLD устранён — дополнительное поручение владельца
На этой машине активен `xray-transparent.service`: `/usr/local/sbin/xray-transparent-up` перенаправляет TCP вне `bypass4` на локальный Xray12345. RU-IP отсутствовал в исключениях. `ip route get` показывал физический интерфейс, но не последующий NAT redirect. До исправления пять отправленных ClientHello не дали ни одного пакета в ограниченном по времени захвате входящего RU443; публичный RU-IP действительно назначен интерфейсу. Часы совпадают, min/maxClientVer/maxTimeDiff не заданы. Причина HOLD — локальный прозрачный перехват; внешний DPI/MITM не заявляется.
После аутентификации владельца штатным `pkexec` добавлен **только RU-IP** в действующий nft set `inet xray_transparent bypass4` и тот же список startup script. Перед изменением исходный root-owned script и JSON активного set сохранены в локальном `/root/codex-ru-direct-before-*` (0700). Проверены IPv4, права, сохранение прежних CIDR/redirect, идемпотентность формирования патча; `bash -n` и `nft -c` PASS. Запись атомарная, при её неудаче предусмотрен rollback добавленного элемента. Общий прокси не останавливался и не перезапускался; RU/DE конфигурации, ключи и тестовый клиент не менялись. FI/DK не затронуты.
Временный клиент Xray26.10.10 подключался к публичному RU443 обычным VLESS+Reality. Proxy env удалены у клиента и curl; SSH/dialerProxy в клиентском data path отсутствуют. SSH использован только для административного захвата на RU, без forwarding443. Оба запроса сначала PASS, затем отдельный свежий клиент повторил оба PASS: `api.ipify.org` видит DE_IP, API Яндекса RU_IP, curl exit0. На RU совпали **2 из2 префиксов ClientHello** — SHA-256 первых76байт отправленного/принятого handshake; fallback «received real certificate» отсутствует. Прямой вход из текущей сети принят. Персистентный script после записи прошёл `bash -n`; перезагрузка этой машины/перезапуск общего прокси ради проверки не выполнялись.
Тестовая ссылка только `/home/ochenstarik/srgfd/vpn_test_link.txt` (0600), не ротировалась. При переносе проверить фактическое устройство/сеть семьи; до переноса FI не переустанавливать.
## Доступ к панелям
Актуальные значения панели сохранены в защищённых `panel_ru.env` и `panel_de.env`; в report/Git/вывод команд их нет. Одноразовые параметры upstream-установщика, который внутри использует CLI-setting, аннулированы: логин/пароль и секретный path сменены штатным API без секретов в argv. Новый вход проверен. Проверка journal показала отсутствие актуального пароля и секретного path. Сам идентификатор пользователя 3x-ui записывает в штатный authentication audit; утверждать, что username нигде на VPS не встречается, нельзя. Аудит не отключался и не удалялся. Полный набор реквизитов сохраняется только в указанных env-файлах. Подробные готовые команды с приватными адресами/именем/SSH-портом и PuTTY-параметрами — `/home/ochenstarik/srgfd/PANEL_HOWTO.txt` (0600). SSH alias config — `/home/ochenstarik/srgfd/ssh_config` (0600).
Готовые команды из этой машины без публикации приватных адресов и значений из env:
```bash
# RU — оставить открытым в отдельном терминале:
PANEL_PORT="$(python3 -c 'from pathlib import Path; import shlex; print(dict((k,shlex.split(v)[0]) for k,v in (s.split("=",1) for s in Path("/home/ochenstarik/srgfd/panel_ru.env").read_text().splitlines()))["port"])')"
ssh -F /home/ochenstarik/srgfd/ssh_config -i ~/.ssh/kagent_vps_ed25519 -N -L "127.0.0.1:18053:127.0.0.1:$PANEL_PORT" ru-vpn
# DE — в другом терминале:
PANEL_PORT="$(python3 -c 'from pathlib import Path; import shlex; print(dict((k,shlex.split(v)[0]) for k,v in (s.split("=",1) for s in Path("/home/ochenstarik/srgfd/panel_de.env").read_text().splitlines()))["port"])')"
ssh -F /home/ochenstarik/srgfd/ssh_config -i ~/.ssh/kagent_vps_ed25519 -N -L "127.0.0.1:18054:127.0.0.1:$PANEL_PORT" de-vpn
```
PuTTY: SSH Auth → указать существующий `.ppk` владельца; Connection → SSH → Tunnels: source18053(RU)/18054(DE), destination `127.0.0.1:<PANEL_PORT из panel_*.env>`, Local, Add. Адрес браузера: `http://127.0.0.1:18053<path из panel_ru.env>` либо `http://127.0.0.1:18054<path из panel_de.env>`. В HOWTO есть команда открытия URL через чтение env без печати секретного пути. Обе страницы входа реально проверены, не только описаны.
## Снимки приёмки (значения доступов и panel port отредактированы)
### RU
`os_kernel`, exit=0
```text
Operating System: Ubuntu 24.04.5 LTS
Kernel: Linux 6.8.0-100-generic
```
`timezone`, exit=0
```text
Europe/Moscow
```
`swap`, exit=0
```text
NAME TYPE SIZE USED PRIO
/swapfile file 2G 0B -2
```
`sshd`, exit=0
```text
port <SSH_PORT>
permitrootlogin no
passwordauthentication no
kbdinteractiveauthentication no
```
`ufw`, exit=0
```text
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To Action From
-- ------ ----
<SSH_PORT>/tcp ALLOW IN Anywhere
443/tcp ALLOW IN Anywhere
```
`ss`, exit=0
```text
127.0.0.53%lo:53 users:(("systemd-resolve",pid=500,fd=15))
127.0.0.1:2096 users:(("x-ui",pid=746,fd=19))
127.0.0.54:53 users:(("systemd-resolve",pid=500,fd=17))
127.0.0.1:<PANEL_PORT> users:(("x-ui",pid=746,fd=10))
0.0.0.0:<SSH_PORT> users:(("sshd",pid=754,fd=3),("systemd",pid=1,fd=90))
127.0.0.1:11111 users:(("xray-linux-amd6",pid=906,fd=7))
127.0.0.1:62789 users:(("xray-linux-amd6",pid=906,fd=3))
[::]:<SSH_PORT> users:(("sshd",pid=754,fd=4),("systemd",pid=1,fd=91))
*:443 users:(("xray-linux-amd6",pid=906,fd=6))
```
`nft_tables`, exit=0
```text
table ip filter
table ip6 filter
```
`services`, exit=0
```text
active
active
```
Panel DB (без path/credentials):
```json
{
"webListen": "127.0.0.1",
"webPort": "<PANEL_PORT>",
"subListen": "127.0.0.1",
"subPort": "2096",
"subEnable": "true"
}
```
### DE
`os_kernel`, exit=0
```text
Operating System: Ubuntu 24.04.5 LTS
Kernel: Linux 6.8.0-147-generic
```
`timezone`, exit=0
```text
Europe/Berlin
```
`swap`, exit=0
```text
NAME TYPE SIZE USED PRIO
/swapfile file 512M 0B -2
```
`sshd`, exit=0
```text
port <SSH_PORT>
permitrootlogin no
passwordauthentication no
kbdinteractiveauthentication no
```
`ufw`, exit=0
```text
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To Action From
-- ------ ----
<SSH_PORT>/tcp ALLOW IN Anywhere
443/tcp ALLOW IN Anywhere
24443/tcp ALLOW IN <RU_IP> # RU-DE Reality upstream
```
`ss`, exit=0
```text
127.0.0.1:62789 users:(("xray-linux-amd6",pid=1023,fd=3))
0.0.0.0:<SSH_PORT> users:(("sshd",pid=792,fd=3),("systemd",pid=1,fd=87))
127.0.0.1:<PANEL_PORT> users:(("x-ui",pid=788,fd=10))
127.0.0.1:2096 users:(("x-ui",pid=788,fd=18))
127.0.0.54:53 users:(("systemd-resolve",pid=746,fd=17))
127.0.0.1:11111 users:(("xray-linux-amd6",pid=1023,fd=7))
127.0.0.53%lo:53 users:(("systemd-resolve",pid=746,fd=15))
[::]:<SSH_PORT> users:(("sshd",pid=792,fd=4),("systemd",pid=1,fd=88))
*:24443 users:(("xray-linux-amd6",pid=1023,fd=6))
```
`nft_tables`, exit=0
```text
table ip filter
table ip6 filter
```
`services`, exit=0
```text
active
active
```
Panel DB (без path/credentials):
```json
{
"webListen": "127.0.0.1",
"webPort": "<PANEL_PORT>",
"subListen": "127.0.0.1",
"subPort": "2096",
"subEnable": "true"
}
```
## Границы результата и следующая приёмка
- Прямой публичный вход из текущей сети и оба egress-направления PASS после локального исключения RU-IP. Проверка другой сети/устройства семьи и полный вход владельца своим приватным PuTTY-ключом остаются отдельными действиями.
- Публичная панель, публичные подписки, Telegram-ноды, AI-агенты и WARP не включались. Новых дополнительных мониторов/автоматизаций нет; прежний тик30 не возобновлялся.
- Для послеустановочного восстановления оставлены согласованные SQLite checkpoints и локальные initial/daily snapshots с проверенными checksum. Локальная копия не заменяет отдельное внешнее хранилище; offsite не включался.
- Этот PR содержит только данный обезличенный отчёт. Code PR20 отдельный, автор не вливает. Прежний release gate для новых PR/документов без CI не обходится.