jueves, abril 04, 2019

Kubernetes: Cómo gestionar autorización de recursos con RBAC (Parte 2) #docker #kubernetes

Continuamos con nuestro artículo sobre Kubernetes y RBAC justo donde lo dejamos en la primera parte (jugando con los espacios de nombres), así que echa un vistazo al anterior post para repasar y manos a la obra. Ahora vamos a centrarnos en la creación de roles y su asociación a diferentes usuarios y grupos.

Figura 6: Kubernetes: Cómo gestionar autorización de recursos con RBAC (Parte 2)

En este ejemplo, que vamos a usar de base para explicar sus componentes, podemos ver un fichero de creación de un role para tener control total total al espacio de nombres de cybercaronte (luego veremos como se crea):
    kind: Role
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
        name: cybercaronte-full-control
        namespace: cybercaronte
    rules:
    - apiGroups: [""]
      resources: ["*"]
      verbs: ["*"]
Como todo objeto en Kubernetes, vemos como en este caso tenemos definidos los atributos kind, apiVersion y metadata. Ojo a este caso, como estamos creando un role, tenemos que especificar el espacio de nombres donde queremos crearlo con el atributo namespace dentro de metadata.

Figura 7: Proceso de arranque de minikube

Lo siguiente es definir las reglas (rules). En este case vemos como tenemos entradas bajo rules:
apiGroups: aquí especificamos la(s) API(s) que queremos autorizar. Si lo dejamos vacío (""), implica que dicha regla (rule), se aplica a las APIs core de Kubernetes. Alguno de los grupos disponibles, además del core, son: extensions, apps, etc. Si quieres ver todas las APIs disponible en tu clúster, puedes ejecutar el siguiente comando:
kubectl api-versions
resources: aquí, si queremos aplicar la regla a todos los dispositivos, podemos poner un asterisco, o bien listar los recursos que deseamos. Por ejempo: pods, secrets, configmaps, deployments, services, etc.
verbs: en Kubernetes el recibe todas las peticiones es el component API Server, que no es ni más ni menos que un servicio Rest. Por lo que toda petición se basa en un recurso y un verbo: GET, POST, PUT, etc. En el caso de Kubernetes, éste define su propia lista de verbos como son: get, list, watch, create, update, patch, delete, bind, etcetera.
Si creamos ese role y se lo asignamos a un usuario, dicho usuario prácticamente tendría control total sobre el espacio de nombres donde dicho role es creado. Un apunte importante es que podemos añadir varias reglas a un role:
    rules:
    - apiGroups: [""]
      resources: ["pods"]
      verbs: ["get", "list", "watch"]
    - apiGroups: ["batch", "extensions"]
      resources: ["jobs"]
      verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
En este caso, tenemos dos reglas, la primera indica que podemos leer, listar y observar pods a través del uso de cualquier API de Kubernetes. La segunda indica que podemos obtener, listar, observar, crear, actualizar, parchear y borrar recursos del tipo (Kind) job definidos bajo las APIs batch y extensions.

Ahora sí, vamos a crear los dos roles, uno para el espacio de nombres cybercaronte:
    kind: Role
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: cybercaronte-full-control
      namespace: cybercaronte
    rules:
    - apiGroups: [""]
      resources: ["*"]
      verbs: ["*"]
Y otro para el espacio de nombres tuxotron:
    kind: Role
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: tuxotron-full-control
      namespace: tuxotron
    rules:
    - apiGroups: [""]
      resources: ["*"]
      verbs: ["*"]
Creamos dos ficheros (cybercaronte-fullcontrol-role.yaml y tuxotron-fullcontrol-role.yaml) con dicho contenido y ejecuta:
  kubectl apply -f cybercaronte-fullcontrol-role.yaml
  kubectl apply -f tuxotron-fullcontrol-role.yaml
Para comprobar que los roles se han creado:
  kubectl get role -n cybercaronte
  kubectl get role -n tuxotron
Ahora que tenemos los usuarios y los roles creados, tenemos que asignarles los roles a los usuarios. Para ello necesitamos crear los objectos RoleBinding.
Primero vamos a asignar al usuario cybercaronte el role cybercaronte-full-control. Para ello creamos un fichero (cybercaronte-fullcontrol-binding.yaml) con el siguiente contenido
    kind: RoleBinding
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: full-control
      namespace: cybercaronte
    subjects:
    - kind: User
      name: cybercaronte
      apiGroup: rbac.authorization.k8s.io
    roleRef:
      kind: Role
      name: cybercaronte-full-control
      apiGroup: rbac.authorization.k8s.io
Crea el RoleBinding:
    kubectl apply -f cybercaronte-fullcontrol-binding.yaml
Para probar si funciona, cambiemos de contexto a cybercaronte y a ver si ahora podemos consultar los pods del espacio de nombres cybercaronte:
kubectl config use-context cybercaronte kubectl get pods -n cybercaronte No resources found.
Como podemos ver esta vez no recibimos ningún error. Si intentamos consultar los pods del espacio de nombres tuxotron:
  kubectl get pods -n tuxotron
  Error from server (Forbidden): pods is forbidden: User "cybercaronte" cannot list resource "pods" in API group "" in the namespace "tuxotron"
Vemos como recibimos un error porque el usuario cybercaronte no tiene acceso a los pods del espacio de nombres tuxotron.

Figura 8: Rafael Troncoso (tuxotron), Chema Alonso (sujetando el libro Docker:SecDevOps), Elías Grande
y Fran Ramírez (cybercaronte) en la RootedCon 2019. Al fondo, el poster del "Profesor Alonso" de Cels Piñol.
Hagamos lo mismo con el usuario tuxotron. Crea un fichero (tuxotron-fullcontrol-binding.yaml) con el siguiente contenido:
    kind: RoleBinding
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: full-control
      namespace: tuxotron
    subjects:
      - kind: User
        name: tuxotron
        apiGroup: rbac.authorization.k8s.io
    roleRef:
      - kind: Role
        name: tuxotron-full-control
        apiGroup: rbac.authorization.k8s.io
Antes de poder crear este RoleBinding tenemos que cambiar de contexto a minikube primero:
    kubectl config use-context minikube
  kubectl apply -f tuxotron-fullcontrol-binding.yaml
Vamos a explicar un poco la estructura de este objeto. Lo primero a recalcar es el nombre (metadata.name), si te fijas bien en los dos RoleBindings que hemos creado usan exactamente el mismo nombre. Esto es posible, porque los RoleBindings pertenecen a un nombre de espacio, y en nuestro ejemplo hemos creado cada RoleBidning en distintos espacios de nombre. Si hubiéramos usado el CLusterRoleBinding, esto no sería posible porque en ese caso habría un conflicto de nombres.

El apartado subjects es donde decimos a quién la vamos a asignar el role. El atributo Kind debe ser User, Group o ServiceAccount. Luego tenemos name, éste corresponde al nombre de usuario, grupo o cuenta de servicio y por último el apiGroup que es rbac.authorization.k8s.io

Luego tenemos el apartado roleRef. Aquí es dónde especificamos el role que queremos asignar. El atributo Kind define el tipo de role: Role o ClusterRole. Name es el nombre del role en sí y finalmente el apiGroup que al igual que el caso anterior es rbac.authorization.k8s.io

Por último, veamos un ejemplo donde daremos acceso al espacio de nombres equipo al grupo developers. Vamos a crear un role a nivel de clúster para dar acceso total sobre pods, deployments, servicios, replicasets y secretos:
    kind: ClusterRole
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: full-control
    rules:
      - apiGroups: ["extensions", "apps", ""]
        resources: ["pods", "deployments", "secrets", "services", "replicasets"]
        verbs: ["*"]
Copiamos el contenido a un fichero (developers-role.yaml) y ejecuta el siguiente comando:
    kubectl apply -f developers-role.yaml
Un par de apuntes sobre este fichero. Lo primero es que el tipo (Kind) es ClusterRole y como vemos no tiene especificado el espacio de nombres (namespace). Este es un objeto global del clúster. Ahora creemos otro fichero (developers-binding.yaml) con el siguiente contenido:
    kind: RoleBinding
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: full-control
      namespace: equipo
    subjects:
      - kind: Group
      name: developers
      apiGroup: rbac.authorization.k8s.io
    roleRef:
      kind: ClusterRole
      name: full-control
      apiGroup: rbac.authorization.k8s.io
Como vemos, el contenido de este fichero es similar a los anteriores, con par de diferencias. En el apartado subjects, usamos el tipo Group, porque estamos asignando el role a un grupo y no a un usuario. Y en el apartado roleRef, el tipo es ClusterRole y no Role, ya que el role al que estamos haciendo referencia es del tipo ClusterRole. Para crear dicho RoleBinding:
    kubectl apply -f developers-binding.yaml
Recuerda que cuando creamos los usuarios, ambos los creamos bajo la organización developers `/O=developers`. Por lo tanto, cybercaronte y tuxotron deberían tener acceso a los pods, servicios, secretos, replicasets y deployments del espacio de nombres. Comprobemos que cybercaronte tiene acceso:
kubectl config use-context cybercaronte
Switched to context "cybercaronte". kubectl get pod -n equipo No resources found. kubectl get deployment -n equipo No resources found. kubectl get configmap -n equipo Error from server (Forbidden): configmaps is forbidden: User "cybercaronte" cannot list resource "configmaps" in API group "" in the namespace "equipo"
Como hemos podido comprobar, cuando consultamos los pods y los deployments no recibimos ningún error. “No resources found” es porque no hemos creado nada en el espacio de nombres. Cuando consultamos los configmaps vemos que recibimos un error, ya que ese recurso no tenemos acceso. Si hacemos las mismas pruebas con el usuario tuxotron, los resultados deberían ser el mismo.


Figura 9: Kubernetes: Cómo gestionar autorización de recursos con RBAC [Parte 2]

Kubernetes no sólo permite el control de acceso a un recurso complete, sino que incluso a parte del mismo. Por ejemplo, podrías dar acceso a los logs de los pods, sin dar acceso a los pods en sí. Tienes en el vídeo todo el proceso que hemos realizado en esta parte del artículo.

Esperamos que te hayas animado a llegar al final de este artículo y por lo menos tengas suficiente información para entender como funciona el RBAC en Kubernetes. Os dejamos por aquí también algunas de las referencias que tenemos sobre Docker escritas ya en este blog, para que tengáis toda la información disponible y accesible.

[BlogPost] Docker: SecDevOps: Contenido y descripción del libro
[BlogPost] SecDevOps: Una explicación en 5 minutos (o poco más)
[BlogPost] Cómo montar un entorno de pentesting desde cero en Docker
[BlogPost] SuperLatch Docker: Integrar Latch con Docker y Kubernetes en tus apps
[BlogPost] Docker Distroless Images: Cómo crear imágenes Docker sin SO ni shell
[BlogPost] Dagda Docker Security Suite: Auditoría de seguridad en aplicaciones dockerizadas
[BlogPost] Docker de My WordPress in Paranoid Mode
[BlogPost] Docker de My WordPress in Paranoid Mode (WPM): "The Making of"
[BlogPost] Hacking Kubernetes: Auditoría de seguridad y explotación de vulnerabilidades
[BlogPost] Kubernetes: Cómo gestionar autorización de recursos con RBAC

Fran Ramírez, (@cyberhadesblog) es investigador de seguridad y miembro del equipo de Ideas Locas en CDO en Telefónica, co-autor del libro "Microhistorias: Anécdotas y Curiosidades de la historia de la informática (y los hackers)", del libro "Docker: SecDevOps" y del blog Cyberhades.

Rafael Troncoso (@tuxotron) es Senior Software Engineer en SAP Concur, co-autor del libro "Microhistorias: Anécdotas y Curiosidades de la historia de la informática (y los hackers)", del libro "Docker: SecDevOps" y del blog Cyberhades.

1 comentario:

  1. hola estimados. yo he instalado k8 con un user "x", mi duda es como logro que el server API no me rechace si intento usar kubectl con otro usuario de Linux. Este user adicional lo use a traves de esta guia y la numero uno.

    Gracias¡¡¡

    ResponderEliminar