Criar conta de serviço/usuário Kubernetes restrita a um namespace
Como posso criar um usuário e restringir o usuário para acessar apenas um namespace no Kubernetes? O Kubernetes oferece uma maneira de regular o acesso aos clusters e recursos do Kubernetes com base nas funções de usuários individuais por meio de um recurso chamado controle de acesso baseado em funções (RBAC). A partir da versão 1.8 do Kubernetes, o modo RBAC é estável e é apoiado pela API rbac.authorization.k8s.io/v1.
Existem algumas definições que você precisa entender antes de prosseguirmos:
- Função: uma função contém regras que representam um conjunto de permissões. Uma função é usada para conceder acesso a recursos dentro de um namespace.
- RoleBinding: uma vinculação de função é usada para conceder as permissões definidas em uma função a um usuário ou conjunto de usuários. Contém uma lista de assuntos (usuários, grupos ou contas de serviço) e uma referência à função que está sendo concedida
- Conta de serviço: conta destinada a processos executados em pods.
Para obter um isolamento completo no Kubernetes, usaremos os conceitos de namespaces e controle de acesso baseado em funções. A ideia por trás das contas de serviço é baseada no princípio do menor privilégio. Uma conta é criada para tarefas específicas.
Pré-requisitos de configuração
Um cluster Kubernetes em execução – verifique os guias abaixo:
- Como implantar cluster leve do Kubernetes em 5 minutos com K3s
- Implante cluster Kubernetes pronto para produção com Ansible e Kubespray
- Como executar cluster Kubernetes local em contêineres Docker
- Implante Kubernetes leves com MicroK8s e Snap
- Como configurar o cluster Kubernetes de 3 nós no Ubuntu com Weave Net CNI
Ferramenta de linha de comando de gerenciamento kubectl Kubernetes configurada
- Gerencie facilmente vários clusters Kubernetes com kubectl e kubectx
Crie e limite uma conta de serviço a um namespace no Kubernetes
Etapa 1: crie um namespace
Vamos começar criando um namespace que será usado nesta demonstração.
$ kubectl create namespace demo
namespace/demo created
$ kubectl get namespaces
NAME STATUS AGE
default Active 6d14h
kube-system Active 6d14h
kube-public Active 6d14h
kube-node-lease Active 6d14h
ingress-nginx Active 4d21h
demo Active 24s
Etapa 2: crie uma conta de serviço
Criaremos uma conta de serviço chamada demo-user no namespace demo .
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: demo-user
namespace: demo
EOF
Você obterá uma saída como esta:
serviceaccount/demo-user created
Etapa 3: crie uma função
Conforme explicado anteriormente, uma função contém regras que representam um conjunto de permissões que concedem acesso a recursos dentro de um namespace.
Primeiro, confirme as versões da API para RBAC disponíveis em seu cluster Kubernetes:
$ kubectl api-versions| grep rbac
rbac.authorization.k8s.io/v1
rbac.authorization.k8s.io/v1beta1
Vamos criar uma função que dará à conta criada acesso completo aos recursos do namespace.
cat <<EOF | kubectl apply -f -
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: admin
namespace: demo
rules:
- apiGroups: ["", "extensions", "apps"]
resources: ["*"]
verbs: ["*"]
- apiGroups: ["batch"]
resources:
- jobs
- cronjobs
verbs: ["*"]
EOF
Confirme a criação:
$ kubectl get roles -n demo
NAME AGE
admin 94s
Uma função também pode ser criada com acesso limitado a recursos em um namespace, por exemplo:
cat <<EOF | kubectl apply -f -
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: demo
name: deployment-admin
rules:
- apiGroups: ["", "extensions", "apps"]
resources: ["deployments", "replicasets", "pods", "services", "ingresses"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] # You can also use ["*"]
EOF
Etapa 4: vincular a função a um usuário
Agora que criamos uma conta de usuário e uma função, podemos prosseguir para vincular uma função ao usuário.
cat <<EOF | kubectl apply -f -
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: admin-view
namespace: demo
subjects:
- kind: ServiceAccount
name: demo-user
namespace: demo
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: admin
EOF
Confirmação:
$ kubectl get rolebindings --namespace demo
NAME AGE
admin-view 30s
Verifique o nome do token do usuário:
$ kubectl describe sa demo-user -n demo
Name: demo-user
Namespace: demo
Labels:
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","kind":"ServiceAccount","metadata":{"annotations":{},"name":"demo-user","namespace":"demo"}}
Image pull secrets:
Mountable secrets: demo-user-token-k9qbl
Tokens: demo-user-token-k9qbl
Events:
Obtenha o token da conta de serviço a ser usado para acessar o Kubernetes no painel ou por meio da linha de comando do kubectl.
Kubernetes <=1.23
export NAMESPACE="demo"
export K8S_USER="demo-user"
kubectl -n ${NAMESPACE} describe secret $(kubectl -n ${NAMESPACE} get secret | (grep ${K8S_USER} || echo "$_") | awk '{print $1}') | grep token: | awk '{print $2}'\n
Minha saída é:
eyJhbGciOiJSUzI1NiIsImtpZCI6IkRrUEFveUZGUGZZS0Q3Tzl5eVZpcFE5elFYZEI5SWZ6ZlVhYXFzLU04ZTQifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZW1vIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlbW8tdXNlci10b2tlbi1rOXFibCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJkZW1vLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJjOWRhNGVmOC1jNmQ5LTQ0NTEtYTQ5Ny02ODc1MjY1MzAwMzQiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVtbzpkZW1vLXVzZXIifQ.CnrAkziL_Qr8QNQCV_PDkXCi2H-4MoPUGoPVxjSWGZUTXd6V-a9_JKv6t5Vqhrh5vXNTkDSaR1BtLCpKYdXTyqY6CjbyI7gYYcA2M22nkCDjUiwDhxInlios29SAtoOAXq7rwg_cdgdA7XWAWEcWDtT1vRe5LLbXsnORuJ5BtYXynQXWjWjbcC6T9XqRL7iZX4VUk4YCAkX7N89OGzvyycUjjHzOne67qzqOawzjYqeSzHiXIXILwHk4KKhU8tdGG6shYF7niazdp6ZyssdQ24lQext9jzDeUZf3iXPJ_bvZUv4Jo0_eZjldi9WW0dgN5PXe5r-cD1nOJHE8sClBsg
Obtenha dados do certificado
kubectl -n ${NAMESPACE} get secret `kubectl -n ${NAMESPACE} get secret | (grep ${K8S_USER} || echo "$_") | awk '{print $1}'` -o "jsonpath={.data['ca\.crt']}"
Minha saída:
LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJXRENCL3FBREFnRUNBZ0VBTUFvR0NDcUdTTTQ5QkFNQ01DTXhJVEFmQmdOVkJBTU1HR3N6Y3kxelpYSjIKWlhJdFkyRkFNVFUzTmpNd01qVXdNakFlRncweE9URXlNVFF3TlRRNE1qSmFGdzB5T1RFeU1URXdOVFE0TWpKYQpNQ014SVRBZkJnTlZCQU1NR0dzemN5MXpaWEoyWlhJdFkyRkFNVFUzTmpNd01qVXdNakJaTUJNR0J5cUdTTTQ5CkFnRUdDQ3FHU000OUF3RUhBMElBQlBiSmdKQ2w0elZsNFlaQUJ4dThnbFk1c2hEeDYwaVd6cXY0RU96MS93K24KKzJpMG5heUxuRTF1MjZmT1ZkY3dlMFdjUFJCb2J0M2ViNnNtYWRKQUhBV2pJekFoTUE0R0ExVWREd0VCL3dRRQpBd0lDcERBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUFvR0NDcUdTTTQ5QkFNQ0Ewa0FNRVlDSVFEczN4YTArK0E4CktVd0w2NUZyR25vWW9sTWNUei81QnoxSmNJc292VkxncUFJaEFJV0xRUXpyWkpDem9TaVlEMFZvUXhlaXIwOUEKVWhnTDBucFlzRDVUUlVYKwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
Kubernetes >=1,24
A partir do Kubernetes 1.24 em diante, o token não é mais criado automaticamente. Em vez disso, usaremos a API TokenRequest para criar tokens.
kubectl create token demo-user -n demo
Em seguida, adicione um novo segredo ao seu cluster com o seguinte código:
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Secret
metadata:
name: demo-user-token
annotations:
kubernetes.io/service-account.name: demo-user
type: kubernetes.io/service-account-token
EOF
Você pode então imprimir o token.
export NAMESPACE="demo"
export K8S_USER="demo-user"
kubectl -n ${NAMESPACE} describe secret $(kubectl -n ${NAMESPACE} get secret | (grep ${K8S_USER} || echo "$_") | awk '{print $1}') | grep token: | awk '{print $2}'\n
Imprimir dados do certificado:
kubectl -n ${NAMESPACE} get secret `kubectl -n ${NAMESPACE} get secret | (grep ${K8S_USER} || echo "$_") | awk '{print $1}'` -o "jsonpath={.data['ca\.crt']}"
Etapa 5: Criando a configuração do kubectl
Se você deseja configurar o kubectl com as credenciais obtidas, será semelhante a abaixo.
$ cat .kube/config
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJXRENCL3FBREFnRUNBZ0VBTUFvR0NDcUdTTTQ5QkFNQ01DTXhJVEFmQmdOVkJBTU1HR3N6Y3kxelpYSjIKWlhJdFkyRkFNVFUzTmpNd01qVXdNakFlRncweE9URXlNVFF3TlRRNE1qSmFGdzB5T1RFeU1URXdOVFE0TWpKYQpNQ014SVRBZkJnTlZCQU1NR0dzemN5MXpaWEoyWlhJdFkyRkFNVFUzTmpNd01qVXdNakJaTUJNR0J5cUdTTTQ5CkFnRUdDQ3FHU000OUF3RUhBMElBQlBiSmdKQ2w0elZsNFlaQUJ4dThnbFk1c2hEeDYwaVd6cXY0RU96MS93K24KKzJpMG5heUxuRTF1MjZmT1ZkY3dlMFdjUFJCb2J0M2ViNnNtYWRKQUhBV2pJekFoTUE0R0ExVWREd0VCL3dRRQpBd0lDcERBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUFvR0NDcUdTTTQ5QkFNQ0Ewa0FNRVlDSVFEczN4YTArK0E4CktVd0w2NUZyR25vWW9sTWNUei81QnoxSmNJc292VkxncUFJaEFJV0xRUXpyWkpDem9TaVlEMFZvUXhlaXIwOUEKVWhnTDBucFlzRDVUUlVYKwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
server: https://k3s-master01:6443
name: mycluster
contexts:
- context:
cluster: mycluster
namespace: demo
user: demo-user
name: demo
current-context: demo
kind: Config
preferences: {}
users:
- name: demo-user
user:
token: eyJhbGciOiJSUzI1NiIsImtpZCI6IkRrUEFveUZGUGZZS0Q3Tzl5eVZpcFE5elFYZEI5SWZ6ZlVhYXFzLU04ZTQifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZW1vIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlbW8tdXNlci10b2tlbi1rOXFibCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJkZW1vLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJjOWRhNGVmOC1jNmQ5LTQ0NTEtYTQ5Ny02ODc1MjY1MzAwMzQiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVtbzpkZW1vLXVzZXIifQ.CnrAkziL_Qr8QNQCV_PDkXCi2H-4MoPUGoPVxjSWGZUTXd6V-a9_JKv6t5Vqhrh5vXNTkDSaR1BtLCpKYdXTyqY6CjbyI7gYYcA2M22nkCDjUiwDhxInlios29SAtoOAXq7rwg_cdgdA7XWAWEcWDtT1vRe5LLbXsnORuJ5BtYXynQXWjWjbcC6T9XqRL7iZX4VUk4YCAkX7N89OGzvyycUjjHzOne67qzqOawzjYqeSzHiXIXILwHk4KKhU8tdGG6shYF7niazdp6ZyssdQ24lQext9jzDeUZf3iXPJ_bvZUv4Jo0_eZjldi9WW0dgN5PXe5r-cD1nOJHE8sClBsg
client-key-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJXRENCL3FBREFnRUNBZ0VBTUFvR0NDcUdTTTQ5QkFNQ01DTXhJVEFmQmdOVkJBTU1HR3N6Y3kxelpYSjIKWlhJdFkyRkFNVFUzTmpNd01qVXdNakFlRncweE9URXlNVFF3TlRRNE1qSmFGdzB5T1RFeU1URXdOVFE0TWpKYQpNQ014SVRBZkJnTlZCQU1NR0dzemN5MXpaWEoyWlhJdFkyRkFNVFUzTmpNd01qVXdNakJaTUJNR0J5cUdTTTQ5CkFnRUdDQ3FHU000OUF3RUhBMElBQlBiSmdKQ2w0elZsNFlaQUJ4dThnbFk1c2hEeDYwaVd6cXY0RU96MS93K24KKzJpMG5heUxuRTF1MjZmT1ZkY3dlMFdjUFJCb2J0M2ViNnNtYWRKQUhBV2pJekFoTUE0R0ExVWREd0VCL3dRRQpBd0lDcERBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUFvR0NDcUdTTTQ5QkFNQ0Ewa0FNRVlDSVFEczN4YTArK0E4CktVd0w2NUZyR25vWW9sTWNUei81QnoxSmNJc292VkxncUFJaEFJV0xRUXpyWkpDem9TaVlEMFZvUXhlaXIwOUEKVWhnTDBucFlzRDVUUlVYKwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
Vamos confirmar se funciona:
$ kubectl get secrets
NAME TYPE DATA AGE
default-token-25lbj kubernetes.io/service-account-token 3 55m
demo-user-token-k9qbl kubernetes.io/service-account-token 3 50m
$ kubectl get nodes
Error from server (Forbidden): nodes is forbidden: User "system:serviceaccount:demo:demo-user" cannot list resource "nodes" in API group "" at the cluster scope
Também podemos criar implantações de teste:
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: busybox-sleep
spec:
containers:
- name: busybox
image: busybox
args:
- sleep
- "1000000"
---
apiVersion: v1
kind: Pod
metadata:
name: busybox-sleep-less
spec:
containers:
- name: busybox
image: busybox
args:
- sleep
- "1000"
EOF
Confirme e limpe:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
busybox-sleep-less 1/1 Running 0 65s
busybox-sleep 1/1 Running 0 65s
$ for i in busybox-sleep-less busybox-sleep; do kubectl delete pod $i; done
pod "busybox-sleep-less" deleted
pod "busybox-sleep" deleted
Para configurações de vários clusters, verifique: Gerencie facilmente vários clusters Kubernetes com kubectl e kubectx
Referência:
- Página de referência do Kubernetes RBAC