# RDP FIDO для Linux

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

- **шлюз `rdpfido-gate`** — на целевой машине с Linux (Ubuntu 22.04/24.04,
  Astra Linux, РЕД ОС 7.3). Держит порт 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.20.0_amd64.deb
# РЕД ОС
sudo dnf install ./rdpfido-gate-1.20.0-1.x86_64.rpm
# любой другой дистрибутив
tar xzf rdpfido-1.20.0-linux-x86_64.tar.gz && cd rdpfido-1.20.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.20.0_amd64.deb      # Ubuntu, Debian, Astra
sudo dnf install ./rdpfido-client-1.20.0-1.x86_64.rpm   # РЕД ОС
```

Нужны FreeRDP (`freerdp2-x11`/`freerdp3-x11`, в РЕД ОС `freerdp`) и
libfido2 (есть в Ubuntu, РЕД ОС; для Astra Орёл пакет содержит свою копию).
Пакет ставит правило 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.20.0 — до
27.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
```

Тестовые инструменты (в пакеты не входят): `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 отключено: шлюз
  обновляется пакетным менеджером.
