1{ lib, pkgs, config, ... }:
2
3let
4 inherit (lib) mkEnableOption mkPackageOption mkOption types;
5
6 cfg = config.services.c2fmzq-server;
7
8 argsFormat = {
9 type = with lib.types; attrsOf (nullOr (oneOf [ bool int str ]));
10 generate = lib.cli.toGNUCommandLineShell {
11 mkBool = k: v: [
12 "--${k}=${if v then "true" else "false"}"
13 ];
14 };
15 };
16in {
17 options.services.c2fmzq-server = {
18 enable = mkEnableOption "c2fmzq-server";
19
20 bindIP = mkOption {
21 type = types.str;
22 default = "127.0.0.1";
23 description = "The local address to use.";
24 };
25
26 port = mkOption {
27 type = types.port;
28 default = 8080;
29 description = "The local port to use.";
30 };
31
32 passphraseFile = mkOption {
33 type = types.str;
34 example = "/run/secrets/c2fmzq/pwfile";
35 description = "Path to file containing the database passphrase";
36 };
37
38 package = mkPackageOption pkgs "c2fmzq" { };
39
40 settings = mkOption {
41 type = types.submodule {
42 freeformType = argsFormat.type;
43
44 options = {
45 address = mkOption {
46 internal = true;
47 type = types.str;
48 default = "${cfg.bindIP}:${toString cfg.port}";
49 };
50
51 database = mkOption {
52 type = types.str;
53 default = "%S/c2fmzq-server/data";
54 description = "Path of the database";
55 };
56
57 verbose = mkOption {
58 type = types.ints.between 1 3;
59 default = 2;
60 description = "The level of logging verbosity: 1:Error 2:Info 3:Debug";
61 };
62 };
63 };
64 description = ''
65 Configuration for c2FmZQ-server passed as CLI arguments.
66 Run {command}`c2FmZQ-server help` for supported values.
67 '';
68 example = {
69 verbose = 3;
70 allow-new-accounts = true;
71 auto-approve-new-accounts = true;
72 encrypt-metadata = true;
73 enable-webapp = true;
74 };
75 };
76 };
77
78 config = lib.mkIf cfg.enable {
79 systemd.services.c2fmzq-server = {
80 description = "c2FmZQ-server";
81 documentation = [ "https://github.com/c2FmZQ/c2FmZQ/blob/main/README.md" ];
82 wantedBy = [ "multi-user.target" ];
83 wants = [ "network-online.target" ];
84 after = [ "network.target" "network-online.target" ];
85
86 serviceConfig = {
87 ExecStart = "${lib.getExe cfg.package} ${argsFormat.generate cfg.settings}";
88 AmbientCapabilities = "";
89 CapabilityBoundingSet = "";
90 DynamicUser = true;
91 Environment = "C2FMZQ_PASSPHRASE_FILE=%d/passphrase-file";
92 IPAccounting = true;
93 IPAddressAllow = cfg.bindIP;
94 IPAddressDeny = "any";
95 LoadCredential = "passphrase-file:${cfg.passphraseFile}";
96 LockPersonality = true;
97 MemoryDenyWriteExecute = true;
98 NoNewPrivileges = true;
99 PrivateDevices = true;
100 PrivateIPC = true;
101 PrivateTmp = true;
102 PrivateUsers = true;
103 ProtectClock = true;
104 ProtectControlGroups = true;
105 ProtectHome = true;
106 ProtectHostname = true;
107 ProtectKernelLogs = true;
108 ProtectKernelModules = true;
109 ProtectKernelTunables = true;
110 ProtectProc = "invisible";
111 ProtectSystem = "strict";
112 RemoveIPC = true;
113 RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
114 RestrictNamespaces = true;
115 RestrictRealtime = true;
116 RestrictSUIDSGID = true;
117 SocketBindAllow = cfg.port;
118 SocketBindDeny = "any";
119 StateDirectory = "c2fmzq-server";
120 SystemCallArchitectures = "native";
121 SystemCallFilter = [ "@system-service" "~@privileged @obsolete" ];
122 };
123 };
124 };
125
126 meta = {
127 doc = ./c2fmzq-server.md;
128 maintainers = with lib.maintainers; [ hmenke ];
129 };
130}