Publié par

Il y a 4 semaines -

Temps de lecture 6 minutes

Utiliser Terraform pour configurer l’accès de GKE à Cloud SQL

Nous avons comme bonne pratique d’utiliser de l’infra-as-code, notamment via Terraform lorsqu’il s’agit de GCP. Via une seule commande l’infrastructure complète du projet est créée, détruite ou mise à jour.

Voyons dans cet article comment créer un cluster GKE, une base de données Cloud SQL Postgres et comment sécuriser l’accès de nos applications à l’instance Cloud SQL. Nous utiliserons la fonctionnalité Workload Identity recommandée par Google (via Cloud SQL Proxy).

Bien sûr, en utilisant Terraform.

Pour ce faire nous allons créer :

  1. un cluster GKE utilisant Workload Identity ;
  2. un Service Account IAM autorisé à accéder à la base de données Cloud SQL ;
  3. un Service Account Kubernetes et l’associer au Service Account IAM ;
  4. un déploiement utilisant le design pattern Sidecar pour attacher le conteneur Cloud SQL Proxy avec notre application.

Schéma : sécuriser l’accès à Cloud SQL depuis K8s via KSA et GSA

1. Créer le cluster GKE via Terraform

resource "google_container_cluster" "<name>" {
  project = <project_id>
  provider = google-beta
  name = <cluster_name>
  monitoring_service = "monitoring.googleapis.com/kubernetes"
  logging_service = "logging.googleapis.com/kubernetes"
  node_config {
    oauth_scopes = [
      "https://www.googleapis.com/auth/logging.write",
      "https://www.googleapis.com/auth/monitoring",
      "https://www.googleapis.com/auth/devstorage.read_only"
    ]
    workload_metadata_config {
      node_metadata = "GKE_METADATA_SERVER"
    }
  }
  workload_identity_config {
    identity_namespace = "<project_id>.svc.id.goog"
  }
}

Pour créer le cluster GKE via Terraform, il est nécessaire d’utiliser la ressource google_container_cluster. Le YAML ci-dessus correspond au minimum nécessaire pour obtenir un cluster fonctionnel et permettant l’usage de Workload Identity.

Workload Identity est le moyen recommandé pour accéder à des services GCP depuis le cluster Kubernetes (GKE).

Plusieurs choses sont à noter :

  1. L’utilisation d’un provider google-beta permettant l’accès à des fonctionnalités bêtas.
  2. workload_metadata_config (bêta) permettant de configurer l’exposition des metadata des workloads sur les nœuds du pool. Dans notre cas, la metadata GKE_METADATA_SERVER permet d’activer Workload Identity sur le nœud.
  3. workload_identity_config (bêta) permettant à un compte de service Kubernetes d’agir comme un compte de service utilisateur Google IAM via Workload Identity. Pour le moment l’identity namespace autorisé est celui par défaut sur le projet : .svc.id.goog
  4. devstorage.read_only permettant au cluster de lire le dépôt privé GCR pour récupérer l’image Docker de l’application

Une fois le cluster créé et configuré pour utiliser la fonctionnalité de Workload Identity, nous devons à présent créer un Service Account IAM (Google) capable d’accéder à la base de données.

Attention ! Nous parlons ici de « Service Account » (compte de service, utilisés pour des programmes / utilisateurs non humains) dans plusieurs contextes. D’un côté, les Service Account IAM, propres à GCP, et représentant une identité au sein de GCP. De l’autre, les Service Account Kubernetes, représentant une identité au sein d’un cluster Kubernetes. Les deux sont des notions bien différentes, comme le serait par exemple la notion d’identité utilisateur propre à votre application, sans lien avec celle de GCP. Et ce que nous allons voir ici, c’est justement comment lier ces Service Account GCP / Kubernetes. Horgix

2. Créer le Service Account IAM

resource "google_service_account" "gsa" {
  account_id = <service_account_name>
  project = <project_id>
}

Pour créer le Service Account IAM, nous utilisons google_service_account qui n’a besoin que d’un nom account_id et d’un projet associé project. À noter qu’une description et un nom d’affichage peuvent être ajoutés pour plus de lisibilité.

Associer le droit d’accès à Cloud SQL

resource "google_project_iam_member" "cloud-sql-client" {
  project = <project_id>
  role = "roles/cloudsql.client"
  member = "serviceAccount:${google_service_account.gsa.email}"
}

Afin d’associer le rôle roles/cloudsql.client permettant la connexion aux instances Cloud SQL, nous utilisons la ressource google_project_iam_member. Nous précisons que le membre qui acquiert ce rôle est le Service Account précédemment créé.

3. Créer le Service Account Kubernetes

resource "kubernetes_service_account" "ksa" {
  metadata {
    name = <kubernetes_sa_name>
    annotations = {
      "iam.gke.io/gcp-service-account" = google_service_account.gsa.email
    }
  }
}

Afin de permettre à nos Pods d’accéder à Cloud SQL, nous devons créer un Service Account Kubernetes qui est associé au Service Account IAM précédemment créé. Nous utilisons kubernetes_service_account. L‘association se fait via l’annotation iam.gke.io/gcp-service-account en précisant l’email du Service Account IAM.

Association du rôle workload identity entre le SA IAM et le SA K8s

Afin d’associer le rôle roles/iam.workloadIdentityUser du Service Account IAM et le Service Account Kubernetes, il est possible de passer par la ressource google_service_account_iam_binding :

resource "google_service_account_iam_binding" "gke_gsa_ksa_binding" {
  service_account_id = google_service_account.gsa.name
  role = "roles/iam.workloadIdentityUser"

  members = [
    "serviceAccount:<project_id>.svc.id.goog[default/<kubernetes_sa_name>]"
  ]
}

En précisant l’identifiant du Service Account IAM déjà créé service_account_id ; et le Service Account Kubernetes exposé (défini plus haut) members/serviceAccount

4. Déployer notre application et son Sidecar configuré pour utiliser le SA K8s

La dernière étape pour permettre à notre application d’accéder à sa base de données Cloud SQL est de configurer le déploiement. Nous y ajoutons le Proxy Cloud SQL configuré pour utiliser le SA Kubernetes.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: <app-name>
spec:
  selector:
    matchLabels:
      app: <app-name>
  template:
    metadata:
      labels:
        app: <app-name>
    spec:
      serviceAccountName: <kubernetes_sa_name>
      containers:
        - name: <app-name>
          ...
        - name: cloud-sql-proxy
          image: gcr.io/cloudsql-docker/gce-proxy:1.17
          command:
            - "/cloud_sql_proxy"
            - "-instances=<project-id>:<region>:<database-name>=tcp:5432"

L‘information importante est l’entrée serviceAccountName qui renseigne le Service Account Kubernetes.

À noter que les identifiants pour accéder à la base de données restent nécessaire dans votre application (identifiant, mot de passe, nom de la base de données, le host est localhost).

Cloud SQL Proxy est la méthode recommandée pour se connecter à une instance Cloud SQL car le proxy fournis une méthode de cryptage et d’identification utilisant IAM, ce qui peut grandement aider à garder l’accès à la base de données sécurisé. Sur Kubernetes, lorsque l’application se connecte en utilisant le Cloud SQL Proxy, ce dernier est ajouté à notre Pod en utilisant la méthode du Sidecar container pattern. Le conteneur du Proxy est dans le même Pod que notre application, ce qui permet à l’application de se connecter au Proxy via localhost. C’est plus sécurisé et performant que d’accéder directement à Cloud SQL via l’ adresse IP.

Vous retrouvez toutes les étapes de cet article, sans la partie Terraform, directement dans la documentation Google concernant la connexion de Kubernetes à Cloud SQL.

En conclusion, sécuriser l’accès de ses applications GKE à une base de données Cloud SQL se résume en quelques commandes Terraform. Il serait dommage de s’en passer puisque cela permet d’automatiser, documenter et partager l’infrastructure sans grande complexité.

Publié par

Publié par Benjamin Lacroix

Benjamin Lacroix est lead développeur et manager chez Publicis Sapient Engineering.

Commentaire

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Nous recrutons

Être un Sapient, c'est faire partie d'un groupe de passionnés ; C'est l'opportunité de travailler et de partager avec des pairs parmi les plus talentueux.