Docker: ¿una forma de dar acceso a un host USB o dispositivo serie?

Respuestas:

194

Hay un par de opciones. Puede usar el --deviceindicador que puede usar para acceder a dispositivos USB sin --privilegedmodo:

docker run -t -i --device=/dev/ttyUSB0 ubuntu bash

Alternativamente, suponiendo que su dispositivo USB esté disponible con controladores funcionando, etc. en el host /dev/bus/usb, puede montarlo en el contenedor utilizando el modo privilegiado y la opción de volúmenes . Por ejemplo:

docker run -t -i --privileged -v /dev/bus/usb:/dev/bus/usb ubuntu bash

Tenga en cuenta que, como su nombre lo indica, --privilegedes inseguro y debe manejarse con cuidado.

Ben Whaley
fuente
44
No necesita -v - privilegiado ya significa acceso a todos los dispositivos
Art
12
¿Existe algún mecanismo como este para el cliente de Docker de Windows?
Pascal
Al usar esta solución, no veo dispositivos desde un contenedor acoplable ... Aquí están los detalles stackoverflow.com/questions/37213812 de mi problema. Agradezco cualquier ayuda! Gracias.
kashesandr
1
Todavía no funciona si el dispositivo USB está conectado después de que Docker ya se esté ejecutando.
Franklin Dattein
Quiero decir, no asigna el dispositivo en / tty / USBX a pesar de que lsusb puede enumerarlo.
Franklin Dattein
78

Con las versiones actuales de Docker, puede usar la --devicebandera para lograr lo que desea, sin necesidad de dar acceso a todos los dispositivos USB.

Por ejemplo, si desea que solo sea /dev/ttyUSB0accesible dentro de su contenedor Docker, puede hacer algo como:

docker run -t -i --device=/dev/ttyUSB0 ubuntu bash
marca
fuente
3
solo tenga en cuenta que el dispositivo no puede ser un enlace simbólico en este momento. github.com/docker/docker/issues/13840
wligtenberg
66
usando la --devicebandera, ¿cómo determino cuál /dev/<device>es el dispositivo Android asociado en la máquina host, especialmente cuando uso Docker Quickstart Terminal (VirtualBox Host) para Windows o Mac?
DanCat
1
Esto funciona bien si el nombre de su dispositivo nunca cambia. Pero si está usando algo dinámico que usa dispositivos dentro de / dev / bus / usb, entonces esto no funcionará porque el nombre del dispositivo cambia cuando lo conecta y desconecta. Necesitará la solución -v (volúmenes) anterior en su lugar.
Brad Grissom el
1
Las reglas de udev de @DanCat pueden garantizar que su dispositivo se monte en una ruta estática
C. Reed
1
¿Por qué alguien estaría interesado en tener acceso a un solo dispositivo usb? Los dispositivos USB están diseñados para conectarse y desconectarse y eso debe hacerse durante el tiempo de ejecución de las aplicaciones. USB no es SATA o algo así, no se puede esperar que algo siempre esté ahí ... Y no creo que la gente simplemente inicie aplicaciones a través de Docker para ejecuciones individuales y las cierre tan pronto como se desconecten los dispositivos USB, ¿verdad? Me imagino más como aplicaciones de tipo de servicio, no como frascos de una sola ejecución ... Pero gracias, de hecho, eso podría ayudar a algunos para los cuales ese escenario muy limitado sería adecuado
Arturas M
17

--devicefunciona hasta que su dispositivo USB se desenchufe / vuelva a enchufar y luego deje de funcionar. Tienes que usar dispositivos cgroup. Para evitarlo .
Podría usarlo, -v /dev:/devpero eso no es seguro, ya que asigna todos los dispositivos desde su host al contenedor, incluidos los dispositivos de disco sin formato, etc. Básicamente, esto permite que el contenedor obtenga root en el host, que generalmente no es lo que desea.
El uso del enfoque cgroups es mejor en ese sentido y funciona en dispositivos que se agregan después del contenedor como se inició.

Vea los detalles aquí: Acceso a dispositivos USB en Docker sin usar --privileged

Es un poco difícil de pegar, pero en pocas palabras, debes obtener el número principal de tu dispositivo de personaje y enviarlo a cgroup:

189 es el número principal de / dev / ttyUSB *, que puede obtener con 'ls -l'. Puede ser diferente en su sistema que en el mío:

root@server:~# echo 'c 189:* rwm' > /sys/fs/cgroup/devices/docker/$A*/devices.allow  
(A contains the docker containerID)

Luego, inicie su contenedor de esta manera:

docker run -v /dev/bus:/dev/bus:ro -v /dev/serial:/dev/serial:ro -i -t --entrypoint /bin/bash debian:amd64

sin hacer esto, cualquier dispositivo que se haya enchufado o reiniciado nuevamente después de que se inicie el contenedor, obtendrá una nueva ID de bus y no se permitirá el acceso al contenedor.

Marc Merlin
fuente
77
Para las personas que -1 hicieron esto, por favor ayuden y digan lo que les gustaría mejorar. Escribí esta página para ayudar a otros que tuvieron el problema que hicimos. Voy a ser un poco honesto al decir que estoy de ser apagado de tratar a la gente volver y ayudar en acciones sobre stackoverflow también: - /
Marc Merlin
Si lees mi respuesta, verás que agregar el volumen '-v / dev: / dev' dará acceso a dispositivos conectados dinámicamente.
rrpilot
55
rrpilot: -v / dev: / dev te da todo / dev, incluyendo / dev / sda y otras cosas que realmente no quieres exponer a un usuario root en el contenedor. En otras palabras, su solución funciona, pero no es segura. El mío resuelve ese problema. Editaré mi respuesta para señalar eso.
Marc Merlin
1
La respuesta podría mejorarse mostrando cómo obtener el número principal y aclarando que 189debe reemplazarse. devices.allowPuede encontrar una descripción de qué enviar aquí: kernel.org/doc/Documentation/cgroup-v1/devices.txt
Craig Younkins
1
Hay una nueva característica de Docker que hace esto un poco más simple: "--device-cgroup-rule" ( docs.docker.com/engine/reference/commandline/create/… )
tianon el
14

Quería extender las respuestas ya dadas para incluir soporte para dispositivos conectados dinámicamente que no se capturan /dev/bus/usby cómo hacer que esto funcione cuando se usa un host de Windows junto con la máquina virtual boot2docker.

Si está trabajando con Windows, deberá agregar cualquier regla USB para los dispositivos a los que desea que Docker acceda dentro del administrador de VirtualBox. Para hacer esto, puede detener la VM ejecutando:

host:~$ docker-machine stop default

Abra el VirtualBox Manager y agregue soporte USB con filtros según sea necesario.

Inicie la máquina virtual boot2docker:

host:~$ docker-machine start default

Dado que los dispositivos USB están conectados a la máquina virtual boot2docker, los comandos deben ejecutarse desde esa máquina. Abra una terminal con la VM y ejecute el comando docker run:

host:~$ docker-machine ssh
docker@default:~$ docker run -it --privileged ubuntu bash

Tenga en cuenta que cuando el comando se ejecuta de esta manera, solo se capturarán los dispositivos USB conectados anteriormente. El indicador de volúmenes solo es necesario si desea que esto funcione con dispositivos conectados después de que se inicie el contenedor. En ese caso, puede usar:

docker@default:~$ docker run -it --privileged -v /dev:/dev ubuntu bash

Tenga en cuenta que tuve que usar en /devlugar de /dev/bus/usben algunos casos para capturar un dispositivo como /dev/sg2. Solo puedo suponer que lo mismo sería cierto para dispositivos como /dev/ttyACM0o /dev/ttyUSB0.

Los comandos de ejecución de Docker también funcionarán con un host Linux.

rrpilot
fuente
Buen punto para montar / dev: / dev en su lugar. Eso brinda más flexibilidad en términos de capturar otros dispositivos y también ayuda con el elemento dinámico.
kotakotakota
y también compromete la seguridad y el aislamiento de su máquina host.
Exadra37
@ Exadra37 Sí ... y si eso es importante en su aplicación, no debe usar esto. Sin embargo, es importante tener en cuenta que hay algunas aplicaciones en las que no te importa y no estás usando Docker para su aislamiento. En mi caso específico, puedes ejecutar una aplicación Linux empaquetada en Windows.
rrpilot
3

Otra opción es ajustar udev, que controla cómo se montan los dispositivos y con qué privilegios. Útil para permitir el acceso no root a dispositivos seriales. Si tiene dispositivos conectados permanentemente, la --deviceopción es la mejor opción. Si tiene dispositivos efímeros, esto es lo que he estado usando:

1. Establecer la regla de udev

De forma predeterminada, los dispositivos en serie se montan para que solo los usuarios root puedan acceder al dispositivo. Necesitamos agregar una regla udev para que sean legibles por usuarios no root.

Cree un archivo llamado /etc/udev/rules.d/99-serial.rules. Agregue la siguiente línea a ese archivo:

KERNEL=="ttyUSB[0-9]*",MODE="0666"

MODE = "0666" dará a todos los usuarios permisos de lectura / escritura (pero no ejecución) para sus dispositivos ttyUSB. Esta es la opción más permisiva, y es posible que desee restringir esto aún más según sus requisitos de seguridad. Puede leer en udev para obtener más información sobre cómo controlar lo que sucede cuando un dispositivo se conecta a una puerta de enlace Linux.

2. Montar en la carpeta / dev del host al contenedor

Los dispositivos en serie a menudo son efímeros (se pueden conectar y desconectar en cualquier momento). Debido a esto, no podemos montar en el dispositivo directo o incluso en la carpeta / dev / serial, porque pueden desaparecer cuando se desconectan las cosas. Incluso si los vuelve a enchufar y el dispositivo vuelve a aparecer, técnicamente es un archivo diferente al que estaba montado, por lo que Docker no lo verá. Por esta razón, montamos toda la carpeta / dev desde el host al contenedor. Puede hacer esto agregando el siguiente comando de volumen a su comando de ejecución Docker:

-v /dev:/dev

Si su dispositivo está conectado permanentemente, entonces usar la opción --device o un montaje de volumen más específico es probablemente una mejor opción desde una perspectiva de seguridad.

3. Ejecute el contenedor en modo privilegiado

Si no usó la opción --device y se montó en toda la carpeta / dev, se le pedirá que ejecute el contenedor en modo privilegiado (voy a revisar las cosas de cgroup mencionadas anteriormente para ver si esto se puede eliminar ) Puede hacer esto agregando lo siguiente a su comando de ejecución Docker:

--privileged

4. Acceda al dispositivo desde la carpeta / dev / serial / by-id

Si su dispositivo se puede conectar y desconectar, Linux no garantiza que siempre se montará en la misma ubicación ttyUSBxxx (especialmente si tiene varios dispositivos). Afortunadamente, Linux creará un enlace simbólico automáticamente al dispositivo en la carpeta / dev / serial / by-id. El archivo en esta carpeta siempre se llamará igual.

Este es el resumen rápido, tengo un artículo de blog que entra en más detalles.

BadCanyon
fuente
2

Es difícil para nosotros vincular un dispositivo USB específico a un contenedor acoplable que también es específico. Como puede ver, la forma recomendada de lograr es:

docker run -t -i --privileged -v /dev/bus/usb:/dev/bus/usb ubuntu bash

Unirá todos los dispositivos a este contenedor. No es seguro Todos los contenedores fueron otorgados para operarlos todos.

Otra forma es vincular dispositivos por devpath. Puede verse así:

docker run -t -i --privileged -v /dev/bus/usb/001/002:/dev/bus/usb/001/002 ubuntu bash

o --device(mejor, no privileged):

docker run -t -i --device /dev/bus/usb/001/002 ubuntu bash

Mucho más seguro. Pero en realidad es difícil saber cuál es el devpath de un dispositivo específico.

He escrito este repositorio para resolver este problema.

https://github.com/williamfzc/usb2container

Después de implementar este servidor, puede obtener fácilmente toda la información de los dispositivos conectados a través de la solicitud HTTP:

curl 127.0.0.1:9410/api/device

y obten:

{
    "/devices/pci0000:00/0000:00:14.0/usb1/1-13": {
        "ACTION": "add",
        "DEVPATH": "/devices/pci0000:00/0000:00:14.0/usb1/1-13",
        "DEVTYPE": "usb_device",
        "DRIVER": "usb",
        "ID_BUS": "usb",
        "ID_FOR_SEAT": "xxxxx",
        "ID_MODEL": "xxxxx",
        "ID_MODEL_ID": "xxxxx",
        "ID_PATH": "xxxxx",
        "ID_PATH_TAG": "xxxxx",
        "ID_REVISION": "xxxxx",
        "ID_SERIAL": "xxxxx",
        "ID_SERIAL_SHORT": "xxxxx",
        "ID_USB_INTERFACES": "xxxxx",
        "ID_VENDOR": "xxxxx",
        "ID_VENDOR_ENC": "xxxxx",
        "ID_VENDOR_FROM_DATABASE": "",
        "ID_VENDOR_ID": "xxxxx",
        "INTERFACE": "",
        "MAJOR": "189",
        "MINOR": "119",
        "MODALIAS": "",
        "PRODUCT": "xxxxx",
        "SEQNUM": "xxxxx",
        "SUBSYSTEM": "usb",
        "TAGS": "",
        "TYPE": "0/0/0",
        "USEC_INITIALIZED": "xxxxx",
        "adb_user": "",
        "_empty": false,
        "DEVNAME": "/dev/bus/usb/001/120",
        "BUSNUM": "001",
        "DEVNUM": "120",
        "ID_MODEL_ENC": "xxxxx"
    },
    ...
}

y atarlos a sus contenedores. Por ejemplo, puede ver que el DEVNAME de este dispositivo es /dev/bus/usb/001/120:

docker run -t -i --device /dev/bus/usb/001/120 ubuntu bash

Tal vez te ayude.

williamfzc
fuente
0

Con las últimas versiones de Docker, esto es suficiente:

docker run -ti --privileged ubuntu bash

Le dará acceso a todos los recursos del sistema (en / dev por ejemplo)

Thomas Grimault
fuente
2
Priviledged es una opción terrible para usar por seguridad, aunque sí, funciona.
Marc Merlin el
2
Si se usa para programar cosas como cosas relacionadas con Arduino, esta solución es buena
Jose Cabrera Zuniga
0

Además de las respuestas anteriores, para aquellos que desean una forma rápida de usar un dispositivo USB externo (HDD, unidad flash) que funcione dentro de la ventana acoplable y no utilicen el modo privilegiado:

Encuentre el devpath a su dispositivo en el host:

sudo fdisk -l

Puede reconocer su unidad por su capacidad de la lista con bastante facilidad. Copie esta ruta (para el siguiente ejemplo es /dev/sda2).

Disque /dev/sda2 : 554,5 Go, 57151488 octets, 111624 secteurs
Unités : secteur de 1 × 512 = 512 octets
Taille de secteur (logique / physique) : 512 octets / 512 octets
taille d'E/S (minimale / optimale) : 512 octets / 512 octets

Monta este devpath (preferible a /media):

sudo mount <drive path> /media/<mount folder name>

Luego puede usar esto como un parámetro para docker rungustar:

docker run -it -v /media/<mount folder name>:/media/<mount folder name>

o en docker componer en volúmenes:

services:
  whatevermyserviceis:
    volumes:
      - /media/<mount folder name>:/media/<mount folder name>

Y ahora, cuando corres e ingresas a tu contenedor, deberías poder acceder a la unidad dentro del contenedor en /media/<mount folder name>

DESCARGO DE RESPONSABILIDAD:

  1. Esto probablemente no funcionará para dispositivos serie como cámaras web, etc. Solo he probado esto para unidades de almacenamiento USB.
  2. Si necesita volver a conectar y desconectar dispositivos regularmente, este método sería molesto y tampoco funcionaría a menos que restablezca la ruta de montaje y reinicie el contenedor.
  3. Usé docker 17.06 + según lo prescrito en los documentos
toing_toing
fuente
0

Si desea acceder dinámicamente a dispositivos USB que se pueden enchufar mientras el contenedor acoplable ya se está ejecutando, por ejemplo, acceda a una cámara web USB recién conectada en / dev / video0, puede agregar una regla cgroup al iniciar el contenedor. Esta opción no necesita un contenedor privilegiado y solo permite el acceso a tipos específicos de hardware.

Paso 1

Marque el número principal del dispositivo del tipo de dispositivo que desea agregar. Puede buscarlo en la documentación del kernel de Linux . O puede verificarlo para su dispositivo. Por ejemplo, para verificar el número principal del dispositivo para una cámara web conectada a / dev / video0, puede hacer una ls -la /dev/video0. Esto resulta en algo como:

crw-rw----+ 1 root video 81, 0 Jul  6 10:22 /dev/video0

Donde el primer número (81) es el número principal del dispositivo. Algunos números principales de dispositivos comunes:

  • 81: cámaras web usb
  • 188: convertidores usb a serie

Paso 2

Agregue reglas cuando inicie el contenedor acoplable:

  • Agregue una --device-cgroup-rule='c major_number:* rmw'regla para cada tipo de dispositivo al que desee acceder
  • Agregue acceso a la información de udev para que los contenedores docker puedan obtener más información sobre sus dispositivos usb con -v /run/udev:/run/udev:ro
  • Asigne el volumen / dev a su contenedor acoplable con -v /dev:/dev

Envolver

Entonces, para agregar todas las cámaras web usb y dispositivos serial2usb a su contenedor docker, haga lo siguiente:

docker run -it -v /dev:/dev --device-cgroup-rule='c 188:* rmw' --device-cgroup-rule='c 81:* rmw' ubuntu bash
Wout_bb
fuente