Зачем enterprise блокирует USB

Начнём не с технологий, а с причины, по которой эта статья вообще нужна.

В физическом мире USB-порт — дыра в периметре. Воткнул флешку — и у тебя либо данные утекли, либо малварь приехала, либо оба сценария одновременно. Stuxnet, обнаруженный в 2010-м (но активный с ~2007), пробил air gap завода в Натанзе — первоначально через завербованного агента, а дальше распространялся через USB-накопители. FIN7 в 2021-22 годах рассылали по почте подарочные коробки (от имени Amazon и HHS) с «флешками» — на деле это были Arduino ATMEGA32U4, эмулирующие клавиатуру и набивающие PowerShell-команды быстрее любого оператора. Конечная цель — Cobalt Strike, затем ransomware (BlackMatter, REvil). BadUSB, продемонстрированный на Black Hat 2014, показал, что любое USB-устройство может прикинуться клавиатурой и вводить команды — и VID/PID фильтрация тут не спасёт, потому что идентификаторы прошиты в firmware и перешиваются за минуту.

VDI в теории решает часть проблем: данные живут на сервере, а не на эндпоинте. Но USB-редиректор эту изоляцию пробивает — если разрешить проброс mass storage, пользователь утащит файлы на флешку ровно так же, как с физического ПК. А если не разрешить — придут люди со смарт-картами и принтерами, и скажут «нам нужно работать».

Вот тут начинается инженерный компромисс, который не решается одним галочкой в политике.


USB-редиректор SPICE: как это устроено

usbredir — протокол, а не канал

Важно не путать: в SPICE есть канал SPICE_CHANNEL_USBREDIR (тип 9 в протоколе), но внутри него бежит отдельный, независимый протокол — usbredir, разработанный Red Hat и живущий в своём репозитории на gitlab.freedesktop.org/spice/usbredir. Текущая версия пакета — 0.15.0 (январь 2026), протокол — v0.7. Лицензия LGPLv2.1.

usbredir спроектирован так, чтобы не зависеть от SPICE — его можно использовать с VNC, по TCP-сокету, да хоть через SSH-туннель. SPICE просто предоставляет транспорт.

Что происходит, когда вы пробрасываете USB

[Физическое USB-устройство на клиенте]
        |
   libusb (userspace доступ к USB)
        |
   usbredirhost (управляет устройством, отдаёт трансферы)
        |
   usbredirparser (сериализация в wire-формат)
        |
   spice-gtk → SpiceUsbredirChannel
        |
   SPICE канал тип 9 (TCP, опционально TLS)
        |  данные обёрнуты в SPICE_MSG_SPICEVMC_DATA
        |
   QEMU: chardev spicevmc + usb-redir device
        |
   Виртуальный USB-контроллер (EHCI/XHCI)
        |
   Гостевая ОС видит обычное USB-устройство

На клиенте всем рулит SpiceUsbDeviceManager (файл usb-device-manager.c в spice-gtk). Он следит за подключениями через libusb hotplug callbacks, фильтрует устройства и привязывает каждое к свободному SpiceUsbredirChannel. Один канал — одно устройство. Хотите три USB одновременно — конфигурируйте три пары chardev + usb-redir в QEMU.

На QEMU-стороне каждое устройство — это связка:

-chardev spicevmc,name=usbredir,id=usbredirchardev1
-device usb-redir,chardev=usbredirchardev1,id=usbredirdev1

Wire-формат

Протокол передаёт не сырые USB-сигналы, а структурированные USB-трансферы: control, bulk, isochronous, interrupt. Всё little-endian, packed без паддинга. Каждый пакет начинается с заголовка:

// legacy-формат (без cap_64bits_ids):
struct usb_redir_header {
    uint32_t type;     // тип пакета
    uint32_t length;   // длина данных после заголовка
    uint32_t id;       // ID запроса
};

// современный формат (cap_64bits_ids, бит 5):
struct usb_redir_header_64 {
    uint64_t id;       // ID запроса — теперь первое поле
    uint32_t type;
    uint32_t length;
};

Порядок полей в 64-битном варианте другой — id переехал в начало. Совместимость обеспечивается через capabilities: если обе стороны заявили бит 5, используется новый формат. Определения обоих вариантов живут в usbredirproto.h и usbredirproto-compat.h.

Рукопожатие: обе стороны сразу шлют usb_redir_hello (тип 0) с версией и битовой маской capabilities. Потом хост отправляет информацию об эндпоинтах, интерфейсах и подключает устройство. Фича работает, только если обе стороны её заявили.

Детали capabilities (из usbredirproto.h):

Бит

Что даёт

0

USB 3.0 bulk streams (нужен libusb >= 1.0.19)

2

Фильтрация устройств

5

64-битные ID пакетов

6

32-битная длина bulk-трансферов

7

Буферизованный bulk input

Все capabilities умещаются в один uint32_t. Протокол обратно совместим начиная с версии 0.3 — всё новое гейтится через capabilities, а не через номер версии.

Сжатие

spice-gtk умеет сжимать USB-данные LZ4. Условия: не Unix-сокет, данные больше 1000 байт, сервер заявил SPICE_SPICEVMC_CAP_DATA_COMPRESS_LZ4, и устройство не изохронное. Для isochronous-трафика сжатие явно отключено — задержка важнее размера.

USB 1/2/3

Версия

Контроллер в QEMU

Заметки

USB 1.x

UHCI (companion к EHCI)

Нужен EHCI + UHCI комбо

USB 2.0

EHCI + companion UHCI

Самая протестированная конфигурация

USB 3.0

XHCI (nec-usb-xhci или qemu-xhci)

Менее протестировано, но XHCI проще и жрёт меньше CPU

XHCI поддерживает все версии USB (1.1/2.0/3.0), поэтому если не нужна совместимость со старыми гостевыми ОС — проще ставить только его.

USB redirection pipeline — от физического устройства через usbredir/SPICE до виртуального USB-контроллера в госте
USB redirection pipeline — от физического устройства через usbredir/SPICE до виртуального USB-контроллера в госте

Фильтрация: первая линия обороны

Формат фильтра

SPICE использует строковые правила: <class>,<vendor>,<product>,<version>,<allow>, склеенные через |. Значение -1 — wildcard. 1 = разрешить, 0 = запретить. Первое сработавшее правило побеждает. Если ни одно не сработало — устройство заблокировано.

Дефолтный фильтр автоподключения в spice-gtk:

0x03,-1,-1,-1,0|-1,-1,-1,-1,1

Расшифровка: запретить auto-connect для HID-устройств (класс 0x03 — клавиатуры, мыши), разрешить всё остальное. HID блокируется, чтобы вы случайно не перенаправили свою клавиатуру в VM и не потеряли управление клиентом.

Нюанс с разделителями: в spice-gtk внутри правила используется запятая, в QEMU — двоеточие. В libvirt — XML-атрибуты. Три разных синтаксиса для одной и той же логики, потому что каждый компонент парсит по-своему.

Примеры

# Блокировать mass storage, разрешить остальное
0x08,-1,-1,-1,0|-1,-1,-1,-1,1

# Разрешить только конкретное устройство (VID:PID), всё остальное — нет
-1,0x1234,0x5678,-1,1|-1,-1,-1,-1,0

# Блокировать HID + mass storage, разрешить rest
0x03,-1,-1,-1,0|0x08,-1,-1,-1,0|-1,-1,-1,-1,1

Три уровня фильтрации

  1. Клиент: auto-connect-filter — при подключении нового устройства, нужно ли его автоматически пробрасывать

  2. Клиент: redirect-on-connect — при подключении к SPICE-сессии, какие уже воткнутые устройства пробросить

  3. QEMU / libvirt: redirfilter — серверная сторона, последний рубеж

Почему фильтрация — необходимость, но не решение

VID/PID и device class — это то, что устройство говорит о себе. Firmware может заявить любой класс. BadUSB-флешка представляется как HID-клавиатура и начинает набирать powershell -enc ... быстрее, чем вы моргнёте. Фильтрация по классу помогает от случайного проброса (не перехватить мышь, не дать пользователю воткнуть random-флешку), но не от целенаправленной атаки.

Если ваша threat model включает state-level adversary или инсайдера с паяльником — USB-редиректор нужно отключать полностью. Если включает «сотрудник случайно принёс вирусную флешку» — класс-ф��льтрация плюс DLP на уровне гостевой ОС покрывают основное.

Безопасность: что реально опасно, а что — театр

Что VDI даёт

Сетевой USB-редиректор (usbredir через TCP) — это не прямое подключение к шине. Устройство общается с USB-стеком клиентской машины, а в VM приезжают только сериализованные USB-трансферы. Из этого следует:

DMA-атаки невозможны. Thunderbolt/PCIe DMA — это когда устройство напрямую пишет в память хоста. Через сетевой редиректор устройство видит только клиентский USB-стек, а до памяти хоста/VM добирается только через QEMU-эмуляцию. Никакого прямого доступа.

Firmware-атаки на физический USB-контроллер — только на стороне клиента. VM получает уже распарсенные данные.

VM-изоляция работает. Если малварь попала через USB в гостевую ОС — она заперта в VM. Если VM создаётся из snapshot и сбрасывается после сессии — ущерб ограничен.

Что VDI НЕ даёт

QEMU USB-эмуляция — это attack surface. USB-эмуляция в QEMU исторически богата на CVE. Исследование 2025 года по Cross-Domain Attacks (arxiv: 2512.04260) показало, что ~23.9% CVE в QEMU за последние пять лет приводят к pointer corruption — не обязательно эксплуатабельны, но потенциально опасны, включая USB-подсистему. Crafted USB-пакеты от малициозного клиента теоретически могут атаковать QEMU через usb-redir device.

usbredir тоже не без греха. CVE-2021-3700: use-after-free в usbredirparser_serialize() при медленной отправке. CVSS 6.4 (Medium), вектор локальный, сложность эксплуатации высокая, нужны повышенные привилегии. Автор описал как denial-of-service или потенциальное выполнение кода, но надёжная эксплуатация нереалистична. Исправлено в 0.11.0.

Data exfiltration через mass storage работает ровно так же, как на физическом ПК, если класс 0x08 не заблокирован.

Без TLS USB-трафик виден на сети в открытом виде. Всё, что передаётся через USB — данные с токенов, содержимое флешек, APDU смарт-карт — читается снифером.

Сравнение attack surface

Вектор

Физический USB

USB через VDI (usbredir)

Data exfiltration

Прямой

Работает, если mass storage разрешён

BadUSB / HID spoofing

Высокий риск

Блокируется дефолтным HID-фильтром

DMA

Высокий (Thunderbolt)

Невозможен

Firmware-атаки на контроллер

Прямой

Только клиентская сторона

Эксплойт драйвера в ОС

Риск для хоста

Изолирован в VM

Побег из VM через USB-эмуляцию

N/A

Редко, но возможно

Смарт-карты: исключение, которое подтверждает правило

Смарт-карты — единственная категория USB, которую enterprise хочет пробрасывать. CAC/PIV для госструктур США, корпоративные PKI-карты для аутентификации — без них VDI не развернёшь. При этом карта не создаёт риска data exfiltration: приватный ключ не покидает чип.

Два способа проброса

В SPICE есть два пути. И выбор между ними — не очевидный.

Способ 1: Dedicated smartcard channel (канал 8)

SPICE имеет выделенный канал SPICE_CHANNEL_SMARTCARD = 8 с собственным внутренним протоколом VSC (Virtual Smart Card). Работает через libcacard — библиотеку, которую Red Hat изначально писали для CAC-карт (Common Access Card). Архитектура:

Физическая карта → PC/SC (pcscd) → NSS/PKCS#11 → libcacard
    → SPICE smartcard channel (канал 8, VSC-протокол)
        → QEMU: ccid-card-passthru → usb-ccid
            → Гость видит USB CCID-ридер с виртуальной картой

Ключевое: libcacard не пробрасывает сырой USB. Она перехватывает криптографическую идентичность карты через NSS/PKCS#11 на клиенте и эмулирует виртуальную CAC-карту. Через SPICE-канал передаются APDU-команды (ISO 7816), а не USB bulk-трансферы.

Но тут важный нюанс. libcacard может работать в двух режимах: ccid-card-emulated и ccid-card-passthru. В emulated-режиме криптооперации (подпись, расшифровка) выполняются на клиенте через NSS, и PIN обрабатывается локально — в сеть не уходит. А вот в passthru-режиме (который конфигурируется ниже в этой статье) APDU-команды пробрасываются целиком, включая VERIFY PIN (ISO 7816 INS 0x20). То есть PIN всё-таки передаётся по SPICE-каналу. Без TLS — в открытом виде.

Emulated-режим безопаснее, но требует NSS и PKCS#11-модуль карты на клиенте, и работает только с CAC-совместимыми картами. Passthru проще в настройке и поддерживает больше типов карт, но жертвует изоляцией PIN.

Способ 2: USB-проброс ридера (канал 9, usbredir)

Пробрасываете физический USB-ридер целиком через usbredir. Гость получает exclusive доступ к устройству. Простая настройка. Но: весь USB-трафик, включая PIN, идёт по сети (в открытом виде без TLS). И карта недоступна на клиенте, пока проброшена в VM.

Сравнение

Smartcard channel (8)

USB redirection (9)

Карта доступна на клиенте

Да

Нет (exclusive)

PIN по сети

Emulated — нет; Passthru — да (в APDU)

Да (в USB-трафике)

Поддержка типов карт

Только CAC (libcacard)

Любые

Настройка

Сложнее (NSS, PKCS#11)

Проще

Attack surface

Меньше (APDU only)

Больше (полный USB-стек)

Composite-устройства (Yubikey)

N/A

Проблемы (LIBUSB_ERROR_NOT_SUPPORTED)

Ограничения libcacard

Тут нужна честность. libcacard заточена под CAC и реализует только аплеты, нужные CoolKey PKCS#11 драйверу. PIV (NIST SP 800-73) аплетов в ней нет — PIV-карты работают опосредованно через PKCS#11 на клиенте, если OpenSC или аналог установлен. Начиная с libcacard v2.7.0 добавили Microsoft PnP applet и совместимость с ActivClient, что улучшило ситуацию на Windows-гостях.

Но для реальных военных/государственных деплоев с CAC-NG / PIV-II картами open-source стек часто недостаточен — используют коммерческий middleware (ActivClient, 90Meter). И нередко вообще уходят на RDP smart card redirection, потому что он надёжнее.

Развитие libcacard вялое: основной репозиторий на gitlab.freedesktop.org/spice/libcacard, последняя версия 2.8.x, активность низкая.

FIDO2/WebAuthn через VDI

Отдельная боль. USB-ключи типа YubiKey — composite-устройства (HID + CCID + U2F/FIDO2), и proбросить их через usbredir часто не получается. Citrix решила это через HDX FIDO2 virtual channel — перехватывает WebAuthn API на уровне браузера и редиректит на клиент, без USB-проброса вообще. VMware делает аналогичное.

В SPICE этого нет. Если нужен FIDO2 в SPICE-based VDI — либо мучаетесь с usbredir (и готовьтесь к LIBUSB_ERROR_NOT_SUPPORTED на Yubikey 4/5), либо выносите аутентификацию за пределы VDI-сессии.

Конфигурация в QEMU

Для smartcard channel:

qemu-system-x86_64 ... \
  -usb \
  -device usb-ccid \
  -chardev spicevmc,name=smartcard,id=ccid \
  -device ccid-card-passthru,chardev=ccid

В libvirt:

<smartcard mode="passthrough" type="spicevmc"/>

Гость (Windows): увидит USB CCID-ридер в Device Manager, загрузит встроенный Microsoft USBCCID-драйвер автоматически. Для самой карты нужен middleware — CoolKey, OpenSC, или ActivClient.

Гость (Linux): нужен pcscd + OpenSC (или CoolKey, если RHEL 7 и ранее; с RHEL 8 дефолт — OpenSC).

Два пути проброса смарт-карт — dedicated channel vs USB redirection. Показать, где PIN обрабатывается в каждом случае
Два пути проброса смарт-карт — dedicated channel vs USB redirection. Показать, где PIN обрабатывается в каждом случае

Принтеры: дыра, о которой молчат

В SPICE нет печатного канала

Вот полный список каналов протокола SPICE:

Тип

Канал

Назначение

1

MAIN

Управление сессией, vdagent

2

DISPLAY

Картинка

3

INPUTS

Клавиатура, мышь

4

CURSOR

Курсор

5

PLAYBACK

Звук → клиент

6

RECORD

Микрофон → гость

7

TUNNEL

Устаревший, удалён

8

SMARTCARD

Смарт-карты

9

USBREDIR

USB-редиректор

10

PORT

Произвольный байт-стрим

11

WEBDAV

Общие папки

Принтера нет. Был proposal в 2012 году (в spice-devel от Charles Tsai), Alon Levy из Red Hat подробно расписал архитектуру — chardev subtype printer, новый канал по аналогии с usbredir. Не реализовано. Тот устаревший TUNNEL (тип 7) изначально планировался, в том числе, для доступа к клиентским принтерам — но канал deprecated и удалён, замены не появилось.

Как люди всё-таки печатают

На практике есть четыре подхода, ни один из которых не элегантен:

1. Сетевая печать (IP/IPP): Гость печатает напрямую на сетевой принтер. Работает, если VM-сеть видит принтерную подсеть. Нужны драйверы в госте. Самый надёжный вариант для enterprise, но не помогает с локальными принтерами на тонком клиенте.

2. USB-проброс принтера: Через usbredir. Один пользователь — одна VM — один принтер (exclusive access). Нужны драйверы в госте. На Proxmox-форумах есть тред с 2013 года, где люди до сих пор обсуждают BSOD от некоторых принтеров при пробросе. Live migration с пробросом USB невозможна.

3. WebDAV + print-to-file: Печать в PDF через виртуальный принтер (CUPS-PDF на Linux, Microsoft Print to PDF на Windows), файл падает в общую WebDAV-папку SPICE (канал 11), клиент забирает. Работает, но пользовательский опыт — «сохрани PDF, найди его, открой, распечатай». Не для call-центра.

4. CUPS forwarding: Для Linux→Linux сценариев: CUPS на хосте расшаривает принтеры по IPP, гость добавляет их. Driverless IPP Everywhere минимизирует потребность в драйверах. Через NAT нужен port forwarding.

А у Citrix и VMware?

Citrix и VMware решили проблему принтеров много лет назад:

Citrix Universal Print Driver (UPD): Один универсальный драйвер на сервере, который перехватывает print job в формате EMF/XPS. При логине в сессию принтеры клиента автоматически маппятся. Сервер захватывает print job через UPD, сжимает и отправляет по ICA virtual channel на клиент. Финальный рендеринг под конкретное устройство (device-specific) происходит уже на клиенте нативным драйвером. Гостю не нужны драйверы конкретных принтеров — в этом и суть.

VMware / ThinPrint: Аналогичная схема: Output Gateway на виртуальном десктопе (единственный «драйвер»), AutoConnect при логине, сжатие до 98%, нативный рендеринг на эндпоинте.

Сравнение:

SPICE

Citrix UPD

VMware/ThinPrint

Выделенный print-канал

Нет

Да (ICA VC)

Да

Универсальный драйвер

Нет

Да

Да

Auto-mapping принтеров

Нет

Да

Да

Сжатие print-трафика

Нет

Да

Да (до 98%)

Драйвер-free гость

Нет

Да

Да

Это одна из самых заметных gap между SPICE и коммерческими решениями. Если в ТЗ на VDI есть «печать на локальные принтеры» — SPICE потребует значительно больше костылей.

Что реально используют на Proxmox

Судя по форумам (а форумы Proxmox — неплохой срез реальных деплоев):

  1. RDP вместо SPICE для сессий, требующих печати. RDP printer redirection работает из коробки.

  2. Сетевые принтеры по IP — для Linux-гостей.

  3. Выделенная print server VM (Ubuntu + CUPS + Samba) — чтобы не ставить CUPS на хост Proxmox.

  4. USB passthrough на уровне хоста (usb0: host=VENDOR:PRODUCT в conf-файле VM) — для физических USB-принтеров, но только один пользователь, нужен полный stop/start VM.

Compliance: что требуют стандарты

Не абстрактно, а конкретно.

PCI-DSS v4.0 (обязателен с 31 марта 2025):

  • 3.4.2: технические контроли, предотвращающие копирование PAN на removable media при использовании remote-access technologies — требование распространяется именно на сценарий удалённого доступа, что включает VDI

  • 5.3.3: сканирование removable media антималварью перед использованием (обязательно, не рекомендация)

NIST SP 800-53 Rev. 5:

  • MP-7 (Media Use): организация должна ограничить или запретить использование определённых типов media. Portable storage без идентифицируемого владельца — запретить

  • MP-2, MP-5, MP-6: контроль доступа, транспорта и санитизации media

На практике типичная enterprise USB-политика:

  • Весь removable storage заблокирован по дефолту (GPO / Intune)

  • Исключения — только по конкретному hardware ID, с подписью руководителя

  • Аудит всех USB-подключений

  • Smart cards — разрешены (отдельная категория, класс 0x0B)

Для SPICE-based VDI это значит: usbredir-каналы лучше не конфигурировать вообще (нет канала — нет проброса — нет attack surface), а если конфигурировать — с серверным redirfilter, который блокирует mass storage и HID, оставляя только то, что реально нужно.

Практическая конфигурация

USB-проброс: базовая настройка

qemu-system-x86_64 ... \
  -device nec-usb-xhci,id=usb \
  -chardev spicevmc,name=usbredir,id=usbredirchardev1 \
  -device usb-redir,chardev=usbredirchardev1,id=usbredirdev1 \
  -chardev spicevmc,name=usbredir,id=usbredirchardev2 \
  -device usb-redir,chardev=usbredirchardev2,id=usbredirdev2

Два слота = два устройства одновременно. XHCI проще, чем EHCI+UHCI комбо, и поддерживает USB 1.1/2.0/3.0.

В libvirt:

<controller type='usb' model='nec-xhci'/>
<redirdev bus='usb' type='spicevmc'/>
<redirdev bus='usb' type='spicevmc'/>

Серверный фильтр (блокировать mass storage):

<redirfilter>
  <usbdev class='0x08' vendor='-1' product='-1' version='-1' allow='no'/>
  <usbdev class='-1' vendor='-1' product='-1' version='-1' allow='yes'/>
</redirfilter>

Smart card: dedicated channel

qemu-system-x86_64 ... \
  -usb \
  -device usb-ccid \
  -chardev spicevmc,name=smartcard,id=ccid \
  -device ccid-card-passthru,chardev=ccid

Libvirt:

<smartcard mode="passthrough" type="spicevmc"/>

QEMU должен быть собран с --enable-smartcard. На клиенте нужен NSS + PKCS#11 модуль (OpenSC или CoolKey).

WebDAV (для print-to-file и файлообмена)

Libvirt:

<channel type='spiceport'>
  <source channel='org.spice-space.webdav.0'/>
  <target type='virtio' name='org.spice-space.webdav.0'/>
</channel>

В госте: установить spice-webdavd. Windows-гость увидит \\localhost@9843\DavWWWRoot. Linux — через gvfs/Nautilus.

Windows-клиент: нужен UsbDk

Для USB-редиректа на Windows-клиенте нужен UsbDk — фильтр-драйвер, дающий userspace-доступ к USB-устройствам. Без него проброс не заработает.

Производительность USB-редиректора: чего ожидать

Bulk-трансферы (флешки, принтеры)

Работают приемлемо. С cap_32bits_bulk_length поддерживаются трансферы до ~4 ГБ. USB 3.0 bulk streams добавляют пропускную способность. На скорость влияет сеть — не ожидайте USB 3.0 speeds через WAN.

Isochronous (веб-камеры, USB-аудио)

Самое проблемное. usbredir дропает isochronous-пакеты при переполнении write queue. На практике:

  • Веб-камеры могут отваливаться через несколько минут

  • USB-аудио glitches под нагрузкой

  • Сжатие для isochronous отключено (latency-sensitive)

  • Синхронизации между аудио и видео нет

Если нужна камера в VDI — лучше выделенный протокол (Citrix HDX RealTime, VMware RTAV). SPICE usbredir для камер — крайний случай.

Interrupt-трансферы (клавиатуры, мыши, HID)

Работают, но вы не должны их пробрасывать (HID блокируется по умолчанию). Для клавиатуры и мыши SPICE использует собственный INPUTS-канал (тип 3), который оптимизирован под это.

Вот итог — написал так, чтобы он не просто повторял "коротко" из статьи, а давал читателю ощущение завершённости и практического вывода:

Итог: компромисс, который придётся принять

USB-редиректор в SPICE — это не фича безопасности и не дыра в периметре. Это инструмент, который делает ровно то, что ему говорят. Если говорить «пробросить всё» — пробросит всё, включая потенциальную малварь с флешки. Если говорить «только смарт-карты и ничего больше» — будет именно так. Проблема в том, что пространство между «запретить всё» и «разрешить всё» — это не один ползунок, а несколько десятков мелких решений, каждое из которых имеет последствия.

Для большинства enterprise-деплоев разумный минимум выглядит так: usbredir-каналы без крайней необходимости не открывать, mass storage блокировать серверным redirfilter как последним рубежом, смарт-карты пробрасывать через dedicated channel (8), а не через usbredir, TLS считать обязательным, а не опциональным. Это не паранойя — это то, что PCI-DSS и NIST уже давно считают само собой разумеющимся.

Честный ответ на вопрос «насколько SPICE готов к enterprise USB?»: зависит от того, что вы называете enterprise. Если это сотрудники за рабочими станциями с сетевыми принтерами и CAC-картами — SPICE справится, пусть и с усилиями. Если это call-центр с локальными USB-принтерами, FIDO2-ключами и требованием auto-mapping — вы скорее всего в какой-то момент откроете документацию Citrix.

SPICE — хороший открытый протокол с понятной архитектурой и честными trade-off. Его ограничения в области USB — не баги, это следствие того, что коммерческий функционал типа Universal Print Driver или HDX FIDO2 требует лет инженерных усилий и нормального финансирования. Проекту это не досталось. Зато досталась прозрачность: исходники открыты, протокол задокументирован, поведение предсказуемо.

Именно поэтому с ним стоит работать осознанно — понимая, где он тянет, а где придётся либо дорабатывать, либо выбирать другой инструмент.