Permissão negada ao acessar o diretório host no Docker

282

Resumindo: estou tentando montar um diretório de host no Docker, mas não consigo acessá-lo no contêiner, mesmo que as permissões de acesso sejam boas.

Os detalhes:

estou fazendo

sudo docker run -i -v /data1/Downloads:/Downloads ubuntu bash

e depois

ls -al

Isso me dá:

total 8892
drwxr-xr-x.  23 root root    4096 Jun 18 14:34 .
drwxr-xr-x.  23 root root    4096 Jun 18 14:34 ..
-rwxr-xr-x.   1 root root       0 Jun 18 14:34 .dockerenv
-rwx------.   1 root root 9014486 Jun 17 22:09 .dockerinit
drwxrwxr-x.  18 1000 1000   12288 Jun 16 11:40 Downloads
drwxr-xr-x.   2 root root    4096 Jan 29 18:10 bin
drwxr-xr-x.   2 root root    4096 Apr 19  2012 boot
drwxr-xr-x.   4 root root     340 Jun 18 14:34 dev
drwxr-xr-x.  56 root root    4096 Jun 18 14:34 etc
drwxr-xr-x.   2 root root    4096 Apr 19  2012 home

e muito mais linhas como essa (acho que essa é a parte relevante).

Se eu fizer

cd /Downloads
ls

o resultado é

ls: cannot open directory .: Permission denied

O host é o Fedora 20, com Docker 1.0.0 e go1.2.2.

O que está acontecendo de errado?

user3753011
fonte

Respostas:

269

Veja esta postagem no blog do Project Atomic sobre Volumes e SELinux para obter a história completa.

Especificamente:

Isso ficou mais fácil recentemente, desde que o Docker finalmente fundiu um patch que aparecerá no docker-1.7 (estivemos carregando o patch no docker-1.6 no RHEL, CentOS e Fedora).

Este patch adiciona suporte para "z" e "Z" como opções nas montagens de volume (-v).

Por exemplo:

docker run -v /var/db:/var/db:z rhel7 /bin/sh

Realizará automaticamente o chcon -Rt svirt_sandbox_file_t /var/db descrito na página de manual.

Melhor ainda, você pode usar o Z.

docker run -v /var/db:/var/db:Z rhel7 /bin/sh

Isso rotulará o conteúdo dentro do contêiner com o rótulo MCS exato com o qual o contêiner será executado, basicamente é executado chcon -Rt svirt_sandbox_file_t -l s0:c1,c2 /var/dbonde s0:c1,c2difere para cada contêiner.

gregswift
fonte
18
Isso funciona como um encanto. Outras soluções são principalmente soluções alternativas.
Tuxdna 01/03
4
cf. a seção de etiquetas de volume na documentação da janela de encaixe
maxschlepzig 18/03/16
Oh, cara, isso realmente funciona. Eu finalmente encontrei isso. Muito Obrigado! Existe alguma documentação oficial sobre isso?
Kirby
1
O Upstream o apresenta como o último parágrafo desta seção docs.docker.com/engine/reference/commandline/run/…
gregswift
1
É possível fixar as permissões sob SELinux durante a montagem do volume como somente de leitura, ao mesmo tempo, utilizando as duas opções ao mesmo tempo separados por vírgulas: -v $(pwd):/app:ro,Z. Isso deve ser marcado como a resposta correta.
Danirod 22/04/19
263

É uma questão do SELinux .

Você pode emitir temporariamente

su -c "setenforce 0"

no host para acessar ou adicione uma regra SELinux executando

chcon -Rt svirt_sandbox_file_t /path/to/volume
user3761313
fonte
3
/ path / to / volume é o caminho do host? Se sim, parece que essa solução não funcionaria com contêineres de dados?
Roy Truelove
6
não se esqueça de executar su -c "setenforce 1" ... caso contrário, ele funcionará apenas porque o SELinux ainda está desativado
vcarel
isso resolveu meu problema. obrigado, espero que eles tenham uma correção para isso.
Hokutosei 29/03
19
Adicionar a regra selinux é a melhor maneira, pois na maioria dos casos não é uma boa ideia executar contêineres no modo privilegiado.
Zoro_77
7
Como disse Zoro_77, adicione uma regra e stopdisablingselinux.com ;)
GabLeRoux
71

AVISO: Esta solução tem riscos de segurança.

Tente executar o contêiner como privilegiado:

sudo docker run --privileged=true -i -v /data1/Downloads:/Downloads ubuntu bash

Outra opção (que eu não tentei) seria criar um contêiner privilegiado e, em seguida, criar contêineres não privilegiados dentro dele.

John Phillips
fonte
1
@JBernardo Qual das duas opções resolveu o problema?
user100464
@ user100464--privileged=true
JBernardo
1
Não ajude no meu caso. Debian Whezzy com kernel 3.16 portado, mas não ativou a configuração do SELinux. :(
aholbreich
se você estiver usando o docker-compositer, adicione 'privileged: true' #
Lionel Morrison /
35
Não faça isso. --privilegedé um risco de segurança
Navin
38

Normalmente, os problemas de permissão com uma montagem de volume do host ocorrem porque o uid / gid dentro do contêiner não tem acesso ao arquivo de acordo com as permissões uid / gid do arquivo no host. No entanto, este caso específico é diferente.

O ponto no final da string de permissão drwxr-xr-x., indica que o SELinux está configurado. Ao usar uma montagem de host com o SELinux, você precisa passar uma opção extra para o final da definição de volume:

  • A zopção indica que o conteúdo da montagem de ligação é compartilhado entre vários contêineres.
  • A Zopção indica que o conteúdo da montagem de ligação é privado e não compartilhado.

Seu comando de montagem de volume ficaria assim:

sudo docker run -i -v /data1/Downloads:/Downloads:z ubuntu bash

Veja mais sobre montagens de host com o SELinux em: https://docs.docker.com/storage/#configure-the-selinux-label


Para outras pessoas que veem esse problema com contêineres em execução como um usuário diferente, é necessário garantir que o uid / gid do usuário dentro do contêiner tenha permissões para o arquivo no host. Nos servidores de produção, isso geralmente é feito controlando o uid / gid no processo de criação da imagem para corresponder a um uid / gid no host que tem acesso aos arquivos (ou melhor ainda, não use montagens de host na produção).

Um volume nomeado é geralmente preferido para hospedar montagens porque inicializará o diretório de volumes a partir do diretório de imagens, incluindo qualquer propriedade e permissão de arquivo. Isso acontece quando o volume está vazio e o contêiner é criado com o volume nomeado.

Os usuários do MacOS agora têm o OSXFS, que lida com o uid / gid automaticamente entre o host e os contêineres do Mac. Um lugar em que não ajuda são os arquivos de dentro da VM incorporada que são montados no contêiner, como /var/lib/docker.sock.

Para ambientes de desenvolvimento em que o host uid / gid pode mudar por desenvolvedor, minha solução preferida é iniciar o contêiner com um ponto de entrada em execução como raiz, corrigir o uid / gid do usuário dentro do contêiner para corresponder ao volume do host uid / gid e então use gosu para soltar da raiz para o usuário do contêiner para executar o aplicativo dentro do contêiner. O script importante para isso está fix-permsnos meus scripts de imagem base, que podem ser encontrados em: https://github.com/sudo-bmitch/docker-base

A parte importante do fix-perms script é:

# update the uid
if [ -n "$opt_u" ]; then
  OLD_UID=$(getent passwd "${opt_u}" | cut -f3 -d:)
  NEW_UID=$(stat -c "%u" "$1")
  if [ "$OLD_UID" != "$NEW_UID" ]; then
    echo "Changing UID of $opt_u from $OLD_UID to $NEW_UID"
    usermod -u "$NEW_UID" -o "$opt_u"
    if [ -n "$opt_r" ]; then
      find / -xdev -user "$OLD_UID" -exec chown -h "$opt_u" {} \;
    fi
  fi
fi

Isso coloca o uid do usuário dentro do contêiner, o uid do arquivo e, se não corresponderem, chama usermod para ajustar o uid. Por fim, ele faz uma busca recursiva para corrigir os arquivos que não mudaram os uids. Eu gosto disso melhor do que executar um contêiner com um -u $(id -u):$(id -g)sinalizador, porque o código do ponto de entrada acima não exige que cada desenvolvedor execute um script para iniciar o contêiner, e quaisquer arquivos fora do volume que pertencem ao usuário terão suas permissões corrigidas.


Você também pode fazer com que o docker inicialize um diretório de host de uma imagem usando um volume nomeado que executa uma montagem de ligação. Esse diretório deve existir com antecedência e você precisa fornecer um caminho absoluto para o diretório do host, diferente dos volumes do host em um arquivo de composição que podem ser caminhos relativos. O diretório também deve estar vazio para a janela de encaixe inicializá-lo. Existem três opções diferentes para definir um volume nomeado para uma montagem de ligação:

  # create the volume in advance
  $ docker volume create --driver local \
      --opt type=none \
      --opt device=/home/user/test \
      --opt o=bind \
      test_vol

  # create on the fly with --mount
  $ docker run -it --rm \
    --mount type=volume,dst=/container/path,volume-driver=local,volume-opt=type=none,volume-opt=o=bind,volume-opt=device=/home/user/test \
    foo

  # inside a docker-compose file
  ...
  volumes:
    bind-test:
      driver: local
      driver_opts:
        type: none
        o: bind
        device: /home/user/test
  ...

Por fim, se você tentar usar espaços de nome de usuário, verá que os volumes do host têm problemas de permissão porque os uid / gid dos contêineres são alterados. Nesse cenário, provavelmente é mais fácil evitar volumes de host e usar apenas volumes nomeados.

BMitch
fonte
32

Em access.redhat.com:Sharing_Data_Across_Containers :

As configurações de volume do host não são portáteis, pois dependem do host e podem não funcionar em nenhuma outra máquina. Por esse motivo, não há Dockerfile equivalente para montar diretórios de host no contêiner. Além disso, esteja ciente de que o sistema host não tem conhecimento da política SELinux de contêiner. Portanto, se a política do SELinux for aplicada, o diretório do host montado não poderá ser gravado no contêiner, independentemente da configuração rw. Atualmente, você pode contornar isso atribuindo o tipo de política SELinux adequado ao diretório host ":

chcon -Rt svirt_sandbox_file_t host_dir

Onde host_dir é um caminho para o diretório no sistema host montado no contêiner.

Parece ser apenas uma solução alternativa, mas tentei e funciona.

Thomas8
fonte
14

Eu verifiquei que chcon -Rt svirt_sandbox_file_t /path/to/volume funciona e você não precisa executar como um contêiner privilegiado.

Isto é sobre:

  • Docker versão 0.11.1-dev, build 02d20af / 0.11.1
  • CentOS 7 como host e contêiner com o SELinux ativado.
jeff mccormick
fonte
2
Consulte github.com/docker/docker/pull/5910 para obter suporte oficial para redefinição de etiquetas no Docker.
cpuguy83
13

Tente docker volume create.

mkdir -p /data1/Downloads
docker volume create --driver local --name hello --opt type=none --opt device=/data1/Downloads --opt o=uid=root,gid=root --opt o=bind
docker run -i -v hello:/Downloads ubuntu bash

Consulte o documento https://docs.docker.com/engine/reference/commandline/volume_create/

cupen
fonte
3
Tentei muitas respostas sobre esse problema no SO, mas na verdade este ajudou. Obrigado!
Paul
Ele resolveu o erro de permissão. Mas agora, se eu estou tentando montar um local físico, ele monta voulme ???? @ cupen
kunal verma
1
@kunalverma Sim. Se você não gostar, aqui está a resposta mais fácil. stackoverflow.com/a/31334443/4909388
cupen
4

Eu tive um problema semelhante, o meu foi causado por uma incompatibilidade entre o UID do host e o UID do usuário do contêiner. A correção foi passar o UID do usuário como argumento para a construção do docker e criar o usuário do contêiner com o mesmo UID.

No DockerFile:

ARG UID=1000
ENV USER="ubuntu"
RUN useradd -u $UID -ms /bin/bash $USER

Na etapa de construção:

docker build <path/to/Dockerfile> -t <tag/name> --build-arg UID=$UID

Depois disso, executar o contêiner e os comandos de acordo com o OP me deu o resultado esperado.

RoboCop87
fonte
1
E se você não conhece o UID até o tempo de execução? (Estou construindo uma imagem para os colegas, para empacotar algumas ferramentas que retornam ao sistema de arquivos, mas eles têm UIDs diferentes). Eu acho que eu poderia mantê-lo root e apenas adduser em execução?
Inger 29/05
Infelizmente não tenho uma boa resposta para isso. Se alguém tiver uma solução, eu também estaria interessado nela. Suspeito que a funcionalidade do ponto de entrada do Docker possa fornecer uma solução.
RoboCop87 16/07/19
0

Resolvi esse problema usando um contêiner de dados, isso também tem a vantagem de isolar os dados da camada de aplicativo. Você pode executá-lo assim:

docker run --volumes-from=<container-data-name> ubuntu

Este tutorial fornece uma boa explicação sobre o uso de contêineres de dados.

tmsss
fonte
-1

Na minha situação, o problema era diferente. Não sei por que, mas mesmo que o diretório no host tivesse chmod 777rodado nele, dentro do docker era visível como 755.

Correndo dentro do contêiner sudo chmod 777 my_volume_dirconsertou.

CodeSandwich
fonte
5
chmod 777quase nunca conserta alguma coisa.
Erki Aring
Sinto muito, mas você perdeu o ponto. O ponto é que os privilégios internos do contêiner foram reduzidos e não puderam ser corrigidos de fora.
CodeSandwich
-2

sudo -s fez o truque para mim no MAC

Nachiket Joshi
fonte
1
Se você está com votos negativos, deixe um comentário e explique o motivo. Encontrei exatamente o mesmo problema e consegui resolver isso com o sudo -s.
Nachiket Joshi
Nem toda imagem do Docker possui sudo, e isso não é possível em todos os cenários.
SOFe 28/12
2
Não instale o sudo em contêineres. Um invasor pode usar o sudo dentro de um contêiner.
Arnold Balliu