# RDP FIDO для Linux

С версии 1.20 обе половины RDP FIDO работают и на Linux:

- **шлюз `rdpfido-gate`** — на целевой машине с Linux (Ubuntu 22.04/24.04,
  Astra Linux, РЕД ОС 7.3, ALT Linux p10). Держит порт RDP (xrdp) закрытым и
  открывает его только адресу, предъявившему зарегистрированный FIDO-ключ;
- **клиент `rdpfido` / `rdpfido-gui`** — на рабочей машине с Linux.
  Подключается к шлюзу на Windows или Linux, подтверждает вход FIDO-ключом и
  запускает FreeRDP;
- **FIDO Stream** — собственный поток с малой задержкой (для игр и
  графики) работает в обе стороны: Linux-машина может быть и хостом
  (`rdpfido-stream-host` в пакете шлюза), и смотрящей стороной
  (`rdpfido-viewer` в пакете клиента). См. раздел [FIDO Stream](#fido-stream).

Протокол общий: Windows-клиент подключается к Linux-шлюзу, Linux-клиент — к
Windows-шлюзу, без переходников и настроек.

## Шлюз на Linux

### Установка

```sh
# Ubuntu / Debian / Astra
sudo apt install ./rdpfido-gate_1.22.0_amd64.deb
# РЕД ОС
sudo dnf install ./rdpfido-gate-1.22.0-1.x86_64.rpm
# ALT Linux (p10 и новее; отдельная сборка RPM под apt-rpm)
sudo apt-get install ./rdpfido-gate-1.22.0-alt1.x86_64.rpm
# любой другой дистрибутив
tar xzf rdpfido-1.22.0-linux-x86_64.tar.gz && cd rdpfido-1.22.0-linux-x86_64
sudo ./install.sh gate
```

Затем одна команда:

```sh
sudo rdpfido-gate setup            # --install-xrdp, если xrdp ещё нет
```

Она создаёт сертификат шлюза (EC P-256, самоподписанный — клиент
запоминает его отпечаток), проверяет xrdp, ставит службу `rdpfido-gate` и
«стража загрузки» `rdpfido-guard`, открывает порт шлюза 7440 в
ufw/firewalld (если они включены) и печатает **одноразовый код регистрации
ключа** (действует 15 минут) и **отпечаток сертификата** шлюза.

Порт 3389 в ufw/firewalld шлюз открывает сам и только тогда, когда его
собственные правила уже закрывают этот порт: иначе ufw отсёк бы и тех, кого
шлюз впустил. Всё, что шлюз открыл в ufw/firewalld, записано в
`/var/lib/rdpfido-gate/hostfw.json`, и `uninstall` возвращает ровно это:
если ufw раньше закрывал 3389, после удаления шлюза он снова закрыт.
Разрешения, сделанные администратором до шлюза, не трогаются.

Как и на Windows, **порт RDP закрывается не при установке, а в момент
регистрации первого ключа**: пока шлюзу нечем вас впустить, он ничего не
отбирает.

### Как закрыт порт

Шлюз держит свою таблицу nftables `inet rdpfido` (IPv4 и IPv6), а если
nftables нет — свои цепочки iptables/ip6tables `RDPFIDO`, первым правилом в
`INPUT`:

```
tcp dport 3389 ct state established,related accept   # идущий сеанс не рвётся
jump grants                                          # адреса, прошедшие FIDO
tcp dport 3389 drop                                  # всем остальным
```

В Linux запрет в любой цепочке окончателен, поэтому никакое «разрешающее»
правило другой программы не откроет порт в обход шлюза. Каждые 30 секунд шлюз
проверяет, что его правила на месте (и что прыжок iptables стоит первым), и
восстанавливает их. Правила iptables меняются атомарно (`iptables-restore`),
так что порт не бывает открыт даже на мгновение перестройки. Посмотреть:
`sudo nft list table inet rdpfido` или `sudo iptables -S RDPFIDO`.

**После перезагрузки.** Правила ядра перезагрузку не переживают, поэтому
`rdpfido-guard.service` ставит их ещё до сети и до xrdp. xrdp запускается
только после стража (`Requires=`): если страж не смог закрыть порт, xrdp не
стартует вовсе — лучше «RDP недоступен», чем «RDP открыт всем».

Если IPv6 включён, а `ip6tables` нет (редкая система без nftables), шлюз
не может закрыть порт по IPv6 и пишет об этом в журнал; поставьте
`ip6tables` или отключите IPv6.

### Команды

```
sudo rdpfido-gate setup            установка (см. выше)
sudo rdpfido-gate code             новый код регистрации ключа
sudo rdpfido-gate status           служба, ключи, правила, активные гранты
sudo rdpfido-gate keys             список ключей
sudo rdpfido-gate remove-key N     удалить ключ (действует сразу)
sudo rdpfido-gate unlock           аварийно: перестать закрывать порт RDP
sudo rdpfido-gate arm              вернуть защиту после unlock
sudo rdpfido-gate geo ...          белый список стран (как на Windows)
rdpfido-gate selftest              самопроверка криптографии и разбора
sudo rdpfido-gate uninstall        убрать службу и правила
```

Журнал: `journalctl -u rdpfido-gate` и `/var/lib/rdpfido-gate/gate.log`.

### Подключение с Windows

В Windows-клиенте всё как обычно: «Добавить» → адрес Linux-машины →
«Зарегистрировать ключ» → код из `rdpfido-gate code`. Клиент сам узнаёт, что
на той стороне xrdp, и подключает mstsc без NLA (xrdp её не поддерживает;
шифрование TLS остаётся). При первом подключении mstsc один раз спросит про
сертификат xrdp — отметьте «Больше не спрашивать».

Ключ Windows Hello подходит для входа с этого же Windows-компьютера. Для
входа с Linux нужен переносной ключ (USB/NFC) — его можно зарегистрировать на
том же шлюзе рядом с Hello.

## Клиент на Linux

### Установка

```sh
sudo apt install ./rdpfido-client_1.22.0_amd64.deb          # Ubuntu, Debian, Astra
sudo dnf install ./rdpfido-client-1.22.0-1.x86_64.rpm       # РЕД ОС
sudo apt-get install ./rdpfido-client-1.22.0-alt1.x86_64.rpm  # ALT Linux
```

Нужны FreeRDP (`freerdp2-x11`/`freerdp3-x11`, в РЕД ОС `freerdp`, в ALT
`xfreerdp`) и libfido2 (есть в Ubuntu, РЕД ОС; для Astra Орёл и ALT пакет
содержит свою копию).
Пакет ставит правило udev, чтобы пользователь за компьютером мог обращаться к
FIDO-ключу без root.

### Окно

`rdpfido-gui` (в меню — «RDP FIDO»): список сессий, «Добавить», «Изменить»,
«Зарегистрировать ключ…», «Подключиться» (двойной щелчок). PIN ключа
спрашивается, когда шлюз требует подтверждения PIN.

### Командная строка

```sh
rdpfido add --name office --host 10.0.0.5 --user ivan --password   # пароль в связку ключей
rdpfido enroll office 6NGQ-MEMG-34M4 --fingerprint 273CE04D…3E67
                                             # код и отпечаток с целевой машины; коснитесь ключа
rdpfido connect office                       # ключ → порт открыт → FreeRDP
rdpfido hello office                         # что сообщает шлюз (версия, платформа, защита)
rdpfido keys                                 # подключённые FIDO-ключи
rdpfido list
```

`--fingerprint` (в окне — поле «Отпечаток шлюза») — строка `Thumbprint`,
которую печатают `rdpfido-gate setup` и `rdpfido-gate code`. С ней клиент
сверяет сертификат шлюза уже при регистрации, и код не уйдёт подставному
серверу. Без неё сертификат запоминается при первом обращении (как у SSH),
а дальше сверяется при каждом подключении.

Пароли хранятся в связке ключей рабочего стола (GNOME Keyring / KWallet через
Secret Service). Если её нет, пароль не сохраняется вовсе и спрашивается при
подключении. FreeRDP получает пароль через stdin, а не в командной строке —
его не видно в `ps`.

Сессии — в `~/.config/rdpfido/sessions.json` (права 0600), журнал —
`~/.config/rdpfido/client.log`, вывод FreeRDP — `freerdp-<id>.log` там же.

## FIDO Stream

Поток вместо RDP: картинка кодируется в H.264 и идёт по UDP со своим
шифрованием, коррекцией потерь и управлением скоростью — задержка ниже, чем у
RDP, и работает звук, геймпады, полноэкранный режим. Открывается тем же
FIDO-ключом: после проверки ключа шлюз запускает агента потока и открывает
UDP-порт (7441, для второго потока 7442 и т. д.) только адресу клиента.

### Linux как хост

Ничего настраивать не нужно: агент потока ставится вместе со шлюзом и
показывает **графический сеанс X11** той машины:

- консольный сеанс (кто вошёл за монитором) имеет приоритет;
- если его нет — сеанс xrdp/VNC; конкретный дисплей можно закрепить в
  `/var/lib/rdpfido-gate/gate.json`: `"streamDisplay": ":10"`;
- **Wayland не поддерживается** (захват только X11): на экране входа выберите
  сеанс «Xorg» / «GNOME на Xorg». При Wayland клиент получит понятную ошибку.

Агента запускает шлюз отдельной службой systemd. Он стартует с правами root
ровно на время, чтобы прочитать и удалить файл с ключами сеанса, найти
рабочий стол и открыть `/dev/uinput` для геймпадов, после чего окончательно
становится пользователем этого рабочего стола (без привилегий,
`NoNewPrivs`). Звук берётся из PulseAudio/PipeWire сеанса, ввод — через
XTest, геймпады — виртуальные Xbox 360 через uinput. Кодирование —
программное (openh264), 1080p60 укладывается в 1–2 ядра.

Порты потока защищены так же, как RDP: пока нет гранта, пакеты на
7441–7444 отбрасываются правилом шлюза; в ufw/firewalld шлюз открывает этот
диапазон сам (и убирает при `uninstall`).

### Linux как смотрящая сторона

```sh
rdpfido stream office                 # ключ -> поток; окно rdpfido-viewer
rdpfido stream office --fullscreen --bias 75
```

В окне `rdpfido-gui` — кнопка «Поток (FIDO Stream)». Сочетания клавиш в окне
потока — как в Windows-версии: Ctrl+Alt+Shift+Q — отключиться,
Ctrl+Alt+Enter — полный экран, Ctrl+Alt+Home — захватить/отпустить мышь,
Ctrl+Alt+Shift+D — Ctrl+Alt+Del на хосте. Декодирование — FFmpeg (своя
сборка только с декодерами, поставляется в пакете клиента), окно и звук —
SDL2 (`libsdl2-2.0-0` из дистрибутива).

### Совместимость

Протокол потока одинаковый на обеих ОС: Windows-клиент открывает поток на
Linux-хост, Linux-клиент — на Windows-хост. Linux-хост кодирует только H.264
8 бит 4:2:0 (без HEVC/AV1/HDR); Linux-вьювер декодирует H.264 и HEVC.

### Файлы (1.21)

Файлы ходят через поток в обе стороны и между ОС в любом сочетании, в фоне
и не мешая картинке (подробности — README, раздел «Файлы между
компьютерами»):

- **с Linux-клиента на хост** — перетащите файлы или папки в окно
  `rdpfido-viewer`; они окажутся на хосте в `Загрузки\RDP FIDO`
  (Windows) или `~/Загрузки/RDP FIDO` (Linux, по `XDG_DOWNLOAD_DIR`, иначе
  `~/Downloads/RDP FIDO`);
- **с Linux-хоста** — положите файл в `~/Загрузки/RDP FIDO/Outbox` (папка
  создаётся при первом потоке): через пару секунд он уедет к смотрящей
  стороне и переедет в `Outbox/Sent`;
- имена в UTF-8, время изменения сохраняется, бит исполнения передаётся
  флагом и ставится на принимающей Linux-стороне;
- ход передачи — в заголовке окна `rdpfido-viewer`; запретить файлы на
  хосте — `"streamFiles": false` в `/var/lib/rdpfido-gate/gate.json`, на
  вьювере — `rdpfido-viewer ... --no-files`.

## Облако (1.22): шлюз за NAT без пробросов

Шлюз, у которого нет белого адреса, можно привязать к хабу RDP FIDO Cloud:
`sudo rdpfido-gate cloud link ХАБ:443 КОД` (код печатает `rdpfido-cloud code`
на хабе), `cloud status`, `cloud unlink`. Привязанный шлюз держит исходящее
соединение к хабу, клиенты приходят на адрес `<id>.g.<домен>:443`, RDP идёт
по билету туннеля (порт 3389 снаружи закрыт), поток — напрямую или через
реле хаба. Непривязанный шлюз не делает ни одного исходящего соединения.
Хаб `rdpfido-cloud` ставится на свой VPS (архив
`rdpfido-cloud-1.22.0-linux-x86_64.tar.gz` рядом с пакетами, описание
`README-cloud.ru.md`); облако Imobile — закрытый сервис: шлюз к нему
подключает Imobile по договорённости, для знакомства открыт демо-доступ.

## Срок работы без обновления

Каждая версия работает **один год с даты выпуска** (у 1.22.0 — до
28.09.2027), затем требует обновления:

- **клиент** перестаёт подключаться и пишет, как обновиться:
  `sudo apt install --only-upgrade rdpfido-client` (РЕД ОС:
  `sudo dnf upgrade rdpfido-client`);
- **шлюз** продолжает проверять ключ, но **не открывает доступ** (ни RDP, ни
  поток), пока его не обновят: `sudo apt install --only-upgrade rdpfido-gate`
  (РЕД ОС: `sudo dnf upgrade rdpfido-gate`). Регистрация ключей и аварийный
  `sudo rdpfido-gate unlock` работают по-прежнему. Срок видно в
  `rdpfido-gate status`, в журнале шлюза и в ответе `hello`.

За 30 дней до срока клиент и шлюз предупреждают. Перевод системных часов
назад срок не продлевает (последнее увиденное время хранится в
`support.json`). Шлюз на Windows, в отличие от Linux, Windows-клиент
обновляет сам при первом же подключении.

## Сборка

Одна сборка работает на всех перечисленных системах: собирается на самой
старой glibc (Astra Орёл 2.12, glibc 2.24) компилятором GCC ≥ 10, OpenSSL 3 и
libstdc++ — статически.

```sh
src/linux/packaging/build-media.sh openh264-2.4.1.tar.gz ffmpeg-6.1.2.tar.xz   # один раз
make -C src/linux CXX=/usr/toolchain/gcc-11.1.0/bin/g++ OPENSSL=/opt/rdpfido-deps
src/linux/packaging/build-packages.sh      # .deb и .tar.gz в build/linux/dist
```

RPM для РЕД ОС собирается на РЕД ОС, для ALT — на ALT с ключом
`--define "rdpfido_alt 1"` (см. заголовок `rdpfido.spec`): у apt-rpm нет
булевых зависимостей и другие имена пакетов.

Тестовые инструменты (в пакеты не входят): `rdpfido-testkit` (программный
ключ, нагрузка, проверка порта, атаки), `rdpfido-virtkey` (виртуальный
USB-ключ через UHID для проверки пути libfido2), `rdpfido-rdpbench` (задержка
«нажатие → картинка»), `rdpfido-streamtest` (транспорт FIDO Stream). Отчёт об
испытаниях — [LINUX-TEST-REPORT.md](LINUX-TEST-REPORT.md).

## Чего пока нет

- FIDO Stream на Linux-хосте: аппаратное кодирование (VAAPI/NVENC), HEVC/AV1,
  HDR, захват Wayland-сеансов, отдача вибрации на геймпад — следующие шаги.
  На Linux-вьювере — аппаратное декодирование.
- Обновление шлюза «с клиента» (`update/push`) на Linux отключено: шлюз
  обновляется пакетным менеджером.
