OIDC Bootstrap

A DevOps Stack module to bootstrap a realm, an administrators group with one or more users and an OIDC client in order to use Keycloak as an OIDC provider.

This module allows you to have a working authentication provider for the DevOps Stack without having to configure Keycloak manually.

Because the main use of this module is to have a working Keycloak instance in a development environment, it provides a sensible configuration with some secure enough defaults. However, it is not recommended to be used in a production environment. For that purpose, we recommend you simply use this module as an example. Take a look at the code and read the provider’s documentation to get an idea on how it can be used manage your Keycloak instance.

Usage

After deploying Keycloak using the main module on this repository, first you need to add the provider configuration necessary on your root module:

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

provider "keycloak" {
  client_id                = "admin-cli"
  username                 = module.keycloak.admin_credentials.username
  password                 = module.keycloak.admin_credentials.password
  url                      = "https://keycloak.apps.${local.cluster_name}.${format("%s.nip.io", replace(module.ingress.external_ip, ".", "-"))}"
  initial_login            = false # Do no try to setup the provider before Keycloak is provisioned.
  tls_insecure_skip_verify = true # Since we are in a testing environment, do not verify the authenticity of SSL certificates.
}
The argument initial_login absolutely needs to be set as false, otherwise Terraform will try to connect to Keycloak before it is deployed. The argument tls_insecure_skip_verify needs to be set as false only on testing environments, when using self-signed SSL certificates.

After setting up the provider, you can then bootstrap the authentication configuration like this:

module "oidc" {
  source = "git::https://github.com/camptocamp/devops-stack-module-keycloak.git//oidc_bootstrap?ref=<RELEASE>"

  cluster_name = local.cluster_name
  base_domain  = format("%s.nip.io", replace(module.ingress.external_ip, ".", "-"))

  dependency_ids = {
    keycloak = module.keycloak.id
  }
}

User Configuration

By default, the oicd_bootstrap module creates a basic realm containing a placeholder user that you can use out-of-the-box to authenticate to the other applications on the DevOps Stack.

However, you can provide a map of desired users and the submodule creates them all with an initial password that can then be changed.

Simply declare the module as follows:

module "oidc" {
  source = "git::https://github.com/camptocamp/devops-stack-module-keycloak.git//oidc_bootstrap?ref=<RELEASE>"

  cluster_name = local.cluster_name
  base_domain  = format("%s.nip.io", replace(module.ingress.external_ip, ".", "-"))

  user_map = {
    johndoe = {
      username   = "johndoe"
      first_name = "John"
      last_name  = "Doe"
      email      = "john.doe@example.com"
    }
    janedoe = {
      username   = "janedoe"
      first_name = "Jane"
      last_name  = "Doe"
      email      = "jane.doe@example.com"
    }
  }

  dependency_ids = {
    keycloak = module.keycloak.id
  }
}
All the fields on each user are required. Besides, since the e-mail is a scope required by most of our apps, the e-mail is automatically set as verified when the users are created.
All users will belong to the administrators group and will have high privileges in applications such as Argo CD.

The module contains an output called devops_stack_users_passwords where you can get a map containing every username and their respective initial password.

OIDC Configuration

By default, the OIDC client is configured to allow returning to any URL after the authentication is successful. If you prefer, you can restrict only the redirect URIs to a list of domains using the input variable oidc_redirect_uris:

module "oidc" {
  source = "git::https://github.com/camptocamp/devops-stack-module-keycloak.git//oidc_bootstrap?ref=<RELEASE>"

  cluster_name = local.cluster_name
  base_domain  = format("%s.nip.io", replace(module.ingress.external_ip, ".", "-"))

  oidc_redirec_uris = [
    "https://argocd.apps.${local.cluster_name}.${format("%s.nip.io", replace(module.ingress.external_ip, ".", "-"))}/auth/callback",
    "https://grafana.apps.${local.cluster_name}.${format("%s.nip.io", replace(module.ingress.external_ip, ".", "-"))}/login/generic_oauth",
    "https://prometheus.apps.${local.cluster_name}.${format("%s.nip.io", replace(module.ingress.external_ip, ".", "-"))}/oauth2/callback",
    "https://thanos-query.apps.${local.cluster_name}.${format("%s.nip.io", replace(module.ingress.external_ip, ".", "-"))}/oauth2/callback",
    "https://thanos-bucketweb.apps.${local.cluster_name}.${format("%s.nip.io", replace(module.ingress.external_ip, ".", "-"))}/oauth2/callback",
    "https://alertmanager.apps.${local.cluster_name}.${format("%s.nip.io", replace(module.ingress.external_ip, ".", "-"))}/oauth2/callback",
  ]

  dependency_ids = {
    keycloak = module.keycloak.id
  }
}

The module provides and output called oidc containing the OIDC configuration that is to be passed on to other modules. This output is an object that outputs the content of local.oidc:

locals {
  oidc = {
    issuer_url    = format("https://keycloak.apps.%s.%s/realms/devops-stack", var.cluster_name, var.base_domain)
    oauth_url     = format("https://keycloak.apps.%s.%s/realms/devops-stack/protocol/openid-connect/auth", var.cluster_name, var.base_domain)
    token_url     = format("https://keycloak.apps.%s.%s/realms/devops-stack/protocol/openid-connect/token", var.cluster_name, var.base_domain)
    api_url       = format("https://keycloak.apps.%s.%s/realms/devops-stack/protocol/openid-connect/userinfo", var.cluster_name, var.base_domain)
    client_id     = "devops-stack-applications"
    client_secret = resource.random_password.client_secret.result
    oauth2_proxy_extra_args = var.cluster_issuer == "ca-issuer" ? [
      "--insecure-oidc-skip-issuer-verification=true",
      "--ssl-insecure-skip-verify=true",
    ] : []
  }
}

Technical Reference

Dependencies

module.keycloak

Obviously, this module must be deployed after module.keycloak, because it needs a working Keycloak instance where to create its resources.

Requirements

The following requirements are needed by this module:

Providers

The following providers are used by this module:

Modules

No modules.

Required Inputs

The following input variables are required:

base_domain

Description: Base domain of the cluster. Value used for the ingress' URL of the application.

Type: string

cluster_name

Description: Name given to the cluster. Value used for the ingress' URL of the application.

Type: string

Optional Inputs

The following input variables are optional (have default values):

cluster_issuer

Description: SSL certificate issuer to use. In this module it is used to conditionally add extra arguments to the OIDC configuration.

Type: string

Default: "ca-issuer"

dependency_ids

Description: IDs of the other modules on which this module depends on.

Type: map(string)

Default: {}

oidc_redirect_uris

Description: List of URIs where the authentication server is allowed to return during the authentication flow.

Type: list(string)

Default:

[
  "*"
]

user_map

Description: List of users to be added to the DevOps Stack Realm. Note that all fields are mandatory.

Type:

map(object({
    username   = string
    email      = string
    first_name = string
    last_name  = string
  }))

Default:

{
  "devopsadmin": {
    "email": "devopsadmin@devops-stack.io",
    "first_name": "Administrator",
    "last_name": "DevOps Stack",
    "username": "devopsadmin"
  }
}

Outputs

The following outputs are exported:

devops_stack_users_passwords

Description: Map containing the credentials of each created user.

id

Description: ID to pass other modules in order to refer to this module as a dependency.

oidc

Description: Object containing multiple OIDC configuration values.

Reference in table format

Show tables

= Requirements

Name Version

>= 4

>= 3

>= 3

= Providers

Name Version

>= 4

>= 3

>= 3

= Resources

Name Type

resource

resource

resource

resource

resource

resource

resource

resource

resource

resource

resource

resource

= Inputs

Name Description Type Default Required

Base domain of the cluster. Value used for the ingress' URL of the application.

string

n/a

yes

SSL certificate issuer to use. In this module it is used to conditionally add extra arguments to the OIDC configuration.

string

"ca-issuer"

no

Name given to the cluster. Value used for the ingress' URL of the application.

string

n/a

yes

IDs of the other modules on which this module depends on.

map(string)

{}

no

List of URIs where the authentication server is allowed to return during the authentication flow.

list(string)

[
  "*"
]

no

List of users to be added to the DevOps Stack Realm. Note that all fields are mandatory.

map(object({
    username   = string
    email      = string
    first_name = string
    last_name  = string
  }))
{
  "devopsadmin": {
    "email": "devopsadmin@devops-stack.io",
    "first_name": "Administrator",
    "last_name": "DevOps Stack",
    "username": "devopsadmin"
  }
}

no

= Outputs

Name Description

Map containing the credentials of each created user.

id

ID to pass other modules in order to refer to this module as a dependency.

Object containing multiple OIDC configuration values.