# 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.25.2_amd64.deb
# РЕД ОС
sudo dnf install ./rdpfido-gate-1.25.2-1.x86_64.rpm
# ALT Linux (p10 и новее; отдельная сборка RPM под apt-rpm)
sudo apt-get install ./rdpfido-gate-1.25.2-alt1.x86_64.rpm
# любой другой дистрибутив
tar xzf rdpfido-1.25.2-linux-x86_64.tar.gz && cd rdpfido-1.25.2-linux-x86_64
sudo ./install.sh gate
```

Пакет шлюза ничего лишнего за собой не тянет (с 1.25.2): на сервере без
рабочего стола ставится он один. xrdp и библиотеки X указаны только как
предлагаемые (пакет для РЕД ОС рекомендует xrdp там, где установлен
X-сервер): для удалённого рабочего стола xrdp ставит `setup --install-xrdp`,
а FIDO Stream нужны `libx11-6 libxext6 libxdamage1 libxfixes3 libxtst6
libpulse0` — на машине с рабочим столом они уже есть.

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

```sh
sudo rdpfido-gate setup            # для RDP: --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 --keep      постоянный код для демо-стенда (многоразовый, до `code --clear`;
                                   ключи по нему живут --key-ttl, по умолчанию 24 ч; роль --role)
sudo rdpfido-gate code             новый код регистрации ключа
sudo rdpfido-gate status           служба, ключи, правила, активные гранты
sudo rdpfido-gate keys             список ключей
sudo rdpfido-gate remove-key N     удалить ключ (действует сразу)
sudo rdpfido-gate ports add 22     ещё порты за тем же ключом: SSH, веб-консоль (см. ниже)
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`.

### SSH и другие порты за тем же ключом (1.24)

Шлюз может закрыть не только RDP, но и любые другие TCP-порты машины — SSH,
веб-консоль Proxmox, панель администратора. Они закрыты для всех, пока ключ
(или код, если шлюз в режиме кодов) не открыл доступ; тот же грант открывает
их вместе с портом RDP и только для адреса, с которого пришло подтверждение.

```
sudo rdpfido-gate ports add 22          закрыть SSH; несколько портов — через пробел
sudo rdpfido-gate ports                 что закрыто сейчас
sudo rdpfido-gate ports remove 22       вернуть порт в обычный режим
sudo rdpfido-gate ports clear           убрать все дополнительные порты
```

С клиента:

```sh
rdpfido open office                     # ключ → порты открыты на время окна (по умолчанию 90 с)
rdpfido ssh office                      # то же и сразу ssh на адрес сессии
rdpfido ssh office --user root -- -p 2222 -L 8006:127.0.0.1:8006
rdpfido console office                  # то же в окне консоли SSH: терминал и панель
```

Консоль SSH (`rdpfido console`, программа `rdpfido-console` из пакета клиента) —
окно с терминалом и панелью: сохранённые пароли вводятся щелчком или сами по
запросу, сохранённые команды, помощник ИИ (Claude или сервер, совместимый с
OpenAI) с промптами сессии и историей разговоров. Окну нужен браузер семейства
Chromium (иначе страница откроется во вкладке браузера по умолчанию), паролям —
связка ключей рабочего стола. Порт, файл ключа и параметры ssh сессии:
`rdpfido add|edit --ssh-port N --ssh-key ФАЙЛ --ssh-opt АРГ`.

Всё после `--` передаётся `ssh` как есть. Имя пользователя берётся из сессии
(если оно не в виде `ДОМЕН\имя`), `--user` задаёт другое.

Что важно знать:

- Окно ограничивает только начало соединения. Сессия, установленная в окне,
  живёт дальше и после его конца; новая потребует ключ снова.
- Уже открытые соединения шлюз не рвёт, поэтому `ports add 22` можно
  выполнять из сессии SSH. Прежде чем её закрыть, проверьте из другого
  терминала, что ключ пускает. Если нет — `ports remove 22` из той же
  сессии, либо `rdpfido-gate unlock` с консоли машины.
- Соединение считается открытым, если шлюз его видел: отслеживание начинается
  с `setup`. Сессия, из которой вы выполняли `setup`, под защиту попадает.
  Соединение, открытое раньше и молчавшее с тех пор до регистрации первого
  ключа, может зависнуть — его достаточно открыть заново, уже по ключу.
  (До 1.25.2 отслеживание начиналось только с первого ключа, и зависнуть
  могла и сама сессия, из которой ставили шлюз.)
- Пока на шлюзе нет ни одного ключа, дополнительные порты, как и RDP,
  остаются открытыми.
- Порт самого шлюза (7440) добавить нельзя. Не больше 16 портов.
- Через облако (1.22) идёт только RDP: к дополнительным портам нужен прямой
  адрес шлюза.
- Шлюз на Windows этот список пока не учитывает.

#### Сервер без рабочего стола: только SSH

Шлюзу не нужны ни xrdp, ни рабочий стол. На сервере, где надо закрыть только
SSH (или другие порты), всё выглядит так:

```sh
sudo apt install ./rdpfido-gate_1.25.2_amd64.deb   # один пакет, без xrdp и X
sudo rdpfido-gate setup                            # код регистрации и отпечаток
sudo rdpfido-gate ports add 22
```

На шаге «RDP server» `setup` сообщит, что сервера RDP нет, — для такой машины
это нормально. Пока не зарегистрирован первый ключ, порт 22 открыт как
раньше; с первым ключом он закрывается для всех, кроме адреса, получившего
доступ по ключу.

- Порт RDP (3389) шлюз закрывает и здесь, хотя за ним никто не слушает; если
  включён ufw или firewalld, шлюз добавляет туда разрешения для 3389 и для
  портов потока (UDP 7441–7448). Доступ они не открывают: впереди стоят
  правила самого шлюза. Список добавленного — в
  `/var/lib/rdpfido-gate/hostfw.json`, удаление пакета его убирает.
- Если ufw включили уже после `setup`, он закроет порт самого шлюза (7440),
  и получить доступ по ключу будет нельзя. Разрешите порт сами
  (`sudo ufw allow 7440/tcp`) или выполните `sudo rdpfido-gate setup` ещё раз.
- Порты, опубликованные из контейнеров Docker (`-p`), идут мимо цепочки
  `INPUT` и шлюзом не закрываются: он охраняет порты, которые слушает сама
  машина.

Проверено на Debian 12 без xrdp (nftables, с ufw и без него): установка,
регистрация ключа, вход по `ssh` в окне доступа, сессия дольше окна,
перезагрузка, `unlock`/`arm`, `ports remove`, удаление пакета.

### Вход по одноразовому коду вместо ключа (1.23)

Шлюз пускает одним способом: ключом FIDO (по умолчанию) или, для тех, у кого
ключей нет, 6-значным кодом из приложения-аутентификатора (Google
Authenticator, Microsoft Authenticator, Aegis, 1Password — любое с TOTP).

```sh
sudo rdpfido-gate setup --auth totp     # при установке
sudo rdpfido-gate auth                  # какой режим сейчас
sudo rdpfido-gate auth totp             # сменить (шлюз перезапускается); auth fido — обратно
sudo rdpfido-gate totp add "Анна"       # учётка прямо на шлюзе, QR-код в терминале
sudo rdpfido-gate totp test 3 123456    # проверить код из приложения
```

Регистрация — как с ключом: `rdpfido-gate code`, затем `rdpfido enroll`
показывает QR-код в терминале (графический клиент — в окне), первый код из
приложения подтверждает его. При подключении клиент спрашивает код; один код
принимается один раз. Код открывает только RDP — xrdp или Windows всё равно
спросят свой пароль; FIDO Stream в этом режиме выключен (он пускает на рабочий
стол без пароля). Коды спрашивают клиенты 1.23 и новее.

### Подключение с 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.25.2_amd64.deb          # Ubuntu, Debian, Astra
sudo dnf install ./rdpfido-client-1.25.2-1.x86_64.rpm       # РЕД ОС
sudo apt-get install ./rdpfido-client-1.25.2-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 open office                          # только ключ: порты открыты, ничего не запускается
rdpfido ssh office                           # ключ → ssh (шлюз 1.24 с `ports add 22`)
rdpfido console office                       # ключ → консоль SSH: терминал, пароли, команды, помощник
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` там же.

### Защита входа (1.25.1)

Сам клиент можно закрыть паролем или ключом FIDO2 — как клиент для Windows
с версии 1.25.0. Вопрос задают все три программы: `rdpfido` перед любой
командой, которая работает с сессиями, `rdpfido-gui` до появления окна,
`rdpfido-console` при запуске.

```sh
rdpfido lock                 # что стоит сейчас
rdpfido lock password        # задать мастер-пароль или сменить его
rdpfido lock key             # привязать ключ FIDO2 (вместо пароля)
rdpfido lock off             # снять защиту
rdpfido lock reset           # забытый пароль, потерянный ключ
```

В окне то же самое — кнопка «Защита входа».

- **Пароль — это мастер-пароль.** Сохранённые пароли сессий, пароли консоли
  SSH и ключ API помощника перед записью в связку ключей шифруются ключом
  AES-256, который существует на диске только обёрнутым этим паролем
  (PBKDF2-SHA256, 600 000 итераций; файл `~/.config/rdpfido/applock.json`,
  права 0600). Связка ключей отдаёт запись любой программе пользователя
  (`secret-tool lookup session …`) — с мастер-паролем там лежит шифртекст
  `rdpfido-mk1:…`, который без пароля ничего не даёт. Удаление файла замка
  пароли не освобождает, а теряет.
- **Ключ FIDO2 охраняет только вход.** Нужен ключ с PIN или с отпечатком:
  при привязке и при каждом входе ключ проверяет владельца, так что ключ,
  оставленный в порту, программу не откроет. Сохранённые пароли ключ не
  шифрует — они лежат в связке ключей как без защиты. Это защита от
  человека, который сел за ваш компьютер, а не от программы под вашей
  учётной записью.
- **Что остаётся открытым.** Список сессий (`sessions.json`: адреса, имена
  пользователей) защищён только правами файла и не шифруется, как и раньше.
  Защита входа закрывает программу и сохранённые пароли, а не этот файл.
- **Неверные пароли.** Пять попыток подряд ничего не стоят, дальше каждая
  следующая ждёт: 30 секунд, минуту, две — до 15 минут.
- **Сменить пароль, перейти на ключ, снять защиту** — теми же командами или
  в окне: сначала спросят текущий пароль или ключ. Смена пароля ничего не
  перешифровывает; установка и снятие пароля перешифровывают записи в связке.
- **Забытый пароль и потерянный ключ не восстанавливаются.**
  `rdpfido lock reset` снимает защиту вместе с тем, что она охраняла:
  сохранёнными паролями сессий и консоли, ключом API помощника и историей
  разговоров с ним. Сессии, команды и настройки остаются.
- **Без терминала** (скрипт, cron) пароль входа читается первой строкой
  стандартного ввода: `printf '%s\n' "$PW" | rdpfido open office`. С
  `--password-stdin` пароль входа идёт первой строкой, пароль RDP — второй.
- **`rdpfido console`** спрашивает один раз: клиент передаёт консоли ключ
  шифрования через унаследованный дескриптор, не в командной строке.
  Запущенная отдельно `rdpfido-console` задаёт вопрос сама.
- **Нет связки ключей** (сеанс по SSH): пароль задать можно, вход он
  закрывает; пароли, сохранённые в связке раньше, шифруются при первом входе
  на рабочем столе.
- Замки Windows и Linux раздельные: файл замка и зашифрованные записи между
  ними не переносятся. Пароли, зашифрованные мастер-паролем в 1.25.1, в
  клиенте 1.24 не откроются.

## 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.25.2 — до
06.10.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 отключено: шлюз
  обновляется пакетным менеджером.
