Docker en –userns-remap, hoe volumerechten te beheren om gegevens tussen host en container te delen?

In docker hebben bestanden die in containers zijn gemaakt de neiging om onvoorspelbaar eigendom te hebben terwijl ze vanaf de host worden geïnspecteerd. De eigenaar van de bestanden op een volume is standaard root (uid 0), maar zodra niet-root-gebruikersaccounts betrokken zijn bij de container en schrijven naar het bestandssysteem, worden eigenaren min of meer willekeurig vanuit het perspectief van de host.

Het is een probleem wanneer u toegang moet krijgen tot volumegegevens van de host met hetzelfde gebruikersaccount dat de docker-opdrachten aanroept.

Typische oplossingen zijn

  • gebruikers uID’s afdwingen tijdens het maken van Dockerfiles (niet-draagbaar)
  • het doorgeven van de UID van de hostgebruiker aan de opdracht docker runals een omgevingsvariabele en vervolgens enkele chown-opdrachten uitvoeren op de volumes in een entrypoint-script.

Beide oplossingen kunnen enige controle geven over de daadwerkelijke machtigingen buiten de container.

Ik verwachtte dat gebruikersnaamruimten de definitieve oplossing voor dit probleem zouden zijn. Ik heb enkele tests uitgevoerd met de onlangs uitgebrachte versie 1.10 en –userns-remap ingesteld op mijn desktopaccount. Ik weet echter niet zeker of het het eigendom van bestanden op gekoppelde volumes gemakkelijker kan maken, ik ben bang dat het eigenlijk het tegenovergestelde zou kunnen zijn.

Stel dat ik deze basiscontainer start

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

En inspecteer dan de inhoud van de host:

ls -lh /var/lib/docker/100000.100000/volumes/<some-id>/_data/
-rw-r--r-- 1 100000 100000 6 Feb  8 19:43 test.txt

Dit nummer ‘100000’ is een sub-UID van mijn hostgebruiker, maar aangezien het niet overeenkomt met de UID van mijn gebruiker, kan ik test.txt nog steeds niet bewerken zonder privileges. Deze subgebruiker lijkt geen affiniteit te hebben met mijn werkelijke gewone gebruiker buiten docker. Het is niet terug in kaart gebracht.

De eerder in dit bericht genoemde tijdelijke oplossingen, die bestonden uit het uitlijnen van UID’s tussen de host en de container, werken niet meer vanwege de UID->sub-UID-toewijzing die in de naamruimte voorkomt.

Is er dan een manier om docker uit te voeren met de gebruikersnaamruimte ingeschakeld (voor verbeterde beveiliging), terwijl de hostgebruiker die docker uitvoert toch eigenaar kan worden van de bestanden die op volumes zijn gegenereerd?


Antwoord 1, autoriteit 100%

Als u gebruikers en groepen van tevoren kunt regelen, is het mogelijk om UID’s en GID’s op zo’n specifieke manier toe te wijzen, zodat hostgebruikers overeenkomen met gebruikers met naamruimte in containers.

Hier is een voorbeeld (Ubuntu 14.04, Docker 1.10):

  1. Maak een aantal gebruikers met vaste numerieke ID’s:

    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. Bewerk handmatig automatisch gegenereerde ondergeschikte ID-bereiken in /etc/subuiden /etc/subgidbestanden:

    ns1:500000:65536
    

    (let op: er zijn geen records voor ns1-rooten ns1-user1vanwege MAX_UIDen MAX_GIDlimieten in /etc/login.defs)

  3. Gebruikersnaamruimten inschakelen in /etc/default/docker:

    DOCKER_OPTS="--userns-remap=ns1"
    

    Herstart daemon service docker restart, zorg ervoor dat de directory /var/lib/docker/500000.500000is aangemaakt.

    In containers heb je nu rooten user1, en op de host — ns1-rooten ns1-user1, met overeenkomende ID’s

    UPDATE:om te garanderen dat niet-rootgebruikers vaste ID’s in containers hebben (bijv. user1 1000:1000), maakt u deze expliciet aan tijdens het bouwen van de image.

Proefrit:

  1. Een volumemap voorbereiden

    mkdir /vol1
    chown ns1-root:ns1-root /vol1
    
  2. Probeer het uit een container

    docker run --rm -ti -v /vol1:/vol1 busybox sh
    echo "Hello from container" > /vol1/file
    exit
    
  3. Probeer het van de host

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

Niet draagbaar en ziet eruit als een hack, maar werkt.


Antwoord 2, autoriteit 6%

Een tijdelijke oplossing is om de gebruikersuid dynamisch toe te wijzen aan de buildtijd zodat deze overeenkomt met de host.

Voorbeeld 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
# ...

Vervolgens bouwen als:

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

en uitvoeren als:

docker run mycontainer

Als je een bestaande container hebt, maak dan een wrapper-container met het volgende Dockerfile:

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

Dit kan worden ingepakt in docker-compose.ymlzoals:

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

Vervolgens bouwen en uitvoeren als:

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

Antwoord 3

U kunt toestemmingsproblemen voorkomen door de docker cpcommando.

Eigendom is ingesteld op de gebruiker en de primaire groep op de bestemming. Bestanden die naar een container zijn gekopieerd, worden bijvoorbeeld gemaakt met UID:GIDvan de rootgebruiker. Bestanden die naar de lokale computer zijn gekopieerd, worden gemaakt met de UID:GIDvan de gebruiker die de opdracht docker cpheeft aangeroepen.

Hier is uw voorbeeld overgeschakeld naar het gebruik van docker cp:

$ docker run -ti -v /data debian:jessie /bin/bash
[email protected]:/# echo 'hello' > /data/test.txt
[email protected]:/# 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
$ 

Als je echter alleen bestanden uit een container wilt lezen, heb je het genoemde volume niet nodig. In dit voorbeeld wordt een benoemde container gebruikt in plaats van een benoemd volume:

$ docker run -ti --name sandbox1 debian:jessie /bin/bash
[email protected]:/# echo 'howdy' > /tmp/test.txt
[email protected]:/# 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
$ 

Ik vind volumes met een naam handig als ik bestanden naar een container wil kopiëren, zoals beschreven in deze vraag.

Other episodes