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, keycloak_role.hedgedoc.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] }