diff --git a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml
index 705d28aad5dc..0902b62251f4 100644
--- a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml
+++ b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml
@@ -398,6 +398,16 @@
systemd.
+
+
+ The services.bookstack.extraConfig option
+ has been replaced by
+ services.bookstack.config which implements
+ a
+ settings-style
+ configuration.
+
+
diff --git a/nixos/doc/manual/release-notes/rl-2205.section.md b/nixos/doc/manual/release-notes/rl-2205.section.md
index 70691ccce877..447b6cabde13 100644
--- a/nixos/doc/manual/release-notes/rl-2205.section.md
+++ b/nixos/doc/manual/release-notes/rl-2205.section.md
@@ -125,6 +125,11 @@ In addition to numerous new and upgraded packages, this release has the followin
- The `services.bookstack.cacheDir` option has been removed, since the
cache directory is now handled by systemd.
+- The `services.bookstack.extraConfig` option has been replaced by
+ `services.bookstack.config` which implements a
+ [settings-style](https://github.com/NixOS/rfcs/blob/master/rfcs/0042-config-option.md)
+ configuration.
+
## Other Notable Changes {#sec-release-22.05-notable-changes}
- The option [services.redis.servers](#opt-services.redis.servers) was added
diff --git a/nixos/modules/services/web-apps/bookstack.nix b/nixos/modules/services/web-apps/bookstack.nix
index c0eec78059b5..54eaea63b6eb 100644
--- a/nixos/modules/services/web-apps/bookstack.nix
+++ b/nixos/modules/services/web-apps/bookstack.nix
@@ -28,6 +28,7 @@ let
in {
imports = [
+ (mkRemovedOptionModule [ "services" "bookstack" "extraConfig" ] "Use services.bookstack.config instead.")
(mkRemovedOptionModule [ "services" "bookstack" "cacheDir" ] "The cache directory is now handled automatically.")
];
@@ -49,8 +50,9 @@ in {
appKeyFile = mkOption {
description = ''
- A file containing the AppKey.
- Used for encryption where needed. Can be generated with head -c 32 /dev/urandom| base64
and must be prefixed with base64:.
+ A file containing the Laravel APP_KEY - a 32 character long,
+ base64 encoded key used for encryption where needed. Can be
+ generated with head -c 32 /dev/urandom | base64
.
'';
example = "/run/keys/bookstack-appkey";
type = types.path;
@@ -216,16 +218,59 @@ in {
'';
};
- extraConfig = mkOption {
- type = types.nullOr types.lines;
- default = null;
- example = ''
- ALLOWED_IFRAME_HOSTS="https://example.com"
- WKHTMLTOPDF=/home/user/bins/wkhtmltopdf
+ config = mkOption {
+ type = with types;
+ attrsOf
+ (nullOr
+ (either
+ (oneOf [
+ bool
+ int
+ port
+ path
+ str
+ ])
+ (submodule {
+ options = {
+ _secret = mkOption {
+ type = nullOr str;
+ description = ''
+ The path to a file containing the value the
+ option should be set to in the final
+ configuration file.
+ '';
+ };
+ };
+ })));
+ default = {};
+ example = literalExpression ''
+ {
+ ALLOWED_IFRAME_HOSTS = "https://example.com";
+ WKHTMLTOPDF = "/home/user/bins/wkhtmltopdf";
+ AUTH_METHOD = "oidc";
+ OIDC_NAME = "MyLogin";
+ OIDC_DISPLAY_NAME_CLAIMS = "name";
+ OIDC_CLIENT_ID = "bookstack";
+ OIDC_CLIENT_SECRET = {_secret = "/run/keys/oidc_secret"};
+ OIDC_ISSUER = "https://keycloak.example.com/auth/realms/My%20Realm";
+ OIDC_ISSUER_DISCOVER = true;
+ }
'';
description = ''
- Lines to be appended verbatim to the BookStack configuration.
- Refer to for details on supported values.
+ BookStack configuration options to set in the
+ .env file.
+
+ Refer to
+ for details on supported values.
+
+ Settings containing secret data should be set to an attribute
+ set containing the attribute _secret - a
+ string pointing to a file containing the value the option
+ should be set to. See the example to get a better picture of
+ this: in the resulting .env file, the
+ OIDC_CLIENT_SECRET key will be set to the
+ contents of the /run/keys/oidc_secret
+ file.
'';
};
@@ -242,6 +287,30 @@ in {
}
];
+ services.bookstack.config = {
+ APP_KEY._secret = cfg.appKeyFile;
+ APP_URL = cfg.appURL;
+ DB_HOST = db.host;
+ DB_PORT = db.port;
+ DB_DATABASE = db.name;
+ DB_USERNAME = db.user;
+ MAIL_DRIVER = mail.driver;
+ MAIL_FROM_NAME = mail.fromName;
+ MAIL_FROM = mail.from;
+ MAIL_HOST = mail.host;
+ MAIL_PORT = mail.port;
+ MAIL_USERNAME = mail.user;
+ MAIL_ENCRYPTION = mail.encryption;
+ DB_PASSWORD._secret = db.passwordFile;
+ MAIL_PASSWORD._secret = mail.passwordFile;
+ APP_SERVICES_CACHE = "/run/bookstack/cache/services.php";
+ APP_PACKAGES_CACHE = "/run/bookstack/cache/packages.php";
+ APP_CONFIG_CACHE = "/run/bookstack/cache/config.php";
+ APP_ROUTES_CACHE = "/run/bookstack/cache/routes-v7.php";
+ APP_EVENTS_CACHE = "/run/bookstack/cache/events.php";
+ SESSION_SECURE_COOKIE = tlsEnabled;
+ };
+
environment.systemPackages = [ artisan ];
services.mysql = mkIf db.createLocally {
@@ -305,34 +374,41 @@ in {
RuntimeDirectory = "bookstack/cache";
RuntimeDirectoryMode = 0700;
};
- script = ''
+ path = [ pkgs.replace-secret ];
+ script =
+ let
+ isSecret = v: isAttrs v && v ? _secret && isString v._secret;
+ bookstackEnvVars = lib.generators.toKeyValue {
+ mkKeyValue = lib.flip lib.generators.mkKeyValueDefault "=" {
+ mkValueString = v: with builtins;
+ if isInt v then toString v
+ else if isString v then v
+ else if true == v then "true"
+ else if false == v then "false"
+ else if isSecret v then v._secret
+ else throw "unsupported type ${typeOf v}: ${(lib.generators.toPretty {}) v}";
+ };
+ };
+ secretPaths = lib.mapAttrsToList (_: v: v._secret) (lib.filterAttrs (_: isSecret) cfg.config);
+ mkSecretReplacement = file: ''
+ replace-secret ${escapeShellArgs [ file file "${cfg.dataDir}/.env" ]}
+ '';
+ secretReplacements = lib.concatMapStrings mkSecretReplacement secretPaths;
+ filteredConfig = lib.converge (lib.filterAttrsRecursive (_: v: ! elem v [ {} null ])) cfg.config;
+ bookstackEnv = pkgs.writeText "bookstack.env" (bookstackEnvVars filteredConfig);
+ in ''
+ # error handling
+ set -euo pipefail
+
# set permissions
umask 077
+
# create .env file
- echo "
- APP_KEY=base64:$(head -n1 ${cfg.appKeyFile})
- APP_URL=${cfg.appURL}
- DB_HOST=${db.host}
- DB_PORT=${toString db.port}
- DB_DATABASE=${db.name}
- DB_USERNAME=${db.user}
- MAIL_DRIVER=${mail.driver}
- MAIL_FROM_NAME=\"${mail.fromName}\"
- MAIL_FROM=${mail.from}
- MAIL_HOST=${mail.host}
- MAIL_PORT=${toString mail.port}
- ${optionalString (mail.user != null) "MAIL_USERNAME=${mail.user};"}
- ${optionalString (mail.encryption != null) "MAIL_ENCRYPTION=${mail.encryption};"}
- ${optionalString (db.passwordFile != null) "DB_PASSWORD=$(head -n1 ${db.passwordFile})"}
- ${optionalString (mail.passwordFile != null) "MAIL_PASSWORD=$(head -n1 ${mail.passwordFile})"}
- APP_SERVICES_CACHE=/run/bookstack/cache/services.php
- APP_PACKAGES_CACHE=/run/bookstack/cache/packages.php
- APP_CONFIG_CACHE=/run/bookstack/cache/config.php
- APP_ROUTES_CACHE=/run/bookstack/cache/routes-v7.php
- APP_EVENTS_CACHE=/run/bookstack/cache/events.php
- ${optionalString (cfg.nginx.addSSL || cfg.nginx.forceSSL || cfg.nginx.onlySSL || cfg.nginx.enableACME) "SESSION_SECURE_COOKIE=true"}
- ${toString cfg.extraConfig}
- " > "${cfg.dataDir}/.env"
+ install -T -m 0600 -o ${user} ${bookstackEnv} "${cfg.dataDir}/.env"
+ ${secretReplacements}
+ if ! grep 'APP_KEY=base64:' "${cfg.dataDir}/.env" >/dev/null; then
+ sed -i 's/APP_KEY=/APP_KEY=base64:/' "${cfg.dataDir}/.env"
+ fi
# migrate db
${pkgs.php}/bin/php artisan migrate --force