Skip to main content

Formatos de imagens de contêiner por dentro

Escrito por
Headshot of Agata Krajewska

Agata Krajewska

18 de novembro de 2020

0 minutos de leitura

Nos últimos anos, desde o lançamento do Docker, os contêineres se tornaram cada vez mais o padrão para distribuir software.

Vemos um número crescente de soluções baseadas em contêineres. Embora a inovação nesse espaço seja sempre bem-vinda, é necessário estabelecer alguns padrões para formatos e ambientes de execução.

Com o rápido crescimento do projeto Docker, as imagens Docker se tornaram padrão para diversas finalidades. Mas há um interesse claro em uma especificação única e aberta para contêineres, que seja:

  • independente de construções de nível mais alto, como um cliente específico ou uma pilha de orquestração;

  • não estar fortemente associada a nenhum fornecedor comercial ou projeto específico;

  • portátil em diversos sistemas operacionais, hardwares, arquiteturas de CPU, nuvens públicas etc.

A OCI (Open Container Initiative) é um projeto da Linux Foundation que desenvolve padrões abertos para virtualização no nível do sistema operacional, sobretudo para contêineres Linux.

O Docker doou à OCI um rascunho da especificação para o formato básico e o ambiente de execução, além do código de uma implementação de referência dessa especificação.

Hoje, várias ferramentas de build de código aberto oferecem suporte ao formato de imagem OCI, incluindo:

  • BuildKit: uma reimplementação otimizada do mecanismo de build do Docker

  • Podman: uma implementação alternativa da ferramenta de linha de comando do Docker

  • Buildah: uma alternativa de linha de comando para escrever Dockerfiles

A Snyk oferece suporte a todos os formatos comuns de imagens de contêineres. Por isso, podemos nos integrar a uma ampla variedade de ferramentas do ecossistema. Neste post, vamos conhecer algumas delas e mostrar como a Snyk funciona com cada uma!

Arquivo Docker

Primeiro, vamos conhecer o formato de arquivo Docker. Ele faz parte da v1 obsoleta da especificação de imagens do Docker. Você encontra a especificação completa aqui. Quando você executa docker save, esse é o formato padrão gerado pelo cliente Docker, usado para garantir a compatibilidade com versões anteriores de outras ferramentas. Os registros de contêineres usam um formato mais recente para armazenar imagens.

Se você estiver executando o Docker localmente e usando a CLI da Snyk ou qualquer uma das nossas integrações para contêineres, usaremos sua instância local do Docker para baixar a imagem, salvá-la no sistema de arquivos e iniciar a análise.

Vamos explorar a estrutura desse arquivo e descobrir o que interessa à Snyk!

Neste post, vou usar a imagem ubuntu:bionic. Primeiro, vamos salvar o arquivo no seu sistema de arquivos local:

docker save --output ubuntu-docker.tar ubuntu:bionic

A distribuição bionic tem três camadas de imagem, que vamos analisar mais de perto.

Depois de descompactar o arquivo tar, podemos conferir o que há dentro dele:

[ubuntu-docker] ll                                                                                                               
total 24
drwxr-xr-x  5 agatakrajewska  staff   160B 25 Sep 23:33 46076a325f0de3f745254638b8b0f0de343685b34e7ca6ec5cd0b6b7930eb7fa
drwxr-xr-x  5 agatakrajewska  staff   160B 25 Sep 23:33 468327b5cd7ce539db695bd0ef05dae8a4ff77b02870a8e823ed74dedad4bd55
-rw-r--r--  1 agatakrajewska  staff   3.3K 25 Sep 23:33 56def654ec22f857f480cdcc640c474e2f84d4be2e549a9d16eaba3f397596e9.json
drwxr-xr-x  5 agatakrajewska  staff   160B 25 Sep 23:33 8bf067b107a6f7444876e33c6ed85652355f679ac98ebab97ab3ebad63f0dff3
-rw-r--r--  1 agatakrajewska  staff   356B  1 Jan  1970 manifest.json
-rw-r--r--  1 agatakrajewska  staff    89B  1 Jan  1970 repositories

Vamos dar uma olhada em manifest.json:

[ubuntu-docker] cat manifest.json | jq                                                                                           
[
  {
    "Config": "56def654ec22f857f480cdcc640c474e2f84d4be2e549a9d16eaba3f397596e9.json",
    "RepoTags": [
      "ubuntu:bionic"
    ],
    "Layers": [
      "8bf067b107a6f7444876e33c6ed85652355f679ac98ebab97ab3ebad63f0dff3/layer.tar",
      "468327b5cd7ce539db695bd0ef05dae8a4ff77b02870a8e823ed74dedad4bd55/layer.tar",
      "46076a325f0de3f745254638b8b0f0de343685b34e7ca6ec5cd0b6b7930eb7fa/layer.tar"
    ]
  }
]

O arquivo aponta para a configuração do contêiner, onde encontramos informações muito úteis, como arquitetura, configuração, camadas do sistema de arquivos raiz etc. E, provavelmente a parte mais interessante para a Snyk, a propriedade Layers aponta para as camadas reais da imagem, que são apenas diretórios dentro do arquivo. Se entrarmos em um desses diretórios, veremos o que ele contém:

[8bf067b107a6f7444876e33c6ed85652355f679ac98ebab97ab3ebad63f0dff3] ll                                                            
total 128160
-rw-r--r--  1 agatakrajewska  staff     3B 25 Sep 23:33 VERSION
-rw-r--r--  1 agatakrajewska  staff   401B 25 Sep 23:33 json
-rw-r--r--  1 agatakrajewska  staff    63M 25 Sep 23:33 layer.tar

Vamos nos aprofundar um pouco mais e descompactar o layer.tar:

[layer] ll                                                                                                                       
total 0
drwxr-xr-x  87 agatakrajewska  staff   2.7K 21 Sep 18:17 bin
drwxr-xr-x   2 agatakrajewska  staff    64B 24 Apr  2018 boot
drwxr-xr-x   2 agatakrajewska  staff    64B 21 Sep 18:17 dev
drwxr-xr-x  68 agatakrajewska  staff   2.1K 21 Sep 18:17 etc
drwxr-xr-x   2 agatakrajewska  staff    64B 24 Apr  2018 home
drwxr-xr-x   8 agatakrajewska  staff   256B 23 May  2017 lib
drwxr-xr-x   3 agatakrajewska  staff    96B 21 Sep 18:16 lib64
drwxr-xr-x   2 agatakrajewska  staff    64B 21 Sep 18:14 media
drwxr-xr-x   2 agatakrajewska  staff    64B 21 Sep 18:14 mnt
drwxr-xr-x   2 agatakrajewska  staff    64B 21 Sep 18:14 opt
drwxr-xr-x   2 agatakrajewska  staff    64B 24 Apr  2018 proc
drwx------   4 agatakrajewska  staff   128B 21 Sep 18:17 root
drwxr-xr-x   5 agatakrajewska  staff   160B 21 Sep 18:14 run
drwxr-xr-x  68 agatakrajewska  staff   2.1K 21 Sep 18:17 sbin
drwxr-xr-x   2 agatakrajewska  staff    64B 21 Sep 18:14 srv
drwxr-xr-x   2 agatakrajewska  staff    64B 24 Apr  2018 sys
drwxrwxrwt   2 agatakrajewska  staff    64B 21 Sep 18:17 tmp
drwxr-xr-x  10 agatakrajewska  staff   320B 21 Sep 18:14 usr
drwxr-xr-x  13 agatakrajewska  staff   416B 21 Sep 18:17 var

Como você pode ver acima, o layer.tar contém apenas as alterações no sistema de arquivos da camada da imagem, incluindo dependências e outros binários, conforme o conteúdo da imagem. Depois de extrair e analisar o conteúdo

dessas camadas, podemos mostrar a lista de caminhos vulneráveis.

Agora, vamos executar snyk test na imagem salva no arquivo Docker. Para testar um arquivo local, basta especificar o prefixo docker-archive::

snyk container test docker-archive:ubuntu-docker.tar ou analisar a imagem do seu repositório Docker local:

snyk container test ubuntu:bionic

Nos dois casos, o resultado será exatamente o mesmo:

Testing ubuntu:bionic...

✗ Low severity vulnerability found in tar
  Description: Loop with Unreachable Exit Condition ('Infinite Loop')
  Info: <https://snyk.io/vuln/SNYK-UBUNTU1804-TAR-312298>
  Introduced through: tar@1.29b-2ubuntu0.1, pam/libpam-runtime@1.1.8-3.6ubuntu2.18.04.2
  From: tar@1.29b-2ubuntu0.1
  From: pam/libpam-runtime@1.1.8-3.6ubuntu2.18.04.2 > debconf@1.5.66ubuntu1 > perl/perl-base@5.26.1-6ubuntu0.3 > dpkg@1.19.0.5ubuntu2.3 > tar@1.29b-2ubuntu0.1

✗ Low severity vulnerability found in tar
  Description: NULL Pointer Dereference
  Info: <https://snyk.io/vuln/SNYK-UBUNTU1804-TAR-559435>
  Introduced through: tar@1.29b-2ubuntu0.1, pam/libpam-runtime@1.1.8-3.6ubuntu2.18.04.2
  From: tar@1.29b-2ubuntu0.1
  From: pam/libpam-runtime@1.1.8-3.6ubuntu2.18.04.2 > debconf@1.5.66ubuntu1 > perl/perl-base@5.26.1-6ubuntu0.3 > dpkg@1.19.0.5ubuntu2.3 > tar@1.29b-2ubuntu0.1

# vuln list continues in here
...

Organization:      example-org
Package manager:   deb
Project name:      docker-image|ubuntu
Docker image:      ubuntu:bionic
Licenses:          enabled

Tested 90 dependencies for known issues, found 31 issues.

Pro tip: use `--file` option to get base image remediation advice.
Example: $ snyk test --docker ubuntu:bionic --file=path/to/Dockerfile

Imagem OCI

Nos últimos meses, também adicionamos à CLI da Snyk a análise de arquivos OCI.

Vamos conferir a especificação e ver o que encontramos no arquivo! Nesta seção, também vou usar ubuntu:bionic.

Se você quiser inspecionar um arquivo tar OCI, execute o seguinte comando:

skopeo copy --override-os linux docker://ubuntu:bionic oci-archive:ubuntu.tar

Vou usar o Skopeo para salvar a imagem. É um utilitário de cli que facilita muito a execução de várias operações em imagens.

Para salvar uma imagem ubuntu no formato OCI, basta especificar o prefixo oci-archive: na saída tar.

Repare que também estou usando a opção --override-os — isso acontece porque estou no macOS e as imagens do Docker Hub para o Ubuntu só estão disponíveis para Linux. Também vamos falar sobre imagens com várias arquiteturas mais adiante.

Depois de descompactar o arquivo tar, podemos conferir o conteúdo:

[ubuntu] ll                                                                                                                      
total 16
drwxr-xr-x  3 agatakrajewska  staff    96B 28 Oct 11:18 blobs
-rw-r--r--  1 agatakrajewska  staff   186B 28 Oct 11:18 index.json
-rw-r--r--  1 agatakrajewska  staff    31B 28 Oct 11:18 oci-layout

Na raiz, temos um index.json interessante. Vamos conferir o conteúdo:

[ubuntu] cat index.json | jq                                                                                                     
{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "digest": "sha256:afa93a8ce255ca452ca8c88f4b5c821a466cf0a3e0148a31d0d97dfdb91d9aef",
      "size": 658
    }
  ]
}

Este é o nosso manifesto opcional de nível mais alto, que aponta para manifestos de imagens específicos. Ele contém informações sobre um conjunto de imagens que pode abranger várias arquiteturas e sistemas operacionais. Se quiser saber mais, confira a especificação completa.

Agora, vamos inspecionar o manifesto específico da imagem, indicado por index.json, no diretório blobs/sha256:

[sha256] cat afa93a8ce255ca452ca8c88f4b5c821a466cf0a3e0148a31d0d97dfdb91d9aef | jq                                               
{
  "schemaVersion": 2,
  "config": {
    "mediaType": "application/vnd.oci.image.config.v1+json",
    "digest": "sha256:33a51d09088285451e7a7525d4bd64fc15563264afe5a91ef84a8b3042018899",
    "size": 2426
  },
  "layers": [
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "digest": "sha256:171857c49d0f5e2ebf623e6cb36a8bcad585ed0c2aa99c87a055df034c1e5848",
      "size": 26701612
    },
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "digest": "sha256:419640447d267f068d2f84a093cb13a56ce77e130877f5b8bdb4294f4a90a84f",
      "size": 852
    },
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "digest": "sha256:61e52f862619ab016d3bcfbd78e5c7aaaa1989b4c295e6dbcacddd2d7b93e1f5",
      "size": 162
    }
  ]
}

Começamos a encontrar conteúdos interessantes, como a configuração do contêiner, que mais uma vez reúne informações úteis sobre a arquitetura das imagens, as camadas da imagem base, a configuração etc. Também temos informações sobre as camadas da imagem, que apontam para as camadas disponíveis na imagem ubuntu:bionic.

Como vemos abaixo, o conteúdo da primeira camada é exatamente igual ao do arquivo Docker mostrado acima.

[171857c49d0f5e2ebf623e6cb36a8bcad585ed0c2aa99c87a055df034c1e5848] ll                                                            
total 0
drwxr-xr-x  87 agatakrajewska  staff   2.7K 21 Sep 18:17 bin
drwxr-xr-x   2 agatakrajewska  staff    64B 24 Apr  2018 boot
drwxr-xr-x   2 agatakrajewska  staff    64B 21 Sep 18:17 dev
drwxr-xr-x  68 agatakrajewska  staff   2.1K 21 Sep 18:17 etc
drwxr-xr-x   2 agatakrajewska  staff    64B 24 Apr  2018 home
drwxr-xr-x   8 agatakrajewska  staff   256B 23 May  2017 lib
drwxr-xr-x   3 agatakrajewska  staff    96B 21 Sep 18:16 lib64
drwxr-xr-x   2 agatakrajewska  staff    64B 21 Sep 18:14 media
drwxr-xr-x   2 agatakrajewska  staff    64B 21 Sep 18:14 mnt
drwxr-xr-x   2 agatakrajewska  staff    64B 21 Sep 18:14 opt
drwxr-xr-x   2 agatakrajewska  staff    64B 24 Apr  2018 proc
drwx------   4 agatakrajewska  staff   128B 21 Sep 18:17 root
drwxr-xr-x   5 agatakrajewska  staff   160B 21 Sep 18:14 run
drwxr-xr-x  68 agatakrajewska  staff   2.1K 21 Sep 18:17 sbin
drwxr-xr-x   2 agatakrajewska  staff    64B 21 Sep 18:14 srv
drwxr-xr-x   2 agatakrajewska  staff    64B 24 Apr  2018 sys
drwxrwxrwt   2 agatakrajewska  staff    64B 21 Sep 18:17 tmp
drwxr-xr-x  10 agatakrajewska  staff   320B 21 Sep 18:14 usr
drwxr-xr-x  13 agatakrajewska  staff   416B 21 Sep 18:17 var

Você pode analisar seu arquivo OCI local executando:

snyk container test oci-archive:ubuntu.tar

Plataformas e arquiteturas variadas

Ao inspecionar os manifestos do arquivo Docker e da imagem OCI, vimos que ambos continham informações sobre a arquitetura. Vamos voltar e conferir o conteúdo do manifesto de configuração do contêiner no diretório ubuntu-docker, onde salvamos nosso docker archive:

[sha256] cat 56def654ec22f857f480cdcc640c474e2f84d4be2e549a9d16eaba3f397596e9.json | jq
{
  ...
  "architecture": "amd64",
  "os": "linux",
  ...
}

Entre outras informações, vemos a arquitetura e o sistema operacional. No meu caso, são amd64 e linux. E se nossa imagem fosse baseada em arm64 ou estivéssemos usando a plataforma windows?

Boa notícia: a Snyk também oferece suporte a outras plataformas. Basta passar a opção -—platform para a CLI da Snyk.

Já falamos sobre o formato de arquivo gerado por docker save. Agora, vamos conhecer o formato v2 schema usado para armazenar imagens no Docker Hub — há detalhes bem interessantes por trás dele. Esta é a segunda versão do v2 schema, criada para atender a dois objetivos principais. Segundo a documentação do Docker:

O primeiro é permitir imagens com várias arquiteturas por meio de um “manifesto fat”, que faz referência a manifestos de imagens para versões específicas de cada plataforma. O segundo é aproximar o mecanismo do Docker de imagens endereçáveis por conteúdo, com suporte a um modelo em que a configuração da imagem pode ser submetida a hash para gerar seu ID.

Usando o Docker local, podemos primeiro inspecionar o “manifesto fat” da imagem desejada para conferir quais variantes de plataforma estão disponíveis. Como explicamos acima, isso significa que um único repositório pode armazenar várias imagens para diferentes arquiteturas.

docker manifest inspect ubuntu:bionic

Como resultado, veremos uma matriz de manifestos:

{
   ...
  "manifests": [
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 943,
         "digest": "sha256:45c6f8f1b2fe15adaa72305616d69a6cd641169bc8b16886756919e7c01fa48b",
         "platform": {
            "architecture": "amd64",
            "os": "linux"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 943,
         "digest": "sha256:e80b8affb2361dc632c1fa8fcbf6b6514f750eb6ef99b7e7f825a55f849bfd89",
         "platform": {
            "architecture": "arm",
            "os": "linux",
            "variant": "v7"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 943,
         "digest": "sha256:01a2038b20d165ab7df81934f9849bdfbc59bd6f6322c5d11e341504f66ec266",
         "platform": {
            "architecture": "arm64",
            "os": "linux",
            "variant": "v8"
         }
      },
...

Agora que confirmamos que a imagem ubuntu está disponível para várias plataformas, vamos ver como analisar a imagem ubuntu para a arquitetura arm64.

Verifique se os recursos experimentais estão ativados no Docker do seu host e execute:

snyk container test --platform=linux/arm64 ubuntu:bionic

Vamos conferir o resultado:

Testing ubuntu:bionic...

✗ Low severity vulnerability found in tar
  Description: Loop with Unreachable Exit Condition ('Infinite Loop')
  Info: <https://snyk.io/vuln/SNYK-UBUNTU1804-TAR-312298>
  Introduced through: tar@1.29b-2ubuntu0.1, pam/libpam-runtime@1.1.8-3.6ubuntu2.18.04.2
  From: tar@1.29b-2ubuntu0.1
  From: pam/libpam-runtime@1.1.8-3.6ubuntu2.18.04.2 > debconf@1.5.66ubuntu1 > perl/perl-base@5.26.1-6ubuntu0.3 > dpkg@1.19.0.5ubuntu2.3 > tar@1.29b-2ubuntu0.1

✗ Low severity vulnerability found in tar
  Description: NULL Pointer Dereference
  Info: <https://snyk.io/vuln/SNYK-UBUNTU1804-TAR-559435>
  Introduced through: tar@1.29b-2ubuntu0.1, pam/libpam-runtime@1.1.8-3.6ubuntu2.18.04.2
  From: tar@1.29b-2ubuntu0.1
  From: pam/libpam-runtime@1.1.8-3.6ubuntu2.18.04.2 > debconf@1.5.66ubuntu1 > perl/perl-base@5.26.1-6ubuntu0.3 > dpkg@1.19.0.5ubuntu2.3 > tar@1.29b-2ubuntu0.1

# vuln list continues in here
...

Organization:      example-org
Package manager:   apk
Project name:      docker-image|ubuntu:bionic
Docker image:      alpine:3.12
Platform:          linux/arm64
Licenses:          enabled

Tested 90 dependencies for known issues, found 31 issues.

Pro tip: use `--file` option to get base image remediation advice.
Example: $ snyk test --docker ubuntu:bionic --file=path/to/Dockerfile

Podemos ver todos os caminhos vulneráveis encontrados e também as informações de plataforma no resultado da análise.

Neste post, exploramos os dois principais formatos de imagens de contêineres — Docker e OCI — e mostramos como a Snyk funciona com ambos, inclusive em várias arquiteturas.

Para finalizar, confira estas boas práticas de segurança para criar imagens Docker otimizadas para aplicações Node.js e Java:

  1. 10 boas práticas de segurança para Docker — conheça as práticas de segurança que você deve seguir ao criar imagens base do Docker e ao baixá-las. O artigo também apresenta o Docker Content Trust.

  2. Você é desenvolvedor Java? Este recurso é para você: Docker para desenvolvedores Java: 5 coisas que você precisa saber para não comprometer sua segurança

  3. 10 boas práticas para conteinerizar aplicações web Node.js com Docker — se você desenvolve em Node.js, vai gostar deste passo a passo, que mostra como criar imagens base do Docker seguras e eficientes para suas aplicações Node.js.

Você pode acessar todos esses recursos da Snyk gratuitamente — crie uma conta.

Comece a jogar Capture the Flag

Aprenda a resolver desafios de Capture the Flag assistindo à gravação sob demanda do nosso workshop virtual introdutório.

Publicado em:

Leia mais

Blog

Verão nos estádios: a turnê Fan Zone do Snyk Connect

A turnê Fan Zone da Snyk levou workshops de segurança de IA, networking e competição amistosa a 8 cidades e 3 sessões virtuais. Os participantes desenvolveram habilidades, trocaram ideias e evoluíram juntos.

Blog

Snyk VulnBench JS 1.0: LLMs conseguem encontrar os mesmos bugs duas vezes?

Snyk VulnBench JS 1.0: 300 varreduras repetidas mostram que as descobertas de segurança dos LLMs variam a cada execução, enquanto SAST e modelos detectam diferentes lacunas de vulnerabilidade.

blog feature toolkit
Blog

Versão maliciosa do pacote elementary-data no PyPI rouba credenciais de nuvem de engenheiros de dados

Atacantes exploraram uma vulnerabilidade de injeção de script no GitHub Actions para publicar uma versão maliciosa da CLI Python elementary-data (v0.23.3), com um backdoor que rouba credenciais e tinha como alvo perfis do dbt, chaves de provedores de nuvem e segredos SSH em ambientes de engenharia de dados.