Pular para o conteúdo principal

Authorization

Já vimos que o kube-apiserver tem a função de autenticar um usuário por meio dos certificados, mas o que ele pode fazer no cluster?

Como administrador podemos fazer qualquer coisa no cluster, mas não queremos que outros usuários possam modificar configurações, aprovar certificados, trabalhar em namespaces do kube-system, deletar secrets, etc.

Podemos permitir que vejam, mas não modifiquem por exemplo.

alt text

Um cenário bem interessante é que um cluster pode ser divido em diferentes organizações ou times e cada um deve trabalhar em seus respectivos namespaces. É nisso que autorização também pode ajudar dentro do cluster.

Existem diferentes mecanismos de autorização:

  • Node
  • ABAC
  • RBAC
  • Webhook
  • AlwaysAllow (Não recomendável)
  • AlwaysDeny (Não recomendável)

Node

Sabemos que usuários podem acessar o kube-apiserver, mas que também o Kubelet dentro dos workers nodes também acessa. Quando o certificado dos Kubelet foi criado também foi especificado que ele deveria pertencer ao grupo system:nodes lembra?

O grupo system:nodes já contém permissões específicas para nodes no Kubernetes. O Kubelet precisa criar os pods no node, atualizar o kube-apiserver, atualizar o status do node, acessar os storages, acessar outros services, etc.

O que um node poderia fazer?

kubectl get clusterroles system:node -o yaml

# Para ficar mais fácil separei os pedaços
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
creationTimestamp: "2024-02-08T23:02:26Z"
labels:
kubernetes.io/bootstrapping: rbac-defaults
name: system:node
resourceVersion: "84"
uid: 923e2cc5-fd5e-47e5-b4ce-69f57431c2ba
rules:

##################
- apiGroups:
- authentication.k8s.io
resources:
- tokenreviews
verbs:
- create

##################
- apiGroups:
- authorization.k8s.io
resources:
- localsubjectaccessreviews
- subjectaccessreviews
verbs:
- create

################## SERVIÇOS
- apiGroups:
- ""
resources:
- services #<<<
verbs:
- get
- list
- watch

##################
- apiGroups:
- ""
resources:
- nodes <------------------------|
verbs: |
- create |
- get |
- list |
- watch |
|
################## ^
- apiGroups: |
- "" |
resources: |
- nodes/status |
verbs: |
- patch |
- update |
|
################## ^
- apiGroups: |
- "" |
resources: |
- nodes # MESMO DE DOIS GRUPOS ACIMA
verbs:
- patch
- update

################## CRIAR E ATUALIZAR EVENTOS
- apiGroups:
- ""
resources:
- events
verbs:
- create
- patch
- update

################## OBSERVE AQUI. TEMPOS DOIS GRUPOS PARA O MESMO RECURSO PODS
# SE JUNTASSE DARIA NA MESMA, MAS SIMPLESMENTE FOI SEPARADO PERMISSÃO PARA LEITURA E ESCRITA COMO UMA FORMA DE ORGANIZAÇÃO
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- list
- watch

- apiGroups:
- ""
resources:
- pods #<< MESMO GRUPO DO DE CIMA
verbs:
- create
- delete

##################
- apiGroups:
- ""
resources:
- pods/status
verbs:
- patch
- update

##################
- apiGroups:
- ""
resources:
- pods/eviction
verbs:
- create

##################
- apiGroups:
- ""
resources:
- configmaps
- secrets
verbs:
- get
- list
- watch

##################
- apiGroups:
- ""
resources:
- persistentvolumeclaims
- persistentvolumes
verbs:
- get

##################
- apiGroups:
- ""
resources:
- endpoints
verbs:
- get

##################
- apiGroups:
- certificates.k8s.io
resources:
- certificatesigningrequests
verbs:
- create
- get
- list
- watch

##################
- apiGroups:
- coordination.k8s.io
resources:
- leases
verbs:
- create
- delete
- get
- patch
- update

##################
- apiGroups:
- storage.k8s.io
resources:
- volumeattachments
verbs:
- get

##################
- apiGroups:
- ""
resources:
- serviceaccounts/token
verbs:
- create

##################
- apiGroups:
- ""
resources:
- persistentvolumeclaims/status
verbs:
- get
- patch
- update

##################
- apiGroups:
- storage.k8s.io
resources:
- csidrivers
verbs:
- get
- list
- watch

##################
- apiGroups:
- storage.k8s.io
resources:
- csinodes
verbs:
- create
- delete
- get
- patch
- update

##################
- apiGroups:
- node.k8s.io
resources:
- runtimeclasses
verbs:
- get
- list
- watch

Observando a saída acima poderíamos fazer isso ficar menor, mas é uma questão de organização.

ABAC (Attribute-Based Access Control) - DESCONTINUADO

O estudo aqui vale para entender a história do Kubernetes pois a partir da versão 1.25 foi removido.

Nesse método associamos um usuário ou um grupo de usuários que tem permissões em determinados recursos e as permissões sobre cada recurso.

Não é possível criar um arquivo YAML para definir isso. É necessário um arquivo JSON ser passado por parâmetro para o kube-apiserver no seu manifesto ou serviço, dependendo de como o cluster foi criado.

Por exemplo, no manifesto do kube-apiserver teríamos:

...
spec:
containers:
- command:
- kube-apiserver
- --advertise-address=172.18.0.4
- --allow-privileged=true
- --authorization-mode=Node,RBAC,ABAC ## adicionado ABAC
- --abac-file=/meu/caminho/para/abac.json ## o Arquivo Json
- --client-ca-file=/etc/kubernetes/pki/ca.crt
...

O modo AlwaysAllow é definido por padrão se nenhum --authorization-mode for definido. Nesse caso, todos os usuários têm acesso irrestrito a todos os recursos do cluster. AlwaysDeny criaria um cluster totalmente restrito, até mesmo para tarefas administrativas. Seria usado em caso de testes de segurança quando tudo for negado.

Nesse arquivo json teríamos.

{
"apiVersion": "abac.authorization.kubernetes.io/v1beta1",
"kind": "Policy",
"spec": {
"policies": [
{
"apiVersion": "abac.authorization.kubernetes.io/v1beta1",
"kind": "Policy",
"spec": {
"user": "david",
"namespace": "default",
"resource": "*", #TUDO LIBERADO
"readonly": false
}
}
{
"apiVersion": "abac.authorization.kubernetes.io/v1beta1",
"kind": "Policy",
"spec": {
"user": "joao",
"namespace": "default",
"resource": "daemonset", # SÓ ACEITA UM RECURSO POR VEZ
"verbs": ["get", "list", "watch"]
}
}
{
"apiVersion": "abac.authorization.kubernetes.io/v1beta1",
"kind": "Policy",
"spec": {
"user": "joao",
"namespace": "default",
"resource": "deployments",
"verbs": "verbs": ["get", "list", "watch", "create", "update", "delete"]
}
}
]
}
}

E para fazer efeito precisaríamos reiniciar o kube-apiserver. Cada política só aceita um nome por vez; no caso de * é aceitável para todos, mas não conseguimos colocar apenas 2 ou 3. É necessário outro bloco como pode observar acima.

Isso não é mais usado nem cairá na prova, mas fica de conhecimento o quão trabalhoso era para configurar.

Webhook

E se quisermos terceirizar todos os mecanismos de autorização ao invés de usar o RBAC?

Aqui algumas ferramentas com esse propósito:

alt text

É necessário ativar o Webhook no kube-apiserver como sendo um modo de autorização. Ele tenta autorizar seguindo a ordem estabelecida quando muitos modos são usados.

--authorization-mode=Node,RBAC,Webhook

Node > RBAC > Webhook

--authorization-mode=Node,Webhook,RBAC

Node > Webhook > RBAC

RBAC (Role-Based Access Control)

Esse é o método built-in do kubernetes.

Definimos uma role (função no cluster) e associamos usuários a essa role.

Por exemplo a role de developer pode fazer tais coisas no cluster e os usuários que são developers atuam usando essa role. Administradores usam roles de administradores, e por ai vai.

Uma vez que mudamos as permissões de uma role todos os usuários que assumem essa role são afetados de uma única vez. É um dos jeitos que temos de agrupar as coisas.

Lembra daquele usuário david que fizemos pro cluster?

Vamos criar uma role que ele possa usar.

Primeiramente precisamos entender que existem dois tipos de role:

  • Role : Usada para dar permissão em nível de namespace.
  • ClusterRole : Usado para dar permissão em nível de cluster.

Por exemplo o get nodes está em que nível? De cluster.

Se queremos fazer uma role admin por exemplo vamos dar permissão à nível de cluster.

Primeiro precisamos criar a role e depois fazer permitir que um usuário possa assumir essa role usando o um binding, Vamos criar dois no mesmo arquivo para que possa ficar fácil de entender.

Roles e RoleBindings

Vamos começar por uma role normal com escopo de namespaces.

cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: dev-role
rules:
- apiGroups:
- ""
resources:
- deployments # <<< Se liga nisso
- pods
- services
# ESSES VERBOS SAO APLICADOS PARA TODOS OS RECURSO ACIMA
verbs:
- create
- list
- get
- update
- delete
- watch
- apiGroups: [""]
resources: ["ConfigMap"]
verbs: ["create"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: david-binding-dev-role
subjects:
- kind: User
name: david
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: dev-role
apiGroup: rbac.authorization.k8s.io
EOF

role.rbac.authorization.k8s.io/dev-role created
rolebinding.rbac.authorization.k8s.io/david-binding-role created

Então criamos uma role que tem permissão para trabalhar com pods e deployments, mas não especificamos um namespace. Vamos conferir.

kubectl get role  
NAME CREATED AT
dev-role 2024-02-11T09:32:38Z

kubectl describe role dev-role
Name: dev-role
Labels: <none>
Annotations: <none>
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
deployments [] [] [create list get update delete watch]
pods [] [] [create list get update delete watch]
services [] [] [create list get update delete watch]
configmaps [] [] [create]

kubectl get rolebindings
NAME ROLE AGE
david-binding-dev-role Role/dev-role 25s

kubectl describe rolebindings david-binding-dev-role
Name: david-binding-dev-role
Labels: <none>
Annotations: <none>
Role:
Kind: Role
Name: dev-role
Subjects:
Kind Name Namespace
---- ---- ---------
User david

Vamos conferir então se um user o que um usuário poderia fazer

## Nesse caso estou conferindo para o usuário que estou no momento
kubectl auth can-i create deployments
yes

## Conferindo para outro usuário
kubectl auth can-i create deployments --as david
no # PQ?

kubectl auth can-i get pods --as david
yes

kubectl auth can-i create configmaps --as david
yes

kubectl auth can-i delete configmaps --as david
no

Por que david não pode criar deployments e consegue criar pods. Por que um funcionou e o outro não?

Por que deployments é de um apigroup diferente lembra? Quando não definimos um apigroup ele automaticamente irá entender que é o core ou seja api, logo deployment está em outro grupo.

No core temos:

  • bindings
  • componentstatuses
  • configmaps
  • endpoints
  • events
  • limitranges
  • namespaces
  • nodes
  • persistentvolumeclaims
  • persistentvolumes
  • pods
  • podtemplates
  • replicationcontrollers
  • resourcequotas
  • secrets
  • serviceaccounts
  • services

Em no apis temos? Dá uma olhada lá para lembrar!

Para deployment funcionar é necessário fazer adicionar isso na role, separando o deployment.

- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["create", "list", "get", "update", "delete", "watch"]
cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: dev-role
rules:
- apiGroups: [""]
resources: ["pods", "services"] # Removido deployments daqui
verbs: ["create", "list", "get", "update", "delete", "watch"]
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["create"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["create", "list", "get", "update", "delete", "watch"]
EOF

kubectl describe role dev-role
Name: dev-role
Labels: <none>
Annotations: <none>
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
pods [] [] [create list get update delete watch]
services [] [] [create list get update delete watch]
deployments.apps [] [] [create list get update delete watch]
configmaps [] [] [create]

kubectl auth can-i create deployments --as david
yes

kubectl auth can-i create deployments --as david --namespace test
no

Quando não passamos o namespace que queremos ele assume o default pois Role tem um escopo de namespace. É uma boa prática sempre especificar o namespace, apesar de eu não ter feito aqui.

Se você não conhece apigroups não entenderá RBAC. O motivo dos resources estarem no plural é exatamente por que dentro das apigroups estão no plural. Outra observação que podemos fazer é que na segunda vez não alteramos o binding, somente a role e tudo funcionou.

Ainda podemos dar permissão a um recurso com nome específico como por exemplo um pod chamado nginx usando o resourceNames

Criar um role pode também ser usado o comando kubectl

## verbs resources e resource-name são listas
create role new-role --verb=list,get,create,delete --resource=pods --resource-name=nginx
role.rbac.authorization.k8s.io/new-role created

kubectl describe role new-role
Name: new-role
Labels: <none>
Annotations: <none>
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
pods [] [nginx] [list get create delete]


kubectl create rolebinding new-role-binding --user=david --role=new-role
rolebinding.rbac.authorization.k8s.io/new-role-binding created

kubectl describe rolebindings new-role-binding
Name: new-role-binding
Labels: <none>
Annotations: <none>
Role:
Kind: Role
Name: new-role
Subjects:
Kind Name Namespace
---- ---- ---------
User david

ClusterRoles e ClusterRoleBindings

Olhando dentro de um recurso podemos ver se um recurso está em nível de namespace ou não.

Se olhar novamente em /api/v1 temos lá os recursos peguei dois de exemplo:

{
...
{
"name": "nodes",
"singularName": "node",
"namespaced": false, # Nível de cluster
"kind": "Node",
"verbs": [
"create",
"delete",
"deletecollection",
"get",
"list",
"patch",
"update",
"watch"
],
"shortNames": [
"no"
],
"storageVersionHash": "XwShjMxG9Fs="
},
...
{
"name": "pods",
"singularName": "pod",
"namespaced": true, # Nível de namespace
"kind": "Pod",
"verbs": [
"create",
"delete",
"deletecollection",
"get",
"list",
"patch",
"update",
"watch"
],
"shortNames": [
"po"
],
"categories": [
"all"
],
"s
...
}
...

Já que os recursos com escopo de cluster não precisamos de um namespace não precisamos especificá-los.

Para saber pegar todos os recursos de nível de cluster podemos fazer.

kubectl api-resources --namespaced=false

NAME SHORTNAMES APIVERSION NAMESPACED KIND
componentstatuses cs v1 false ComponentStatus
namespaces ns v1 false Namespace
nodes no v1 false Node
persistentvolumes pv v1 false PersistentVolume
mutatingwebhookconfigurations admissionregistration.k8s.io/v1 false MutatingWebhookConfiguration
validatingwebhookconfigurations admissionregistration.k8s.io/v1 false ValidatingWebhookConfiguration
customresourcedefinitions crd,crds apiextensions.k8s.io/v1 false CustomResourceDefinition
apiservices apiregistration.k8s.io/v1 false APIService
selfsubjectreviews authentication.k8s.io/v1 false SelfSubjectReview
tokenreviews authentication.k8s.io/v1 false TokenReview
selfsubjectaccessreviews authorization.k8s.io/v1 false SelfSubjectAccessReview
selfsubjectrulesreviews authorization.k8s.io/v1 false SelfSubjectRulesReview
subjectaccessreviews authorization.k8s.io/v1 false SubjectAccessReview
certificatesigningrequests csr certificates.k8s.io/v1 false CertificateSigningRequest
flowschemas flowcontrol.apiserver.k8s.io/v1 false FlowSchema
prioritylevelconfigurations flowcontrol.apiserver.k8s.io/v1 false PriorityLevelConfiguration
ingressclasses networking.k8s.io/v1 false IngressClass
runtimeclasses node.k8s.io/v1 false RuntimeClass
clusterrolebindings rbac.authorization.k8s.io/v1 false ClusterRoleBinding
clusterroles rbac.authorization.k8s.io/v1 false ClusterRole
priorityclasses pc scheduling.k8s.io/v1 false PriorityClass
csidrivers storage.k8s.io/v1 false CSIDriver
csinodes storage.k8s.io/v1 false CSINode
storageclasses sc storage.k8s.io/v1 false StorageClass
volumeattachments storage.k8s.io/v1 false VolumeAttachment

O processo é o mesmo para Roles e RoleBindins mas mudando Role para ClusterRole e Binding para ClusterRoleBinding.

Várias clusterroles são criadas quando um cluster é criado a primeira vez.

Aqui seria uma role que poderíamos fazer qualquer coisa.

cat <<EOF | kubectl apply -f -
# DEFINIMOS UMA ROLE LIBERANDO TUDO
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: admin-role
rules:
- apiGroups: ["*"] # PARA TODOS OS APIGROUPS
resources: ["*"] # PARA TODOS OS RECURSOS
verbs: ["*"] # TODOS OS VERBOS
---

# AGORA BINDAMOS ESSA ROLE COM O USUÁRIO DAVID
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: david-binding-role
subjects:
- kind: User
name: david
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: admin-role
apiGroup: rbac.authorization.k8s.io
EOF

Uma coisa importante de mencionar é que podemos colocar recursos de namespaces dentro de uma ClusterRole normalmente, mas não o contrário. A diferença que é que estamos liberando TODOS OS NAMESPACES.