# Centralizing them signing in to web applications (plus LDAP). { config, lib, pkgs, ... }: let inherit (import ../hardware/networks.nix) interfaces; authDomain = "auth.${config.networking.domain}"; authInternalDomain = "auth.${config.networking.fqdn}"; # This is also set on our own. keycloakUser = config.services.keycloak.database.username; keycloakDbName = if config.services.keycloak.database.createLocally then keycloakUser else config.services.keycloak.database.username; # This is for access to PostgreSQL database. postgresUser = config.users.groups.postgres.name; certs = config.security.acme.certs; host = "localhost"; in { sops.secrets = lib.getSecrets ../../secrets/secrets.yaml { "keycloak/db/password".owner = postgresUser; }; # Hey, the hub for your application sign-in. services.keycloak = { enable = true; # Pls change at first login. Or just change it through `kcadm.sh`. initialAdminPassword = "wow what is this thing"; database = { type = "postgresql"; createLocally = true; passwordFile = config.sops.secrets."keycloak/db/password".path; }; settings = { inherit host; db-schema = keycloakDbName; http-enabled = true; http-port = 8759; https-port = 8760; hostname = authDomain; hostname-strict-backchannel = true; proxy = "passthrough"; }; sslCertificate = "${certs."${authDomain}".directory}/fullchain.pem"; sslCertificateKey = "${certs."${authDomain}".directory}/key.pem"; }; # Configuring the database of choice to play nicely with the service. services.postgresql = { ensureDatabases = [ keycloakDbName ]; ensureUsers = [ { name = keycloakUser; ensurePermissions = { "DATABASE ${keycloakDbName}" = "ALL PRIVILEGES"; "SCHEMA ${keycloakDbName}" = "ALL PRIVILEGES"; }; } ]; }; # Modifying it a little bit for per-user schema. systemd.services.keycloak = { preStart = let psqlBin = "${lib.getBin config.services.postgresql.package}/bin/psql"; in lib.mkAfter '' # Setting up the appropriate schema for PostgreSQL secure schema usage. ${psqlBin} -tAc "SELECT 1 FROM information_schema.schemata WHERE schema_name='${keycloakUser}';" \ | grep -q 1 || ${psqlBin} -tAc "CREATE SCHEMA IF NOT EXISTS AUTHORIZATION ${keycloakUser};" ''; }; # Attaching it to the reverse proxy of choice. services.nginx.virtualHosts = { "${authDomain}" = { forceSSL = true; enableACME = true; acmeRoot = null; # This is based from the reverse proxy guide from the official # documentation at https://www.keycloak.org/server/reverseproxy. locations = let keycloakPath = path: "http://${host}:${toString config.services.keycloak.settings.http-port}"; in lib.listToAttrs (lib.lists.map (appPath: lib.nameValuePair appPath { proxyPass = keycloakPath appPath; }) [ "/js/" "/realms/" "/resources/" "/robots.txt" ]) // { "/".return = "444"; }; }; "${authInternalDomain}" = { locations."/" = { proxyPass = "http://${host}:${toString config.services.keycloak.settings.http-port}"; }; }; }; # Configuring fail2ban for this services which is only present as a neat # little hint from its server administration guide. services.fail2ban.jails = { keycloak.settings = { enabled = true; backend = "systemd"; filter = "keycloak[journalmatch='_SYSTEMD_UNIT=keycloak.service']"; maxretry = 3; }; }; environment.etc = { "fail2ban/filter.d/keycloak.conf".text = '' [Includes] before = common.conf # This is based from the server administration guide at # https://www.keycloak.org/docs/$VERSION/server_admin/index.html. [Definition] failregex = ^.*type=LOGIN_ERROR.*ipAddress=.*$ ignoreregex = ''; }; }