terraform refactor + keycloak terraform tests
This commit is contained in:
parent
341809bd18
commit
6f154ff4b4
14 changed files with 311 additions and 15 deletions
4
.envrc
4
.envrc
|
@ -8,3 +8,7 @@ if [ -f "${kubeconfig}" ]; then
|
|||
fi
|
||||
|
||||
export VAULT_ADDR=https://openbao.k8s.home.finn.io
|
||||
export VAULT_TOKEN="$(jq -r .root_token bao-root.json)"
|
||||
|
||||
export KEYCLOAK_CLIENT_ID="$(bao kv get -format=json static-secrets/tofu/default/oidc-client-credentials-tofu | jq -r .data.data.client_id)"
|
||||
export KEYCLOAK_CLIENT_SECRET="$(bao kv get -format=json static-secrets/tofu/default/oidc-client-credentials-tofu | jq -r .data.data.client_secret)"
|
||||
|
|
|
@ -17,3 +17,28 @@ provider "registry.opentofu.org/hashicorp/vault" {
|
|||
"zh:f465a955cc96f640e7426a648ba672c169a4a2959bad6146fe61583d67642561",
|
||||
]
|
||||
}
|
||||
|
||||
provider "registry.opentofu.org/mrparkers/keycloak" {
|
||||
version = "4.4.0"
|
||||
constraints = ">= 4.0.0"
|
||||
hashes = [
|
||||
"h1:FH9j76zRv05qxk7I/w0mycmBEuew/+XP+Qx+Ptz/onw=",
|
||||
"zh:0116d63fb4a4436d67cc793038899e0de23c3a5c78f5bf3cf76ee006ad886979",
|
||||
"zh:0fa399fcdeef21dd914ff7413b8489e47900cbe7bc65b50eeb0d75b71a2b561d",
|
||||
"zh:30371fee6d0ae438908b1bf03278f6d0a0cb2992a97814028676a05a55d92f19",
|
||||
"zh:39218a95fe6430ac2b44470cb991dbb98f57c5306017a80b81d3a319855094f4",
|
||||
"zh:3b436c471cde4eb9120f609e3aecf12d383e8032aeb9cd12c7476faa7c8b4afb",
|
||||
"zh:9a2a5cc77332e6cd9f6d101d3aff35520a2361fc02f4d436fe176dbd5351f24b",
|
||||
"zh:9a89cc61c303100174cda3783b13fa4f6e2648eb436c1259d1c72264998534e8",
|
||||
"zh:b588cd78d9939523de1fa8202c2757c497a20dcf2bf67cf4daf61836194bfe3a",
|
||||
"zh:c04e6ac2367f55d9cd0893ebebbecb9da685312077e8a7fff299b8d8009955d5",
|
||||
"zh:c23286693edf2024272219f6728bb7eded5ee087956fc527a63f10ea9ec9c9e4",
|
||||
"zh:d7a29a2023f17b24236079789931d53662a2696b13d30140cb75dc0e693a1f94",
|
||||
"zh:ddc0cad0a8ec9e5afc4f4502aed75089c3e9e0bc6da9d4b796728ef5580b94ef",
|
||||
"zh:de8833a1a0a726401380e52302892de782dddb7efa51122c33104dde8e119561",
|
||||
"zh:dee864f90327b149d126d603c5ed58cc196682153ebd1bfa73dd67398f6cbe38",
|
||||
"zh:f63ef9950ebb06fa1daad784a3d0f342803f65404107186bdadb3198ce4d03b2",
|
||||
"zh:f6d2414fec3fcaefc80cbe8e49647221dbbcfd2fe1b0f7619bd68d06c93c30f4",
|
||||
"zh:fb659b5a21ba0ad9ec1c7484f167c51c752abea84dd27e726cc3567e7006e99e",
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
resource "vault_mount" "static_secrets" {
|
||||
path = "static-secrets"
|
||||
type = "kv"
|
||||
options = { version = "2" }
|
||||
description = "Static secrets, organized by <k8s-namespace>/<service-account>/*"
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
resource "vault_policy" "k8s_default" {
|
||||
name = "k8s-default"
|
||||
|
||||
policy = templatefile("bao-policies/k8s-default.hcl", {
|
||||
k8s_auth_backend_accessor = vault_auth_backend.kubernetes.accessor,
|
||||
k8s_secrets_path = vault_mount.static_secrets.path,
|
||||
})
|
||||
}
|
|
@ -17,3 +17,19 @@ resource "vault_kubernetes_auth_backend_role" "k8s-default" {
|
|||
vault_policy.k8s_default.name
|
||||
]
|
||||
}
|
||||
|
||||
resource "vault_mount" "static_secrets" {
|
||||
path = "static-secrets"
|
||||
type = "kv"
|
||||
options = { version = "2" }
|
||||
description = "Static secrets, organized by <k8s-namespace>/<service-account>/*"
|
||||
}
|
||||
|
||||
resource "vault_policy" "k8s_default" {
|
||||
name = "k8s-default"
|
||||
|
||||
policy = templatefile("bao-policies/k8s-default.hcl", {
|
||||
k8s_auth_backend_accessor = vault_auth_backend.kubernetes.accessor,
|
||||
k8s_secrets_path = vault_mount.static_secrets.path,
|
||||
})
|
||||
}
|
24
tf/keycloak-client/main.tf
Normal file
24
tf/keycloak-client/main.tf
Normal file
|
@ -0,0 +1,24 @@
|
|||
resource "keycloak_openid_client" "oidc" {
|
||||
realm_id = var.realm
|
||||
client_id = var.client_id
|
||||
name = var.name != null ? var.name : var.client_id
|
||||
enabled = true
|
||||
use_refresh_tokens = var.use_refresh_tokens
|
||||
service_accounts_enabled = var.service_accounts_enabled
|
||||
|
||||
access_type = "CONFIDENTIAL"
|
||||
standard_flow_enabled = true
|
||||
root_url = var.root_url != null ? var.root_url : "https://${var.client_id}.janky.solutions"
|
||||
valid_redirect_uris = length(var.valid_redirect_uris) == 0 ? ["/*"] : var.valid_redirect_uris
|
||||
}
|
||||
|
||||
# resource "keycloak_openid_client_service_account_realm_role" ""
|
||||
|
||||
resource "vault_kv_secret_v2" "oidc" {
|
||||
mount = var.vault_mount
|
||||
name = "${var.namespace != null ? var.namespace : var.client_id}/default/oidc-client-credentials-${var.client_id}"
|
||||
data_json = jsonencode({
|
||||
client_id = keycloak_openid_client.oidc.client_id,
|
||||
client_secret = keycloak_openid_client.oidc.client_secret
|
||||
})
|
||||
}
|
11
tf/keycloak-client/outputs.tf
Normal file
11
tf/keycloak-client/outputs.tf
Normal file
|
@ -0,0 +1,11 @@
|
|||
output "client_id" {
|
||||
value = keycloak_openid_client.oidc.client_id
|
||||
}
|
||||
|
||||
output "client_secret" {
|
||||
value = keycloak_openid_client.oidc.client_secret
|
||||
}
|
||||
|
||||
output "service_account_user_id" {
|
||||
value = keycloak_openid_client.oidc.service_account_user_id
|
||||
}
|
8
tf/keycloak-client/providers.tf
Normal file
8
tf/keycloak-client/providers.tf
Normal file
|
@ -0,0 +1,8 @@
|
|||
terraform {
|
||||
required_providers {
|
||||
keycloak = {
|
||||
source = "mrparkers/keycloak"
|
||||
version = ">= 4.0.0"
|
||||
}
|
||||
}
|
||||
}
|
49
tf/keycloak-client/variables.tf
Normal file
49
tf/keycloak-client/variables.tf
Normal file
|
@ -0,0 +1,49 @@
|
|||
variable "realm" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "vault_mount" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "client_id" {
|
||||
type = string
|
||||
description = "the keycloak client ID. Ideally this matches the Kubernetes namespace the resource is deployed to."
|
||||
}
|
||||
|
||||
variable "name" {
|
||||
type = string
|
||||
default = null
|
||||
nullable = true
|
||||
description = "An (optional) display name shown to the user in certain dark corners of Keycloak. client_id is used by default."
|
||||
}
|
||||
|
||||
variable "namespace" {
|
||||
type = string
|
||||
default = null
|
||||
nullable = true
|
||||
description = "Kubernetes namespace that will use this client, used for creating vault path. client_id is used by default."
|
||||
}
|
||||
|
||||
variable "root_url" {
|
||||
type = string
|
||||
default = null
|
||||
nullable = true
|
||||
description = "The root URL of the app. https://<client_id>.janky.solutions is used by default"
|
||||
}
|
||||
|
||||
variable "valid_redirect_uris" {
|
||||
type = list(string)
|
||||
default = []
|
||||
description = "URIs the client will ask keycloak to send the user back to after auth. /* is used by default."
|
||||
}
|
||||
|
||||
variable "use_refresh_tokens" {
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "service_accounts_enabled" {
|
||||
type = bool
|
||||
default = false
|
||||
}
|
22
tf/keycloak-clients.tf
Normal file
22
tf/keycloak-clients.tf
Normal file
|
@ -0,0 +1,22 @@
|
|||
// own client
|
||||
module "keycloak_client_tofu" {
|
||||
source = "./keycloak-client"
|
||||
|
||||
realm = keycloak_realm.dev.id
|
||||
vault_mount = vault_mount.static_secrets.path
|
||||
|
||||
client_id = "tofu"
|
||||
service_accounts_enabled = true
|
||||
}
|
||||
|
||||
data "keycloak_openid_client" "realm_management" {
|
||||
realm_id = keycloak_realm.dev.id
|
||||
client_id = "realm-management"
|
||||
}
|
||||
|
||||
resource "keycloak_openid_client_service_account_role" "client_service_account_role" {
|
||||
realm_id = keycloak_realm.dev.id
|
||||
client_id = data.keycloak_openid_client.realm_management.id
|
||||
service_account_user_id = module.keycloak_client_tofu.service_account_user_id
|
||||
role = "realm-admin"
|
||||
}
|
37
tf/keycloak-normal-flow.tf
Normal file
37
tf/keycloak-normal-flow.tf
Normal file
|
@ -0,0 +1,37 @@
|
|||
resource "keycloak_authentication_flow" "webauthn_browser" {
|
||||
realm_id = keycloak_realm.dev.id
|
||||
alias = "webauthn_browser"
|
||||
description = "browser based authentication"
|
||||
}
|
||||
|
||||
resource "keycloak_authentication_execution" "auth_cookie" {
|
||||
realm_id = keycloak_realm.dev.id
|
||||
parent_flow_alias = keycloak_authentication_flow.webauthn_browser.alias
|
||||
authenticator = "auth-cookie"
|
||||
requirement = "ALTERNATIVE"
|
||||
}
|
||||
|
||||
resource "keycloak_authentication_subflow" "webauthn_flow" {
|
||||
realm_id = keycloak_realm.dev.id
|
||||
alias = "webauthn browser forms"
|
||||
description = "Username, password, otp and other auth forms."
|
||||
parent_flow_alias = keycloak_authentication_flow.webauthn_browser.alias
|
||||
provider_id = "basic-flow"
|
||||
requirement = "ALTERNATIVE"
|
||||
depends_on = [ keycloak_authentication_execution.auth_cookie ]
|
||||
}
|
||||
|
||||
resource "keycloak_authentication_execution" "user_pass" {
|
||||
realm_id = keycloak_realm.dev.id
|
||||
parent_flow_alias = keycloak_authentication_subflow.webauthn_flow.alias
|
||||
authenticator = "auth-username-password-form"
|
||||
requirement = "REQUIRED"
|
||||
}
|
||||
|
||||
resource "keycloak_authentication_execution" "webauthn" {
|
||||
realm_id = keycloak_realm.dev.id
|
||||
parent_flow_alias = keycloak_authentication_subflow.webauthn_flow.alias
|
||||
authenticator = "webauthn-authenticator"
|
||||
requirement = "REQUIRED"
|
||||
}
|
||||
|
89
tf/keycloak-passkey-flow.tf
Normal file
89
tf/keycloak-passkey-flow.tf
Normal file
|
@ -0,0 +1,89 @@
|
|||
resource "keycloak_authentication_flow" "passkey" {
|
||||
realm_id = keycloak_realm.dev.id
|
||||
alias = "passkey"
|
||||
description = "browser based authentication"
|
||||
}
|
||||
|
||||
resource "keycloak_authentication_execution" "passkey_auth_cookie" {
|
||||
realm_id = keycloak_realm.dev.id
|
||||
parent_flow_alias = keycloak_authentication_flow.passkey.alias
|
||||
authenticator = "auth-cookie"
|
||||
requirement = "ALTERNATIVE"
|
||||
}
|
||||
|
||||
resource "keycloak_authentication_subflow" "passkey_forms" {
|
||||
realm_id = keycloak_realm.dev.id
|
||||
alias = "passkey browser forms"
|
||||
parent_flow_alias = keycloak_authentication_flow.passkey.alias
|
||||
provider_id = "basic-flow"
|
||||
requirement = "ALTERNATIVE"
|
||||
depends_on = [ keycloak_authentication_execution.auth_cookie ]
|
||||
}
|
||||
|
||||
resource "keycloak_authentication_execution" "passkey_username" {
|
||||
realm_id = keycloak_realm.dev.id
|
||||
parent_flow_alias = keycloak_authentication_subflow.passkey_forms.alias
|
||||
authenticator = "auth-username-form"
|
||||
requirement = "REQUIRED"
|
||||
}
|
||||
|
||||
resource "keycloak_authentication_subflow" "passkey_passwordless_or_2fa" {
|
||||
realm_id = keycloak_realm.dev.id
|
||||
alias = "passkey passkey or 2fa"
|
||||
parent_flow_alias = keycloak_authentication_subflow.passkey_forms.alias
|
||||
provider_id = "basic-flow"
|
||||
requirement = "REQUIRED"
|
||||
depends_on = [ keycloak_authentication_execution.passkey_username ]
|
||||
}
|
||||
|
||||
resource "keycloak_authentication_execution" "passkey_webauthn_passwordless" {
|
||||
realm_id = keycloak_realm.dev.id
|
||||
parent_flow_alias = keycloak_authentication_subflow.passkey_passwordless_or_2fa.alias
|
||||
authenticator = "webauthn-authenticator-passwordless"
|
||||
requirement = "ALTERNATIVE"
|
||||
depends_on = [ keycloak_authentication_execution.passkey_username ]
|
||||
}
|
||||
|
||||
resource "keycloak_authentication_subflow" "passkey_password_and_second_factor" {
|
||||
realm_id = keycloak_realm.dev.id
|
||||
parent_flow_alias = keycloak_authentication_subflow.passkey_passwordless_or_2fa.alias
|
||||
alias = "passkey password and 2fa"
|
||||
provider_id = "basic-flow"
|
||||
requirement = "ALTERNATIVE"
|
||||
}
|
||||
|
||||
resource "keycloak_authentication_execution" "passkey_password" {
|
||||
realm_id = keycloak_realm.dev.id
|
||||
parent_flow_alias = keycloak_authentication_subflow.passkey_password_and_second_factor.alias
|
||||
authenticator = "auth-password-form"
|
||||
requirement = "REQUIRED"
|
||||
}
|
||||
|
||||
resource "keycloak_authentication_subflow" "passkey_second_factor" {
|
||||
realm_id = keycloak_realm.dev.id
|
||||
parent_flow_alias = keycloak_authentication_subflow.passkey_password_and_second_factor.alias
|
||||
alias = "passkey second factor"
|
||||
provider_id = "basic-flow"
|
||||
requirement = "CONDITIONAL"
|
||||
}
|
||||
|
||||
resource "keycloak_authentication_execution" "passkey_user_configured_condition" {
|
||||
realm_id = keycloak_realm.dev.id
|
||||
parent_flow_alias = keycloak_authentication_subflow.passkey_second_factor.alias
|
||||
authenticator = "conditional-user-configured"
|
||||
requirement = "REQUIRED"
|
||||
}
|
||||
|
||||
resource "keycloak_authentication_execution" "passkey_webauthn" {
|
||||
realm_id = keycloak_realm.dev.id
|
||||
parent_flow_alias = keycloak_authentication_subflow.passkey_second_factor.alias
|
||||
authenticator = "webauthn-authenticator"
|
||||
requirement = "ALTERNATIVE"
|
||||
}
|
||||
|
||||
resource "keycloak_authentication_execution" "passkey_otp" {
|
||||
realm_id = keycloak_realm.dev.id
|
||||
parent_flow_alias = keycloak_authentication_subflow.passkey_second_factor.alias
|
||||
authenticator = "auth-otp-form"
|
||||
requirement = "ALTERNATIVE"
|
||||
}
|
11
tf/keycloak.tf
Normal file
11
tf/keycloak.tf
Normal file
|
@ -0,0 +1,11 @@
|
|||
resource "keycloak_realm" "dev" {
|
||||
realm = "dev.janky.solutions"
|
||||
enabled = true
|
||||
display_name = "Janky Solutions (dev)"
|
||||
default_signature_algorithm = "RS256"
|
||||
}
|
||||
|
||||
resource "keycloak_authentication_bindings" "browser_authentication_binding" {
|
||||
realm_id = keycloak_realm.dev.id
|
||||
browser_flow = keycloak_authentication_flow.passkey.alias
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
data "terraform_remote_state" "foo" {
|
||||
data "terraform_remote_state" "kube" {
|
||||
backend = "kubernetes"
|
||||
config = {
|
||||
secret_suffix = "state"
|
||||
|
@ -8,3 +8,17 @@ data "terraform_remote_state" "foo" {
|
|||
}
|
||||
|
||||
provider "vault" {}
|
||||
|
||||
terraform {
|
||||
required_providers {
|
||||
keycloak = {
|
||||
source = "mrparkers/keycloak"
|
||||
version = ">= 4.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "keycloak" {
|
||||
realm = "dev.janky.solutions"
|
||||
url = "https://auth.janky.solutions"
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue