Skip to content

Commit f2828da

Browse files
committed
Add keycloak and LDAP for SSO
1 parent 2687f65 commit f2828da

5 files changed

Lines changed: 197 additions & 3 deletions

File tree

hosts/goldberg/configuration.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
../../services/hedgedoc.nix
1313
../../services/pretix.nix
1414
../../services/pretalx.nix
15+
../../services/keycloak
1516
];
1617

1718
system.stateVersion = "23.05";

packages/default.nix

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,4 +185,33 @@ final: prev:
185185
})];
186186

187187
matrixjoinlink = final.callPackage ./matrixjoinlink {};
188+
keycloak-registration-captcha = final.maven.buildMavenPackage rec {
189+
pname = "keycloak-registration-captcha";
190+
version = "1.1.0";
191+
src = final.fetchFromGitHub {
192+
owner = "chaos-jetzt";
193+
repo = pname;
194+
rev = "v${version}";
195+
hash = "sha256-133cMOSHtDj1eTS01AuiNQa7aI0tNSCt1rg1mgCEinw=";
196+
};
197+
198+
nativeBuildInputs = [ final.maven ];
199+
200+
mvnHash = "sha256-GTFDjQT7g1sMfIbH/wkxBS0yJDctW5l3QFsv2aggPN0=";
201+
202+
installPhase = ''
203+
runHook preInstall
204+
install -Dm444 -t "$out" target/registration-captcha.jar
205+
runHook postInstall
206+
'';
207+
};
208+
209+
cj-keycloak-theme = final.fetchFromGitHub rec {
210+
pname = "cj-keycloak-theme";
211+
version = "2020-12-19-${builtins.substring 0 6 rev}";
212+
owner = "chaos-jetzt";
213+
repo = "cj-keycloak-theme";
214+
rev = "3075080f828782cfae076c8f4f7866112f544d94";
215+
hash = "sha256-uGUFPE1o0RaKxXh+Vv+OUxCSnyzARo9v2ucWHchW/wg=";
216+
};
188217
}

secrets/goldberg/secrets.yaml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ vaultwarden:
1616
hedgedoc_env: ENC[AES256_GCM,data:VHIzmq7P1pqS72HbRXRT3k7n6vyPkzkQFJdveseCAHnzdXlEF0lHr+Up7J6XhfhtQXO3ogV2jkGZpOMY0OuEvhLf2yGkBj3W0ZtG7Kx6Rdcbb5rG7Z6Vb1vpL/aT88QFd3VX23M+FPFyWeYKGOvGRuCela+mUX7jDs2W4jOrYOtEGe3+V08DcvtcCvE2L1NqeDQ=,iv:011/ZRdQlkFQ2TZpzQhfRf/OTawnHFQDockLGlOrkmc=,tag:Y66RIBtyjl5VSo23GU4sNg==,type:str]
1717
pretix_env: ENC[AES256_GCM,data:Cu3S3j49P0IVZMKfzuUBdPVl4YTDUybmVKasGCaqrcyWFKbRrd1XK9NmZ+iYHemuJ7A6F+I1qrnn7GdwayOQr0MCAIpoIuDjiYex1wi3WPkx48Dxk9YInoD6JGzmYQE64Qe08rlH1gt3nBXGKkUQ5D4razNokO1F4pYQxthsTvP3s5+zOD5Z3H6/wy30e5Ihtp3KMiiJ5OJzBUelmA2YN5Q/l4TAR/pTk0R5557TdAmqUuMWzxovCl4cXY8xP6Nlnc9X3Hg7YdfTOTAVLak5aMOi2kZFB79CxF8QBjZzwS1EuyFImyWohP53Yb/SxdnsgDNQQajxfQy/8Z4Bg1T7k2hRZ5LBuITwu15wu5V3h+VPTgZMDi1PeQhN8InZF8nNenpdw+4FRNinR7qIVf31SsCbAOXs2CU3KEi8KfEc7MEsvSG2tNOoy5zX/2H6EKvHJZED+mHcnvffAjTIkvVLvGw=,iv:NdCtoXtL0JtmzrheW6tbYd6XwyfH/DVqL9sZBPCmnws=,tag:/7ts5E1Pc5mF5YpWSGgorw==,type:str]
1818
pretalx_env: ENC[AES256_GCM,data:Fq37a73w5/m/2T/dFqgUx0EIESWLr2+CEYv869YBSnGLAVO5eqzrtXwiPq3g/vtEh/b1T+jbwi2y04PgM4IFOJ2UMYtIrxW0BAsRB26XBTAEF3NLMiHTYj/+4D+NVaSFM7vm4TGTLso//rkBtCJvWnIxbGCphAREgG0qCaacUBfv+HYtdA/REFfqyohxyLleSvvvVdSKbNK4gOpe3Pp1d03Jg3LY6dB8NqNSCZCdSEa65+xcniJjJXqAwhyG3VcefzaQXh+nOLP01ZVijDdRkWw=,iv:jkoxmrZr+vlM92UIGLOlFMC7UeJm7UjRL6eTzVOAcvo=,tag:gGEkRHYbJUWSwy2lTO5HCA==,type:str]
19+
keycloak:
20+
db_password: ENC[AES256_GCM,data:fRSCENb0II+obekeD82JtjSgKfMsfQHPn33jft7xCHqkEpAkBk5TZ30rYGJOQ+09hdKx4NTFS6iYMG1jEDYDLw==,iv:RhsPKrjj+IaRRUK5NELKPTS2vkO7GnTWRMySpzJlzvs=,tag:YOO9cx4nMJwXeiNHoAPsEw==,type:str]
21+
ldap:
22+
admin_password: ENC[AES256_GCM,data:Xj8DiRYGf6Kgk6nnhKfblbQ3u2hcJgl+m+piJpw1ivyJ8224laX9HZHZqYWtczOZi7oy2ddF5XcLVEh0rLa/4w==,iv:dJqusylgVsn0elF3yRuV+6bDB3KiKdxnVf0UL7DE+YQ=,tag:+7oP5sQliwSOF2OE4dO5BA==,type:str]
1923
sops:
2024
kms: []
2125
gcp_kms: []
@@ -31,8 +35,8 @@ sops:
3135
QjBmYlNYWlFoWHd0ZFJkWE0xMkpvZzQKJwKap35S2pWGNOtBHe931dRqAQAczbWv
3236
/BUEtl900F8YLQCB1/myV0Dk5X9XDlww1yrzw/La3gXANY93Ndu3MA==
3337
-----END AGE ENCRYPTED FILE-----
34-
lastmodified: "2024-07-02T19:49:57Z"
35-
mac: ENC[AES256_GCM,data:28L6nbZms7df9fvrcC5HdDl7fwHFlXDrW3JibwzlTARfi7p9goOUxpUUifXcrWU6dawWJS/b9CQyy9eUoT2XpqXFF3bkMK67to8IGJWTjkF2x2hEa+nl+OuXFCz5cqr/QsDtNPzEitDARA7+GNt8qtUoxiZ41oFmEx2rfQyEmj0=,iv:gUL8/7ZcKConZamz3kWTBZuYg+opn8LPueNfxPfzfi0=,tag:9hAPtZmZH+puf0o3WpwpDw==,type:str]
38+
lastmodified: "2024-07-08T09:22:18Z"
39+
mac: ENC[AES256_GCM,data:xtLzeje57woFwmI2vqNcb46utHNQqteQPmVZQartlL1l8E/AKmxPtMMCjG6x7//DOsbt5YliXJ/5Ik0VEkHzVmlK05RSy3CSA8UATaWExAEgyfQIzpvv14Fet0rjE7T48IcqfaGYpz5arzbFF2VJaSCuPLPqwKOaa2aCDdkEzaA=,iv:9o/OGN41XoSjk35Nd+hdgtk4+MuoLTtAeOxUVRGEm3w=,tag:4AK7whc85fjhBJ8mBm+nbA==,type:str]
3640
pgp:
3741
- created_at: "2023-07-23T14:01:56Z"
3842
enc: |-

services/dokuwiki.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ in {
145145
};
146146
plugin.oauthkeycloak = {
147147
key = get_secret "dokuwiki/keycloak_key";
148-
openidurl = "https://sso.chaos.jetzt/auth/realms/${if isDev then "dev" else "chaos-jetzt"}/.well-known/openid-configuration";
148+
openidurl = "https://sso${lib.optionalString isDev ".dev"}.chaos.jetzt/auth/realms/${if isDev then "dev" else "chaos-jetzt"}/.well-known/openid-configuration";
149149
};
150150
};
151151

services/keycloak/default.nix

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
{ pkgs
2+
, config
3+
, lib
4+
, baseDomain
5+
, isDev
6+
, ...
7+
}:
8+
9+
let
10+
sso_domain = "sso.${baseDomain}";
11+
12+
populate_content = pkgs.writeText "populate_content.ldif" ''
13+
dn: dc=chaos, dc=jetzt
14+
dc: chaos
15+
o: chaos.jetzt
16+
objectClass: top
17+
objectclass: organization
18+
objectclass: dcObject
19+
20+
dn: ou=People,dc=chaos,dc=jetzt
21+
changetype: add
22+
objectClass: organizationalUnit
23+
24+
dn: ou=Groups,dc=chaos,dc=jetzt
25+
changetype: add
26+
objectClass: organizationalUnit
27+
'';
28+
init_ldap = pkgs.writeShellScript "init-ldap" ''
29+
export PATH=${config.services.openldap.package}/bin:$PATH
30+
export LDAP_ALREADY_EXISTS=68
31+
ldapadd -c -Y EXTERNAL -H ldapi:/// -f ${populate_content}
32+
ret=$?
33+
if [[ $ret -eq $LDAP_ALREADY_EXISTS ]]; then
34+
echo "Everything already exists"
35+
exit 0
36+
fi
37+
exit $ret
38+
'';
39+
in {
40+
sops.secrets = {
41+
"keycloak/db_password" = {};
42+
"ldap/admin_password".owner = config.services.openldap.user;
43+
};
44+
services.keycloak = {
45+
enable = true;
46+
database = {
47+
createLocally = true;
48+
host = "localhost";
49+
name = "keycloak";
50+
passwordFile = config.sops.secrets."keycloak/db_password".path;
51+
type = "postgresql";
52+
};
53+
settings = {
54+
hostname = sso_domain;
55+
http-host = "127.0.0.1";
56+
# Since we're comming from 12.x
57+
http-relative-path = "/auth";
58+
http-port = 8081;
59+
https-port = 8443;
60+
proxy = "edge";
61+
spi-theme-welcome-theme = "chaos-jetzt";
62+
spi-connections-jpa-legacy-migration-strategy = "update";
63+
};
64+
plugins = [ pkgs.keycloak-registration-captcha ];
65+
themes.cj-keycloak-theme = pkgs.cj-keycloak-theme;
66+
};
67+
68+
services.openldap = {
69+
enable = true;
70+
urlList = [
71+
"ldap:///"
72+
"ldapi:///"
73+
];
74+
settings = {
75+
attrs.olcLogLevel = [ "stats" ];
76+
children = let
77+
root_access = "dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth";
78+
in{
79+
"cn=schema".includes = [
80+
"${pkgs.openldap}/etc/schema/core.ldif"
81+
"${pkgs.openldap}/etc/schema/cosine.ldif"
82+
"${pkgs.openldap}/etc/schema/nis.ldif"
83+
"${pkgs.openldap}/etc/schema/inetorgperson.ldif"
84+
];
85+
"olcDatabase={-1}frontend".attrs = {
86+
objectClass = [ "olcDatabaseConfig" "olcFrontendConfig" ];
87+
olcDatabase = "{-1}frontend";
88+
olcSizeLimit = "500";
89+
# Allows the local root user to see the running config
90+
# ldapsearch -Y EXTERNAL -H ldapi:/// -b cn=config
91+
olcAccess = [
92+
"{0}to * by ${root_access} manage by * break"
93+
"{1}to dn.exact=\"\" by * read"
94+
"{2}to dn.base=\"cn=Subschema\" by * read"
95+
];
96+
structuralObjectClass = "olcDatabaseConfig";
97+
};
98+
/* "olcBackend={0}mdb".attrs = {
99+
objectClass = "olcBackendConfig";
100+
olcBackend = "{0}config";
101+
}; */
102+
"olcDatabase={0}config".attrs = {
103+
objectClass = "olcDatabaseConfig";
104+
olcDatabase = "{0}config";
105+
# Allows the local root user to see the running config
106+
# ldapsearch -Y EXTERNAL -H ldapi:/// -b cn=config
107+
olcAccess = [
108+
"{0}to * by ${root_access} manage by * break"
109+
];
110+
};
111+
"olcDatabase={1}mdb".attrs = {
112+
objectClass = [ "olcDatabaseConfig" "olcMdbConfig" ];
113+
114+
olcDatabase = "{1}mdb";
115+
olcDbDirectory = "/var/lib/openldap/data";
116+
117+
olcSuffix = "dc=chaos,dc=jetzt";
118+
119+
olcRootDN = "cn=admin,dc=chaos,dc=jetzt";
120+
olcRootPW.path = config.sops.secrets."ldap/admin_password".path;
121+
122+
olcLastMod = "TRUE";
123+
olcdbcheckpoint = "512 30";
124+
125+
olcDbIndex = [
126+
"objectClass eq"
127+
"cn,uid eq"
128+
"uidNumber,gidNumber eq"
129+
"member,memberUid eq"
130+
];
131+
olcAccess = [
132+
''{0}to attrs=userPassword by self write by anonymous auth by * none''
133+
''{1}to attrs=shadowLastChange by self write by * read''
134+
];
135+
};
136+
};
137+
};
138+
};
139+
140+
systemd.services.openldap.serviceConfig.ExecStartPost = [
141+
"!${init_ldap}"
142+
];
143+
144+
services.nginx = {
145+
enable = true;
146+
virtualHosts."${sso_domain}" = {
147+
enableACME = true;
148+
forceSSL = true;
149+
locations."/".proxyPass = "http://127.0.0.1:${toString config.services.keycloak.settings.http-port}";
150+
locations."= /".return = "307 ${config.services.keycloak.settings.http-relative-path}/realms/${if !isDev then "chaos-jetzt" else "dev"}/account/";
151+
};
152+
};
153+
154+
/*
155+
NOTE(@e1mo): Gripes with the keycloak module
156+
- `database.createLocaly` doesn't respect username
157+
- `initialAdminPassword` can't be a secret
158+
- `pkgs.keycloak.plugins` can't be discovered via the search
159+
*/
160+
}

0 commit comments

Comments
 (0)