Cribs de segurança: Docker



Os contêineres do Docker são a tecnologia de contêiner mais popular. Inicialmente, era usado principalmente para ambientes de desenvolvimento e teste e, com o tempo, passou para a produção. Os contêineres do Docker começaram a se multiplicar no ambiente de produção, como cogumelos depois da chuva, mas poucos dos que usam essa tecnologia pensaram em como publicar com segurança os contêineres do Docker.

Com base no OWASP , preparamos uma lista de regras, cuja implementação protegerá significativamente seu ambiente, construído em contêineres Docker.

Regra 0

A máquina host e o Docker devem conter todas as atualizações atuais.


Para se proteger contra vulnerabilidades conhecidas que levam ao escape do ambiente de contêiner para o sistema host, o que geralmente resulta em escalonamento de privilégios no sistema host, a instalação de todos os patches para o SO host, o Docker Engine e o Docker Machine é extremamente importante.

Além disso, os contêineres (ao contrário das máquinas virtuais) compartilham o kernel com o host, portanto a exploração do kernel em execução no contêiner é executada diretamente no kernel do host. Por exemplo, uma exploração de escalonamento de privilégios do kernel (como Dirty COW) em execução em um contêiner bem isolado resultará em acesso root no host.

Regra 1

Não dê acesso ao soquete do daemon do Docker


O serviço Docker (daemon) usa o soquete UNIX /var/run/docker.sock para conexões de API recebidas. O proprietário deste recurso deve ser o usuário raiz. E de nenhuma outra maneira. Alterar os direitos de acesso a esse soquete é essencialmente equivalente a conceder acesso root ao sistema host.

Além disso, você não deve atrapalhar o soquete /var/run/docker.sock com contêineres, onde é possível ficar sem ele, pois, nesse caso, comprometer o serviço no contêiner levará ao controle completo do sistema host. Se você tiver contêineres que usem algo parecido com isto:

-v /var/run/docker.sock://var/run/docker.sock 

ou para docker-compor:

 volumes: - "/var/run/docker.sock:/var/run/docker.sock" 

necessidade urgente de mudar isso.

E a última - nunca, ouça, nunca use o soquete TCP do Docker sem a certeza absoluta de que você precisa, especialmente sem o uso de métodos de proteção adicionais (pelo menos autorização). Por padrão, o soquete do Docker TCP abre a porta na interface externa 0.0.0.0:2375 (2376, no caso de HTTPs) e permite controlar totalmente os contêineres e, com ele, o sistema host em potencial.

Regra 2

Configurar um usuário não privilegiado dentro do contêiner


Configurar um contêiner para usar um usuário sem privilégios é a melhor maneira de evitar um ataque de escalação de privilégios. Isso pode ser feito de várias maneiras:

1. Usando a opção "-u" do comando "docker run":

 docker run -u 4000 alpine 

2. Durante a criação da imagem:

 FROM alpine RUN groupadd -r myuser && useradd -r -g myuser myuser <      root-, ,  > USER myuser 

3. Ative o suporte ao "espaço de nome do usuário" (ambiente do usuário) no daemon do Docker:

 --userns-remap=default 

Leia mais sobre isso na documentação oficial .

No Kubernetes, o último é configurado no Contexto de Segurança através da opção runAsNonRoot:

 kind: ... apiVersion: ... metadata: name: ... spec: ... containers: - name: ... image: .... securityContext: ... runAsNonRoot: true ... 

Regra 3

Limitar recursos de contêiner


No Linux, a partir do kernel 2.2, existe uma maneira de controlar os recursos de processos privilegiados chamados Linux Kernel Capabilities (para obter detalhes, consulte o link).

O Docker usa um conjunto predefinido desses recursos do kernel por padrão. E isso permite que você altere esse conjunto usando os comandos:

 --cap-drop —     --cap-add —     

A melhor configuração de segurança é primeiro desativar todos os recursos (--cap-drop all) e depois conectar apenas os necessários. Por exemplo, assim:

 docker run --cap-drop all --cap-add CHOWN alpine 

E o mais importante (!): Evite executar contêineres com o sinalizador –privileged !!!

No Kubernetes, a restrição de recursos do kernel do Linux é configurada no contexto de segurança por meio da opção de recursos:

 kind: ... apiVersion: ... metadata: name: ... spec: ... containers: - name: ... image: .... securityContext: ... capabilities: drop: - all add: - CHOWN ... 

Regra 4

Use o sinalizador sem novos privilégios


Ao iniciar um contêiner, é útil usar o sinalizador --security-opt = no-new-privilégios, que impede a escalação de privilégios dentro do contêiner.

No Kubernetes, a restrição Linux Kernel Capabilities é configurada no Contexto de Segurança por meio da opção allowPrivilegeEscalation:

 kind: ... apiVersion: ... metadata: name: ... spec: ... containers: - name: ... image: .... securityContext: ... allowPrivilegeEscalation: false ... 

Regra 5

Desativar a comunicação entre contêineres


Por padrão, a comunicação entre contêineres é ativada no Docker, o que significa que todos os contêineres podem se comunicar (usando a rede docker0). Esse recurso pode ser desativado executando o serviço Docker com o sinalizador –icc = false.

Regra 6

Usar módulos de segurança do Linux (Linux Security Module - seccomp, AppArmor, SELinux)


Por padrão, o Docker já usa perfis para os módulos de segurança do Linux. Portanto, nunca desative os perfis de segurança! O máximo que pode ser feito com eles é reforçar as regras.

O perfil padrão para seccomp está disponível aqui .

O Docker também usa o AppArmor para proteção e o próprio Docker Engine gera um perfil padrão para o AppArmor quando o contêiner é iniciado. Em outras palavras, em vez de:

 $ docker run --rm -it hello-world 

inicia:

 $ docker run --rm -it --security-opt apparmor=docker-default hello-world 

A documentação também fornece um exemplo de um perfil do AppArmor para nginx, o que é bem possível (necessário!) Para usar:

 #include <tunables/global> profile docker-nginx flags=(attach_disconnected,mediate_deleted) { #include <abstractions/base> network inet tcp, network inet udp, network inet icmp, deny network raw, deny network packet, file, umount, deny /bin/** wl, deny /boot/** wl, deny /dev/** wl, deny /etc/** wl, deny /home/** wl, deny /lib/** wl, deny /lib64/** wl, deny /media/** wl, deny /mnt/** wl, deny /opt/** wl, deny /proc/** wl, deny /root/** wl, deny /sbin/** wl, deny /srv/** wl, deny /tmp/** wl, deny /sys/** wl, deny /usr/** wl, audit /** w, /var/run/nginx.pid w, /usr/sbin/nginx ix, deny /bin/dash mrwklx, deny /bin/sh mrwklx, deny /usr/bin/top mrwklx, capability chown, capability dac_override, capability setuid, capability setgid, capability net_bind_service, deny @{PROC}/* w, # deny write for all files directly in /proc (not in a subdir) # deny write to files not in /proc/<number>/** or /proc/sys/** deny @{PROC}/{[^1-9],[^1-9][^0-9],[^1-9s][^0-9y][^0-9s],[^1-9][^0-9][^0-9][^0-9]*}/** w, deny @{PROC}/sys/[^k]** w, # deny /proc/sys except /proc/sys/k* (effectively /proc/sys/kernel) deny @{PROC}/sys/kernel/{?,??,[^s][^h][^m]**} w, # deny everything except shm* in /proc/sys/kernel/ deny @{PROC}/sysrq-trigger rwklx, deny @{PROC}/mem rwklx, deny @{PROC}/kmem rwklx, deny @{PROC}/kcore rwklx, deny mount, deny /sys/[^f]*/** wklx, deny /sys/f[^s]*/** wklx, deny /sys/fs/[^c]*/** wklx, deny /sys/fs/c[^g]*/** wklx, deny /sys/fs/cg[^r]*/** wklx, deny /sys/firmware/** rwklx, deny /sys/kernel/security/** rwklx, } 

Regra 7

Limitar recursos de contêiner


Essa regra é bastante simples: para impedir que os contêineres devorem todos os recursos do servidor durante o próximo ataque de DoS / DDoS, podemos definir limites de uso de memória para cada contêiner individualmente. Você pode limitar: quantidade de memória, CPU, número de contêineres reiniciados.

Então vamos em ordem.

A memória

A opção -m ou --memory

A quantidade máxima de memória que um contêiner pode usar. O valor mínimo é 4m (4 megabytes).

Opção --memory-swap

Opção para configurar a troca (arquivo de troca). Configurado astuciosamente:

  • Se --memory-swap> 0, o sinalizador –memory também deve ser definido. Nesse caso, a troca de memória mostra a quantidade total de memória disponível para o contêiner junto com a troca.
  • Um exemplo mais simples. Se --memory = "300m" e --memory-swap = "1g", o contêiner poderá usar 300MB de memória e 700MB de swap (1g - 300m).
  • Se --memory-swap = 0, a configuração será ignorada.
  • Se --memory-swap estiver definido com o mesmo valor que --memory, o contêiner não terá swap.
  • Se --memory-swap não for especificado, mas --memory for especificado, o número de trocas será igual ao dobro da quantidade de memória especificada. Por exemplo, se --memory = "300m" e --memory-swap não estiver definido, o contêiner utilizará 300MB de memória e 600MB de swap.
  • Se --memory-swap = -1, o contêiner utilizará toda a troca possível no sistema host.

Nota para a hostess: o utilitário gratuito lançado dentro do contêiner não mostra o valor real da troca disponível para o contêiner, mas o número de trocas de host.

Opção --oom-kill-disable

Permite ativar ou desativar o killer OOM (falta de memória).

Atenção! Você pode desativar o OOM Killer apenas com a opção --memory definida, caso contrário, com falta de memória dentro do contêiner, o kernel começará a matar os processos do sistema host.

Outras opções de configuração de gerenciamento de memória, como --memory-swappiness, --memory-reservation e --kernel-memory, são mais para ajustar o desempenho do contêiner.

CPU

Opção --cpus

A opção define a quantidade de recursos de processador disponíveis que o contêiner pode usar. Por exemplo, se tivermos um host com duas CPUs e definirmos --cpus = "1.5", será garantido que o contêiner use um processador e meio.

Opção --cpuset-cpus

Configura o uso de núcleos ou CPUs específicos. O valor pode ser especificado com um hífen ou vírgula. No primeiro caso, a faixa de núcleos permitidos será indicada, no segundo núcleos específicos.

Número de reinicializações do contêiner

 --restart=on-failure:<number_of_restarts> 

Essa configuração define quantas vezes o Docker tentará reiniciar o contêiner se ele travar inesperadamente. O contador é redefinido se o estado do contêiner tiver sido alterado para em execução.

É recomendável definir um número positivo pequeno, por exemplo, 5, para evitar reinicializações sem fim de um serviço que não está funcionando.

Regra 8

Use sistemas de arquivos e volume somente leitura


Se o contêiner não gravar nada em algum lugar, será necessário usar o sistema de arquivos somente leitura o máximo possível. Isso complicará bastante a vida de um intruso em potencial.

Um exemplo de como iniciar um contêiner com sistema de arquivos somente leitura:

 docker run --read-only alpine 

Um exemplo de conexão de volume no modo somente leitura:

 docker run -v volume-name:/path/in/container:ro alpine 

Regra 9

Use ferramentas de análise de segurança de contêiner


As ferramentas devem ser usadas para detectar contêineres com vulnerabilidades conhecidas. Ainda não existem muitos, mas são:

• Grátis:


• Comercial:


E para o Kubernetes, existem ferramentas para detectar erros de configuração:

Source: https://habr.com/ru/post/pt448704/


All Articles