Skip to main content

Service Accounts

Existe dois tipos de contas no kubernetes.

  • User Accounts: Essas contas são usadas por humanos.
  • Service Accounts: São usadas por aplicação.

Em qualquer namespace criado uma service account default é criada por padrão, vamos conferir.

kubectl create namespace demo  
namespace/demo created

kubectl get serviceaccounts -n demo
NAME SECRETS AGE
default 0 16s

# Mas nada impede de criamos uma service account específica.
create sa -n default demo-sa
serviceaccount/demo-sa created

kubectl get serviceaccounts -n demo
NAME SECRETS AGE
default 0 2m20s
demo-sa 0 4s

# Para deletar

kubectl delete sa -n demo demo-sa
serviceaccount "demo-sa" deleted

Vamos observar que não existe uma secret vinculada nesse service account ainda por isso temos 0. A secret ali é o nome da secret que guardará um token usado pela aplicação como bearer token para acesso a api do kubernetes.

Antes da versão 1.24 a secret era gerada automaticamente e o token era colocado dentro da secret.

Vamos entender um pouco da história.

Toda vez que um pod é criado, automaticamente a conta de serviço default do namespace é automaticamente montada dentro do pod como um volumeMount.

kubectl create deployment nginx --image nginx

kubectl describe pods nginx-7854ff8877-8r7hs

Name: nginx-7854ff8877-8r7hs
Namespace: default
Priority: 0
Service Account: default #<<<< NEM PASSAMOS ISSO
...
Containers:
nginx:
Container ID: containerd://c7c95a9dda456e6da5ad14e17f8d5ab496991d177b1cadbc69ca28614a903a8a
Image: nginx
Image ID: docker.io/library/nginx@sha256:84c52dfd55c467e12ef85cad6a252c0990564f03c4850799bf41dd738738691f
...
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-c9f7j (ro) #<<<<O recurso kube-api-access-c9f7j não é diretamente acessível para inspeção ou visualização no nível do usuário. Ele é criado e gerenciado internamente pelo Kubernetes como parte do processo de criação e execução de pods para gerar o token automaticamente.
...
Volumes:
kube-api-access-c9f7j:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
...

Claro poderíamos usar um outro service account se quisermos passando dentro da spec do pod.

apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: nginx
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
resources: {}
serviceAccountName: my-service-account #<<<
automountServiceAccountToken: true #<<< ESSE É O VALOR PADRÃO
status: {}

Por padrão o kubernetes faz automount do service account token, mas é possível desativar caso necessário.

O que temos ali?

kubectl exec -it nginx-7854ff8877-8r7hs -- ls /var/run/secrets/kubernetes.io/serviceaccount
ca.crt namespace token

#ca do cluster
kubectl exec -it nginx-7854ff8877-8r7hs -- cat /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
-----BEGIN CERTIFICATE-----
MIIDBTCCAe2gAwIBAgIIeL8dKSH2rh8wDQYJKoZIhvcNAQELBQAwFTETMBEGA1UE
AxMKa3ViZXJuZXRlczAeFw0yNDAyMDgyMjU3MThaFw0zNDAyMDUyMzAyMThaMBUx
EzARBgNVBAMTCmt1YmVybmV0ZXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQDRCy18tHA4HJbSDtOZo2yp4yzyJ7QnAirXImfoqjjjOe0uF8Jo4GjbCb61
GhBiWt3+A4rfJ3yGdx75PuOcnhN6siOR5oOWUHx2zDbJwSDA9pdvXJa0GdU0LTUY
mvSkUOn7DtZtLZ8q/lElIHphnSX4uqQTTL87NPL9ComFTHZAXqR8WjSQTFcYle4b
hiu6coMUq/1BKCTeN84omVqnLxuDdkdjoxnXm+Q3Jj4iQujFJ5NrxFOfCR2g4OTY
x92p1K5krnXS+z26NrzR1Up9k/2fQs129Rsn4+oAPkgs4s1CXfs9mS/5rJhqemRo
IghxaAKP+0hmSWSyKbZgqxDs3dAPAgMBAAGjWTBXMA4GA1UdDwEB/wQEAwICpDAP
BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQaEQhPwBz2wWOm66FEI7HiE+V1JjAV
BgNVHREEDjAMggprdWJlcm5ldGVzMA0GCSqGSIb3DQEBCwUAA4IBAQCRkBml9DKD
iX7Qfc/jguqr+K5FRtvraanbl4v6/fP2duGzU6fvRMv0Qs4yKnPSg0QsvXyItHXZ
4P+AgPLTYnzAuK8uqUoCLF3q/DCuH7plWn7HmXGL/JQ0KBIxwgk3pYPCO0Y53ROJ
vxiXPvFgGovtZWYj5RKn9T4F+dir6gQufXctKHel2o0OP8KfzNqz5L2pbXE58vMO
Jk6Fvd82mH1N7SpWBJgW6d3Co4FW+Zp527sjmu+IHzyctLUfK/hSSDhLDC1MWmP4
99DV0sgY1mRXTz7AIzjXyDTy0z0Rdgy9ibPSwDRbVqiZ+v2mfszkPunazohlHv8J
5w63PiNNZBeA
-----END CERTIFICATE-----
#o namespace

kubectl exec -it nginx-7854ff8877-8r7hs -- cat /var/run/secrets/kubernetes.io/serviceaccount/namespace
default

#e o token criado automagicamente pelo kube-api-acess
kubectl exec -it nginx-7854ff8877-8r7hs -- cat /var/run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsImtpZCI6Img5U19OUGN0WnNicWk2Z0hrZmk4VkpjSUpQRWV3c1QyRHZEdU9HOVZsa0UifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNzM5Mjc3NDY0LCJpYXQiOjE3MDc3NDE0NjQsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZWZhdWx0IiwicG9kIjp7Im5hbWUiOiJuZ2lueC03ODU0ZmY4ODc3LThyN2hzIiwidWlkIjoiMTk3Y2E2YjctZTEyMC00OTgxLWFmOGEtZTllOTllMmIxZjBiIn0sInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJkZWZhdWx0IiwidWlkIjoiYjRlZDJhMGUtNDBmMC00Yjc0LWEzNzgtZjgwYmJkOTEzNzNiIn0sIndhcm5hZnRlciI6MTcwNzc0NTA3MX0sIm5iZiI6MTcwNzc0MTQ2NCwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRlZmF1bHQ6ZGVmYXVsdCJ9.KvlOcSuG_W3YqOKUKC9nIEI8ksITGwUoKboubyGcozHRg9v_OycyG-5O6EAOdmYKE5Hd1u-b9Bl8n4NhYDF_ygf8gzu406nEf-BpBgWjogOaGylD0dIopaeC3rK7G47S9nMD1cFDGTz07hygxIQSezUfZd1EkmaSq3nK1g0yk3lcKv9fKZpFHcBsZAOEUMJyQ2cLeE_Rdaf9i4w1XTtWL-5fcbWuOjHq96rBjIejP-myjE971bvCyN3WE39vRUYZMq8aO1XIVt9_bsOd-GaTVXgN1r0LNeCz1ye743J2JYWin2_ic5fkc5vUxeNObgg03WXvUAAkcvOaetSp5yLLIQ

O jwt.io faz o decode para gente de forma visual.

alt text

Antigamente não tínhamos a data de expiração e o token para uma conta de serviço era sempre o mesmo e válido enquanto a service account existir.

Todos os pods do mesmo namespace que não definiam um service account usavam o mesmo token. Com essa modificação todos os pods podem usar a mesma service account, mas com tokens diferentes o que melhora a segurança e garante a rotação de token (toda vez que um pod é recriado novos tokens são gerados).

Outra situação é que uma service account perdida no cluster poderia dar acesso a todo aquele namespace se tivesse permissão.

Por isso foi proposto um TokenRequestAPI que provisionasse tokens automaticamente com maior segurança e resolvia também algumas issues de escalabilidade que existiam.

Além do jwt.io, o comando abaixo também verifica usando o jq ou o jwt cli.

jq -R 'split(".") | select(length > 0) | .[0],.[1] | @base64d | fromjson' <<< eyJhbGciOiJSUzI1NiIsImtpZCI6Img5U19OUGN0WnNicWk2Z0hrZmk4VkpjSUpQRWV3c1QyRHZEdU9HOVZsa0UifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNzM5Mjc3NDY0LCJpYXQiOjE3MDc3NDE0NjQsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZWZhdWx0IiwicG9kIjp7Im5hbWUiOiJuZ2lueC03ODU0ZmY4ODc3LThyN2hzIiwidWlkIjoiMTk3Y2E2YjctZTEyMC00OTgxLWFmOGEtZTllOTllMmIxZjBiIn0sInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJkZWZhdWx0IiwidWlkIjoiYjRlZDJhMGUtNDBmMC00Yjc0LWEzNzgtZjgwYmJkOTEzNzNiIn0sIndhcm5hZnRlciI6MTcwNzc0NTA3MX0sIm5iZiI6MTcwNzc0MTQ2NCwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRlZmF1bHQ6ZGVmYXVsdCJ9.KvlOcSuG_W3YqOKUKC9nIEI8ksITGwUoKboubyGcozHRg9v_OycyG-5O6EAOdmYKE5Hd1u-b9Bl8n4NhYDF_ygf8gzu406nEf-BpBgWjogOaGylD0dIopaeC3rK7G47S9nMD1cFDGTz07hygxIQSezUfZd1EkmaSq3nK1g0yk3lcKv9fKZpFHcBsZAOEUMJyQ2cLeE_Rdaf9i4w1XTtWL-5fcbWuOjHq96rBjIejP-myjE971bvCyN3WE39vRUYZMq8aO1XIVt9_bsOd-GaTVXgN1r0LNeCz1ye743J2JYWin2_ic5fkc5vUxeNObgg03WXvUAAkcvOaetSp5yLLIQ

## usando jwt-cli que vai verifica no jwt.io
npm install -g jwt-cli

jwt eyJhbGciOiJSUzI1NiIsImtpZCI6Img5U19OUGN0WnNicWk2Z0hrZmk4VkpjSUpQRWV3c1QyRHZEdU9HOVZsa0UifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNzM5Mjc3NDY0LCJpYXQiOjE3MDc3NDE0NjQsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZWZhdWx0IiwicG9kIjp7Im5hbWUiOiJuZ2lueC03ODU0ZmY4ODc3LThyN2hzIiwidWlkIjoiMTk3Y2E2YjctZTEyMC00OTgxLWFmOGEtZTllOTllMmIxZjBiIn0sInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJkZWZhdWx0IiwidWlkIjoiYjRlZDJhMGUtNDBmMC00Yjc0LWEzNzgtZjgwYmJkOTEzNzNiIn0sIndhcm5hZnRlciI6MTcwNzc0NTA3MX0sIm5iZiI6MTcwNzc0MTQ2NCwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRlZmF1bHQ6ZGVmYXVsdCJ9.KvlOcSuG_W3YqOKUKC9nIEI8ksITGwUoKboubyGcozHRg9v_OycyG-5O6EAOdmYKE5Hd1u-b9Bl8n4NhYDF_ygf8gzu406nEf-BpBgWjogOaGylD0dIopaeC3rK7G47S9nMD1cFDGTz07hygxIQSezUfZd1EkmaSq3nK1g0yk3lcKv9fKZpFHcBsZAOEUMJyQ2cLeE_Rdaf9i4w1XTtWL-5fcbWuOjHq96rBjIejP-myjE971bvCyN3WE39vRUYZMq8aO1XIVt9_bsOd-GaTVXgN1r0LNeCz1ye743J2JYWin2_ic5fkc5vUxeNObgg03WXvUAAkcvOaetSp5yLLIQ
...
✻ Header
{
"alg": "RS256",
"kid": "h9S_NPctZsbqi6gHkfi8VJcIJPEewsT2DvDuOG9VlkE"
}

✻ Payload
{
"aud": [
"https://kubernetes.default.svc.cluster.local"
],
"exp": 1739277464, # Essa data de expiração são é uma epoch ou seja o número de segundos a partir de 1970
"iat": 1707741464,
"iss": "https://kubernetes.default.svc.cluster.local",
"kubernetes.io": {
"namespace": "default",
"pod": {
"name": "nginx-7854ff8877-8r7hs",
"uid": "197ca6b7-e120-4981-af8a-e9e99e2b1f0b"
},
"serviceaccount": {
"name": "default",
"uid": "b4ed2a0e-40f0-4b74-a378-f80bbd91373b"
},
"warnafter": 1707745071
},
"nbf": 1707741464,
"sub": "system:serviceaccount:default:default"
}
iat: 1707741464 2/12/2024, 9:37:44 AM
nbf: 1707741464 2/12/2024, 9:37:44 AM
exp: 1739277464 2/11/2025, 9:37:44 AM

✻ Signature KvlOcSuG_W3YqOKUKC9nIEI8ksITGwUoKboubyGcozHRg9v_OycyG-5O6EAOdmYKE5Hd1u-b9Bl8n4NhYDF_ygf8gzu406nEf-BpBgWjogOaGylD0dIopaeC3rK7G47S9nMD1cFDGTz07hygxIQSezUfZd1EkmaSq3nK1g0yk3lcKv9fKZpFHcBsZAOEUMJyQ2cLeE_Rdaf9i4w1XTtWL-5fcbWuOjHq96rBjIejP-myjE971bvCyN3WE39vRUYZMq8aO1XIVt9_bsOd-GaTVXgN1r0LNeCz1ye743J2JYWin2_ic5fkc5vUxeNObgg03WXvUAAkcvOaetSp5yLLIQ

# Transformando a data para algo que possamos ler, só de curiosidade
date -d @1739277464
ter 11 fev 2025 09:37:44 -03 # 1 ANO

Não é possível editar a service account de um pod, é necessário deletar e recriar um novo. No caso de um deployment é possível editar o deployment pois ele criar um novo replicaset com novas configurações de pod.

Se necessário podemos criar um token para uma service account que não é usada em nenhum pod, mas para acesso de terceiro podemos fazer.

kubectl get sa  
NAME SECRETS AGE
default 0 3d15h
my-service-account 0 157m #<<< vamos usar essa

kubectl create token my-service-account --duration 87600h
eyJhbGciOiJSUzI1NiIsImtpZCI6Img5U19OUGN0WnNicWk2Z0hrZmk4VkpjSUpQRWV3c1QyRHZEdU9HOVZsa0UifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoyMDIzMTA4NjU1LCJpYXQiOjE3MDc3NDg2NTUsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZWZhdWx0Iiwic2VydmljZWFjY291bnQiOnsibmFtZSI6Im15LXNlcnZpY2UtYWNjb3VudCIsInVpZCI6IjExMDcwMmZmLTkwNjAtNDAyNy1hMTlmLWNjMDQyOGE2NzBiZiJ9fSwibmJmIjoxNzA3NzQ4NjU1LCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDpteS1zZXJ2aWNlLWFjY291bnQifQ.BzVKg4oVTCKScv76zb-507q3EenpaQF-0XbcmqDmEK7t23GY1euIMQdolgAriu8KzYet9TY_1SCKRn3vsR4a6qjl89BmWO9yxUuuaLLy1TmGRKr3KC854XaldlhZ7aT4hqg20Ut9V7lnPjUKBvIZ8xzILHkhv_ZlZUS9C7BzBeaR9IJY-qnQ5PUnxb5WqyThmdLWWwyUPQtve7YJuOEGSpvXo3AoXad9OjNXAkjPuxqWtxyIus8BIzTWxn0loQ1L47mHfMY_AAfBhblwNLx15GZr6Mjh1wzR0uNmzh2stcANYudcagz9bGtkMqel-rF-9ingoSm2cHEFeHdBbFY3EQ

Esse token eu posso usar mesmo? E se eu quiser deletar? Onde eu posso ver os tokens criados para deletar?

Para replicar o cenário antigo e ter um token sem expiração vinculado em uma service account, primeiro precisamos ter a service account e depois criar uma secret com um tipo espefícido a através das annotations vincular a secret a serviceaccount.

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
type: kubernetes.io/service-account-token # Tipo especifico de token
metadata:
name: my-service-account-token #nome
annotations:
kubernetes.io/service-account.name: my-service-account #Service Account que usará
EOF
kubectl describe secrets my-service-account-token  
Name: my-service-account-token
Namespace: default
Labels: <none>
Annotations: kubernetes.io/service-account.name: my-service-account
kubernetes.io/service-account.uid: 110702ff-9060-4027-a19f-cc0428a670bf

Type: kubernetes.io/service-account-token

Data
====
ca.crt: 1107 bytes
namespace: 7 bytes
## Vamos verificar esse token aqui
token: eyJhbGciOiJSUzI1NiIsImtpZCI6Img5U19OUGN0WnNicWk2Z0hrZmk4VkpjSUpQRWV3c1QyRHZEdU9HOVZsa0UifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6Im15LXNlcnZpY2UtYWNjb3VudC10b2tlbiIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJteS1zZXJ2aWNlLWFjY291bnQiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiIxMTA3MDJmZi05MDYwLTQwMjctYTE5Zi1jYzA0MjhhNjcwYmYiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDpteS1zZXJ2aWNlLWFjY291bnQifQ.frN4SSbI-SgoGLNjyapdOaUlc_WmTTFmWBO9hejdCnCkf5NfjiFVNkxSGht-cKNvlI2ADLz1-gVB3vIRbxFw-hXmxsZeH5heK5OeyMctpcRZrj-BRpwo1ubZ14VH94VH-nF8q7MwaA84uuuK0CiBOdSLITXkKRzVUkxR3qPa7MBNUs-0HIlMZQBzxkgQ24lu3cvPRX1rP9HX3UmxZaemARBBrqki03t1udJzDWwPkc48H_r3I0zSjdqxoIJ0NtDXj_fJdv8rjaeJC8TiXJvOmtvhpkleU0NC51BsjRi7ennWZ2MYO_9XjX4NDUkBcsovEAOOo4T7olJpv9lC3zSlwg

# VERIFICANDO
jwt eyJhbGciOiJSUzI1NiIsImtpZCI6Img5U19OUGN0WnNicWk2Z0hrZmk4VkpjSUpQRWV3c1QyRHZEdU9HOVZsa0UifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6Im15LXNlcnZpY2UtYWNjb3VudC10b2tlbiIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJteS1zZXJ2aWNlLWFjY291bnQiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiIxMTA3MDJmZi05MDYwLTQwMjctYTE5Zi1jYzA0MjhhNjcwYmYiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDpteS1zZXJ2aWNlLWFjY291bnQifQ.frN4SSbI-SgoGLNjyapdOaUlc_WmTTFmWBO9hejdCnCkf5NfjiFVNkxSGht-cKNvlI2ADLz1-gVB3vIRbxFw-hXmxsZeH5heK5OeyMctpcRZrj-BRpwo1ubZ14VH94VH-nF8q7MwaA84uuuK0CiBOdSLITXkKRzVUkxR3qPa7MBNUs-0HIlMZQBzxkgQ24lu3cvPRX1rP9HX3UmxZaemARBBrqki03t1udJzDWwPkc48H_r3I0zSjdqxoIJ0NtDXj_fJdv8rjaeJC8TiXJvOmtvhpkleU0NC51BsjRi7ennWZ2MYO_9XjX4NDUkBcsovEAOOo4T7olJpv9lC3zSlwg

...
## NÃO TEMOS TEMPO DE EXPIRAÇÃO
✻ Header
{
"alg": "RS256",
"kid": "h9S_NPctZsbqi6gHkfi8VJcIJPEewsT2DvDuOG9VlkE"
}

✻ Payload
{
"iss": "kubernetes/serviceaccount",
"kubernetes.io/serviceaccount/namespace": "default",
"kubernetes.io/serviceaccount/secret.name": "my-service-account-token",
"kubernetes.io/serviceaccount/service-account.name": "my-service-account",
"kubernetes.io/serviceaccount/service-account.uid": "110702ff-9060-4027-a19f-cc0428a670bf",
"sub": "system:serviceaccount:default:my-service-account"
}

✻ Signature frN4SSbI-SgoGLNjyapdOaUlc_WmTTFmWBO9hejdCnCkf5NfjiFVNkxSGht-cKNvlI2ADLz1-gVB3vIRbxFw-hXmxsZeH5heK5OeyMctpcRZrj-BRpwo1ubZ14VH94VH-nF8q7MwaA84uuuK0CiBOdSLITXkKRzVUkxR3qPa7MBNUs-0HIlMZQBzxkgQ24lu3cvPRX1rP9HX3UmxZaemARBBrqki03t1udJzDWwPkc48H_r3I0zSjdqxoIJ0NtDXj_fJdv8rjaeJC8TiXJvOmtvhpkleU0NC51BsjRi7ennWZ2MYO_9XjX4NDUkBcsovEAOOo4T7olJpv9lC3zSlwg

Agora algumas curiosidades que encontrei no caminho.

## Se olharmos a service account temos o token lá vinculado
kubectl describe sa my-service-account
Name: my-service-account
Namespace: default
Labels: <none>
Annotations: <none>
Image pull secrets: <none>
Mountable secrets: <none>
Tokens: my-service-account-token #<<<<
Events: <none>

## Temos a secret
kubectl get secret
NAME TYPE DATA AGE
my-service-account-token kubernetes.io/service-account-token 3 4m54s

## Mas o Service Account não mostra de fato que tem uma secret. DOIDERA?
kubectl get serviceaccounts
NAME SECRETS AGE
default 0 3d15h
my-service-account 0 3h2m #<<<< ????

Podemos criar roles e fazer rolebinding com os service accounts normalmente. Ao invés de User é ServiceAccount.