Ferramentas de linha de comando para contêineres — usando Snyk com Buildah, Podman e Skopeo
9 de dezembro de 2020
0 minutos de leituraCom o amadurecimento do ecossistema de contêineres, uma coisa não falta: opções — tanto de software quanto de maneiras de integrar tudo.
Uma dessas opções é a combinação de Buildah, Podman e Skopeo — três ferramentas de linha de comando de código aberto que surgiram no ecossistema RedHat. Como o nome sugere, Buildah oferece uma ampla gama de recursos para criar contêineres e imagens compatíveis com OCI; Podman permite gerenciar e executar contêineres OCI; e Skopeo é uma ferramenta para trabalhar com registros de contêineres.
Neste post, vamos ver como o uso de padrões abertos permite que a verificação de contêineres da Snyk funcione com todas essas tecnologias.
Primeiro, vamos conhecer o Skopeo. Essa é uma ferramenta de linha de comando para mover imagens de contêineres entre repositórios e também salvá-las localmente. O Skopeo permite gerar imagens como arquivos Docker ou arquivos OCI, formatos compatíveis com a Snyk ao verificar imagens no sistema de arquivos. Vamos usá-lo para baixar uma imagem do Quay.io e verificá-la localmente no nosso sistema de arquivos:
Como podemos ver, essa imagem usa uma versão do Ubuntu que chegou ao fim da vida útil e, por isso, contém várias vulnerabilidades que não serão corrigidas. Com o Skopeo, podemos baixar imagens de qualquer registro de contêineres compatível com os padrões, em formatos aceitos pela Snyk, e verificá-las localmente.
Buildah
Agora, vamos conhecer o Buildah. O Buildah é uma ferramenta de linha de comando que cria imagens, mas não as executa. Por isso, foi pensado para ser usado em conjunto com o Podman. Ele também foi projetado para operar sem privilégios de root, usando namespaces de usuário no kernel do Linux para isolar um shell semelhante ao root, mas com apenas privilégios de usuário. Assim, administradores podem permitir que desenvolvedores criem e mantenham imagens de contêineres e, ao mesmo tempo, sigam o princípio do menor privilégio no ambiente de build.
Embora o Buildah possa trabalhar com Dockerfiles de forma semelhante ao próprio Docker, ele também permite desenvolver contêineres e imagens usando outros fluxos de trabalho, como Bash ou Python. Isso pode trazer mais flexibilidade e facilitar a depuração dos builds de contêineres. O Buildah permite, por exemplo, montar o contêiner intermediário durante o processo de build, para que scripts verifiquem se a execução ocorreu corretamente antes de continuar, compilem software externamente ou até solicitem dados ao usuário. Isso oferece uma alternativa aos processos de build em várias etapas, às vezes complicados, que se tornaram prática recomendada no Docker.
Vamos ver um exemplo prático de uso do Buildah.
Uma das primeiras coisas a observar no Buildah é que podemos usar Dockerfiles para criar nossos contêineres, se esse for o fluxo de trabalho ideal para você. Veja este Dockerfile como exemplo:
Aqui usamos a imagem CentOS 8 como base, clonamos o código-fonte do GNU Hello, instalamos alguns pré-requisitos e, em seguida, compilamos e instalamos o binário.
Podemos fazer esse build automaticamente com o Buildah usando a opção bud (build-using-dockerfile):
Isso claramente não é uma boa prática, pois incluímos tanto o código-fonte quanto os pré-requisitos do build na imagem final, que também viola a maioria das boas práticas para o desenvolvimento de imagens seguras. Com o Docker, a prática recomendada nesse caso seria um build em várias etapas. Poderíamos usar um Dockerfile com várias etapas no Buildah, mas é ainda mais interessante usar comandos nativos do Buildah e escrever scripts para isso. Para reproduzir exatamente o que fizemos com o Dockerfile acima, executaríamos o seguinte script:
No entanto, o Buildah também permite montar o contêiner intermediário durante o processo de build. Assim, podemos compilar o código-fonte no host e simplesmente copiar o binário resultante para o contêiner antes de criar a imagem. Dessa forma, aproveitamos os recursos do host durante os builds e podemos fazer outras coisas, como usar o gerenciador de pacotes do host para instalar pacotes no contêiner. Também não precisamos de acesso root para isso, pois o Buildah permite usar namespaces de usuário para montar sem privilégios, com o comando unshare:
Como você pode ver no exemplo acima, depois de executar o comando unshare, temos um shell root, mas em um namespace de usuário, sem privilégios completos de root. Isso permite montar o sistema de arquivos do contêiner durante o processo de build. Como se trata de um script Bash, também podemos aproveitar todos os recursos de uma linguagem de programação: usar condicionais e loops ou até solicitar dados ao usuário durante o processo.
Podman
Depois de executar esse script e criar nossa imagem, também podemos verificá-la com a Snyk — é aí que entra o Podman. O Podman é um mecanismo de contêineres sem daemon para desenvolver, gerenciar e executar contêineres OCI. Como Buildah e Podman são baseados em padrões abertos, a Snyk sempre conseguiu verificar imagens criadas ou baixadas pelo Podman: basta usá-lo para salvar a imagem em disco e verificá-la no sistema de arquivos. O Podman permite salvar imagens como arquivos Docker padrão ou no formato de arquivo OCI, ambos compatíveis com a Snyk.
Como nossa imagem base é pública e está no Dockerhub, também podemos simplesmente fazer o seguinte para verificá-la com a Snyk CLI:
Nesse caso, a Snyk interage diretamente com o Dockerhub e verifica a imagem original.
No entanto, se a imagem existir apenas localmente no Podman ou estiver em um repositório privado no Dockerhub que exige login, nenhuma dessas opções funcionará. Para demonstrar, vamos tentar verificar nossa imagem criada diretamente no Podman:
Para esses casos de uso, a Snyk utiliza o cliente local do Docker, seja pela API do registro, seja pelo socket do Docker. Como não há um binário do Docker no sistema, a Snyk tentou usar a API do registro e não conseguiu se conectar. Por padrão, a Snyk não sabe que essas imagens existem localmente no Podman nem como usá-lo para se comunicar com a origem.
Felizmente, o Podman oferece recursos simples de compatibilidade por meio do pacote podman-docker. Ele cria um binário falso do Docker, que basicamente é um wrapper de shell para o binário do Podman, e adiciona um link simbólico entre o socket da API do Podman e o local padrão do arquivo de socket do Docker. Por padrão, esse pacote cria um link simbólico para /var/run/podman/podman.sock, que é o local padrão quando a API do Podman é executada como root.
Ainda não funciona, mas desta vez recebemos um erro diferente. A Snyk detectou um binário chamado docker e agora está tentando se conectar ao local padrão do socket privilegiado do Docker. Como ainda não estamos executando a API do Podman, a conexão falha novamente.
Como vimos antes, o pacote podman-docker cria um link simbólico de /var/run/podman/podman.sock para /var/run/docker.sock, supondo que a API do Podman será executada com privilégios.
Um dos principais problemas do socket do Docker é que, por padrão, ele é executado com privilégios, o que é considerado um risco de segurança em determinados ambientes. Na maioria dos ambientes locais de desenvolvimento, não precisamos executar a API do Podman dessa forma: podemos usar um socket sem privilégios em outro local. Também podemos informar esse local à Snyk usando a variável de ambiente DOCKER_HOST, que aceita os formatos de URL tcp:// e unix://.
Em outro shell, vamos executar a API do Podman com um socket sem privilégios:
Esse processo será executado em primeiro plano, então você precisará pressionar Ctrl+C para encerrá-lo ao terminar o teste. Agora, precisamos definir a variável de ambiente DOCKER_HOST:
Por fim, vamos tentar verificar novamente nossa imagem local com a Snyk:
Funcionou! Agora a Snyk está operando corretamente e se comunicando com o Podman pelo socket sem privilégios.
Para configurar o DOCKER_HOST permanentemente no seu shell, você pode adicionar o comando export ao arquivo .bashrc para manter essa configuração.
Por fim, queremos que toda essa configuração esteja disponível automaticamente sempre que fizermos login. Para isso, podemos aproveitar os recursos de ativação por socket do systemd, que iniciam nosso socket automaticamente quando fazemos login e tentamos nos conectar a ele.
Primeiro, precisamos configurar o socket:
Depois, associamos o socket a um serviço:
Por fim, recarregamos a configuração do systemd e habilitamos o serviço!
Conclusão
Pronto: a verificação de imagens da Snyk CLI com o Podman funciona exatamente como com o Docker. Assim, desenvolvedores podem verificar de forma abrangente a segurança de imagens Docker ou OCI locais como parte do fluxo de desenvolvimento, sem precisar de privilégios elevados. Também vimos como usar Skopeo e Buildah com a Snyk graças aos padrões abertos!
Ainda não tem uma conta na Snyk? Cadastre-se gratuitamente e use a Snyk para verificar imagens de contêineres e dependências de código aberto.
Comece a jogar Capture the Flag
Aprenda a resolver desafios de Capture the Flag assistindo sob demanda ao nosso workshop virtual introdutório.