KVMとProxmoxでUSBパススルーを設定する:セキュリティドングル・カメラ・3Gモデムを仮想マシンに接続する方法

Virtualization tutorial - IT technology blog
Virtualization tutorial - IT technology blog

問題:物理USBデバイスが仮想マシンで使えない

会計ソフトのライセンス認証に使うセキュリティドングル(USB ハードウェアキー)を持っているのだが、そのソフトはWindowsでしか動かない。一方、ホストマシンはKVMを使ったUbuntuだ。ドングルをホストに挿すとホストは認識するが、Windows VMからは一切見えない。

3G/4G USBモデムも同様の話だ。VMルーターに固定で割り当てて、ホストに依存せず自動ダイヤルアップさせたい場合がある。あるいはUSBカメラをNVR(カメラ管理ソフト)を動かすVMに直接渡したいケースもある。

これらはすべてUSBパススルーで解決できる。仮想マシンが物理USBデバイスの完全な制御権を持ち、中間層を介さずファームウェアと直接通信する仕組みだ。

なぜデフォルトでは仮想マシンから物理USBが見えないのか

KVM/QEMUはデフォルトで仮想(エミュレート)USBコントローラーを作成する。VMから見えるのはクリーンな仮想コントローラーだけで、物理デバイスは存在しない。ハイパーバイザーがホストとVMの間に立ちはだかり、デフォルトではUSBハードウェアを下に転送しない。

パススルーを機能させるため、QEMUはusb-hostメカニズムを使う。ホスト上の/dev/bus/usb/...デバイスファイルをVFIOまたはlibusbを経由してVMに直接マッピングする。するとVMはUSBディスクリプターやエンドポイントをすべて受け取り、物理的に挿したのと同じようにデバイスのファームウェアと直接通信できる。

ProxmoxはWebUIでラップして使いやすくしているが、内部では同じQEMUの仕組みが動いている。

2つのパススルー方式:どちらを選ぶか

方式1:Vendor IDとProduct IDによるパススルー(推奨)

デバイスのモデル(Vendor:Product ID)で割り当てる方式。どのポートに挿してもVMは正しくデバイスを認識する。セキュリティドングル、3Gモデム、長期的に割り当てたいデバイスにはこちらが適している。

方式2:Bus:Deviceアドレスによるパススルー

物理的な位置(バス番号・ポート番号)で割り当てる方式。デバイスを抜き差しするたびにアドレスが変わり、VMの接続が切れる。特定のポートを制御したい場合にのみ使うべきで、特定のデバイスを固定したい場合には向いていない。

KVM/libvirtでの設定手順

ステップ1:Vendor IDとProduct IDを調べる

ホストにデバイスを挿して、以下を実行:

lsusb

出力例:

Bus 001 Device 003: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC
Bus 001 Device 004: ID 12d1:1506 Huawei Technologies Co., Ltd. Modem/Networkcard
Bus 002 Device 002: ID 0529:0001 Aladdin Knowledge Systems HASP v0.06

最後の行がHASPドングルだ。0529がVendor ID、0001がProduct IDになる。この2つの値を控えておこう。

ステップ2:VMのXMLにUSBデバイスを追加する

手早く行うにはvirsh editを使う:

virsh edit 仮想マシン名

<devices>セクションを見つけて、その中に追加:

<hostdev mode='subsystem' type='usb' managed='yes'>
  <source>
    <vendor id='0x0529'/>
    <product id='0x0001'/>
  </source>
</hostdev>

保存してVMを再起動:

virsh shutdown 仮想マシン名
virsh start 仮想マシン名

ステップ3:VM内で確認する

Windows VMならデバイスマネージャーを開くとすぐに表示される。Linux VMの場合は以下で確認:

lsusb  # VM内部で実行

ホットプラグ:VM稼働中にUSBを追加する

再起動不要。一時XMLファイルを作成して直接アタッチできる:

cat > /tmp/usb-dongle.xml <<EOF
<hostdev mode='subsystem' type='usb' managed='yes'>
  <source>
    <vendor id='0x0529'/>
    <product id='0x0001'/>
  </source>
</hostdev>
EOF

virsh attach-device 仮想マシン名 /tmp/usb-dongle.xml --live

VM稼働中にデタッチする場合:

virsh detach-device 仮想マシン名 /tmp/usb-dongle.xml --live

Proxmox VEでの設定手順

自分のホームラボではProxmoxで12台のVMとコンテナを管理している。本番環境に展開する前にあらゆるものをここでテストする。USBパススルーはほぼ毎週使う機能で、特にドングルライセンスが必要なソフトウェアのテスト時に活躍する。

Web UIから設定(最も手軽)

  1. Proxmox Web UI → VMを選択 → Hardwareタブを開く
  2. Addをクリック → USB Deviceを選択
  3. Use USB Vendor/Device IDを選択 → ホストに接続中のUSBデバイスがドロップダウンに一覧表示される
  4. パススルーしたいデバイスを選択 → OK
  5. VMを起動(または再起動)

Proxmoxは自動的に/etc/pve/qemu-server/<VMID>.confに設定を書き込む:

usb0: host=0529:0001

設定ファイルから設定(自動化・スクリプト向け)

ProxmoxノードにSSHして設定ファイルを直接編集:

nano /etc/pve/qemu-server/100.conf  # 100はVMID

以下を追加:

# 会計ソフト用ドングル
usb0: host=0529:0001

# 2台目のHuaweiモデム
usb1: host=12d1:1506

VMを再起動:

qm stop 100 && qm start 100

物理USBポートごとパススルーする

特定のUSBポート(何を挿しても)を固定で割り当てたい場合は、Proxmox UIでUse USB Portオプションを使う。対応する設定:

usb0: host=1-2  # バス1、ポート2

バス/ポートを確認するには:

lsusb -t  # USBトポロジーのツリー表示

よくあるエラーの対処法

USBアタッチ時にPermission denied

Libvirtがデバイスファイルを読む権限が必要だ。まず所有権を確認:

ls -la /dev/bus/usb/001/004  # バス/デバイス番号は適宜変更

所有者がroot:rootなのにQEMUが別ユーザーで動いている場合は、udevルールを追加する:

cat > /etc/udev/rules.d/99-usb-passthrough.rules <<EOF
SUBSYSTEM=="usb", ATTR{idVendor}=="0529", ATTR{idProduct}=="0001", MODE="0666"
EOF

udevadm control --reload-rules
udevadm trigger

VM再起動後にデバイスが切断される

ほぼ確実にVendor:ProductではなくBus:Deviceでパススルーしているのが原因だ。デバイスが再列挙されるたびにBus:Deviceアドレスは変わる。Vendor:Product IDに切り替えれば解決する。

USB 3.0がVMで認識されない

QEMUはデフォルトでUSB 2.0コントローラー(EHCI)を使う。USB 3.0デバイスにはxHCIが必要だ。Proxmox UIの場合:Hardware → Add → USB Controller → xHCIを選択。libvirt XMLの場合:

<controller type='usb' model='qemu-xhci'/>

USBを挿したら自動でアタッチするスクリプト

ホームラボ用の小さなスクリプト。デバイスを挿した瞬間に自動でVMにアタッチし、手動操作が不要になる:

#!/bin/bash
# usb-attach.sh — デバイス接続時にUSBをVMへアタッチする
# udevルールと組み合わせるか、手動で実行する

VENDOR_ID="0529"
PRODUCT_ID="0001"
VM_NAME="windows-accounting"

if virsh list --state-running | grep -q "$VM_NAME"; then
  echo "VMが起動中です。USBをアタッチします..."
  virsh attach-device "$VM_NAME" - --live <<EOF
<hostdev mode='subsystem' type='usb' managed='yes'>
  <source>
    <vendor id='0x${VENDOR_ID}'/>
    <product id='0x${PRODUCT_ID}'/>
  </source>
</hostdev>
EOF
  echo "アタッチ成功!"
else
  echo "VMが起動していません。スキップします。"
fi

まとめ

全体の流れはたった2ステップだ。デバイスのVendor:Product IDを調べて、VMの設定に記述する。以上。Proxmoxならば数クリックのWeb UI操作で完結する。KVM/libvirtはvirsh editvirsh attach-deviceでより細かい制御ができ、VM稼働中のホットプラグにも対応している。

重要なポイントをひとつ:必ずVendor:Product IDを使い、Bus:Deviceは使わないこと。Bus:Deviceで設定したせいでドングルが再起動のたびに「消える」原因を半日かけてデバッグしたことがある。実はBus:Deviceアドレスが変わっていただけだった。Vendor:ProductIDに切り替えれば、どのポートに挿しても永続的に固定される。

Share: