Pular para o conteúdo principal

Hashes

Podemos conferir através dos hashes a integridade das imagens usadas pelos containers.

Recomendo a leitura de hashes antes continuar.

Vamos comparar o binário do apiserver com o binario dentro do container. Estamos em um cluster de versão 1.30.3. Então temos analisar tudo no documento https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.30.md#downloads-for-v1303.

root@cks-master:~# wget https://dl.k8s.io/v1.30.3/kubernetes-server-linux-amd64.tar.gz
--2024-08-20 17:43:28-- https://dl.k8s.io/v1.30.3/kubernetes-server-linux-amd64.tar.gz
Resolving dl.k8s.io (dl.k8s.io)... 34.107.204.206, 2600:1901:0:26f3::
Connecting to dl.k8s.io (dl.k8s.io)|34.107.204.206|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://cdn.dl.k8s.io/release/v1.30.3/kubernetes-server-linux-amd64.tar.gz [following]
--2024-08-20 17:43:28-- https://cdn.dl.k8s.io/release/v1.30.3/kubernetes-server-linux-amd64.tar.gz
Resolving cdn.dl.k8s.io (cdn.dl.k8s.io)... 151.101.193.55, 151.101.129.55, 151.101.65.55, ...
Connecting to cdn.dl.k8s.io (cdn.dl.k8s.io)|151.101.193.55|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 374416169 (357M) [application/x-tar]
Saving to: ‘kubernetes-server-linux-amd64.tar.gz’

kubernetes-server-l 100%[===================>] 357.07M 89.4MB/s in 4.0s

2024-08-20 17:43:32 (89.9 MB/s) - ‘kubernetes-server-linux-amd64.tar.gz’ saved [374416169/374416169]

# Salvando o hash retirado do link acima em compare

root@cks-master:~# echo "67282a349bd203fcc8d5d1d59d5b82fc56a14ea66f5a769ef457177ac5bcfb2fb65c239503a68f06a256f8919521fc96b5aad563bfec74eec13afb79a174f96b" > compare

# Adicionar o hash que temos também no arquivo compare
root@cks-master:~# sha512sum kubernetes-server-linux-amd64.tar.gz | cut -d ' ' -f 1 >> compare
67282a349bd203fcc8d5d1d59d5b82fc56a14ea66f5a769ef457177ac5bcfb2fb65c239503a68f06a256f8919521fc96b5aad563bfec74eec13afb79a174f96b

# Se somente tivermos uma linha então é o mesmo.
root@cks-master:~# cat compare | uniq
67282a349bd203fcc8d5d1d59d5b82fc56a14ea66f5a769ef457177ac5bcfb2fb65c239503a68f06a256f8919521fc96b5aad563bfec74eec13afb79a174f96b

# Vamos fazero unzip para pegar o binário e ir até ele
root@cks-master:~# tar xzvf kubernetes-server-linux-amd64.tar.gz
root@cks-master:~# cd kubernetes/server/bin/

# Pegando o sha512 dele e salvando
root@cks-master:~/kubernetes/server/bin# ls kube-apiserver
kube-apiserver

root@cks-master:~/kubernetes/server/bin# ./kube-apiserver --version
Kubernetes v1.30.3

root@cks-master:~/kubernetes/server/bin# sha256sum kube-apiserver | cut -d ' ' -f 1 > apiserver-sha512

Já temos acesso ao hash SHA256 do kube-apiserver agora precisamos conferir se bate com o hash do que estamos rodando.

# Se tentarmos entrar no shell do container no kube-apiserver não conseguimos porque não tem shell.
root@cks-master:/etc/kubernetes/manifests# k -n kube-system exec pods/kube-apiserver-cks-master -- sh
error: Internal error occurred: Internal error occurred: error executing command in container: failed to exec in container: failed to start exec "adc8bb808392966cfe63f7f425e72ec33c3494ea2e1d8d477741abcd19a875ba": OCI runtime exec failed: exec failed: unable to start container process: exec: "sh": executable file not found in $PATH: unknown

# Se temos o número do processo podemos conferir o filesystem do processo e encontrar o binário que está rodando, pois estamos no node que ele está rodando.

# O numero do processo é o 1479759
root@cks-master:~/kubernetes/server/bin# ps -aux | grep kube-apiserver
root 1479759 6.9 7.5 1541316 302980 ? Ssl 18:10 0:50 kube-apiserver --advertise-address=10.128.0.5 --allow-privileged=true --authorization-mode=Node,RBAC --client-ca-file=/etc/kubernetes/pki/ca.crt --enable-admission-plugins=NodeRestriction --enable-bootstrap-token-auth=true --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key --etcd-servers=https://127.0.0.1:2379 --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key --requestheader-allowed-names=front-proxy-client --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt --requestheader-extra-headers-prefix=X-Remote-Extra- --requestheader-group-headers=X-Remote-Group --requestheader-username-headers=X-Remote-User --secure-port=6443 --service-account-issuer=https://kubernetes.default.svc.cluster.local --service-account-key-file=/etc/kubernetes/pki/sa.pub --service-account-signing-key-file=/etc/kubernetes/pki/sa.key --service-cluster-ip-range=10.96.0.0/12 --tls-cert-file=/etc/kubernetes/pki/apiserver.crt --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
root 1485729 0.0 0.0 8172 2560 pts/0 S+ 18:22 0:00 grep --color=auto kube-apiserver

# Olhando os processos vamos ver o filesystem desse processo
root@cks-master:~/kubernetes/server/bin# ls /proc/1479759/root
bin boot dev etc go-runner home lib proc root run sbin sys tmp usr var

# Procurando pelo kube-apiserver

root@cks-master:~/kubernetes/server/bin# find /proc/1479759/root/ | grep kube-apiserver
/proc/1479759/root/usr/local/bin/kube-apiserver

root@cks-master:~/kubernetes/server/bin# sha512sum /proc/1479759/root/usr/local/bin/kube-apiserver
e2a9bfddc7caa8139279b18b8c1588fc7f24fc87f44ec74a5878d90ab9c6d3003bdca81006058af0d189bd3ef2d99fc847e443a9242667129934f66960449ba3 /proc/1479759/root/usr/local/bin/kube-apiserver

# Retirando só o que precisamos e fazendo append no arquivo
root@cks-master:~/kubernetes/server/bin# sha512sum /proc/1479759/root/usr/local/bin/kube-apiserver | cut -d ' ' -f 1
e2a9bfddc7caa8139279b18b8c1588fc7f24fc87f44ec74a5878d90ab9c6d3003bdca81006058af0d189bd3ef2d99fc847e443a9242667129934f66960449ba3

root@cks-master:~/kubernetes/server/bin# sha512sum /proc/1479759/root/usr/local/bin/kube-apiserver | cut -d ' ' -f 1 >> apiserver-sha512

# Confirmando os valores interno e fazendo o uniq
root@cks-master:~/kubernetes/server/bin# cat apiserver-sha512
e2a9bfddc7caa8139279b18b8c1588fc7f24fc87f44ec74a5878d90ab9c6d3003bdca81006058af0d189bd3ef2d99fc847e443a9242667129934f66960449ba3
e2a9bfddc7caa8139279b18b8c1588fc7f24fc87f44ec74a5878d90ab9c6d3003bdca81006058af0d189bd3ef2d99fc847e443a9242667129934f66960449ba3

root@cks-master:~/kubernetes/server/bin# cat apiserver-sha512 | uniq
e2a9bfddc7caa8139279b18b8c1588fc7f24fc87f44ec74a5878d90ab9c6d3003bdca81006058af0d189bd3ef2d99fc847e443a9242667129934f66960449ba3

Agora vamos partir para uma curiosidade. Quando fazemos o comando k get -n kube-system pod kube-apiserver-cks-master -o yaml podemos observer o seguinte bloco.

  containerStatuses:
- containerID: containerd://84177449d2fcf44f6040a92914d5a5f9a2bb91c6bf341fb77026952b02092a30
image: registry.k8s.io/kube-apiserver:v1.30.3
# Temos esse sha256
imageID: registry.k8s.io/kube-apiserver@sha256:a36d558835e48950f6d13b1edbe20605b8dfbc81e088f58221796631e107966c

Este sha256 é da IMAGEM e não do binário. Vamos conferir. Sabendo que os arquivos são os mesmo tanto faz se tiramos o sh256 do binário do container ou do que baixamos. Se o arquivo é exatamente o mesmo e a função é a mesma então o hash é o mesmo.

root@cks-master:~/kubernetes/server/bin# sha256sum kube-apiserver
286c16cf16389dfdfcd7e641859078c7b89f275c25e073d1c9f963cee393ccaa kube-apiserver
root@cks-master:~/kubernetes/server/bin# sha256sum /proc/1479759/root/usr/local/bin/kube-apiserver
286c16cf16389dfdfcd7e641859078c7b89f275c25e073d1c9f963cee393ccaa /proc/1479759/root/usr/local/bin/kube-apiserver

E podemos ver que são dois hashes diferentes.

# Do binário
286c16cf16389dfdfcd7e641859078c7b89f275c25e073d1c9f963cee393ccaa
# Da Imagem
a36d558835e48950f6d13b1edbe20605b8dfbc81e088f58221796631e107966c

O kube-apiserver é normalmente construído sobre uma imagem base extremamente minimalista, geralmente baseada em distribuições Linux otimizadas para containers, como o distroless ou o scratch.

Opções Comuns:

  • Distroless: é um conjunto de imagens base criadas pelo Google que contêm apenas o que é necessário para executar a aplicação, sem um shell, sem gerenciadores de pacotes, e sem qualquer ferramenta que não seja essencial. Por que usar? Essas imagens são pequenas, seguras e focadas em rodar a aplicação com o mínimo de sobrecarga.

  • Scratch: Não é exatamente uma imagem base, mas sim uma base vazia, literalmente sem sistema operacional. Apenas binários estáticos ou extremamente minimalistas são incluídos. Por que usar? Scratch é o nível mais alto de minimalismo possível para containers, e o kube-apiserver pode ser empacotado dessa forma se for completamente auto suficiente.

root@cks-master:~/kubernetes/server/bin# cat /proc/1479759/root/etc/os-release
PRETTY_NAME="Distroless"
NAME="Debian GNU/Linux"
ID="debian"
VERSION_ID="12"
VERSION="Debian GNU/Linux 12 (bookworm)"
HOME_URL="https://github.com/GoogleContainerTools/distroless"
SUPPORT_URL="https://github.com/GoogleContainerTools/distroless/blob/master/README.md"
BUG_REPORT_URL="https://github.com/GoogleContainerTools/distroless/issues/new"

Então fica ai mais uma dica de segurança, diminuir a superfície de ataque dos imagens do seus microserviços usando imagens base minimalistas.