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