Skip to main content

Security Context

No CKS precisamos nos aprofundar mais nesse tema. Faça um overview em cka security context.

Security context permite definirmos privilégios e controle de acesso em nível de pod ou em nível de container.

Podemos especificar:

  • userID
  • groupID
  • Escalada de privilégios
  • Linux capabilities
  • outros
spec:
#Nível de pod (Aplicado em todos os containers)
securityContext:
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000

containers:
- name: busybox
command: ["sleep","3600"]
image: busybox
#Nível de container
securityContext:
runAsUser: 0 # Vai sobrescrever o user neste pod
#
#runAsUser: 0
#runAsGroup: 3000
#fsGroup: 2000
- name: busybox-2
command: ["sleep","3600"]
image: busybox2
#Isso será herdado da definição de pods
#securityContext:
#runAsUser: 1000
#runAsGroup: 3000
#fsGroup: 2000

Nessa definição estamos dizendo que:

  • runAsUser -> uid=1000 (user ID)
  • runAsGroup -> gid=3000 (main group ID)
  • fsGroup -> groups=2000 (suplementary group ID)

A partir do Kubernetes 1.25 foi introduzido no spec do pod o campo de sistema operacional (os) com os valores para name podendo ser windows ou linux (padrão). Este campo indicará em qual sistema operacional o pod será executado. Além de ser útil no futuro para o kube-scheduler também terá funcionalidade no security context.

spec:
os:
name: windows
containers:
- name: windows-container
image: mcr.microsoft.com/windows/servercore:ltsc2022

Existem parâmetros que são possíveis existir em nível de pod, em nível de container ou em ambos. Vamos fazer uma análise rápida só para saber o que é possível. Ninguém precisa guardar isso para o CKS.

Uma tabela geral rápida no qual falaremos somente dos principais.

ParâmetroTipoOSPod LevelContainer LevelDescrição
allowPrivilegeEscalationbooleanLinuxNãoSimControla se o processo pode ganhar mais privilégios do que quem o lançou. É automaticamente definido como true em caso de "privileged true" ou tiver uma capability CAP_SYS_ADMIN.
appArmorProfileAppArmorProfileLinuxSimSimSe setado o appArmorProfile do pod será alterado
capabilitiesCapabilitiesLinuxNãoSimPara adicionar ou dropar capabilities no container.
fsGroupIntegerLinuxSimNãoUm supplemental group que será aplicado em todos os containers do pod.
privilegedbooleanLinuxNãoSimPara rodar o container como root garantindo privilégios equivalentes ao root do host. O padrão é falso.
procMountstringLinuxNãoSimEnvolve o tipo de proc mount usado pelo container.
readOnlyRootFilesystembooleanLinuxNãoSimSe /root do container deve ser somente leitura. O padrão é falso
runAsGroupintegerLinuxSimSimO gid para executar o entrypoint do processo.
runAsNonRootbooleanLinuxSimSimIndica que o container precisa rodar com um usuário que não é root. O Kubelet valida se a imagem esta setando o usuário.
runAsUserintegerLinuxSimSimUID do usuário. O padrão é o mesmo utilizado na imagem do container.
seLinuxOptionsSELinuxOptionsLinuxSimSimSe não especificado, o container runtime irá alocar um SELinux randomico para cada container.
seccompProfileSeccompProfileLinuxSimSimOpções do seccomp para os containers.
supplementalGroupsinteger arrayLinuxSimNãoUma lista de GID aplicados ao primeiro processo de cada container.
supplementalGroupsPolicystringLinuxSimNãoUsado somente se supplementalGroups for definido.
sysctlssysctls arrayLinuxSimNãoContém uma lista de sysctls com namespaces usados pelo pod.
windowsOptionsWindowsSecurityContextOptionswindowsSimSimConfigurações específicas do Windows aplicadas a todos os contêineres.

Vamos usar esse yaml como base e vamos alterá-lo várias vezes.

root@cks-master:~# k run pod --image=busybox --command -oyaml --dry-run=client -- sh -c 'sleep 1d' > pod.yaml

root@cks-master:~# cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod
name: pod
spec:
containers:
- command:
- sh
- -c
- sleep 1d
image: busybox
name: pod
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
root@cks-master:~#

runAsUser e runAsGroup (pod e container levels)

  • A imagem busybox utiliza utiliza o usuário root na própria image e não passamos nada para alterá-lo.
  • Se criamos um arquivo ele será criado de acordo com o usuário que estamos usando que nesse caso é o root.
root@cks-master:~# k apply -f pod.yaml
pod/pod created

root@cks-master:~# k exec -it pod -- sh
/ # id
uid=0(root) gid=0(root) groups=10(wheel)
/ # touch test
/ # ls -lh test
-rw-r--r-- 1 root root 0 Aug 29 12:00 test
/ # exit

root@cks-master:~# k delete pod pod --force --grace-period 0
Warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.
pod "pod" force deleted

Vamos definir um usuário para o pod, que será herdado pelo container pois não sobreescreveremos.

  • Alteramos o usuário do pod.
  • Como a imagem define o workdir direto para o /, como o usuário 1000 não temos permissão para criar nada.
  • Se alteramos para um local como o tmp que todos os usuários tem permissão podemos criar e o arquivo criado pertencerá ao usuário específicado.
root@cks-master:~# vim pod.yaml

root@cks-master:~# cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod
name: pod
spec:
securityContext:
runAsUser: 1000
runAsGroup: 3000
containers:
- command:
- sh
- -c
- sleep 1d
image: busybox
name: pod
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}

root@cks-master:~# k apply -f pod.yaml
pod/pod created

root@cks-master:~# k exec -it pod -- sh
~ $ id
uid=1000 gid=3000
~ $ touch test
touch: test: Permission denied
~ $ pwd
/
~ $ cd tmp/
/tmp $ touch test
/tmp $ ls -lh test
-rw-r--r-- 1 1000 3000 0 Aug 29 12:07 test
/tmp $ exit

root@cks-master:~# k delete pod pod --force --grace-period 0
Warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.

runAsNonRoot (pod e container Level)

Agora vamos forçar o container rodar como não root, mas não passaremos nenhum user. Se a imagem do container já define um usuário não teremos problemas, mas se define como root não poderá ser executada.

  • Nesse cenário mantemos o user e não temos problemas pois estamos alterando o usuário dono do processo principal.
  • Observe que o dono do processo 1 é o user 1000 que mantemos.
root@cks-master:~# vim pod.yaml

root@cks-master:~# cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod
name: pod
spec:
securityContext:
runAsUser: 1000
runAsGroup: 3000
containers:
- command:
- sh
- -c
- sleep 1d
image: busybox
name: pod
resources: {}
securityContext:
runAsNonRoot: true
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
root@cks-master:~# k apply -f pod.yaml
pod/pod created
root@cks-master:~# k get pods
NAME READY STATUS RESTARTS AGE
pod 1/1 Running 0 5s

root@cks-master:~# k exec -it pod -- sh
~ $
~ $ ps
PID USER TIME COMMAND
1 1000 0:00 sh -c sleep 1d
8 1000 0:00 sh
14 1000 0:00 ps
~ $ exit

root@cks-master:~# k delete pod pod --force --grace-period 0
Warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.
pod "pod" force deleted

Porém removendo o usuário temos o problema acima citado.

  • Nesse caso estamos forçando que a imagem tenha um usuário definido para o processo, o que não é o caso do busybox.
root@cks-master:~# vim pod.yaml

root@cks-master:~# cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod
name: pod
spec:
# securityContext:
# runAsUser: 1000
# runAsGroup: 3000
containers:
- command:
- sh
- -c
- sleep 1d
image: busybox
name: pod
resources: {}
securityContext:
runAsNonRoot: true
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}

root@cks-master:~# k apply -f pod.yaml
pod/pod created

root@cks-master:~# k get pod
NAME READY STATUS RESTARTS AGE
pod 0/1 CreateContainerConfigError 0 3s

root@cks-master:~# k get pod pod -o jsonpath={.status.containerStatuses.*.state} | jq
{
"waiting": {
"message": "container has runAsNonRoot and image will run as root (pod: \"pod_default(7dd567c0-ead9-4460-a012-35acd9122bad)\", container: pod)",
"reason": "CreateContainerConfigError"
}
}

root@cks-master:~# k delete pod pod
pod "pod" deleted

A imagem do nginx padrão utiliza o root, mas existe uma outra imagem que não utiliza, vamos usá-la para teste

root@cks-master:~# k run nginx --image=nginxinc/nginx-unprivileged -o yaml --dry-run=client > podnonroot.yaml

root@cks-master:~# vim podnonroot.yaml

root@cks-master:~# cat podnonroot.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: nginx
name: nginx
spec:
containers:
- image: nginxinc/nginx-unprivileged
name: nginx
resources: {}
securityContext:
runAsNonRoot: true
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
root@cks-master:~# k apply -f podnonroot.yaml
pod/nginx created

root@cks-master:~# k exec -it nginx -- bash
nginx@nginx:/$ id
uid=101(nginx) gid=101(nginx) groups=101(nginx)
nginx@nginx:/$ exit
exit

root@cks-master:~# k delete pod nginx
pod "nginx" deleted

privileged (Container Level)

Por padrão os containers rodam como unprivileged, mas é possível rodar como privileged.

Um caso que isso poderia acontecer é se quisessemos rodar um docker-in-docker no container, ou seja, um container dentro do outro. Poderíamos também ter um container que precisa de acesso a todos os devices.

Rodar um container como privileged quer dizer que o user 0 (root) do container é diretamente mapeado para o user 0 (root) do host. Uma das abstrações do uso de container é que dentro do container podemos ter o mesmo id de um usuário do host ou de outros container mas são diferentes, eles podem ter o mesmo id mas permissões diferentes.

Com o comando sysctl podemos setar parâmetros do kernel em tempo de execução, mas para isso precisamos de permissão de root.

Sem o privileged, mesmo sendo root no container não conseguimos alterar.

root@cks-master:~# vim pod.yaml

root@cks-master:~# cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod
name: pod
spec:
containers:
- command:
- sh
- -c
- sleep 1d
image: busybox
name: pod
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}

root@cks-master:~# k apply -f pod.yaml
pod/pod created

root@cks-master:~# k exec -it pod -- bash
root@cks-master:~# k exec -it pod -- sh
/ # id
uid=0(root) gid=0(root) groups=10(wheel)
/ # sysctl kernel.hostname=cks
sysctl: error setting key 'kernel.hostname': Read-only file system
/ # sysctl kernel.hostname=cks

Colocando em privileged true. Lembrando que privileged é um security context em nível de container somente.

root@cks-master:~# vim pod.yaml

root@cks-master:~# cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod
name: pod
spec:
containers:
- command:
- sh
- -c
- sleep 1d
image: busybox
name: pod
resources: {}
securityContext:
privileged: true
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}

# Só para constar, o hostname é o próprio nome do pod (neste caso pod)
# O comando sysctl muda em proc de forma temporária não em /etc/hostname.
root@cks-master:~# k exec pod -it -- sh
/ # cat /proc/sys/kernel/hostname
pod
/ # sysctl kernel.hostname=cks-test
kernel.hostname = cks-test
/ # cat /proc/sys/kernel/hostname
cks-test
/ # cat /etc/hostname
pod
/ # exit

# No worker onde o pod esta rodando a alteração em nível de kernel mas no kernel group do pod e não no host. Não é o mesmo filesystem.
root@cks-worker:~# cat /proc/sys/kernel/hostname
cks-worker

root@cks-master:~# k delete pod pod --force --grace-period 0
Warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.
pod "pod" force deleted

allowPrivilegeEscalation (Container Level)

Agora vamos falar sobre allowPrivilegeEscalation que é automaticamente true.

O recurso allowPrivilegeEscalation no Kubernetes é uma configuração de segurança que controla se um processo dentro de um container pode obter privilégios adicionais, como através do comando sudo ou ao usar setuid binaries.

Como funciona:

  • allowPrivilegeEscalation: true (default): Permite que processos dentro do container escalem seus privilégios. Isso pode ser necessário para algumas aplicações que precisam elevar seus privilégios temporariamente para executar certas operações.

  • allowPrivilegeEscalation: false: Bloqueia a elevação de privilégios. Mesmo que o container seja executado com privilégios de root, ele não poderá usar mecanismos como sudo ou setuid para ganhar privilégios adicionais. Essa configuração é usada como uma camada extra de segurança para limitar as capabilities dos processos dentro do container.

Relação com privileged e runAsNonRoot:

  • privileged: Se o container estiver em modo privileged: true, ele ignora a configuração de allowPrivilegeEscalation porque já tem privilégios totais no host.

  • runAsNonRoot: Se runAsNonRoot: true estiver configurado, allowPrivilegeEscalation geralmente deve ser false, pois o objetivo é garantir que o container não tenha acesso root ou capacidade de escalá-lo.

NoNewPrivs 0 significa que esta desativado, ou seja, pode elevar os privilégios.

root@cks-master:~# cat pod.yaml

root@cks-master:~# vim pod.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod
name: pod
spec:
containers:
- command:
- sh
- -c
- sleep 1d
image: busybox
name: pod
resources: {}
securityContext:
#Esse já é o default, é só para confirmar
allowPrivilegeEscalation: true
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
root@cks-master:~# k apply -f pod.yaml
pod/pod created
root@cks-master:~# k exec -it pod -- sh
/ #
/ # cat /proc/1/status | grep NoNewPrivs
NoNewPrivs:0
/ # exit
root@cks-master:~# k delete pod pod --force --grace-period 0
Warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.
pod "pod" force deleted

Vamos alterar para false NoNewPrivs deve ser 1 mostrando que esta ativado.

root@cks-master:~# vim pod.yaml
root@cks-master:~# cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod
name: pod
spec:
containers:
- command:
- sh
- -c
- sleep 1d
image: busybox
name: pod
resources: {}
securityContext:
allowPrivilegeEscalation: false
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
root@cks-master:~# k apply -f pod.yaml
pod/pod created
root@cks-master:~# k exec -it pod -- sh
/ #
/ # cat /proc/1/status | grep NoNewPrivs
NoNewPrivs:1
/ # exit
root@cks-master:~# k delete pod pod --force --grace-period 0
Warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.
pod "pod" force deleted

AppArmor e SecComp

Faça um overview sobre esses dois recursos que temos no Linux.

Esse conteúdo, apesar de cobrado no CKS foi disponibilizado em apparmor, seccomp.

É necessário o estudo de ambas as ferramentas.