Docker - một cách để cấp quyền truy cập vào máy chủ USB hoặc thiết bị nối tiếp?


Câu trả lời:


194

Có một vài sự lựa chon. Bạn có thể sử dụng --devicecờ sử dụng có thể sử dụng để truy cập các thiết bị USB mà không cần --privilegedchế độ:

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

Ngoài ra, giả sử thiết bị USB của bạn khả dụng với trình điều khiển hoạt động, v.v. trên máy chủ lưu trữ /dev/bus/usb, bạn có thể gắn thiết bị này vào bộ chứa bằng chế độ đặc quyềntùy chọn âm lượng . Ví dụ:

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

Lưu ý rằng như tên của nó, --privilegedkhông an toàn và cần được xử lý cẩn thận.


4
Không cần -v - đặc quyền đã có nghĩa là quyền truy cập vào tất cả các thiết bị
Art

12
Có cơ chế nào như thế này cho máy khách Windows docker không?
Pascal

Sử dụng giải pháp này tôi không thấy các thiết bị từ thùng chứa docker ... Dưới đây là chi tiết stackoverflow.com/questions/37213812 về vấn đề của tôi. Đánh giá cao sự giúp đỡ! Cảm ơn.
kashandr

1
Vẫn không hoạt động nếu thiết bị USB được kết nối sau khi Docker đang chạy.
Franklin Dattein

Ý tôi là, nó không ánh xạ thiết bị theo / tty / USBX mặc dù lsusb có thể liệt kê nó.
Franklin Dattein

78

Với các phiên bản hiện tại của Docker, bạn có thể sử dụng --devicecờ để đạt được những gì bạn muốn mà không cần phải cấp quyền truy cập cho tất cả các thiết bị USB.

Ví dụ: nếu bạn chỉ muốn /dev/ttyUSB0truy cập trong bộ chứa Docker của mình, bạn có thể làm một cái gì đó như:

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

3
chỉ cần lưu ý rằng thiết bị không thể là một liên kết tượng trưng tại thời điểm này. github.com/docker/docker/issues/13840
wligtenberg

6
bằng cách sử dụng --devicecờ, làm cách nào để xác định /dev/<device>thiết bị Android được liên kết trên máy chủ, đặc biệt là khi sử dụng Docker Quickstart Terminal (VirtualBox Host) cho Windows hoặc Mac?
DanCat

1
Điều này hoạt động tốt nếu tên thiết bị của bạn không bao giờ thay đổi. Nhưng nếu bạn đang sử dụng một cái gì đó động sử dụng các thiết bị trong / dev / bus / usb thì điều này sẽ không hoạt động vì tên thiết bị thay đổi khi bạn cắm và rút phích cắm. Thay vào đó, bạn sẽ cần giải pháp -v (âm lượng) ở trên.
Brad Grissom

1
Các quy tắc @DanCat udev có thể đảm bảo rằng thiết bị của bạn gắn kết với một đường dẫn tĩnh
C. Reed

1
Tại sao mọi người sẽ quan tâm đến việc chỉ có quyền truy cập vào một thiết bị usb ?? Các thiết bị USB có nghĩa là được kết nối với qnd bị ngắt kết nối và điều đó cần được thực hiện trong thời gian chạy ứng dụng. USB không phải là SATA hay thứ gì đó, bạn không thể mong đợi thứ gì đó luôn tồn tại ... Và tôi không nghĩ mọi người chỉ khởi động ứng dụng qua docker cho các lần chạy duy nhất và thoát chúng ngay khi các thiết bị usb bị ngắt kết nối, phải không? Tôi tưởng tượng giống như các ứng dụng loại dịch vụ hơn, không phải là các lọ chạy đơn lẻ ... Nhưng cảm ơn, thực sự điều đó có thể giúp một số kịch bản rất hạn chế phù hợp
Arturas M

17

--devicehoạt động cho đến khi thiết bị USB của bạn được rút / cắm lại và sau đó thiết bị ngừng hoạt động. Bạn phải sử dụng thiết bị cgroup. Hãy đi xung quanh nó.
Bạn chỉ có thể sử dụng -v /dev:/devnhưng điều đó không an toàn vì nó ánh xạ tất cả các thiết bị từ máy chủ của bạn vào trong thùng chứa, bao gồm cả các thiết bị đĩa thô, v.v. Về cơ bản, điều này cho phép container chứa root trên máy chủ, thường không phải là những gì bạn muốn.
Sử dụng phương pháp tiếp cận nhóm là tốt hơn trong khía cạnh đó và hoạt động trên các thiết bị được thêm vào sau khi container được bắt đầu.

Xem chi tiết tại đây: Truy cập các thiết bị USB trong Docker mà không cần sử dụng - đặc quyền

Hơi khó dán một chút, nhưng tóm lại, bạn cần lấy số chính cho thiết bị nhân vật của mình và gửi nó đến cgroup:

189 là số lượng lớn / dev / ttyUSB *, mà bạn có thể nhận được với 'ls -l'. Nó có thể khác trên hệ thống của bạn so với của tôi:

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

Sau đó bắt đầu container của bạn như thế này:

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

không làm điều này, bất kỳ thiết bị mới được cắm hoặc khởi động lại sau khi container bắt đầu, sẽ nhận được ID bus mới và sẽ không được phép truy cập vào container.


7
Đối với những người đã làm điều này, xin vui lòng giúp đỡ và nói những gì bạn muốn được cải thiện. Tôi đã viết trang này để giúp đỡ những người khác gặp phải vấn đề chúng tôi đã làm. Tôi sẽ thành thật một chút khi nói rằng tôi đang bị tắt khi cố gắng chia sẻ lại và cũng giúp mọi người về stackoverflow: - /
Marc Merlin

Nếu bạn đọc câu trả lời của tôi, bạn sẽ thấy rằng việc thêm âm lượng '-v / dev: / dev' sẽ cấp quyền truy cập vào các thiết bị được cắm động.
rrpilot

5
rrpilot: -v / dev: / dev không cung cấp cho bạn tất cả / dev, bao gồm / dev / sda và những thứ khác mà bạn thực sự không muốn để lộ cho người dùng root trong vùng chứa. Nói cách khác, giải pháp của bạn không hoạt động, nhưng nó không an toàn. Của tôi xung quanh vấn đề đó. Tôi sẽ chỉnh sửa câu trả lời của tôi để chỉ ra điều đó.
Marc Merlin

1
Câu trả lời có thể được thực hiện tốt hơn bằng cách hiển thị cách lấy số chính và làm rõ rằng 189phải được thay thế. Có devices.allowthể tìm thấy mô tả về những gì cần gửi tại đây: kernel.org/doc/Documentation/cgroup-v1/devices.txt
Craig Younkins

1
Có một tính năng mới của Docker làm cho việc này đơn giản hơn một chút: "--device-cgroup-rule" ( docs.docker.com/engine/reference/commandline/create/ kẹp )
tianon

14

Tôi muốn mở rộng các câu trả lời đã được đưa ra để bao gồm hỗ trợ cho các thiết bị được kết nối động không được ghi lại /dev/bus/usbvà cách để hoạt động này khi sử dụng máy chủ Windows cùng với VM boot2docker.

Nếu bạn đang làm việc với Windows, bạn sẽ cần thêm bất kỳ quy tắc USB nào cho các thiết bị mà bạn muốn Docker truy cập trong trình quản lý VirtualBox. Để làm điều này, bạn có thể dừng VM bằng cách chạy:

host:~$ docker-machine stop default

Mở Trình quản lý VirtualBox và thêm hỗ trợ USB với các bộ lọc theo yêu cầu.

Khởi động VM boot2docker:

host:~$ docker-machine start default

Vì các thiết bị USB được kết nối với VM boot2docker, nên các lệnh cần được chạy từ máy đó. Mở một thiết bị đầu cuối với VM và chạy lệnh chạy docker:

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

Lưu ý, khi lệnh được chạy như thế này, thì chỉ các thiết bị USB được kết nối trước đó mới được chụp. Cờ âm lượng chỉ được yêu cầu nếu bạn muốn nó hoạt động với các thiết bị được kết nối sau khi container được khởi động. Trong trường hợp đó, bạn có thể sử dụng:

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

Lưu ý, tôi đã phải sử dụng /devthay vì /dev/bus/usbtrong một số trường hợp để chụp một thiết bị như thế nào /dev/sg2. Tôi chỉ có thể giả sử điều tương tự sẽ đúng với các thiết bị như /dev/ttyACM0hoặc /dev/ttyUSB0.

Các lệnh chạy docker cũng sẽ hoạt động với máy chủ Linux.


Điểm tốt trong việc gắn / dev: / dev thay thế. Điều đó cho phép linh hoạt hơn trong việc chụp các thiết bị khác và cũng giúp cho yếu tố động.
kotakotakota

và cũng ảnh hưởng đến bảo mật và sự cô lập của máy chủ của bạn.
Exadra37

@ Exadra37 Nó ... và nếu điều đó quan trọng trong ứng dụng của bạn, bạn không nên sử dụng cái này. Tuy nhiên, điều quan trọng cần lưu ý là có một số ứng dụng mà bạn không quan tâm và không sử dụng docker để cách ly. Trong trường hợp cụ thể của tôi, bạn có thể chạy một ứng dụng Linux được đóng gói trên Windows.
rrpilot

3

Một tùy chọn khác là điều chỉnh udev, điều khiển cách các thiết bị được gắn kết và với những đặc quyền nào. Hữu ích để cho phép truy cập không root vào các thiết bị nối tiếp. Nếu bạn có thiết bị gắn vĩnh viễn, --devicetùy chọn là cách tốt nhất để đi. Nếu bạn có thiết bị phù du, đây là những gì tôi đã sử dụng:

1. Đặt quy tắc udev

Theo mặc định, các thiết bị nối tiếp được gắn để chỉ người dùng root mới có thể truy cập thiết bị. Chúng ta cần thêm một quy tắc udev để làm cho chúng dễ đọc bởi những người dùng không phải root.

Tạo một tệp có tên /etc/udev/rules.d/99-serial.rules. Thêm dòng sau vào tập tin đó:

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

MODE = "0666" sẽ cung cấp cho tất cả người dùng quyền đọc / ghi (nhưng không thực thi) cho các thiết bị ttyUSB của bạn. Đây là tùy chọn cho phép nhất và bạn có thể muốn hạn chế điều này hơn nữa tùy thuộc vào yêu cầu bảo mật của bạn. Bạn có thể đọc trên udev để tìm hiểu thêm về cách kiểm soát những gì xảy ra khi thiết bị được cắm vào cổng Linux.

2. Gắn thư mục vào / dev từ máy chủ đến vùng chứa

Các thiết bị nối tiếp thường là phù du (có thể cắm và rút phích cắm bất cứ lúc nào). Vì lý do này, chúng tôi không thể gắn vào thiết bị trực tiếp hoặc thậm chí thư mục / dev / serial, bởi vì những thứ đó có thể biến mất khi mọi thứ được rút ra. Ngay cả khi bạn cắm lại và thiết bị xuất hiện trở lại, về mặt kỹ thuật, đây là một tệp khác với tệp được gắn vào, vì vậy Docker sẽ không nhìn thấy nó. Vì lý do này, chúng tôi gắn toàn bộ thư mục / dev từ máy chủ vào thùng chứa. Bạn có thể làm điều này bằng cách thêm lệnh âm lượng sau vào lệnh chạy Docker của bạn:

-v /dev:/dev

Nếu thiết bị của bạn được gắn vĩnh viễn, thì sử dụng tùy chọn --device hoặc giá đỡ âm lượng cụ thể hơn có thể là một tùy chọn tốt hơn từ góc độ bảo mật.

3. Chạy container trong chế độ đặc quyền

Nếu bạn không sử dụng tùy chọn --device và được gắn trong toàn bộ thư mục / dev, bạn sẽ được yêu cầu chạy container ở chế độ đặc quyền (Tôi sẽ kiểm tra nội dung cgroup được đề cập ở trên để xem liệu có thể xóa được không ). Bạn có thể làm điều này bằng cách thêm phần sau vào lệnh chạy Docker của bạn:

--privileged

4. Truy cập thiết bị từ thư mục / dev / serial / by-id

Nếu thiết bị của bạn có thể được cắm và rút phích cắm, Linux không đảm bảo thiết bị sẽ luôn được gắn tại cùng một vị trí ttyUSBxxx (đặc biệt nếu bạn có nhiều thiết bị). May mắn thay, Linux sẽ tự động tạo một liên kết tượng trưng đến thiết bị trong thư mục / dev / serial / by-id. Các tập tin trong thư mục này sẽ luôn được đặt tên giống nhau.

Đây là tóm tắt nhanh, tôi có một bài viết blog đi sâu vào chi tiết hơn.


2

Thật khó để chúng tôi liên kết một thiết bị USB cụ thể với hộp chứa docker cũng cụ thể. Như bạn có thể thấy, cách được đề xuất để đạt được là:

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

Nó sẽ liên kết tất cả các thiết bị với container này. Nó không an toàn. Mỗi container được cấp để vận hành tất cả chúng.

Một cách khác là ràng buộc các thiết bị bằng devpath. Nó có thể trông giống như:

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

hoặc --device(tốt hơn, không privileged):

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

An toàn hơn nhiều. Nhưng thực sự rất khó để biết sự sai lệch của một thiết bị cụ thể là gì.

Tôi đã viết repo này để giải quyết vấn đề này.

https://github.com/williamfzc/usb2container

Sau khi triển khai máy chủ này, bạn có thể dễ dàng lấy tất cả thông tin của các thiết bị được kết nối thông qua yêu cầu HTTP:

curl 127.0.0.1:9410/api/device

và lấy:

{
    "/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"
    },
    ...
}

và ràng buộc chúng vào thùng chứa của bạn. Ví dụ: bạn có thể thấy DEVNAME của thiết bị này là /dev/bus/usb/001/120:

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

Có lẽ nó sẽ giúp.


0

Với các phiên bản mới nhất của docker, điều này là đủ:

docker run -ti --privileged ubuntu bash

Nó sẽ cấp quyền truy cập vào tất cả các tài nguyên hệ thống (ví dụ: in / dev)


2
privateileded là một lựa chọn khủng khiếp để sử dụng cho bảo mật, mặc dù có, nó hoạt động.
Marc Merlin

2
Nếu được sử dụng để lập trình những thứ như công cụ liên quan đến Arduino thì giải pháp này rất tốt
Jose Cabrera Zuniga

0

Thêm vào các câu trả lời ở trên, đối với những người muốn sử dụng nhanh thiết bị USB ngoài (ổ cứng, ổ đĩa flash) hoạt động bên trong docker và không sử dụng chế độ riêng tư:

Tìm đường dẫn đến thiết bị của bạn trên máy chủ:

sudo fdisk -l

Bạn có thể nhận ra ổ đĩa của mình bằng dung lượng khá dễ dàng từ danh sách. Sao chép đường dẫn này (ví dụ như sau /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

Mount devpath này (thích hợp hơn /media):

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

Sau đó, bạn có thể sử dụng điều này như một thông số để docker runthích:

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

hoặc trong docker soạn theo khối lượng:

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

Và bây giờ khi bạn chạy và nhập container của mình, bạn sẽ có thể truy cập ổ đĩa bên trong container tại /media/<mount folder name>

TUYÊN BỐ TỪ CHỐI:

  1. Điều này có thể sẽ không hoạt động đối với các thiết bị nối tiếp như webcam, v.v. Tôi chỉ thử nghiệm điều này cho các ổ lưu trữ USB.
  2. Nếu bạn cần kết nối lại và ngắt kết nối thiết bị thường xuyên, phương pháp này sẽ gây khó chịu và cũng không hoạt động trừ khi bạn đặt lại đường dẫn gắn kết và khởi động lại container.
  3. Tôi đã sử dụng docker 17,06 + theo quy định trong các tài liệu

0

Nếu bạn muốn truy cập động các thiết bị USB có thể được cắm trong khi bộ chứa docker đang chạy, ví dụ: truy cập webcam usb được đính kèm tại / dev / video0, bạn có thể thêm quy tắc nhóm khi khởi động bộ chứa. Tùy chọn này không cần một thùng chứa - đặc quyền và chỉ cho phép truy cập vào các loại phần cứng cụ thể.

Bước 1

Kiểm tra số lượng thiết bị chính của loại thiết bị bạn muốn thêm. Bạn có thể tra cứu nó trong tài liệu kernel linux . Hoặc bạn có thể kiểm tra nó cho thiết bị của bạn. Ví dụ: để kiểm tra số chính của thiết bị cho webcam được kết nối với / dev / video0, bạn có thể thực hiện a ls -la /dev/video0. Điều này dẫn đến một cái gì đó như:

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

Trong đó số đầu tiên (81) là số chính của thiết bị. Một số số chính của thiết bị phổ biến:

  • 81: webcam usb
  • 188: chuyển đổi usb sang nối tiếp

Bước 2

Thêm quy tắc khi bạn khởi động container docker:

  • Thêm --device-cgroup-rule='c major_number:* rmw'quy tắc cho mọi loại thiết bị bạn muốn truy cập
  • Thêm quyền truy cập vào thông tin udev để bộ chứa docker có thể nhận thêm thông tin trên các thiết bị usb của bạn với -v /run/udev:/run/udev:ro
  • Ánh xạ âm lượng / dev vào thùng chứa docker của bạn với -v /dev:/dev

Gói (lại

Vì vậy, để thêm tất cả webcam webcam và thiết bị serial2usb vào thùng chứa docker của bạn, hãy làm:

docker run -it -v /dev:/dev --device-cgroup-rule='c 188:* rmw' --device-cgroup-rule='c 81:* rmw' ubuntu bash
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.