Упрощение конфигурации Keycloak
с помощью Terraform и Terragrunt

Рассказываем, как интегрировать инструменты, даем практические советы и примеры разработчикам, которые хотят оптимизировать настройки Keycloak.
Ручная настройка и управление Keycloak может быть сложной и подверженной ошибкам, особенно в крупных и масштабируемых системах. Управление конфигурацией Keycloak становится проще, если использовать автоматизацию и инфраструктуру как код (IaC). С этим помогают Terraform и Terragrunt.

Почему используем Terraform и Terragrunt

Terraform — это инструмент, который позволяет управлять инфраструктурой как кодом (IaC). С помощью Terraform вы можете определить вашу инфраструктуру в простых конфигурационных файлах, и сервис автоматически создаст вашу инфраструктуру в соответствии с этими конфигурациями.


Terragrunt — это легкая обертка для Terraform, которая предоставляет дополнительные инструменты для поддержания ваших конфигураций в DRY (Don't Repeat Yourself) стиле, управления удаленным состоянием и работы с несколькими модулями Terraform. Terragrunt помогает поддерживать чистоту и повторяемость кода Terraform, обеспечивая дополнительный функционал и лучшие практики.


В паре эти инструменты дают такие преимущества:

  • Инфраструктура как код (IaC): определяя конфигурацию Keycloak как код, мы можем контролировать версии, просматривать изменения и обеспечивать согласованность в разных средах.
  • Модульность: модулируем конфигурацию Keycloak, упрощая управление сложными настройками.
  • Управление состоянием инфраструктуры помогает предотвратить дрейф конфигурации и обеспечить стабильность.

Инструкции по установке можно найти в официальной документации:

Структура проекта

Ниже приведена структура, наиболее подходящая для организации компонентов, концепций и настройки Keycloak.

.
├── README.md                 <- The project overview
├── .tool-versions            <- Used tools versions (managed by asdf. see https://asdf-vm.com) 
├── README.md                 <- The project overview
├── modules                   <- Terraform modules
|   └── common
│       ├── provider.tf
│       └── variables.tf
│       └── output.tf
│       └── main.tf
│       └── README.md
│       └── docs/
|   └── clients
│       ├── provider.tf
│       └── variables.tf
│       └── output.tf
│       └── main.tf
│       └── README.md
│       └── docs/
|   └── my-realm
│       ├── provider.tf
│       └── variables.tf
│       └── output.tf
│       └── main.tf
│       └── README.md
│       └── docs/
|   └── other
│       ├── provider.tf
│       └── variables.tf
│       └── output.tf
│       └── main.tf
│       └── README.md
│       └── docs/
└── how-to                    <- Documentation
└── stage                     <- Terraform for environment stage
    ├── .terraform.lock.hcl   <- Terraform lock file
    └── terragrunt.hcl        <- Terragrunt file
    └── env.yaml              <- environment related variables
    └── main.tf               <- environment modules
└── prod                      <- Terraform for environment prod
    ├── .terraform.lock.hcl   <- Terraform lock file
    └── terragrunt.hcl        <- Terragrunt file
    └── env.yaml              <- environment related variables
    └── main.tf               <- environment modules

└── local                     <- Terraform for environment local
    ├── .terraform.lock.hcl   <- Terraform lock file
    └── terragrunt.hcl        <- Terragrunt file
    └── env.yaml              <- environment related variables
    └── main.tf               <- environment modules

Каждый модуль включает файл main.tf, инкапсулирующий ресурсы модуля, а также файлы input.tf, output.tf, и variable.tfдля легкой настройки в разных средах.


Common-модуль


Предположим, что в common модуле мы настраиваем события реального времени, используя слушатель событий jboss-logging с некоторыми нестандартными конфигурациями. Ниже приведен пример того, как может выглядеть файл main.tf:

resource "keycloak_realm_events" "realm_events" {
  realm_id                     = var.realm_id
  events_enabled               = true
  events_expiration            = 1800
  admin_events_enabled         = true
  admin_events_details_enabled = true
  ]

  events_listeners = [
    "jboss-logging"
  ]
}

Чтобы включить переменную realm_id, ее необходимо определить в variables.tf, как показано ниже:

variable "realm_id" {
  description = "Realm ID"
  type        = string
}

Также стоит настроить используемые провайдеры в нашем модуле. В этом случае provider.tf будет выглядеть так:

terraform {
  required_providers {
    keycloak = {
      source = "mrparkers/keycloak"
    }
  }
}

Модуль Master Realm


В realm-master модуле main.tf должен ссылаться на common. Вот пример того, как main.tf может выглядеть:

data "keycloak_realm" "master" {
  realm = "master"
}

module "realm-master" {
  source = "../../modules/common"
  realm_id = data.keycloak_realm.master.id
}

Файл provider.tf настраиваем аналогично:

terraform {
  required_providers {
    keycloak = {
      source = "mrparkers/keycloak"
    }
  }
}

Local environment


В main.tf нам необходимо определить главную область, чтобы ссылаться на realm-master модуль в нашем проекте.

# Define master realm
module "realm-master" {
  source = "../modules/realm-master"
}

В каждой среде (например, prod, stage и local) есть env.yaml, содержащий все переменные, специфичные для этой среды.


Например, env.yaml для локальной среды может выглядеть так:

---
environment: local
url: http://localhost:8080/keycloak

И, конечно же, не забудьте включить файл terragrunt-local.hcl, который должен быть определен в родительском модуле.

include "root" {
  path = find_in_parent_folders("terragrunt-local.hcl")
}

Конфигурация Terragrunt

Terragrunt очень полезен для сохранения конфигурации Don't Repeat Yourself (DRY). Например, файл terragrunt.hcl может выглядеть так:

# Generates the backend for all modules.
remote_state {
  backend = "s3"
  config  = {
    encrypt        = true
    key            = "keycloak/${path_relative_to_include()}/terraform.tfstate"
    region         = "<AWS REGION>"
    bucket         = "terraform-states"
    dynamodb_table = "terraform-lock"
  }
}

# Read the local "env.yaml" in every environment.
locals {
  vars        = yamldecode(file("${path_relative_to_include()}/env.yaml"))
  environment = local.vars.environment
  url         = local.vars.url
}

# Generate the "provider.tf" file for every module.
generate "provider" {
  path      = "provider.tf"
  if_exists = "overwrite"
  contents  = <<EOF
terraform {
  required_providers {
    keycloak = {
      source  = "mrparkers/keycloak"
      version = "4.4.0"
    }

    http = {
      source  = "hashicorp/http"
      version = "3.2.1"
    }
  }
}

data "http" "config" {
  url = "<INTERNAL CONFIGURATION URL>"
}

provider "keycloak" {
  client_id     = jsondecode(data.http.config.response_body).terraform-client-id
  client_secret = jsondecode(data.http.config.response_body).terraform-client-secret
  url           = "${local.url}"
}

EOF
}

# Generate the "backend.tf" file for every module.
generate "backend" {
  path      = "backend.tf"
  if_exists = "overwrite"
  contents  = <<EOF
terraform {
  backend "s3" {}
}
EOF
}

В remote_state мы используем AWS S3 для хранения состояния наших конфигураций среды. Кроме того, мы генерируем backend и providerдля каждой среды.


Блок locals в основном используется для чтения env.yaml и назначения переменных.


Наконец, http config отвечает за выполнение HTTP-вызова туда, где вы безопасно храните конфигурации своей среды (по крайней мере, идентификатор клиента Terraform и секрет), для передачи их провайдеру keycloak.


Файл terragrunt-local.hcl похож на terragrunt.hcl, за исключением конфигурации удаленного состояния, которая в данном случае не нужна. Файл local в первую очередь предназначен для локального тестирования конфигурации.

Docker Compose

Мы используем Docker Compose для запуска кластера Keycloak, что позволяет нам беспрепятственно запускать наши локальные конфигурации Terraform на нем.

version: '3'

volumes:
  postgres_data:
      driver: local

services:
  postgres:
      image: postgres
      volumes:
        - postgres_data:/var/lib/postgresql/data
      environment:
        POSTGRES_DB: keycloak
        POSTGRES_USER: keycloak
        POSTGRES_PASSWORD: password
      ports:
        - "5432:5432"
  keycloak:
      image: quay.io/keycloak/keycloak:23.0.6
      environment:
        KC_DB_USERNAME: keycloak
        KC_DB_PASSWORD: password
        KC_DB_URL_HOST: postgres
        KC_DB: postgres
        KC_DB_SCHEMA: public
        KC_HTTP_RELATIVE_PATH: /keycloak
        KC_HOSTNAME_ADMIN: 127.0.0.1
        KC_HOSTNAME: localhost
        KEYCLOAK_ADMIN: admin
        KEYCLOAK_ADMIN_PASSWORD: admin
      command:
        - start-dev
      ports:
        - "8080:8080"
        - "8787:8787"
      depends_on:
        - postgres
  config_keycloak:
    image: ubuntu
    volumes:
      - ./keycloak-docker-config.sh:/opt/keycloak-docker-config.sh
    command: ./opt/keycloak-docker-config.sh
    depends_on:
      - keycloak

Скрипт keycloak-docker-config.sh в основном используется для настройки клиента с привилегиями админа, которые Terraform будет использовать во время своей работы.

#!/bin/bash

apt update -y && apt -y install jq curl

until $(curl --output /dev/null --silent --head --fail http://keycloak:8080/keycloak); do
    printf '.'
    sleep 5
done

# Get access token
TOKEN=$( \
  curl -X POST \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -d "client_id=admin-cli" \
    -d "username=admin" \
    -d "password=admin" \
    -d "grant_type=password" \
    "http://keycloak:8080/keycloak/realms/master/protocol/openid-connect/token" | jq -r '.access_token')

# Create Terraform client (terraform/terraform)
curl -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ${TOKEN}" \
  -d '{"clientId": "terraform", "name": "terraform", "enabled": true, "publicClient": false, "secret": "terraform", "serviceAccountsEnabled": true}' \
  "http://keycloak:8080/keycloak/admin/realms/master/clients"

# Get the Terraform service account user ID
USER_ID=$( \
  curl -X GET \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer ${TOKEN}" \
    "http://keycloak:8080/keycloak/admin/realms/master/users?username=service-account-terraform" | jq -r '.[0].id')

# Get the admin role ID
ROLE_ID=$( \
  curl -X GET \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer ${TOKEN}" \
    "http://keycloak:8080/keycloak/admin/realms/master/roles" | jq -r '.[] | select(.name == "admin") | .id')

# Add the admin role to the Terraform service account user
curl -kv -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ${TOKEN}" \
  -d '[{"id":"'"${ROLE_ID}"'", "name":"admin"}]' \
  "http://keycloak:8080/keycloak/admin/realms/master/users/$USER_ID/role-mappings/realm"

Чтобы запустить его, откройте terminal и выполните следующую команду docker compose:

docker-compose up --build

После того, как контейнеры docker-compose будут запущены, перейдите на http://localhost:8080/keycloak и войдите в систему, используя учетные данные admin/admin. Убедитесь, что клиент Terraform настроен в пределах главной области.

Теперь пришло время запустить ваши локальные конфигурации Terraform. Откройте терминал и выполните следующие команды:

# Navigate to the local environment
$ cd local 
# Ensure that Terraform-related files, including the auto-generated backend.tf and provider.tf, are removed
$ rm -r backend.tf provider.tf terraform.tfstate terraform.tfstate.backup .terraform.lock.hcl .terraform 
# Initialize Terraform to create all necessary files
$ terragrunt init --terragrunt-config terragrunt-local.hcl 
# Apply the Terraform configurations
$ terragrunt apply --terragrunt-config terragrunt-local.hcl 
  1. Откройте браузер и перейдите по адресу http://localhost:8080/keycloak.
  2. Войдите в систему, используя учетные данные администратора
  3. Убедитесь, что ваши конфигурации настроены правильно

Заключение

Управление конфигурациями Keycloak с помощью Terraform может значительно упростить процесс разработки. Следуя шагам, описанным в этом руководстве, вы сможете настраивать и поддерживать среды Keycloak, обеспечивая согласованность и масштабируемость в своих проектах.

А мы напоминаем, что инженеры Core 24/7 разработали плагин для Keycloak — с авторизацией через казахстанский ЭЦП (НУЦ РК). И выложили его в открытый доступ 🥳

👉 В репозитории есть версия для физических и юрлиц.

Получите бесплатную консультацию по DevOps-поддержке


Узнайте об услуге devops support и закажите звонок у наших менеджеров. Поможем с любой задачей


Узнать больше