Hướng dẫn cấu hình USB Passthrough trên KVM và Proxmox: Kết nối dongle bảo mật, camera, modem 3G vào máy ảo

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

Vấn đề: Thiết bị USB vật lý không dùng được trong máy ảo

Mình có một con dongle bảo mật (USB hardware key) dùng để kích hoạt phần mềm kế toán. Vấn đề là phần mềm đó chỉ chạy được trên Windows, trong khi host mình dùng Ubuntu với KVM. Cắm dongle vào máy host thì host nhận ra, nhưng Windows VM không thấy gì hết.

Tương tự với modem 3G/4G USB — bạn muốn gán cố định cho một VM router để nó tự dial-up, không phụ thuộc host. Hay camera USB muốn đưa thẳng vào VM chạy NVR (phần mềm quản lý camera).

Tất cả đều giải quyết bằng USB Passthrough: máy ảo chiếm quyền kiểm soát hoàn toàn thiết bị USB vật lý, giao tiếp trực tiếp với firmware của nó — không qua trung gian.

Tại sao mặc định máy ảo không thấy USB vật lý?

KVM/QEMU mặc định tạo ra USB controller ảo (emulated). VM nhìn vào thấy một controller giả lập sạch bóng, không có thiết bị vật lý nào. Hypervisor đứng giữa host và VM — và mặc định nó không chuyển tiếp USB hardware xuống.

Để passthrough hoạt động, QEMU dùng cơ chế usb-host: ánh xạ trực tiếp device file /dev/bus/usb/... từ host vào VM qua VFIO hoặc libusb. VM khi đó nhận đủ USB descriptor, endpoint, và giao tiếp thẳng với firmware thiết bị — giống hệt cắm vật lý.

Proxmox bọc thêm Web UI cho dễ dùng, nhưng bên dưới vẫn là cùng cơ chế QEMU đó.

Hai cách passthrough, chọn cái nào?

Cách 1: Passthrough theo Vendor ID và Product ID (nên dùng)

Gán thiết bị theo model — Vendor:Product ID. Cắm vào cổng nào VM vẫn nhận đúng thiết bị. Đây là cách phù hợp cho dongle bảo mật, modem 3G, hoặc bất kỳ thiết bị nào bạn cần gán lâu dài.

Cách 2: Passthrough theo địa chỉ Bus:Device

Gán theo vị trí vật lý — bus số mấy, port số mấy. Rút thiết bị ra rồi cắm lại là địa chỉ thay đổi, VM mất kết nối ngay. Chỉ dùng khi thực sự cần kiểm soát cổng cụ thể, không phải thiết bị cụ thể.

Thực hiện trên KVM/libvirt

Bước 1: Tìm Vendor ID và Product ID

Cắm thiết bị vào host, chạy:

lsusb

Kết quả mẫu:

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

Dòng cuối là dongle HASP — 0529 là Vendor ID, 0001 là Product ID. Ghi lại hai số này.

Bước 2: Thêm USB device vào XML của VM

Mình dùng virsh edit cho nhanh:

virsh edit ten-may-ao

Tìm section <devices>, thêm vào trong đó:

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

Lưu lại rồi restart VM:

virsh shutdown ten-may-ao
virsh start ten-may-ao

Bước 3: Kiểm tra trong VM

Windows VM: mở Device Manager, thiết bị sẽ xuất hiện ngay. Linux VM thì kiểm tra bằng:

lsusb  # chạy bên trong VM

Hot-plug: thêm USB khi VM đang chạy

Không cần restart — tạo file XML tạm rồi attach trực tiếp:

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 ten-may-ao /tmp/usb-dongle.xml --live

Gỡ ra khi VM vẫn chạy:

virsh detach-device ten-may-ao /tmp/usb-dongle.xml --live

Thực hiện trên Proxmox VE

Homelab của mình chạy Proxmox quản lý 12 VM và container — đây là nơi mình test mọi thứ trước khi đưa lên production. USB passthrough là tính năng dùng gần như hằng tuần, đặc biệt khi cần test phần mềm yêu cầu dongle license.

Qua Web UI (nhanh nhất)

  1. Vào Proxmox Web UI → chọn VM → tab Hardware
  2. Click Add → chọn USB Device
  3. Chọn Use USB Vendor/Device ID → dropdown liệt kê các thiết bị USB đang cắm vào host
  4. Chọn thiết bị cần passthrough → OK
  5. Start (hoặc restart) VM

Proxmox tự ghi config vào /etc/pve/qemu-server/<VMID>.conf:

usb0: host=0529:0001

Qua config file (cho automation hoặc scripting)

SSH vào Proxmox node, mở file config trực tiếp:

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

Thêm:

# Dongle kế toán
usb0: host=0529:0001

# Modem Huawei thứ hai
usb1: host=12d1:1506

Restart VM:

qm stop 100 && qm start 100

Passthrough toàn bộ cổng USB vật lý

Muốn gán cố định một cổng USB (bất kể cắm thiết bị gì vào đó), dùng option Use USB Port trong Proxmox UI. Config tương ứng:

usb0: host=1-2  # Bus 1, Port 2

Xác định bus/port bằng:

lsusb -t  # Xem cây USB topology

Xử lý lỗi thường gặp

Permission denied khi attach USB

Libvirt cần quyền đọc device file. Kiểm tra ownership trước:

ls -la /dev/bus/usb/001/004  # thay số bus/device cho đúng

Owner là root:root trong khi QEMU chạy user khác? Thêm udev rule:

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

Thiết bị mất kết nối sau khi VM restart

Gần như chắc chắn là đang dùng passthrough theo Bus:Device thay vì Vendor:Product. Địa chỉ Bus:Device thay đổi mỗi lần thiết bị được enum lại. Chuyển sang Vendor:Product ID là xong.

USB 3.0 không nhận trên VM

QEMU mặc định dùng USB 2.0 controller (EHCI). Thiết bị USB 3.0 cần xHCI. Trong Proxmox UI: Hardware → Add → USB Controller → chọn xHCI. Hoặc trong libvirt XML:

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

Script tự động attach USB khi cắm vào

Script nhỏ dùng cho homelab — attach USB vào VM ngay khi thiết bị được cắm, không cần can thiệp thủ công:

#!/bin/bash
# usb-attach.sh — Attach USB device vào VM khi cắm vào
# Dùng với udev rule hoặc chạy thủ công

VENDOR_ID="0529"
PRODUCT_ID="0001"
VM_NAME="windows-kế-toán"

if virsh list --state-running | grep -q "$VM_NAME"; then
  echo "VM đang chạy, attach 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 "Attach thành công!"
else
  echo "VM chưa chạy, bỏ qua."
fi

Tổng kết

Toàn bộ flow chỉ có hai bước: tìm Vendor:Product ID của thiết bị, khai báo vào config VM. Xong. Proxmox làm điều đó qua vài click trong Web UI. KVM/libvirt thì virsh editvirsh attach-device cho kiểm soát chi tiết hơn — bao gồm cả hot-plug khi VM đang chạy.

Một điểm quan trọng: luôn dùng Vendor:Product ID, đừng dùng Bus:Device. Mình từng mất cả buổi debug vì dongle “biến mất” sau mỗi lần restart — hóa ra chỉ vì địa chỉ Bus:Device thay đổi. Chuyển sang Vendor:Product là cố định vĩnh viễn, dù cắm vào cổng nào.

Share: