Docker และ --userns-remap วิธีจัดการสิทธิ์ระดับเสียงเพื่อแชร์ข้อมูลระหว่างโฮสต์และคอนเทนเนอร์


96

ในนักเทียบท่าไฟล์ที่สร้างขึ้นภายในคอนเทนเนอร์มักจะมีความเป็นเจ้าของที่ไม่สามารถคาดเดาได้ในขณะที่ตรวจสอบไฟล์เหล่านั้นจากโฮสต์ เจ้าของไฟล์บนไดรฟ์ข้อมูลคือ root (uid 0) โดยค่าเริ่มต้น แต่ทันทีที่บัญชีผู้ใช้ที่ไม่ใช่รูทเกี่ยวข้องกับคอนเทนเนอร์และเขียนไปยังระบบไฟล์เจ้าของจะสุ่มจากมุมมองของโฮสต์มากขึ้นหรือน้อยลง

เป็นปัญหาเมื่อคุณต้องการเข้าถึงข้อมูลปริมาณจากโฮสต์โดยใช้บัญชีผู้ใช้เดียวกันซึ่งเรียกใช้คำสั่งนักเทียบท่า

วิธีแก้ปัญหาทั่วไปคือ

  • บังคับให้ผู้ใช้ uIDs ในเวลาสร้างใน Dockerfiles (ไม่ใช่แบบพกพา)
  • ส่งผ่าน UID ของผู้ใช้โฮสต์ไปยังdocker runคำสั่งเป็นตัวแปรสภาพแวดล้อมจากนั้นรันchownคำสั่งบางคำบนไดรฟ์ข้อมูลในสคริปต์จุดเริ่มต้น

โซลูชันทั้งสองนี้สามารถควบคุมสิทธิ์จริงภายนอกคอนเทนเนอร์ได้

ฉันคาดว่าเนมสเปซของผู้ใช้จะเป็นทางออกสุดท้ายสำหรับปัญหานี้ ฉันได้ทำการทดสอบกับเวอร์ชัน 1.10 ที่เพิ่งเปิดตัวและ - ผู้ใช้ - ทำการรีแมปไปยังบัญชีเดสก์ท็อปของฉัน อย่างไรก็ตามฉันไม่แน่ใจว่ามันสามารถทำให้การเป็นเจ้าของไฟล์บนไดรฟ์ข้อมูลที่เมาท์จัดการง่ายขึ้นฉันกลัวว่ามันอาจจะตรงกันข้าม

สมมติว่าฉันเริ่มคอนเทนเนอร์พื้นฐานนี้

docker run -ti -v /data debian:jessie /bin/bash
echo 'hello' > /data/test.txt
exit

จากนั้นตรวจสอบเนื้อหาจากโฮสต์:

ls -lh /var/lib/docker/100000.100000/volumes/<some-id>/_data/

-rw-r--r-- 1 100000 100000 6 Feb  8 19:43 test.txt

หมายเลข '100000' นี้เป็น UID ย่อยของผู้ใช้โฮสต์ของฉัน แต่เนื่องจากไม่ตรงกับ UID ของผู้ใช้ของฉันฉันจึงยังไม่สามารถแก้ไข test.txt ได้หากไม่มีสิทธิ์ ผู้ใช้ย่อยรายนี้ดูเหมือนจะไม่มีความสัมพันธ์ใด ๆ กับผู้ใช้ปกติของฉันนอกนักเทียบท่า มันไม่ถูกแมปกลับ

วิธีแก้ปัญหาที่กล่าวถึงก่อนหน้านี้ในโพสต์นี้ซึ่งประกอบด้วยการจัดแนว UID ระหว่างโฮสต์และคอนเทนเนอร์ไม่ทำงานอีกต่อไปเนื่องจากการUID->sub-UIDแมปที่เกิดขึ้นในเนมสเปซ

จากนั้นมีวิธีเรียกใช้นักเทียบท่าโดยเปิดใช้งานเนมสเปซผู้ใช้ (เพื่อความปลอดภัยที่ดีขึ้น) ในขณะที่ยังทำให้ผู้ใช้โฮสต์ที่เรียกใช้นักเทียบท่าสามารถเป็นเจ้าของไฟล์ที่สร้างจากไดรฟ์ข้อมูลได้หรือไม่


ฉันคิดว่าถ้าคุณจะแชร์ไดรฟ์ข้อมูลระหว่างโฮสต์และคอนเทนเนอร์ที่ผู้ใช้เนมสเปซจะไม่เป็นส่วนหนึ่งของการแก้ปัญหา ตัวเลือกที่สองของคุณ ("การส่ง UID ของผู้ใช้โฮสต์ไปยังคำสั่ง docker run เป็นตัวแปรสภาพแวดล้อมแล้วรันคำสั่ง chown บนไดรฟ์ข้อมูลใน entrypoint script") น่าจะเป็นทางออกที่ดีที่สุด
larsks

4
นักเทียบท่าเองดูเหมือนจะไม่สนับสนุนให้ใช้ไดรฟ์ข้อมูลที่เขียนได้ที่ติดตั้งบนโฮสต์ เนื่องจากฉันไม่ได้ใช้บริการคลาวด์และใช้เฉพาะรูปภาพที่เชื่อถือได้ของฉันเองตอนนี้ฉันจึงสงสัยว่าประโยชน์ด้านความปลอดภัยของผู้ใช้ NS นั้นคุ้มค่ากับความสะดวกสบายมากหรือไม่
Stéphane C.

@ StéphaneC. คุณเคยพบแนวทางที่ดีกว่านี้หรือไม่?
EightyEight

4
น่าเสียดายที่การไม่ใช้เนมสเปซผู้ใช้และการส่ง UID จากโฮสต์ยังคงเป็นตัวเลือกของฉัน ฉันหวังว่าจะมีวิธีที่เหมาะสมในการจับคู่ผู้ใช้ในอนาคต ฉันสงสัย แต่ก็ยังคงลืมตาอยู่
Stéphane C.

คำตอบ:


46

หากคุณสามารถจัดเรียงผู้ใช้และกลุ่มล่วงหน้าได้ก็เป็นไปได้ที่จะกำหนด UID และ GID ด้วยวิธีที่เฉพาะเจาะจงดังกล่าวเพื่อให้ผู้ใช้โฮสต์สอดคล้องกับผู้ใช้ที่ระบุชื่อภายในคอนเทนเนอร์

นี่คือตัวอย่าง (Ubuntu 14.04, Docker 1.10):

  1. สร้างผู้ใช้บางรายที่มีรหัสตัวเลขคงที่:

    useradd -u 5000 ns1
    
    groupadd -g 500000 ns1-root
    groupadd -g 501000 ns1-user1
    
    useradd -u 500000 -g ns1-root ns1-root
    useradd -u 501000 -g ns1-user1 ns1-user1 -m
    
  2. แก้ไขช่วง ID รองที่สร้างขึ้นโดยอัตโนมัติใน/etc/subuidและ/etc/subgidไฟล์:

    ns1:500000:65536
    

    (โปรดทราบว่าไม่มีบันทึกสำหรับns1-rootและns1-user1เนื่องจากMAX_UIDและMAX_GIDข้อ จำกัด ใน/etc/login.defs)

  3. เปิดใช้งานเนมสเปซผู้ใช้ใน/etc/default/docker:

    DOCKER_OPTS="--userns-remap=ns1"
    

    รีสตาร์ท daemon service docker restartตรวจสอบให้แน่ใจว่า/var/lib/docker/500000.500000ไดเร็กทอรีถูกสร้างขึ้น

    ตอนนี้ภายในคอนเทนเนอร์ที่คุณมีrootและuser1และบนโฮสต์ - ns1-rootและns1-user1ด้วยรหัสที่ตรงกัน

    อัปเดต:เพื่อรับประกันว่าผู้ใช้ที่ไม่ใช่รูทมี ID คงที่ในคอนเทนเนอร์ (เช่น user1 1000: 1000) ให้สร้างอย่างชัดเจนในระหว่างการสร้างอิมเมจ

ทดลองขับ:

  1. เตรียมไดเร็กทอรีวอลุ่ม

    mkdir /vol1
    chown ns1-root:ns1-root /vol1
    
  2. ลองใช้จากภาชนะ

    docker run --rm -ti -v /vol1:/vol1 busybox sh
    echo "Hello from container" > /vol1/file
    exit
    
  3. ลองดูจากโฮสต์

    passwd ns1-root
    login ns1-root
    cat /vol1/file
    echo "can write" >> /vol1/file
    

ไม่พกพาและดูเหมือนแฮ็ค แต่ใช้งานได้


3
น่าสนใจมากและสมควรได้รับ +1 แต่คุณยังต้องตรวจสอบให้แน่ใจว่า user1 ในภาพของคุณได้รับการกำหนด UID 1000 มิฉะนั้นคุณจะไม่แน่ใจว่าจะได้รับ UID 501000 บนโฮสต์ Btw เราแน่ใจอย่างยิ่งว่าสูตรนี้จะอยู่เสมอsubUID lower bound + UID in imageหากเราเรียกใช้รูปภาพต่างๆจำนวนมากโดยมีผู้ใช้ตั้งค่า ID เป็น 1,000
Stéphane C.

@ StéphaneC. จุดดี! เพิ่มหมายเหตุเกี่ยวกับการแก้ไข ID ในรูปภาพ สำหรับสูตรฉันจะทดลองเพิ่มเติมกับรูปภาพของตัวเองและจะอัปเดตคำตอบหากพบสิ่งใด
amartynov

1
หากคุณจัดเรียงผู้ใช้และกลุ่มด้วยตัวเองในโฮสต์และในคอนเทนเนอร์คุณจำเป็นต้องใช้คุณลักษณะ "เนมสเปซผู้ใช้" หรือไม่
Tristan

1
เนมสเปซที่คุณสร้างจะแยกผู้ใช้โฮสต์ออกจากผู้ใช้คอนเทนเนอร์ แต่คุณอาจต้องใช้หลายเนมสเปซสำหรับคอนเทนเนอร์โดยเฉพาะอย่างยิ่งเมื่ออิมเมจที่เป็นทางการ (เช่น mysql one) สร้างผู้ใช้โดยไม่มี uid อย่างชัดเจน คุณจะจัดการกับหลายเนมสเปซได้อย่างไรเมื่อตัวเลือก - ผู้ใช้รีแมปต้องการเพียงอันเดียว
Tristan

2
@amartynov ฉันขอถามว่าทำไมคุณถึงมีปัญหาในการระบุ UID (5000) สำหรับผู้ใช้ "ns1" ของคุณ เนื่องจากเป็นชื่อ (ไม่ใช่ UID) ที่คุณอ้างอิงในไฟล์ subuid และ subgid จึงดูเหมือนว่าไม่สำคัญว่าผู้ใช้รายนี้จะได้รับ UID อะไร ฉันขาดความสัมพันธ์บางอย่างเนื่องจากความคล้ายคลึงกันระหว่าง 5,000 ถึง 500000 อาจแนะนำได้หรือไม่?
Jollymorphic

2

วิธีแก้ปัญหาอย่างหนึ่งคือการกำหนด uid ของผู้ใช้แบบไดนามิกในเวลาสร้างเพื่อให้ตรงกับโฮสต์

ตัวอย่างDockerfile:

FROM ubuntu
# Defines argument which can be passed during build time.
ARG UID=1000
# Create a user with given UID.
RUN useradd -d /home/ubuntu -ms /bin/bash -g root -G sudo -u $UID ubuntu
# Switch to ubuntu user by default.
USER ubuntu
# Check the current uid of the user.
RUN id
# ...

จากนั้นสร้างเป็น:

docker build --build-arg UID=$UID -t mycontainer .

และเรียกใช้เป็น:

docker run mycontainer

หากคุณมีคอนเทนเนอร์อยู่แล้วให้สร้างที่เก็บกระดาษห่อด้วยสิ่งต่อไปนี้Dockerfile:

FROM someexistingcontainer
ARG UID=1000
USER root
# This assumes you've the existing user ubuntu.
RUN usermod -u $UID ubuntu
USER ubuntu

สิ่งนี้สามารถห่อด้วยdocker-compose.yml:

version: '3.4'
services:
  myservice:
    command: id
    image: myservice
    build:
      context: .
    volumes:
    - /data:/data:rw

จากนั้นสร้างและเรียกใช้เป็น:

docker-compose build --build-arg UID=$UID myservice; docker-compose run myservice

1
ฉันไม่อยากดูไม่เป็นมิตร แต่นี่เป็นหนึ่งในวิธีแก้ปัญหาที่ระบุไว้ในคำถามเดิม แต่ไม่ใช่วิธีแก้ปัญหาและไม่เกี่ยวข้องกับเนมสเปซของผู้ใช้
Stéphane C.

@ StéphaneC. คุณสามารถแสดงความคิดเห็นเกี่ยวกับคำถามที่เกี่ยวข้องนี้ได้หรือไม่? stackoverflow.com/questions/60274418/…
Overexchange

-1

คุณสามารถหลีกเลี่ยงปัญหาที่ได้รับอนุญาตโดยใช้คำสั่งdocker cp

การเป็นเจ้าของถูกกำหนดให้กับผู้ใช้และกลุ่มหลักที่ปลายทาง ตัวอย่างเช่นไฟล์ที่คัดลอกไปยังคอนเทนเนอร์ถูกสร้างขึ้นโดยUID:GIDผู้ใช้รูท ไฟล์ที่คัดลอกไปยังเครื่องโลคัลจะถูกสร้างขึ้นด้วยUID:GIDผู้ใช้ที่เรียกใช้docker cpคำสั่ง

นี่คือตัวอย่างของคุณที่เปลี่ยนไปใช้docker cp:

$ docker run -ti -v /data debian:jessie /bin/bash
root@e33bb735a70f:/# echo 'hello' > /data/test.txt
root@e33bb735a70f:/# exit
exit
$ docker volume ls
DRIVER              VOLUME NAME
local               f073d0e001fb8a95ad8d919a5680e72b21a457f62a40d671b63c62ae0827bf93
$ sudo ls -l /var/lib/docker/100000.100000/volumes/f073d0e001fb8a95ad8d919a5680e72b21a457f62a40d671b63c62ae0827bf93/_data
total 4
-rw-r--r-- 1 100000 100000 6 Oct  6 10:34 test.txt
$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS                          PORTS               NAMES
e33bb735a70f        debian:jessie       "/bin/bash"         About a minute ago   Exited (0) About a minute ago                       determined_hypatia
$ docker cp determined_hypatia:/data/test.txt .
$ ls -l test.txt 
-rw-r--r-- 1 don don 6 Oct  6 10:34 test.txt
$ cat test.txt
hello
$ 

อย่างไรก็ตามหากคุณต้องการอ่านไฟล์จากคอนเทนเนอร์คุณไม่จำเป็นต้องมีไดรฟ์ข้อมูลที่ตั้งชื่อ ตัวอย่างนี้ใช้คอนเทนเนอร์ที่มีชื่อแทนไดรฟ์ข้อมูลที่ตั้งชื่อ:

$ docker run -ti --name sandbox1 debian:jessie /bin/bash
root@93d098233cf3:/# echo 'howdy' > /tmp/test.txt
root@93d098233cf3:/# exit
exit
$ docker cp sandbox1:/tmp/test.txt .
$ ls -l test.txt
-rw-r--r-- 1 don don 6 Oct  6 10:52 test.txt
$ cat test.txt
howdy
$ 

ฉันพบว่าไดรฟ์ข้อมูลที่ตั้งชื่อมีประโยชน์เมื่อฉันต้องการคัดลอกไฟล์ลงในคอนเทนเนอร์ตามที่อธิบายไว้ในคำถามนี้


แต่docker cpเกี่ยวข้องกับการทำซ้ำข้อมูล ยิ่งไปกว่านั้นตามเอกสารเมื่อคัดลอกข้อมูลไปยังคอนเทนเนอร์จะตั้งรหัสความเป็นเจ้าของตามผู้ใช้รูทซึ่งมักไม่ใช่บัญชีที่เรียกใช้แอปที่มีคอนเทนเนอร์ ฉันไม่เห็นว่ามันช่วยแก้ปัญหาของเราได้อย่างไร
Stéphane C.

คุณพูดถูก @ Stéphaneเกี่ยวข้องกับการทำสำเนาข้อมูล อย่างไรก็ตามการทำสำเนาไฟล์ช่วยให้คุณสามารถกำหนดความเป็นเจ้าของและสิทธิ์ที่แตกต่างกันบนโฮสต์และในคอนเทนเนอร์ได้ docker cpช่วยให้คุณสามารถควบคุมการเป็นเจ้าของไฟล์ได้อย่างเต็มที่เมื่อคุณสตรีมไฟล์เก็บถาวร tar เข้าหรือออกจากคอนเทนเนอร์ คุณสามารถปรับความเป็นเจ้าของและสิทธิ์ของแต่ละรายการในไฟล์ tar ขณะสตรีมได้ดังนั้นคุณจึงไม่ จำกัด เฉพาะผู้ใช้รูท
Don Kirkby
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.