2020-04-24 23:36:52 +00:00
|
|
|
{ config, lib, pkgs, ... }:
|
|
|
|
|
|
|
|
let
|
2023-11-16 04:20:00 +00:00
|
|
|
inherit (lib) mkOption types mdDoc literalExpression;
|
2023-08-04 22:07:22 +00:00
|
|
|
|
2020-12-25 13:55:36 +00:00
|
|
|
cfg = config.services.hedgedoc;
|
|
|
|
|
2021-02-13 14:23:35 +00:00
|
|
|
# 21.03 will not be an official release - it was instead 21.05. This
|
|
|
|
# versionAtLeast statement remains set to 21.03 for backwards compatibility.
|
|
|
|
# See https://github.com/NixOS/nixpkgs/pull/108899 and
|
|
|
|
# https://github.com/NixOS/rfcs/blob/master/rfcs/0080-nixos-release-schedule.md.
|
2023-11-16 04:20:00 +00:00
|
|
|
name = if lib.versionAtLeast config.system.stateVersion "21.03" then
|
|
|
|
"hedgedoc"
|
|
|
|
else
|
|
|
|
"codimd";
|
2020-04-24 23:36:52 +00:00
|
|
|
|
2023-11-16 04:20:00 +00:00
|
|
|
settingsFormat = pkgs.formats.json { };
|
2020-04-24 23:36:52 +00:00
|
|
|
in
|
|
|
|
{
|
2023-11-16 04:20:00 +00:00
|
|
|
meta.maintainers = with lib.maintainers; [ SuperSandro2000 h7x4 ];
|
|
|
|
|
2020-12-25 13:55:36 +00:00
|
|
|
imports = [
|
2023-11-16 04:20:00 +00:00
|
|
|
(lib.mkRenamedOptionModule [ "services" "codimd" ] [ "services" "hedgedoc" ])
|
|
|
|
(lib.mkRenamedOptionModule [ "services" "hedgedoc" "configuration" ] [ "services" "hedgedoc" "settings" ])
|
|
|
|
(lib.mkRenamedOptionModule [ "services" "hedgedoc" "groups" ] [ "users" "users" "hedgedoc" "extraGroups" ])
|
|
|
|
(lib.mkRemovedOptionModule [ "services" "hedgedoc" "workDir" ] ''
|
|
|
|
This option has been removed in favor of systemd managing the state directory.
|
|
|
|
|
|
|
|
If you have set this option without specifying `services.settings.uploadsDir`,
|
|
|
|
please move these files to `/var/lib/hedgedoc/uploads`, or set the option to point
|
|
|
|
at the correct location.
|
|
|
|
'')
|
2020-12-25 13:55:36 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
options.services.hedgedoc = {
|
2024-01-02 11:29:13 +00:00
|
|
|
package = lib.mkPackageOption pkgs "hedgedoc" { };
|
2023-11-16 04:20:00 +00:00
|
|
|
enable = lib.mkEnableOption (mdDoc "the HedgeDoc Markdown Editor");
|
2020-04-24 23:36:52 +00:00
|
|
|
|
2023-11-16 04:20:00 +00:00
|
|
|
settings = mkOption {
|
|
|
|
type = types.submodule {
|
|
|
|
freeformType = settingsFormat.type;
|
|
|
|
options = {
|
|
|
|
domain = mkOption {
|
|
|
|
type = with types; nullOr str;
|
|
|
|
default = null;
|
|
|
|
example = "hedgedoc.org";
|
|
|
|
description = mdDoc ''
|
|
|
|
Domain to use for website.
|
2020-04-24 23:36:52 +00:00
|
|
|
|
2023-11-16 04:20:00 +00:00
|
|
|
This is useful if you are trying to run hedgedoc behind
|
|
|
|
a reverse proxy.
|
|
|
|
'';
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
2023-11-16 04:20:00 +00:00
|
|
|
urlPath = mkOption {
|
|
|
|
type = with types; nullOr str;
|
|
|
|
default = null;
|
|
|
|
example = "hedgedoc";
|
|
|
|
description = mdDoc ''
|
|
|
|
URL path for the website.
|
|
|
|
|
|
|
|
This is useful if you are hosting hedgedoc on a path like
|
|
|
|
`www.example.com/hedgedoc`
|
|
|
|
'';
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
2023-11-16 04:20:00 +00:00
|
|
|
host = mkOption {
|
|
|
|
type = with types; nullOr str;
|
|
|
|
default = "localhost";
|
|
|
|
description = mdDoc ''
|
|
|
|
Address to listen on.
|
|
|
|
'';
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
2023-11-16 04:20:00 +00:00
|
|
|
port = mkOption {
|
|
|
|
type = types.port;
|
|
|
|
default = 3000;
|
|
|
|
example = 80;
|
|
|
|
description = mdDoc ''
|
|
|
|
Port to listen on.
|
|
|
|
'';
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
2023-11-16 04:20:00 +00:00
|
|
|
path = mkOption {
|
|
|
|
type = with types; nullOr path;
|
|
|
|
default = null;
|
|
|
|
example = "/run/hedgedoc/hedgedoc.sock";
|
|
|
|
description = mdDoc ''
|
|
|
|
Path to UNIX domain socket to listen on
|
|
|
|
|
|
|
|
::: {.note}
|
|
|
|
If specified, {option}`host` and {option}`port` will be ignored.
|
|
|
|
:::
|
|
|
|
'';
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
2023-11-16 04:20:00 +00:00
|
|
|
protocolUseSSL = mkOption {
|
|
|
|
type = types.bool;
|
|
|
|
default = false;
|
|
|
|
example = true;
|
|
|
|
description = mdDoc ''
|
|
|
|
Use `https://` for all links.
|
|
|
|
|
|
|
|
This is useful if you are trying to run hedgedoc behind
|
|
|
|
a reverse proxy.
|
|
|
|
|
|
|
|
::: {.note}
|
|
|
|
Only applied if {option}`domain` is set.
|
|
|
|
:::
|
|
|
|
'';
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
2023-11-16 04:20:00 +00:00
|
|
|
allowOrigin = mkOption {
|
|
|
|
type = with types; listOf str;
|
|
|
|
default = with cfg.settings; [ host ] ++ lib.optionals (domain != null) [ domain ];
|
|
|
|
defaultText = literalExpression ''
|
|
|
|
with config.services.hedgedoc.settings; [ host ] ++ lib.optionals (domain != null) [ domain ]
|
|
|
|
'';
|
|
|
|
example = [ "localhost" "hedgedoc.org" ];
|
|
|
|
description = mdDoc ''
|
|
|
|
List of domains to whitelist.
|
|
|
|
'';
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
2023-11-16 04:20:00 +00:00
|
|
|
db = mkOption {
|
|
|
|
type = types.attrs;
|
|
|
|
default = {
|
|
|
|
dialect = "sqlite";
|
|
|
|
storage = "/var/lib/${name}/db.sqlite";
|
|
|
|
};
|
|
|
|
defaultText = literalExpression ''
|
|
|
|
{
|
|
|
|
dialect = "sqlite";
|
|
|
|
storage = "/var/lib/hedgedoc/db.sqlite";
|
|
|
|
}
|
|
|
|
'';
|
|
|
|
example = literalExpression ''
|
|
|
|
db = {
|
|
|
|
username = "hedgedoc";
|
|
|
|
database = "hedgedoc";
|
|
|
|
host = "localhost:5432";
|
|
|
|
# or via socket
|
|
|
|
# host = "/run/postgresql";
|
|
|
|
dialect = "postgresql";
|
|
|
|
};
|
|
|
|
'';
|
|
|
|
description = mdDoc ''
|
|
|
|
Specify the configuration for sequelize.
|
|
|
|
HedgeDoc supports `mysql`, `postgres`, `sqlite` and `mssql`.
|
|
|
|
See <https://sequelize.readthedocs.io/en/v3/>
|
|
|
|
for more information.
|
|
|
|
|
|
|
|
::: {.note}
|
|
|
|
The relevant parts will be overriden if you set {option}`dbURL`.
|
|
|
|
:::
|
|
|
|
'';
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
2023-11-16 04:20:00 +00:00
|
|
|
useSSL = mkOption {
|
|
|
|
type = types.bool;
|
|
|
|
default = false;
|
|
|
|
description = mdDoc ''
|
|
|
|
Enable to use SSL server.
|
|
|
|
|
|
|
|
::: {.note}
|
|
|
|
This will also enable {option}`protocolUseSSL`.
|
|
|
|
|
|
|
|
It will also require you to set the following:
|
|
|
|
|
|
|
|
- {option}`sslKeyPath`
|
|
|
|
- {option}`sslCertPath`
|
|
|
|
- {option}`sslCAPath`
|
|
|
|
- {option}`dhParamPath`
|
|
|
|
:::
|
|
|
|
'';
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
2023-11-16 04:20:00 +00:00
|
|
|
uploadsPath = mkOption {
|
|
|
|
type = types.path;
|
|
|
|
default = "/var/lib/${name}/uploads";
|
|
|
|
defaultText = "/var/lib/hedgedoc/uploads";
|
|
|
|
description = mdDoc ''
|
|
|
|
Directory for storing uploaded images.
|
|
|
|
'';
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
2023-11-16 04:20:00 +00:00
|
|
|
|
|
|
|
# Declared because we change the default to false.
|
|
|
|
allowGravatar = mkOption {
|
|
|
|
type = types.bool;
|
|
|
|
default = false;
|
|
|
|
example = true;
|
|
|
|
description = mdDoc ''
|
|
|
|
Whether to enable [Libravatar](https://wiki.libravatar.org/) as
|
|
|
|
profile picture source on your instance.
|
|
|
|
|
|
|
|
Despite the naming of the setting, Hedgedoc replaced Gravatar
|
|
|
|
with Libravatar in [CodiMD 1.4.0](https://hedgedoc.org/releases/1.4.0/)
|
|
|
|
'';
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
2023-11-16 04:20:00 +00:00
|
|
|
};
|
2022-07-14 12:49:19 +00:00
|
|
|
};
|
2023-11-16 04:20:00 +00:00
|
|
|
|
|
|
|
description = mdDoc ''
|
2022-07-14 12:49:19 +00:00
|
|
|
HedgeDoc configuration, see
|
2022-08-12 12:06:08 +00:00
|
|
|
<https://docs.hedgedoc.org/configuration/>
|
2022-07-14 12:49:19 +00:00
|
|
|
for documentation.
|
|
|
|
'';
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
2020-09-25 04:45:31 +00:00
|
|
|
|
|
|
|
environmentFile = mkOption {
|
|
|
|
type = with types; nullOr path;
|
|
|
|
default = null;
|
2020-12-25 13:55:36 +00:00
|
|
|
example = "/var/lib/hedgedoc/hedgedoc.env";
|
2023-11-16 04:20:00 +00:00
|
|
|
description = mdDoc ''
|
2022-09-09 14:08:57 +00:00
|
|
|
Environment file as defined in {manpage}`systemd.exec(5)`.
|
2020-09-25 04:45:31 +00:00
|
|
|
|
|
|
|
Secrets may be passed to the service without adding them to the world-readable
|
|
|
|
Nix store, by specifying placeholder variables as the option value in Nix and
|
|
|
|
setting these variables accordingly in the environment file.
|
|
|
|
|
2022-09-09 14:08:57 +00:00
|
|
|
```
|
2020-12-25 13:55:36 +00:00
|
|
|
# snippet of HedgeDoc-related config
|
2022-11-27 09:42:12 +00:00
|
|
|
services.hedgedoc.settings.dbURL = "postgres://hedgedoc:\''${DB_PASSWORD}@db-host:5432/hedgedocdb";
|
|
|
|
services.hedgedoc.settings.minio.secretKey = "$MINIO_SECRET_KEY";
|
2022-09-09 14:08:57 +00:00
|
|
|
```
|
2020-09-25 04:45:31 +00:00
|
|
|
|
2022-09-09 14:08:57 +00:00
|
|
|
```
|
2020-09-25 04:45:31 +00:00
|
|
|
# content of the environment file
|
|
|
|
DB_PASSWORD=verysecretdbpassword
|
|
|
|
MINIO_SECRET_KEY=verysecretminiokey
|
2022-09-09 14:08:57 +00:00
|
|
|
```
|
2020-09-25 04:45:31 +00:00
|
|
|
|
|
|
|
Note that this file needs to be available on the host on which
|
2022-09-09 14:08:57 +00:00
|
|
|
`HedgeDoc` is running.
|
2020-09-25 04:45:31 +00:00
|
|
|
'';
|
|
|
|
};
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
|
|
|
|
2023-11-16 04:20:00 +00:00
|
|
|
config = lib.mkIf cfg.enable {
|
|
|
|
users.groups.${name} = { };
|
2020-12-25 13:55:36 +00:00
|
|
|
users.users.${name} = {
|
|
|
|
description = "HedgeDoc service user";
|
|
|
|
group = name;
|
2020-04-24 23:36:52 +00:00
|
|
|
isSystemUser = true;
|
|
|
|
};
|
|
|
|
|
2023-11-16 04:20:00 +00:00
|
|
|
services.hedgedoc.settings = {
|
|
|
|
defaultNotePath = lib.mkDefault "${cfg.package}/public/default.md";
|
|
|
|
docsPath = lib.mkDefault "${cfg.package}/public/docs";
|
|
|
|
viewPath = lib.mkDefault "${cfg.package}/public/views";
|
|
|
|
};
|
|
|
|
|
2020-12-25 13:55:36 +00:00
|
|
|
systemd.services.hedgedoc = {
|
|
|
|
description = "HedgeDoc Service";
|
2023-11-16 04:20:00 +00:00
|
|
|
documentation = [ "https://docs.hedgedoc.org/" ];
|
2020-04-24 23:36:52 +00:00
|
|
|
wantedBy = [ "multi-user.target" ];
|
|
|
|
after = [ "networking.target" ];
|
2023-11-16 04:20:00 +00:00
|
|
|
preStart =
|
|
|
|
let
|
|
|
|
configFile = settingsFormat.generate "hedgedoc-config.json" {
|
|
|
|
production = cfg.settings;
|
|
|
|
};
|
|
|
|
in
|
|
|
|
''
|
|
|
|
${pkgs.envsubst}/bin/envsubst \
|
|
|
|
-o /run/${name}/config.json \
|
|
|
|
-i ${configFile}
|
|
|
|
${pkgs.coreutils}/bin/mkdir -p ${cfg.settings.uploadsPath}
|
|
|
|
'';
|
2020-04-24 23:36:52 +00:00
|
|
|
serviceConfig = {
|
2023-11-16 04:20:00 +00:00
|
|
|
User = name;
|
|
|
|
Group = name;
|
|
|
|
|
|
|
|
Restart = "always";
|
|
|
|
ExecStart = "${cfg.package}/bin/hedgedoc";
|
|
|
|
RuntimeDirectory = [ name ];
|
|
|
|
StateDirectory = [ name ];
|
|
|
|
WorkingDirectory = "/run/${name}";
|
|
|
|
ReadWritePaths = [
|
|
|
|
"-${cfg.settings.uploadsPath}"
|
|
|
|
] ++ lib.optionals (cfg.settings.db ? "storage") [ "-${cfg.settings.db.storage}" ];
|
|
|
|
EnvironmentFile = lib.mkIf (cfg.environmentFile != null) [ cfg.environmentFile ];
|
2020-04-24 23:36:52 +00:00
|
|
|
Environment = [
|
2023-11-16 04:20:00 +00:00
|
|
|
"CMD_CONFIG_FILE=/run/${name}/config.json"
|
2020-04-24 23:36:52 +00:00
|
|
|
"NODE_ENV=production"
|
|
|
|
];
|
2023-11-16 04:20:00 +00:00
|
|
|
|
|
|
|
# Hardening
|
|
|
|
AmbientCapabilities = "";
|
|
|
|
CapabilityBoundingSet = "";
|
|
|
|
LockPersonality = true;
|
|
|
|
NoNewPrivileges = true;
|
|
|
|
PrivateDevices = true;
|
|
|
|
PrivateMounts = true;
|
2020-04-24 23:36:52 +00:00
|
|
|
PrivateTmp = true;
|
2023-11-16 04:20:00 +00:00
|
|
|
PrivateUsers = true;
|
|
|
|
ProcSubset = "pid";
|
|
|
|
ProtectClock = true;
|
|
|
|
ProtectControlGroups = true;
|
|
|
|
ProtectHome = true;
|
|
|
|
ProtectHostname = true;
|
|
|
|
ProtectKernelLogs = true;
|
|
|
|
ProtectKernelModules = true;
|
|
|
|
ProtectKernelTunables = true;
|
|
|
|
ProtectProc = "invisible";
|
|
|
|
ProtectSystem = "strict";
|
|
|
|
RemoveIPC = true;
|
|
|
|
RestrictAddressFamilies = [
|
|
|
|
"AF_INET"
|
|
|
|
"AF_INET6"
|
|
|
|
# Required for connecting to database sockets,
|
|
|
|
# and listening to unix socket at `cfg.settings.path`
|
|
|
|
"AF_UNIX"
|
|
|
|
];
|
|
|
|
RestrictNamespaces = true;
|
|
|
|
RestrictRealtime = true;
|
|
|
|
RestrictSUIDSGID = true;
|
|
|
|
SocketBindAllow = lib.mkIf (cfg.settings.path == null) cfg.settings.port;
|
|
|
|
SocketBindDeny = "any";
|
|
|
|
SystemCallArchitectures = "native";
|
|
|
|
SystemCallFilter = [
|
|
|
|
"@system-service"
|
|
|
|
"~@privileged @obsolete"
|
|
|
|
"@pkey"
|
|
|
|
];
|
|
|
|
UMask = "0007";
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|