···299 # Ensure essential files exist.
300 if [[ ! -f ${cfg.dataDir}/configs/znc.conf ]]; then
301 echo "No znc.conf file found in ${cfg.dataDir}. Creating one now."
302- cp --no-clobber ${cfg.configFile} ${cfg.dataDir}/configs/znc.conf
303 chmod u+rw ${cfg.dataDir}/configs/znc.conf
304- chown ${cfg.user} ${cfg.dataDir}/configs/znc.conf
305 fi
306307 if [[ ! -f ${cfg.dataDir}/znc.pem ]]; then
···299 # Ensure essential files exist.
300 if [[ ! -f ${cfg.dataDir}/configs/znc.conf ]]; then
301 echo "No znc.conf file found in ${cfg.dataDir}. Creating one now."
302+ cp --no-preserve=ownership --no-clobber ${cfg.configFile} ${cfg.dataDir}/configs/znc.conf
303 chmod u+rw ${cfg.dataDir}/configs/znc.conf
0304 fi
305306 if [[ ! -f ${cfg.dataDir}/znc.pem ]]; then
+159-123
nixos/modules/services/web-apps/keycloak.nix
···5455 frontendUrl = lib.mkOption {
56 type = lib.types.str;
057 example = "keycloak.example.com/auth";
58 description = ''
59 The public URL used as base for all frontend requests. Should
···84 '';
85 };
8687- certificatePrivateKeyBundle = lib.mkOption {
88 type = lib.types.nullOr lib.types.path;
89 default = null;
90 example = "/run/keys/ssl_cert";
91 description = ''
92- The path to a PEM formatted bundle of the private key and
93- certificate to use for TLS connections.
9495 This should be a string, not a Nix path, since Nix paths are
96 copied into the world-readable Nix store.
97 '';
98 };
99100- databaseType = lib.mkOption {
101- type = lib.types.enum [ "mysql" "postgresql" ];
102- default = "postgresql";
103- example = "mysql";
104 description = ''
105- The type of database Keycloak should connect to.
106- '';
107- };
108109- databaseHost = lib.mkOption {
110- type = lib.types.str;
111- default = "localhost";
112- description = ''
113- Hostname of the database to connect to.
114 '';
115 };
116117- databasePort =
118- let
119- dbPorts = {
120- postgresql = 5432;
121- mysql = 3306;
122- };
123- in
124- lib.mkOption {
125- type = lib.types.port;
126- default = dbPorts.${cfg.databaseType};
127- description = ''
128- Port of the database to connect to.
129- '';
130- };
131132- databaseUseSSL = lib.mkOption {
133- type = lib.types.bool;
134- default = cfg.databaseHost != "localhost";
135- description = ''
136- Whether the database connection should be secured by SSL /
137- TLS.
138- '';
139- };
140141- databaseCaCert = lib.mkOption {
142- type = lib.types.nullOr lib.types.path;
143- default = null;
144- description = ''
145- The SSL / TLS CA certificate that verifies the identity of the
146- database server.
00000000147148- Required when PostgreSQL is used and SSL is turned on.
0000000149150- For MySQL, if left at <literal>null</literal>, the default
151- Java keystore is used, which should suffice if the server
152- certificate is issued by an official CA.
153- '';
154- };
0155156- databaseCreateLocally = lib.mkOption {
157- type = lib.types.bool;
158- default = true;
159- description = ''
160- Whether a database should be automatically created on the
161- local host. Set this to false if you plan on provisioning a
162- local database yourself. This has no effect if
163- services.keycloak.databaseHost is customized.
164- '';
165- };
166167- databaseUsername = lib.mkOption {
168- type = lib.types.str;
169- default = "keycloak";
170- description = ''
171- Username to use when connecting to an external or manually
172- provisioned database; has no effect when a local database is
173- automatically provisioned.
174175- To use this with a local database, set <xref
176- linkend="opt-services.keycloak.databaseCreateLocally" /> to
177- <literal>false</literal> and create the database and user
178- manually. The database should be called
179- <literal>keycloak</literal>.
180- '';
181- };
000182183- databasePasswordFile = lib.mkOption {
184- type = lib.types.path;
185- example = "/run/keys/db_password";
186- description = ''
187- File containing the database password.
00188189- This should be a string, not a Nix path, since Nix paths are
190- copied into the world-readable Nix store.
191- '';
000000000000000192 };
193194 package = lib.mkOption {
···261 config =
262 let
263 # We only want to create a database if we're actually going to connect to it.
264- databaseActuallyCreateLocally = cfg.databaseCreateLocally && cfg.databaseHost == "localhost";
265- createLocalPostgreSQL = databaseActuallyCreateLocally && cfg.databaseType == "postgresql";
266- createLocalMySQL = databaseActuallyCreateLocally && cfg.databaseType == "mysql";
267268 mySqlCaKeystore = pkgs.runCommandNoCC "mysql-ca-keystore" {} ''
269- ${pkgs.jre}/bin/keytool -importcert -trustcacerts -alias MySQLCACert -file ${cfg.databaseCaCert} -keystore $out -storepass notsosecretpassword -noprompt
270 '';
271272 keycloakConfig' = builtins.foldl' lib.recursiveUpdate {
···282 };
283 "subsystem=datasources"."data-source=KeycloakDS" = {
284 max-pool-size = "20";
285- user-name = if databaseActuallyCreateLocally then "keycloak" else cfg.databaseUsername;
286 password = "@db-password@";
287 };
288 } [
289- (lib.optionalAttrs (cfg.databaseType == "postgresql") {
290 "subsystem=datasources" = {
291 "jdbc-driver=postgresql" = {
292 driver-module-name = "org.postgresql";
···294 driver-xa-datasource-class-name = "org.postgresql.xa.PGXADataSource";
295 };
296 "data-source=KeycloakDS" = {
297- connection-url = "jdbc:postgresql://${cfg.databaseHost}:${builtins.toString cfg.databasePort}/keycloak";
298 driver-name = "postgresql";
299- "connection-properties=ssl".value = lib.boolToString cfg.databaseUseSSL;
300- } // (lib.optionalAttrs (cfg.databaseCaCert != null) {
301- "connection-properties=sslrootcert".value = cfg.databaseCaCert;
302 "connection-properties=sslmode".value = "verify-ca";
303 });
304 };
305 })
306- (lib.optionalAttrs (cfg.databaseType == "mysql") {
307 "subsystem=datasources" = {
308 "jdbc-driver=mysql" = {
309 driver-module-name = "com.mysql";
···311 driver-class-name = "com.mysql.jdbc.Driver";
312 };
313 "data-source=KeycloakDS" = {
314- connection-url = "jdbc:mysql://${cfg.databaseHost}:${builtins.toString cfg.databasePort}/keycloak";
315 driver-name = "mysql";
316- "connection-properties=useSSL".value = lib.boolToString cfg.databaseUseSSL;
317- "connection-properties=requireSSL".value = lib.boolToString cfg.databaseUseSSL;
318- "connection-properties=verifyServerCertificate".value = lib.boolToString cfg.databaseUseSSL;
319 "connection-properties=characterEncoding".value = "UTF-8";
320 valid-connection-checker-class-name = "org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLValidConnectionChecker";
321 validate-on-match = true;
322 exception-sorter-class-name = "org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLExceptionSorter";
323- } // (lib.optionalAttrs (cfg.databaseCaCert != null) {
324 "connection-properties=trustCertificateKeyStoreUrl".value = "file:${mySqlCaKeystore}";
325 "connection-properties=trustCertificateKeyStorePassword".value = "notsosecretpassword";
326 });
327 };
328 })
329- (lib.optionalAttrs (cfg.certificatePrivateKeyBundle != null) {
330 "socket-binding-group=standard-sockets"."socket-binding=https".port = cfg.httpsPort;
331 "core-service=management"."security-realm=UndertowRealm"."server-identity=ssl" = {
332 keystore-path = "/run/keycloak/ssl/certificate_private_key_bundle.p12";
···537538 jbossCliScript = pkgs.writeText "jboss-cli-script" (mkJbossScript keycloakConfig');
539540- keycloakConfig = pkgs.runCommandNoCC "keycloak-config" {} ''
00541 export JBOSS_BASE_DIR="$(pwd -P)";
542 export JBOSS_MODULEPATH="${cfg.package}/modules";
543 export JBOSS_LOG_DIR="$JBOSS_BASE_DIR/log";
···547548 mkdir -p {deployments,ssl}
549550- "${cfg.package}/bin/standalone.sh"&
551552 attempt=1
553 max_attempts=30
554- while ! ${cfg.package}/bin/jboss-cli.sh --connect ':read-attribute(name=server-state)'; do
555 if [[ "$attempt" == "$max_attempts" ]]; then
556 echo "ERROR: Could not connect to Keycloak after $attempt attempts! Failing.." >&2
557 exit 1
···561 (( attempt++ ))
562 done
563564- ${cfg.package}/bin/jboss-cli.sh --connect --file=${jbossCliScript} --echo-command
565566 cp configuration/standalone.xml $out
567 '';
···570571 assertions = [
572 {
573- assertion = (cfg.databaseUseSSL && cfg.databaseType == "postgresql") -> (cfg.databaseCaCert != null);
574- message = "A CA certificate must be specified (in 'services.keycloak.databaseCaCert') when PostgreSQL is used with SSL";
575 }
576 ];
577···581 after = [ "postgresql.service" ];
582 before = [ "keycloak.service" ];
583 bindsTo = [ "postgresql.service" ];
0584 serviceConfig = {
585 Type = "oneshot";
586 RemainAfterExit = true;
···588 Group = "postgres";
589 };
590 script = ''
591- set -eu
0592593- PSQL=${config.services.postgresql.package}/bin/psql
0594595- db_password="$(<'${cfg.databasePasswordFile}')"
596- $PSQL -tAc "SELECT 1 FROM pg_roles WHERE rolname='keycloak'" | grep -q 1 || $PSQL -tAc "CREATE ROLE keycloak WITH LOGIN PASSWORD '$db_password' CREATEDB"
597- $PSQL -tAc "SELECT 1 FROM pg_database WHERE datname = 'keycloak'" | grep -q 1 || $PSQL -tAc 'CREATE DATABASE "keycloak" OWNER "keycloak"'
598 '';
599 };
600···602 after = [ "mysql.service" ];
603 before = [ "keycloak.service" ];
604 bindsTo = [ "mysql.service" ];
0605 serviceConfig = {
606 Type = "oneshot";
607 RemainAfterExit = true;
···609 Group = config.services.mysql.group;
610 };
611 script = ''
612- set -eu
0613614- db_password="$(<'${cfg.databasePasswordFile}')"
615 ( echo "CREATE USER IF NOT EXISTS 'keycloak'@'localhost' IDENTIFIED BY '$db_password';"
616 echo "CREATE DATABASE keycloak CHARACTER SET utf8 COLLATE utf8_unicode_ci;"
617 echo "GRANT ALL PRIVILEGES ON keycloak.* TO 'keycloak'@'localhost';"
618- ) | ${config.services.mysql.package}/bin/mysql -N
619 '';
620 };
621···634 bindsTo = databaseServices;
635 wantedBy = [ "multi-user.target" ];
636 path = with pkgs; [
00637 replace-secret
638 ];
639 environment = {
···644 serviceConfig = {
645 ExecStartPre = let
646 startPreFullPrivileges = ''
647- set -eu
0648649- install -T -m 0400 -o keycloak -g keycloak '${cfg.databasePasswordFile}' /run/keycloak/secrets/db_password
650- '' + lib.optionalString (cfg.certificatePrivateKeyBundle != null) ''
651- install -T -m 0400 -o keycloak -g keycloak '${cfg.certificatePrivateKeyBundle}' /run/keycloak/secrets/ssl_cert_pk_bundle
000652 '';
653 startPre = ''
654- set -eu
000655656 install -m 0600 ${cfg.package}/standalone/configuration/*.properties /run/keycloak/configuration
657 install -T -m 0600 ${keycloakConfig} /run/keycloak/configuration/standalone.xml
···659 replace-secret '@db-password@' '/run/keycloak/secrets/db_password' /run/keycloak/configuration/standalone.xml
660661 export JAVA_OPTS=-Djboss.server.config.user.dir=/run/keycloak/configuration
662- ${cfg.package}/bin/add-user-keycloak.sh -u admin -p '${cfg.initialAdminPassword}'
663- '' + lib.optionalString (cfg.certificatePrivateKeyBundle != null) ''
664 pushd /run/keycloak/ssl/
665- cat /run/keycloak/secrets/ssl_cert_pk_bundle <(echo) /etc/ssl/certs/ca-certificates.crt > allcerts.pem
666- ${pkgs.openssl}/bin/openssl pkcs12 -export -in /run/keycloak/secrets/ssl_cert_pk_bundle -chain \
667- -name "${cfg.frontendUrl}" -out certificate_private_key_bundle.p12 \
668- -CAfile allcerts.pem -passout pass:notsosecretpassword
000669 popd
670 '';
671 in [
···697 };
698699 meta.doc = ./keycloak.xml;
0700}
···5455 frontendUrl = lib.mkOption {
56 type = lib.types.str;
57+ apply = x: if lib.hasSuffix "/" x then x else x + "/";
58 example = "keycloak.example.com/auth";
59 description = ''
60 The public URL used as base for all frontend requests. Should
···85 '';
86 };
8788+ sslCertificate = lib.mkOption {
89 type = lib.types.nullOr lib.types.path;
90 default = null;
91 example = "/run/keys/ssl_cert";
92 description = ''
93+ The path to a PEM formatted certificate to use for TLS/SSL
94+ connections.
9596 This should be a string, not a Nix path, since Nix paths are
97 copied into the world-readable Nix store.
98 '';
99 };
100101+ sslCertificateKey = lib.mkOption {
102+ type = lib.types.nullOr lib.types.path;
103+ default = null;
104+ example = "/run/keys/ssl_key";
105 description = ''
106+ The path to a PEM formatted private key to use for TLS/SSL
107+ connections.
0108109+ This should be a string, not a Nix path, since Nix paths are
110+ copied into the world-readable Nix store.
000111 '';
112 };
113114+ database = {
115+ type = lib.mkOption {
116+ type = lib.types.enum [ "mysql" "postgresql" ];
117+ default = "postgresql";
118+ example = "mysql";
119+ description = ''
120+ The type of database Keycloak should connect to.
121+ '';
122+ };
00000123124+ host = lib.mkOption {
125+ type = lib.types.str;
126+ default = "localhost";
127+ description = ''
128+ Hostname of the database to connect to.
129+ '';
130+ };
0131132+ port =
133+ let
134+ dbPorts = {
135+ postgresql = 5432;
136+ mysql = 3306;
137+ };
138+ in
139+ lib.mkOption {
140+ type = lib.types.port;
141+ default = dbPorts.${cfg.database.type};
142+ description = ''
143+ Port of the database to connect to.
144+ '';
145+ };
146147+ useSSL = lib.mkOption {
148+ type = lib.types.bool;
149+ default = cfg.database.host != "localhost";
150+ description = ''
151+ Whether the database connection should be secured by SSL /
152+ TLS.
153+ '';
154+ };
155156+ caCert = lib.mkOption {
157+ type = lib.types.nullOr lib.types.path;
158+ default = null;
159+ description = ''
160+ The SSL / TLS CA certificate that verifies the identity of the
161+ database server.
162163+ Required when PostgreSQL is used and SSL is turned on.
000000000164165+ For MySQL, if left at <literal>null</literal>, the default
166+ Java keystore is used, which should suffice if the server
167+ certificate is issued by an official CA.
168+ '';
169+ };
00170171+ createLocally = lib.mkOption {
172+ type = lib.types.bool;
173+ default = true;
174+ description = ''
175+ Whether a database should be automatically created on the
176+ local host. Set this to false if you plan on provisioning a
177+ local database yourself. This has no effect if
178+ services.keycloak.database.host is customized.
179+ '';
180+ };
181182+ username = lib.mkOption {
183+ type = lib.types.str;
184+ default = "keycloak";
185+ description = ''
186+ Username to use when connecting to an external or manually
187+ provisioned database; has no effect when a local database is
188+ automatically provisioned.
189190+ To use this with a local database, set <xref
191+ linkend="opt-services.keycloak.database.createLocally" /> to
192+ <literal>false</literal> and create the database and user
193+ manually. The database should be called
194+ <literal>keycloak</literal>.
195+ '';
196+ };
197+198+ passwordFile = lib.mkOption {
199+ type = lib.types.path;
200+ example = "/run/keys/db_password";
201+ description = ''
202+ File containing the database password.
203+204+ This should be a string, not a Nix path, since Nix paths are
205+ copied into the world-readable Nix store.
206+ '';
207+ };
208 };
209210 package = lib.mkOption {
···277 config =
278 let
279 # We only want to create a database if we're actually going to connect to it.
280+ databaseActuallyCreateLocally = cfg.database.createLocally && cfg.database.host == "localhost";
281+ createLocalPostgreSQL = databaseActuallyCreateLocally && cfg.database.type == "postgresql";
282+ createLocalMySQL = databaseActuallyCreateLocally && cfg.database.type == "mysql";
283284 mySqlCaKeystore = pkgs.runCommandNoCC "mysql-ca-keystore" {} ''
285+ ${pkgs.jre}/bin/keytool -importcert -trustcacerts -alias MySQLCACert -file ${cfg.database.caCert} -keystore $out -storepass notsosecretpassword -noprompt
286 '';
287288 keycloakConfig' = builtins.foldl' lib.recursiveUpdate {
···298 };
299 "subsystem=datasources"."data-source=KeycloakDS" = {
300 max-pool-size = "20";
301+ user-name = if databaseActuallyCreateLocally then "keycloak" else cfg.database.username;
302 password = "@db-password@";
303 };
304 } [
305+ (lib.optionalAttrs (cfg.database.type == "postgresql") {
306 "subsystem=datasources" = {
307 "jdbc-driver=postgresql" = {
308 driver-module-name = "org.postgresql";
···310 driver-xa-datasource-class-name = "org.postgresql.xa.PGXADataSource";
311 };
312 "data-source=KeycloakDS" = {
313+ connection-url = "jdbc:postgresql://${cfg.database.host}:${builtins.toString cfg.database.port}/keycloak";
314 driver-name = "postgresql";
315+ "connection-properties=ssl".value = lib.boolToString cfg.database.useSSL;
316+ } // (lib.optionalAttrs (cfg.database.caCert != null) {
317+ "connection-properties=sslrootcert".value = cfg.database.caCert;
318 "connection-properties=sslmode".value = "verify-ca";
319 });
320 };
321 })
322+ (lib.optionalAttrs (cfg.database.type == "mysql") {
323 "subsystem=datasources" = {
324 "jdbc-driver=mysql" = {
325 driver-module-name = "com.mysql";
···327 driver-class-name = "com.mysql.jdbc.Driver";
328 };
329 "data-source=KeycloakDS" = {
330+ connection-url = "jdbc:mysql://${cfg.database.host}:${builtins.toString cfg.database.port}/keycloak";
331 driver-name = "mysql";
332+ "connection-properties=useSSL".value = lib.boolToString cfg.database.useSSL;
333+ "connection-properties=requireSSL".value = lib.boolToString cfg.database.useSSL;
334+ "connection-properties=verifyServerCertificate".value = lib.boolToString cfg.database.useSSL;
335 "connection-properties=characterEncoding".value = "UTF-8";
336 valid-connection-checker-class-name = "org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLValidConnectionChecker";
337 validate-on-match = true;
338 exception-sorter-class-name = "org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLExceptionSorter";
339+ } // (lib.optionalAttrs (cfg.database.caCert != null) {
340 "connection-properties=trustCertificateKeyStoreUrl".value = "file:${mySqlCaKeystore}";
341 "connection-properties=trustCertificateKeyStorePassword".value = "notsosecretpassword";
342 });
343 };
344 })
345+ (lib.optionalAttrs (cfg.sslCertificate != null && cfg.sslCertificateKey != null) {
346 "socket-binding-group=standard-sockets"."socket-binding=https".port = cfg.httpsPort;
347 "core-service=management"."security-realm=UndertowRealm"."server-identity=ssl" = {
348 keystore-path = "/run/keycloak/ssl/certificate_private_key_bundle.p12";
···553554 jbossCliScript = pkgs.writeText "jboss-cli-script" (mkJbossScript keycloakConfig');
555556+ keycloakConfig = pkgs.runCommandNoCC "keycloak-config" {
557+ nativeBuildInputs = [ cfg.package ];
558+ } ''
559 export JBOSS_BASE_DIR="$(pwd -P)";
560 export JBOSS_MODULEPATH="${cfg.package}/modules";
561 export JBOSS_LOG_DIR="$JBOSS_BASE_DIR/log";
···565566 mkdir -p {deployments,ssl}
567568+ standalone.sh&
569570 attempt=1
571 max_attempts=30
572+ while ! jboss-cli.sh --connect ':read-attribute(name=server-state)'; do
573 if [[ "$attempt" == "$max_attempts" ]]; then
574 echo "ERROR: Could not connect to Keycloak after $attempt attempts! Failing.." >&2
575 exit 1
···579 (( attempt++ ))
580 done
581582+ jboss-cli.sh --connect --file=${jbossCliScript} --echo-command
583584 cp configuration/standalone.xml $out
585 '';
···588589 assertions = [
590 {
591+ assertion = (cfg.database.useSSL && cfg.database.type == "postgresql") -> (cfg.database.caCert != null);
592+ message = "A CA certificate must be specified (in 'services.keycloak.database.caCert') when PostgreSQL is used with SSL";
593 }
594 ];
595···599 after = [ "postgresql.service" ];
600 before = [ "keycloak.service" ];
601 bindsTo = [ "postgresql.service" ];
602+ path = [ config.services.postgresql.package ];
603 serviceConfig = {
604 Type = "oneshot";
605 RemainAfterExit = true;
···607 Group = "postgres";
608 };
609 script = ''
610+ set -o errexit -o pipefail -o nounset -o errtrace
611+ shopt -s inherit_errexit
612613+ create_role="$(mktemp)"
614+ trap 'rm -f "$create_role"' ERR EXIT
615616+ echo "CREATE ROLE keycloak WITH LOGIN PASSWORD '$(<'${cfg.database.passwordFile}')' CREATEDB" > "$create_role"
617+ psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='keycloak'" | grep -q 1 || psql -tA --file="$create_role"
618+ psql -tAc "SELECT 1 FROM pg_database WHERE datname = 'keycloak'" | grep -q 1 || psql -tAc 'CREATE DATABASE "keycloak" OWNER "keycloak"'
619 '';
620 };
621···623 after = [ "mysql.service" ];
624 before = [ "keycloak.service" ];
625 bindsTo = [ "mysql.service" ];
626+ path = [ config.services.mysql.package ];
627 serviceConfig = {
628 Type = "oneshot";
629 RemainAfterExit = true;
···631 Group = config.services.mysql.group;
632 };
633 script = ''
634+ set -o errexit -o pipefail -o nounset -o errtrace
635+ shopt -s inherit_errexit
636637+ db_password="$(<'${cfg.database.passwordFile}')"
638 ( echo "CREATE USER IF NOT EXISTS 'keycloak'@'localhost' IDENTIFIED BY '$db_password';"
639 echo "CREATE DATABASE keycloak CHARACTER SET utf8 COLLATE utf8_unicode_ci;"
640 echo "GRANT ALL PRIVILEGES ON keycloak.* TO 'keycloak'@'localhost';"
641+ ) | mysql -N
642 '';
643 };
644···657 bindsTo = databaseServices;
658 wantedBy = [ "multi-user.target" ];
659 path = with pkgs; [
660+ cfg.package
661+ openssl
662 replace-secret
663 ];
664 environment = {
···669 serviceConfig = {
670 ExecStartPre = let
671 startPreFullPrivileges = ''
672+ set -o errexit -o pipefail -o nounset -o errtrace
673+ shopt -s inherit_errexit
674675+ umask u=rwx,g=,o=
676+677+ install -T -m 0400 -o keycloak -g keycloak '${cfg.database.passwordFile}' /run/keycloak/secrets/db_password
678+ '' + lib.optionalString (cfg.sslCertificate != null && cfg.sslCertificateKey != null) ''
679+ install -T -m 0400 -o keycloak -g keycloak '${cfg.sslCertificate}' /run/keycloak/secrets/ssl_cert
680+ install -T -m 0400 -o keycloak -g keycloak '${cfg.sslCertificateKey}' /run/keycloak/secrets/ssl_key
681 '';
682 startPre = ''
683+ set -o errexit -o pipefail -o nounset -o errtrace
684+ shopt -s inherit_errexit
685+686+ umask u=rwx,g=,o=
687688 install -m 0600 ${cfg.package}/standalone/configuration/*.properties /run/keycloak/configuration
689 install -T -m 0600 ${keycloakConfig} /run/keycloak/configuration/standalone.xml
···691 replace-secret '@db-password@' '/run/keycloak/secrets/db_password' /run/keycloak/configuration/standalone.xml
692693 export JAVA_OPTS=-Djboss.server.config.user.dir=/run/keycloak/configuration
694+ add-user-keycloak.sh -u admin -p '${cfg.initialAdminPassword}'
695+ '' + lib.optionalString (cfg.sslCertificate != null && cfg.sslCertificateKey != null) ''
696 pushd /run/keycloak/ssl/
697+ cat /run/keycloak/secrets/ssl_cert <(echo) \
698+ /run/keycloak/secrets/ssl_key <(echo) \
699+ /etc/ssl/certs/ca-certificates.crt \
700+ > allcerts.pem
701+ openssl pkcs12 -export -in /run/keycloak/secrets/ssl_cert -inkey /run/keycloak/secrets/ssl_key -chain \
702+ -name "${cfg.frontendUrl}" -out certificate_private_key_bundle.p12 \
703+ -CAfile allcerts.pem -passout pass:notsosecretpassword
704 popd
705 '';
706 in [
···732 };
733734 meta.doc = ./keycloak.xml;
735+ meta.maintainers = [ lib.maintainers.talyz ];
736}
+19-18
nixos/modules/services/web-apps/keycloak.xml
···41 <productname>PostgreSQL</productname> or
42 <productname>MySQL</productname>. Which one is used can be
43 configured in <xref
44- linkend="opt-services.keycloak.databaseType" />. The selected
45 database will automatically be enabled and a database and role
46 created unless <xref
47- linkend="opt-services.keycloak.databaseHost" /> is changed from
48 its default of <literal>localhost</literal> or <xref
49- linkend="opt-services.keycloak.databaseCreateLocally" /> is set
50 to <literal>false</literal>.
51 </para>
5253 <para>
54 External database access can also be configured by setting
55- <xref linkend="opt-services.keycloak.databaseHost" />, <xref
56- linkend="opt-services.keycloak.databaseUsername" />, <xref
57- linkend="opt-services.keycloak.databaseUseSSL" /> and <xref
58- linkend="opt-services.keycloak.databaseCaCert" /> as
59 appropriate. Note that you need to manually create a database
60 called <literal>keycloak</literal> and allow the configured
61 database user full access to it.
62 </para>
6364 <para>
65- <xref linkend="opt-services.keycloak.databasePasswordFile" />
66 must be set to the path to a file containing the password used
67- to log in to the database. If <xref linkend="opt-services.keycloak.databaseHost" />
68- and <xref linkend="opt-services.keycloak.databaseCreateLocally" />
69 are kept at their defaults, the database role
70 <literal>keycloak</literal> with that password is provisioned
71 on the local database instance.
···115 </para>
116117 <para>
118- For HTTPS support, a TLS certificate and private key is
119- required. They should be <link
120 xlink:href="https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail">PEM
121- formatted</link> and concatenated into a single file. The path
122- to this file should be configured in
123- <xref linkend="opt-services.keycloak.certificatePrivateKeyBundle" />.
124 </para>
125126 <warning>
127 <para>
128- The path should be provided as a string, not a Nix path,
129 since Nix paths are copied into the world readable Nix store.
130 </para>
131 </warning>
···195 <link linkend="opt-services.keycloak.initialAdminPassword">initialAdminPassword</link> = "e6Wcm0RrtegMEHl"; # change on first login
196 <link linkend="opt-services.keycloak.frontendUrl">frontendUrl</link> = "https://keycloak.example.com/auth";
197 <link linkend="opt-services.keycloak.forceBackendUrlToFrontendUrl">forceBackendUrlToFrontendUrl</link> = true;
198- <link linkend="opt-services.keycloak.certificatePrivateKeyBundle">certificatePrivateKeyBundle</link> = "/run/keys/ssl_cert";
199- <link linkend="opt-services.keycloak.databasePasswordFile">databasePasswordFile</link> = "/run/keys/db_password";
0200};
201</programlisting>
202 </para>
···41 <productname>PostgreSQL</productname> or
42 <productname>MySQL</productname>. Which one is used can be
43 configured in <xref
44+ linkend="opt-services.keycloak.database.type" />. The selected
45 database will automatically be enabled and a database and role
46 created unless <xref
47+ linkend="opt-services.keycloak.database.host" /> is changed from
48 its default of <literal>localhost</literal> or <xref
49+ linkend="opt-services.keycloak.database.createLocally" /> is set
50 to <literal>false</literal>.
51 </para>
5253 <para>
54 External database access can also be configured by setting
55+ <xref linkend="opt-services.keycloak.database.host" />, <xref
56+ linkend="opt-services.keycloak.database.username" />, <xref
57+ linkend="opt-services.keycloak.database.useSSL" /> and <xref
58+ linkend="opt-services.keycloak.database.caCert" /> as
59 appropriate. Note that you need to manually create a database
60 called <literal>keycloak</literal> and allow the configured
61 database user full access to it.
62 </para>
6364 <para>
65+ <xref linkend="opt-services.keycloak.database.passwordFile" />
66 must be set to the path to a file containing the password used
67+ to log in to the database. If <xref linkend="opt-services.keycloak.database.host" />
68+ and <xref linkend="opt-services.keycloak.database.createLocally" />
69 are kept at their defaults, the database role
70 <literal>keycloak</literal> with that password is provisioned
71 on the local database instance.
···115 </para>
116117 <para>
118+ HTTPS support requires a TLS/SSL certificate and a private key,
119+ both <link
120 xlink:href="https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail">PEM
121+ formatted</link>. Their paths should be set through <xref
122+ linkend="opt-services.keycloak.sslCertificate" /> and <xref
123+ linkend="opt-services.keycloak.sslCertificateKey" />.
124 </para>
125126 <warning>
127 <para>
128+ The paths should be provided as a strings, not a Nix paths,
129 since Nix paths are copied into the world readable Nix store.
130 </para>
131 </warning>
···195 <link linkend="opt-services.keycloak.initialAdminPassword">initialAdminPassword</link> = "e6Wcm0RrtegMEHl"; # change on first login
196 <link linkend="opt-services.keycloak.frontendUrl">frontendUrl</link> = "https://keycloak.example.com/auth";
197 <link linkend="opt-services.keycloak.forceBackendUrlToFrontendUrl">forceBackendUrlToFrontendUrl</link> = true;
198+ <link linkend="opt-services.keycloak.sslCertificate">sslCertificate</link> = "/run/keys/ssl_cert";
199+ <link linkend="opt-services.keycloak.sslCertificateKey">sslCertificateKey</link> = "/run/keys/ssl_key";
200+ <link linkend="opt-services.keycloak.database.passwordFile">database.passwordFile</link> = "/run/keys/db_password";
201};
202</programlisting>
203 </para>
+3-3
nixos/modules/services/web-apps/mastodon.nix
···448 join pg_namespace s on s.oid = c.relnamespace \
449 where s.nspname not in ('pg_catalog', 'pg_toast', 'information_schema') \
450 and s.nspname not like 'pg_temp%';" | sed -n 3p` -eq 0 ]; then
451- SAFETY_ASSURED=1 rake db:schema:load
452- rake db:seed
453 else
454- rake db:migrate
455 fi
456 '';
457 path = [ cfg.package pkgs.postgresql ];
···448 join pg_namespace s on s.oid = c.relnamespace \
449 where s.nspname not in ('pg_catalog', 'pg_toast', 'information_schema') \
450 and s.nspname not like 'pg_temp%';" | sed -n 3p` -eq 0 ]; then
451+ SAFETY_ASSURED=1 rails db:schema:load
452+ rails db:seed
453 else
454+ rails db:migrate
455 fi
456 '';
457 path = [ cfg.package pkgs.postgresql ];