terraform {
backend "local" {
path = "/home/xenrox/decrypted/terraform/keycloak.tfstate"
}
}
data "vault_generic_secret" "keycloak" {
path = "ansible/keycloak"
}
data "vault_generic_secret" "email" {
path = "ansible/email"
}
provider "keycloak" {
client_id = "admin-cli"
username = data.vault_generic_secret.keycloak.data["admin_username"]
password = data.vault_generic_secret.keycloak.data["admin_password"]
url = "https://keycloak.xenrox.net"
}
resource "keycloak_realm" "xenrox" {
realm = "xenrox"
enabled = true
reset_password_allowed = true
remember_me = true
verify_email = true
login_with_email_allowed = true
password_policy = "length(20) and notUsername"
browser_flow = keycloak_authentication_flow.webauthn.alias
# browser_flow = "browser"
smtp_server {
host = "mail.xenrox.net"
port = "465"
from = "noreply@xenrox.net"
from_display_name = "xenrox Keycloak"
reply_to = "admin@xenrox.net"
reply_to_display_name = "Thorben Günther"
starttls = false
ssl = true
auth {
username = data.vault_generic_secret.email.data["noreply_user"]
password = data.vault_generic_secret.email.data["noreply_password"]
}
}
security_defenses {
headers {
x_frame_options = "DENY"
content_security_policy = "frame-src 'self'; frame-ancestors 'self'; object-src 'none';"
content_security_policy_report_only = ""
x_content_type_options = "nosniff"
x_robots_tag = "none"
x_xss_protection = "1; mode=block"
strict_transport_security = "max-age=31536000; includeSubDomains"
}
brute_force_detection {
permanent_lockout = false
max_login_failures = 3
wait_increment_seconds = 600
quick_login_check_milli_seconds = 1000
minimum_quick_login_wait_seconds = 60
max_failure_wait_seconds = 9000
failure_reset_time_seconds = 43200
}
}
web_authn_policy {
relying_party_entity_name = "Keycloak xenrox"
relying_party_id = "keycloak.xenrox.net"
signature_algorithms = ["ES256", "RS256", "ES512", "RS512"]
}
}
resource "keycloak_required_action" "webauthn_register" {
realm_id = "xenrox"
alias = "webauthn-register"
name = "Webauthn Register"
enabled = true
}
resource "keycloak_realm_events" "realm_events" {
realm_id = "xenrox"
events_enabled = true
events_expiration = 2592000 # retain 30 days
admin_events_enabled = true
admin_events_details_enabled = true
events_listeners = [
"jboss-logging",
"metrics-listener"
]
}
# Login flow
resource "keycloak_authentication_flow" "webauthn" {
realm_id = "xenrox"
alias = "Webauthn"
description = "Browser flow with WebAuthn"
}
resource "keycloak_authentication_execution" "cookie" {
realm_id = "xenrox"
parent_flow_alias = keycloak_authentication_flow.webauthn.alias
authenticator = "auth-cookie"
requirement = "ALTERNATIVE"
depends_on = [keycloak_authentication_flow.webauthn]
}
resource "keycloak_authentication_execution" "identity_provider_redirector" {
realm_id = "xenrox"
parent_flow_alias = keycloak_authentication_flow.webauthn.alias
authenticator = "identity-provider-redirector"
requirement = "ALTERNATIVE"
depends_on = [keycloak_authentication_execution.cookie]
}
resource "keycloak_authentication_subflow" "forms" {
realm_id = "xenrox"
alias = "Forms"
parent_flow_alias = keycloak_authentication_flow.webauthn.alias
requirement = "ALTERNATIVE"
depends_on = [keycloak_authentication_execution.identity_provider_redirector]
}
resource "keycloak_authentication_execution" "username_password_form" {
realm_id = "xenrox"
parent_flow_alias = keycloak_authentication_subflow.forms.alias
authenticator = "auth-username-password-form"
requirement = "REQUIRED"
}
resource "keycloak_authentication_subflow" "conditional_2fa" {
realm_id = "xenrox"
alias = "Browser - Conditional 2FA"
parent_flow_alias = keycloak_authentication_subflow.forms.alias
requirement = "CONDITIONAL"
depends_on = [keycloak_authentication_execution.username_password_form]
}
resource "keycloak_authentication_execution" "condition" {
realm_id = "xenrox"
parent_flow_alias = keycloak_authentication_subflow.conditional_2fa.alias
authenticator = "conditional-user-configured"
requirement = "REQUIRED"
}
resource "keycloak_authentication_execution" "otp_form" {
realm_id = "xenrox"
parent_flow_alias = keycloak_authentication_subflow.conditional_2fa.alias
authenticator = "auth-otp-form"
requirement = "ALTERNATIVE"
depends_on = [keycloak_authentication_execution.condition]
}
resource "keycloak_authentication_execution" "webauthn" {
realm_id = "xenrox"
parent_flow_alias = keycloak_authentication_subflow.conditional_2fa.alias
authenticator = "webauthn-authenticator"
requirement = "ALTERNATIVE"
depends_on = [keycloak_authentication_execution.otp_form]
}
resource "keycloak_group" "admin" {
realm_id = "xenrox"
name = "Admin"
}
resource "keycloak_group_roles" "admin" {
realm_id = "xenrox"
group_id = keycloak_group.admin.id
role_ids = [
keycloak_role.vault_admin.id,
keycloak_role.peertube.id,
keycloak_role.grafana_admin.id,
keycloak_role.matrix.id
]
}
# Vault
data "vault_generic_secret" "vault" {
path = "ansible/vault"
}
resource "keycloak_openid_client" "vault_openid_client" {
realm_id = "xenrox"
client_id = "openid_vault"
client_secret = data.vault_generic_secret.vault.data["oidc_secret"]
name = "Vault"
enabled = true
standard_flow_enabled = true
access_type = "CONFIDENTIAL"
valid_redirect_uris = [
"https://vault.xenrox.net/*",
"http://localhost:8250/oidc/callback"
]
}
resource "keycloak_openid_user_realm_role_protocol_mapper" "vault_user_realm_role_mapper" {
realm_id = "xenrox"
client_id = keycloak_openid_client.vault_openid_client.id
name = "user realm role mapper"
claim_name = "roles"
multivalued = true
}
resource "keycloak_role" "vault_admin" {
realm_id = "xenrox"
name = "vault_admin"
description = "Vault admin"
}
# Peertube
data "vault_generic_secret" "peertube" {
path = "ansible/peertube"
}
resource "keycloak_openid_client" "peertube_openid_client" {
realm_id = "xenrox"
client_id = "openid_peertube"
client_secret = data.vault_generic_secret.peertube.data["oidc_secret"]
name = "Peertube"
enabled = true
standard_flow_enabled = true
access_type = "CONFIDENTIAL"
valid_redirect_uris = [
"https://tube.xenrox.net/*"
]
}
resource "keycloak_openid_user_realm_role_protocol_mapper" "peertube_user_realm_role_mapper" {
realm_id = "xenrox"
client_id = keycloak_openid_client.peertube_openid_client.id
name = "user realm role mapper"
claim_name = "roles"
multivalued = true
}
resource "keycloak_group" "peertube" {
realm_id = "xenrox"
name = "Peertube"
}
resource "keycloak_role" "peertube" {
realm_id = "xenrox"
name = "peertube"
description = "Peertube user"
}
resource "keycloak_group_roles" "peertube" {
realm_id = "xenrox"
group_id = keycloak_group.peertube.id
role_ids = [
keycloak_role.peertube.id
]
}
# Nextcloud
data "vault_generic_secret" "nextcloud" {
path = "ansible/nextcloud"
}
resource "keycloak_openid_client" "nextcloud_openid_client" {
realm_id = "xenrox"
client_id = "openid_nextcloud"
client_secret = data.vault_generic_secret.nextcloud.data["oidc_secret"]
name = "Nextcloud"
enabled = true
standard_flow_enabled = true
access_type = "CONFIDENTIAL"
valid_redirect_uris = [
"https://cloud.xenrox.net/*"
]
}
# Grafana
#
data "vault_generic_secret" "grafana" {
path = "ansible/grafana"
}
resource "keycloak_openid_client" "grafana_openid_client" {
realm_id = "xenrox"
client_id = "openid_grafana"
client_secret = data.vault_generic_secret.grafana.data["oidc_secret"]
name = "Grafana"
enabled = true
standard_flow_enabled = true
access_type = "CONFIDENTIAL"
valid_redirect_uris = [
"https://grafana.xenrox.net/*"
]
}
resource "keycloak_openid_user_realm_role_protocol_mapper" "grafana_user_realm_role_mapper" {
realm_id = "xenrox"
client_id = keycloak_openid_client.grafana_openid_client.id
name = "user realm role mapper"
claim_name = "roles"
multivalued = true
}
resource "keycloak_role" "grafana_admin" {
realm_id = "xenrox"
name = "grafana_admin"
description = "Grafana admin"
}
# Matrix
data "vault_generic_secret" "matrix" {
path = "ansible/matrix"
}
resource "keycloak_openid_client" "matrix_openid_client" {
realm_id = "xenrox"
client_id = "openid_matrix"
client_secret = data.vault_generic_secret.matrix.data["oidc_secret"]
name = "Matrix"
enabled = true
standard_flow_enabled = true
access_type = "CONFIDENTIAL"
valid_redirect_uris = [
"https://matrix.xenrox.net/_synapse/client/oidc/callback"
]
}
resource "keycloak_openid_user_realm_role_protocol_mapper" "matrix_user_realm_role_mapper" {
realm_id = "xenrox"
client_id = keycloak_openid_client.matrix_openid_client.id
name = "user realm role mapper"
claim_name = "roles"
multivalued = true
}
resource "keycloak_group" "matrix" {
realm_id = "xenrox"
name = "Matrix"
}
resource "keycloak_role" "matrix" {
realm_id = "xenrox"
name = "matrix"
description = "Matrix user"
}
resource "keycloak_group_roles" "matrix" {
realm_id = "xenrox"
group_id = keycloak_group.matrix.id
role_ids = [keycloak_role.matrix.id]
}
# Hedgedoc
data "vault_generic_secret" "hedgedoc" {
path = "ansible/hedgedoc"
}
resource "keycloak_openid_client" "hedgedoc_openid_client" {
realm_id = "xenrox"
client_id = "openid_hedgedoc"
client_secret = data.vault_generic_secret.hedgedoc.data["oidc_secret"]
name = "Hedgedoc"
enabled = true
standard_flow_enabled = true
access_type = "CONFIDENTIAL"
valid_redirect_uris = [
"https://hedgedoc.xenrox.net/*"
]
}
resource "keycloak_openid_user_realm_role_protocol_mapper" "hedgedoc_user_realm_role_mapper" {
realm_id = "xenrox"
client_id = keycloak_openid_client.hedgedoc_openid_client.id
name = "user realm role mapper"
claim_name = "roles"
multivalued = true
}
resource "keycloak_group" "hedgedoc" {
realm_id = "xenrox"
name = "Hedgedoc"
}
resource "keycloak_role" "hedgedoc" {
realm_id = "xenrox"
name = "hedgedoc"
description = "Hedgedoc user"
}
resource "keycloak_group_roles" "hedgedoc" {
realm_id = "xenrox"
group_id = keycloak_group.hedgedoc.id
role_ids = [keycloak_role.hedgedoc.id]
}