Appliquer des règles relatives à la sécurité des pods personnalisées à l'aide de GateKeeper


Cette page explique comment utiliser le contrôleur d'admission Gatekeeper pour appliquer des contrôles de sécurité au niveau des pods à vos clusters Google Kubernetes Engine (GKE).

Présentation

Gatekeeper est un contrôleur d'admission qui valide les demandes de création et de mise à jour de pods sur les clusters Kubernetes à l'aide d'OPA (Open Policy Agent).

L'utilisation de Gatekeeper permet aux administrateurs de définir des stratégies avec une contrainte, un ensemble de conditions qui autorisent ou refusent les comportements de déploiement dans Kubernetes. Vous pouvez ensuite appliquer ces stratégies sur un cluster à l'aide d'un ConstraintTemplate. Ce document fournit des exemples permettant de restreindre les capacités de sécurité des charges de travail pour appliquer, tester et auditer des stratégies de sécurité à l'aide de Gatekeeper.

Gatekeeper peut également :

  • Déployer des stratégies : appliquer des stratégies de manière progressive pour limiter le risque de perturbation des charges de travail.
  • Simuler des changements de stratégie : fournir des mécanismes permettant de tester l'impact des stratégies et la plage avant leur application.
  • Auditer les stratégies existantes : vérifier l'application des contrôles de sécurité aux charges de travail nouvelles et existantes (contrôles d'audit).

Concepts

Gatekeeper propose deux concepts permettant aux administrateurs de disposer d'un moyen efficace et flexible de contrôler leur cluster : les contraintes et les modèles de contraintes, qui sont tous deux des concepts hérités d'OPA Constraint Framework.

Les contraintes représentent votre stratégie de sécurité. Elles définissent les exigences et la plage d'application de la stratégie. Les modèles de contraintes sont des instructions réutilisables (écrites dans Rego) qui permettent d'appliquer une logique afin d'évaluer des champs spécifiques dans des objets Kubernetes, en fonction des exigences définies dans les contraintes.

Par exemple, vous pouvez avoir une contrainte qui déclare des profils seccomp autorisés pouvant être appliqués aux pods dans un espace de noms spécifique, et un modèle de contrainte comparable qui fournit la logique d'extraction de ces valeurs et de traitement de leur application.

Le modèle de contrainte suivant, à partir du dépôt Gatekeeper, vérifie l'existence de securityContext.privileged dans une spécification de pod :

apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: k8spspprivilegedcontainer
spec:
  crd:
    spec:
      names:
        kind: K8sPSPPrivilegedContainer
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8spspprivileged

        violation[{"msg": msg, "details": {}}] {
            c := input_containers[_]
            c.securityContext.privileged
            msg := sprintf("Privileged container is not allowed: %v, securityContext: %v", [c.name, c.securityContext])
        }
        input_containers[c] {
            c := input.review.object.spec.containers[_]
        }
        input_containers[c] {
            c := input.review.object.spec.initContainers[_]
        }

Pour étendre le modèle de contrainte ci-dessus, la contrainte suivante définit le champ d'application (kinds) pour l'application spécifique de ce modèle de contrainte dans un mode dryrun :

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPPrivilegedContainer
metadata:
  name: psp-privileged-container
spec:
  enforcementAction: dryrun
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]

Avec Gatekeeper, vous pouvez créer vos propres contraintes et modèles de contraintes pour répondre à vos besoins spécifiques. Vous pouvez également utiliser un ensemble standard de contraintes et de modèles de contraintes dans le dépôt Gatekeeper, qui a été défini pour permettre l'adoption rapide et l'application de mesures de sécurité. Chaque contrainte est également accompagnée d'exemples de configurations de pod.

Google Cloud fournit une version gérée et officiellement compatible de Gatekeeper Open Source, nommé Policy Controller. Google n'accepte pas officiellement le projet Open Source Gatekeeper.

Avant de commencer

Avant de commencer, effectuez les tâches suivantes :

  • Activez l'API Google Kubernetes Engine.
  • Activer l'API Google Kubernetes Engine
  • Si vous souhaitez utiliser Google Cloud CLI pour cette tâche, installez puis initialisez gcloud CLI. Si vous avez déjà installé gcloud CLI, assurez-vous de disposer de la dernière version en exécutant la commande gcloud components update.

Activer le contrôleur d’accès sur un cluster avec Policy Controller

Policy Controller est un moteur de règles basé sur le projet Open Source Gatekeeper. Google recommande l'utilisation de Policy Controller, car il inclut des fonctionnalités supplémentaires pour faciliter l'application des règles à grande échelle, telles que la stratégie en tant que code, la compatibilité multicluster, l'intégration à Cloud Logging et la capacité à afficher l'état des règles dans la console Google Cloud. Policy Controller est disponible avec une licence Google Kubernetes Engine (GKE) Enterprise, mais vous pouvez installer Gatekeeper sur votre cluster à la place.

Pour activer Policy Controller sur un cluster, suivez le guide d'installation de Policy Controller.

Activer des contraintes et des modèles de contraintes

Gatekeeper et ses modèles de contraintes peuvent être installés et activés sans affecter les charges de travail existantes ou nouvelles. Pour cette raison, il est recommandé d'appliquer tous les modèles de contraintes de sécurité des pods applicables au cluster.

En outre, les contraintes Gatekeeper peuvent être mises en œuvre pour appliquer des contrôles à des objets spécifiques, tels que des espaces de noms et des pods.

Examinez l'exemple ci-dessous qui limite le champ d'application aux pods situés dans l'espace de noms production en les définissant dans l'instruction de correspondance de contrainte :

...
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]
    namespaces:
      - "production"

Pour en savoir plus sur les options disponibles pour les objets Constraint et ConstraintTemplate, consultez la page Comment utiliser Gatekeeper.

Stratégies de test

L'introduction de nouvelles stratégies dans les clusters existants peut avoir des effets négatifs, par exemple en limitant les charges de travail existantes. L'un des avantages de Gatekeeper pour la sécurité des pods est sa capacité à tester l'efficacité et l'impact d'une stratégie sans apporter de modifications réelles grâce à un mode de simulation. Cela permet de tester la configuration d'une stratégie avec des clusters en cours d'exécution sans l'appliquer. Les cas de non-respect des règles sont enregistrés et identifiés, sans interférence.

Les étapes suivantes expliquent comment un développeur, un opérateur ou un administrateur peut appliquer des modèles de contraintes et des contraintes pour déterminer leur efficacité ou leur impact potentiel :

  1. Appliquez la configuration Gatekeeper afin de répliquer les données pour les fonctionnalités d'audit et de simulation :

    kubectl create -f- <<EOF
    apiVersion: config.gatekeeper.sh/v1alpha1
    kind: Config
    metadata:
      name: config
      namespace: "gatekeeper-system"
    spec:
      sync:
        syncOnly:
          - group: ""
            version: "v1"
            kind: "Namespace"
          - group: ""
            version: "v1"
            kind: "Pod"
    EOF
    
  2. Sans contrainte appliquée, exécutez une charge de travail avec des privilèges élevés :

    kubectl create -f- <<EOF
    apiVersion: v1
    kind: Pod
    metadata:
      name: nginx
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        securityContext:
          privileged: true
    EOF
    
  3. Chargez le modèle de contrainte k8spspprivilegedcontainer mentionné ci-dessus :

    kubectl create -f- <<EOF
    apiVersion: templates.gatekeeper.sh/v1beta1
    kind: ConstraintTemplate
    metadata:
      name: k8spspprivilegedcontainer
    spec:
      crd:
        spec:
          names:
            kind: K8sPSPPrivilegedContainer
      targets:
        - target: admission.k8s.gatekeeper.sh
          rego: |
            package k8spspprivileged
    
            violation[{"msg": msg, "details": {}}] {
                c := input_containers[_]
                c.securityContext.privileged
                msg := sprintf("Privileged container is not allowed: %v, securityContext: %v", [c.name, c.securityContext])
            }
            input_containers[c] {
                c := input.review.object.spec.containers[_]
            }
            input_containers[c] {
                c := input.review.object.spec.initContainers[_]
            }
    EOF
    
  4. À présent, créez une contrainte pour étendre ce modèle de contrainte. Pour le moment, nous allons définir enforcementAction sur dryrun :

    kubectl create -f- <<EOF
    apiVersion: constraints.gatekeeper.sh/v1beta1
    kind: K8sPSPPrivilegedContainer
    metadata:
      name: psp-privileged-container
    spec:
      enforcementAction: dryrun
      match:
        kinds:
          - apiGroups: [""]
            kinds: ["Pod"]
    EOF
    
  5. Grâce à Gatekeeper qui synchronise les données d'objet en cours d'exécution et vérifie les violations de manière passive, nous pouvons vérifier si des violations ont été détectées en examinant la valeur status de la contrainte :

    kubectl get k8spspprivilegedcontainer.constraints.gatekeeper.sh/psp-privileged-container -o yaml
    
    apiVersion: constraints.gatekeeper.sh/v1beta1
    kind: K8sPSPPrivilegedContainer
    metadata:
    ...
     name: psp-privileged-container
    ...
    spec:
     enforcementAction: dryrun
     match:
       kinds:
       - apiGroups:
         - ""
         kinds:
         - Pod
    status:
     auditTimestamp: "2019-12-15T22:19:54Z"
     byPod:
     - enforced: true
       id: gatekeeper-controller-manager-0
     violations:
     - enforcementAction: dryrun
       kind: Pod
       message: 'Privileged container is not allowed: nginx, securityContext: {"privileged":
         true}'
       name: nginx
       namespace: default
    
  6. Exécutez un autre pod privilégié pour vérifier que la stratégie n'interfère pas avec les déploiements :

    kubectl create -f- <<EOF
    apiVersion: v1
    kind: Pod
    metadata:
      name: privpod
      labels:
        app: privpod
    spec:
      containers:
      - name: nginx
        image: nginx
        securityContext:
          privileged: true
    EOF
    

    Ce nouveau pod sera déployé avec succès.

  7. Pour nettoyer les ressources créées dans cette section, exécutez les commandes suivantes :

    kubectl delete k8spspprivilegedcontainer.constraints.gatekeeper.sh/psp-privileged-container
    kubectl delete constrainttemplate k8spspprivilegedcontainer
    kubectl delete pod/nginx
    kubectl delete pod/privpod
    

Appliquer des règles

Maintenant qu'il est possible de confirmer la validité et l'impact d'une stratégie sans affecter les charges de travail existantes ou nouvelles, passons à la mise en œuvre d'une stratégie avec son application complète.

En fonction des exemples utilisés pour valider la stratégie ci-dessus, la procédure suivante montre comment un développeur, un opérateur ou un administrateur peut appliquer des modèles de contraintes et des contraintes à une stratégie :

  1. Chargez le modèle de contrainte k8spspprivilegedcontainer mentionné précédemment :

    kubectl create -f- <<EOF
    apiVersion: templates.gatekeeper.sh/v1beta1
    kind: ConstraintTemplate
    metadata:
      name: k8spspprivilegedcontainer
    spec:
      crd:
        spec:
          names:
            kind: K8sPSPPrivilegedContainer
      targets:
        - target: admission.k8s.gatekeeper.sh
          rego: |
            package k8spspprivileged
    
            violation[{"msg": msg, "details": {}}] {
                c := input_containers[_]
                c.securityContext.privileged
                msg := sprintf("Privileged container is not allowed: %v, securityContext: %v", [c.name, c.securityContext])
            }
            input_containers[c] {
                c := input.review.object.spec.containers[_]
            }
            input_containers[c] {
                c := input.review.object.spec.initContainers[_]
            }
    EOF
    
  2. À présent, créez une contrainte pour étendre ce modèle de contrainte. Pour le moment, nous n'allons pas définir la clé enforcementAction. Par défaut, la clé enforcementAction est définie sur deny.

    kubectl create -f- <<EOF
    apiVersion: constraints.gatekeeper.sh/v1beta1
    kind: K8sPSPPrivilegedContainer
    metadata:
      name: psp-privileged-container
    spec:
      match:
        kinds:
          - apiGroups: [""]
            kinds: ["Pod"]
    EOF
    
  3. Tentative de déploiement d'un conteneur qui déclare des autorisations privilégiées :

    kubectl create -f- <<EOF
    apiVersion: v1
    kind: Pod
    metadata:
      name: nginx
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        securityContext:
          privileged: true
    EOF
    

    Le message d'erreur suivant doit s'afficher :

    Error from server ([denied by psp-privileged-container] Privileged container is not allowed:
    nginx, securityContext: {"privileged": true}): error when creating "STDIN": admission webhook "validation.gatekeeper.sh" denied the request: [denied by psp-privileged-container]
    Privileged container is not allowed: nginx, securityContext: {"privileged": true}
    
  4. Pour effectuer un nettoyage, exécutez les commandes suivantes :

    kubectl delete k8spspprivilegedcontainer.constraints.gatekeeper.sh/psp-privileged-container
    kubectl delete constrainttemplate k8spspprivilegedcontainer
    

Alternatives à Gatekeeper

Gatekeeper vous permet de déclarer et d'appliquer des règles de sécurité personnalisées au niveau des pods. Vous pouvez également utiliser le contrôleur d'admission PodSecurity intégré à Kubernetes pour appliquer des stratégies de sécurité prédéfinies au niveau du pod. Ces règles prédéfinies sont conformes aux niveaux définis par les normes de sécurité des pods.

Étape suivante

Gatekeeper fournit un moyen incroyablement efficace pour appliquer et valider la sécurité sur des clusters GKE à l'aide de stratégies déclaratives. Toutefois, l'utilisation de Gatekeeper s'étend au-delà de la sécurité, et peut servir dans d'autres domaines administratifs et opérationnels.