Construir uma Imagem Mínima Aprovada

DockerBeginner
Pratique Agora

Introdução

A segurança da cadeia de suprimentos começa antes mesmo de uma carga de trabalho ser executada no cluster. Se uma imagem contiver ferramentas desnecessárias, procedência obscura ou uma tag não aprovada, os controles de tempo de execução que você aprendeu anteriormente terão um risco maior de serem comprometidos. Uma imagem mínima aprovada deve ter um contexto de build pequeno, uma imagem base clara, um ponto de entrada (entrypoint) de aplicação explícito, rótulos de metadados úteis e evidências de que pode ser executada no cluster de destino.

Neste laboratório, você criará um pequeno contexto de build de imagem local, construirá a imagem dentro do perfil minikube labex-v134, inspecionará os metadados da imagem resultante, implantará a imagem com imagePullPolicy: Never e verificará o conteúdo em tempo de execução a partir do Kubernetes. Este laboratório mantém o escopo prático: você ainda não está escaneando imagens nem aplicando políticas de registro. Você está aprendendo o primeiro hábito da cadeia de suprimentos: construir uma imagem pequena, nomeada e inspecionável, e provar o que há dentro dela.

Criar um Contexto de Build de Imagem Pequeno

Nesta etapa, você criará os arquivos que comporão a imagem. Um contexto de build é o diretório enviado ao construtor de imagens. Manter o contexto de build pequeno é importante, pois cada arquivo extra é uma oportunidade de copiar algo desnecessário para dentro de uma imagem.

Comece no diretório de trabalho do laboratório:

cd /home/labex/project/minimal-approved-image

Leia as notas do laboratório:

sed -n '1,160p' IMAGE_BUILD_NOTES.md

Confirme o contexto e o namespace do CKS:

kubectl config current-context | tee context-check.txt
kubectl get namespace cks-prep-image | tee namespace-check.txt

Crie o script da aplicação. Este script imprime uma mensagem de prontidão, imprime o UID de tempo de execução e, em seguida, entra em suspensão para que o Kubernetes possa manter o container em execução para inspeção.

cat > app.sh <<'EOF'
#!/bin/sh
set -eu

echo "minimal approved image ready"
echo "runtime uid: $(id -u)"
sleep 300
EOF
chmod 0755 app.sh

Crie o Dockerfile. busybox:1.36 é a imagem base pequena para este laboratório. Os rótulos (labels) no estilo OCI registram o motivo pelo qual esta imagem é aprovada no laboratório, e USER 65534:65534 faz com que a imagem seja executada como um usuário numérico não-root por padrão.

cat > Dockerfile <<'EOF'
FROM busybox:1.36

LABEL org.opencontainers.image.title="minimal-approved-app"
LABEL org.opencontainers.image.source="local-cks-prep"
LABEL security.labex.io.approved="true"

WORKDIR /app
COPY app.sh /app/app.sh
RUN chmod 0555 /app/app.sh

USER 65534:65534
ENTRYPOINT ["/app/app.sh"]
EOF

Salve o inventário do contexto de build e um checksum do script da aplicação:

find . -maxdepth 1 -type f -printf '%f\n' | sort | tee build-context-files.txt
sha256sum app.sh | tee app-sha256.txt
sed -n '1,120p' Dockerfile | tee dockerfile-review.txt

Seu diretório agora conterá o Dockerfile, app.sh, as notas e os arquivos de evidência que você está salvando. O limite importante da imagem é a linha COPY no Dockerfile: apenas o app.sh é copiado para /app/app.sh dentro da imagem.

Construir a Imagem dentro do Perfil Minikube

Nesta etapa, você construirá a imagem no perfil minikube labex-v134. Construí-la lá é importante porque o Kubernetes executará containers a partir do runtime do nó do minikube. Se você construir uma imagem apenas no daemon Docker da VM externa, o nó do minikube pode não conseguir vê-la.

Construa a imagem com uma tag local clara:

minikube -p labex-v134 image build -t labex/minimal-approved:1.0 .

Inspecione a imagem dentro do perfil minikube. O primeiro comando confirma que a imagem existe no runtime do nó. O segundo comando salva metadados importantes, incluindo o usuário configurado, o ponto de entrada e os rótulos.

minikube -p labex-v134 ssh -- 'docker image ls labex/minimal-approved:1.0' | tee built-image-list.txt
minikube -p labex-v134 ssh -- 'docker image inspect labex/minimal-approved:1.0' | tee built-image-inspect.json

Use o jq para extrair os campos importantes para a evidência de aprovação:

jq '.[0].Config | {User, Entrypoint, Labels}' built-image-inspect.json | tee built-image-approval.json

Você deverá ver a tag da imagem em built-image-list.txt, o User definido como 65534:65534, o Entrypoint definido como /app/app.sh e o rótulo de aprovação definido como true.

Executar a Imagem Aprovada no Kubernetes

Nesta etapa, você executará a imagem local no Kubernetes. O Pod utiliza imagePullPolicy: Never porque a imagem foi construída dentro do perfil minikube em vez de ser enviada para um registro. Essa configuração instrui o Kubernetes a usar a imagem do nó local e falhar caso ela não esteja presente.

Crie o manifesto do Pod:

cat > approved-image-pod.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: approved-image-app
  namespace: cks-prep-image
  labels:
    app: approved-image-app
    supply-chain.labex.io/approved: "true"
spec:
  restartPolicy: Never
  securityContext:
    runAsNonRoot: true
    runAsUser: 65534
    runAsGroup: 65534
  containers:
  - name: app
    image: labex/minimal-approved:1.0
    imagePullPolicy: Never
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop:
        - ALL
EOF

Aplique o Pod e aguarde sua inicialização:

kubectl apply -f approved-image-pod.yaml
kubectl wait --for=condition=Ready pod/approved-image-app -n cks-prep-image --timeout=120s

Salve os logs do Pod e o YAML atual do Pod:

kubectl logs approved-image-app -n cks-prep-image | tee approved-image-logs.txt
kubectl get pod approved-image-app -n cks-prep-image -o yaml | tee approved-image-pod.current.yaml

Os logs devem mostrar minimal approved image ready e runtime uid: 65534. Isso prova que o Kubernetes está executando o ponto de entrada da imagem como o usuário não-root pretendido.

Verificar o Conteúdo em Tempo de Execução

Nesta etapa, você inspecionará o container em execução a partir do Kubernetes. A aprovação da imagem não deve parar apenas no nome da tag. Você precisa de evidências de que o runtime contém o caminho da aplicação esperado e não contém arquivos extras óbvios que nunca deveriam fazer parte da imagem.

Inspecione o diretório da aplicação:

kubectl exec -n cks-prep-image approved-image-app -- sh -c 'ls -la /app; test -f /app/app.sh; echo APP_PATH_OK' | tee runtime-app-path.txt

Verifique o usuário de tempo de execução e faça uma pequena verificação negativa para um arquivo de depuração desnecessário:

kubectl exec -n cks-prep-image approved-image-app -- sh -c 'id -u; test ! -e /app/debug-tool && echo NO_DEBUG_TOOL' | tee runtime-content-check.txt

Salve um registro final de aprovação compacto que combina o estado do Kubernetes com os metadados da imagem:

kubectl get pod approved-image-app -n cks-prep-image -o json | jq '{image: .spec.containers[0].image, imagePullPolicy: .spec.containers[0].imagePullPolicy, podSecurityContext: .spec.securityContext, containerSecurityContext: .spec.containers[0].securityContext}' | tee final-pod-approval.json
jq '.[0].Config | {User, Entrypoint, Labels}' built-image-inspect.json | tee final-image-approval.json

A evidência final deve mostrar a tag da imagem aprovada local, imagePullPolicy: Never, as configurações de runtime não-root, o ponto de entrada /app/app.sh e o rótulo de aprovação.

Resumo

Você praticou o primeiro fluxo de trabalho da cadeia de suprimentos neste curso de preparação para o CKS: construir e comprovar uma imagem mínima aprovada. Você criou um pequeno contexto de build, construiu a labex/minimal-approved:1.0 dentro do perfil minikube labex-v134, inspecionou metadados e rótulos da imagem, implantou a imagem local com imagePullPolicy: Never e verificou o usuário de runtime e o conteúdo da aplicação a partir do Kubernetes. Laboratórios futuros sobre cadeia de suprimentos se basearão nesta fundação com escaneamento de manifestos, revisão de saída do Helm, evidências de SBOM e checksum, controles de registro confiáveis e limpeza de segredos de build.