1{ config, lib, pkgs, ... }:
2
3let
4 cfg = config.services.wastebin;
5 inherit (lib)
6 mkEnableOption mkPackageOption mkIf mkOption
7 types mapAttrs isBool getExe boolToString optionalAttrs;
8in
9{
10
11 options.services.wastebin = {
12
13 enable = mkEnableOption "Wastebin, a pastebin service";
14
15 package = mkPackageOption pkgs "wastebin" { };
16
17 stateDir = mkOption {
18 type = types.path;
19 default = "/var/lib/wastebin";
20 description = "State directory of the daemon.";
21 };
22
23 secretFile = mkOption {
24 type = types.nullOr types.path;
25 default = null;
26 example = "/run/secrets/wastebin.env";
27 description = ''
28 Path to file containing sensitive environment variables.
29 Some variables that can be considered secrets are:
30
31 - WASTEBIN_PASSWORD_SALT:
32 salt used to hash user passwords used for encrypting pastes.
33
34 - WASTEBIN_SIGNING_KEY:
35 sets the key to sign cookies. If not set, a random key will be
36 generated which means cookies will become invalid after restarts and
37 paste creators will not be able to delete their pastes anymore.
38 '';
39 };
40
41 settings = mkOption {
42
43 description = ''
44 Additional configuration for wastebin, see
45 <https://github.com/matze/wastebin#usage> for supported values.
46 For secrets use secretFile option instead.
47 '';
48
49 type = types.submodule {
50
51 freeformType = with types; attrsOf (oneOf [ bool int str ]);
52
53 options = {
54
55 WASTEBIN_ADDRESS_PORT = mkOption {
56 type = types.str;
57 default = "0.0.0.0:8088";
58 description = "Address and port to bind to";
59 };
60
61 WASTEBIN_BASE_URL = mkOption {
62 default = "http://localhost";
63 example = "https://myhost.tld";
64 type = types.str;
65 description = ''
66 Base URL for the QR code display. If not set, the user agent's Host
67 header field is used as an approximation.
68 '';
69 };
70
71 WASTEBIN_CACHE_SIZE = mkOption {
72 default = 128;
73 type = types.int;
74 description = "Number of rendered syntax highlight items to cache. Can be disabled by setting to 0.";
75 };
76
77 WASTEBIN_DATABASE_PATH = mkOption {
78 default = "/var/lib/wastebin/sqlite3.db"; # TODO make this default to stateDir/sqlite3.db
79 type = types.str;
80 description = "Path to the sqlite3 database file. If not set, an in-memory database is used.";
81 };
82
83 WASTEBIN_HTTP_TIMEOUT = mkOption {
84 default = 5;
85 type = types.int;
86 description = "Maximum number of seconds a request can be processed until wastebin responds with 408";
87 };
88
89 WASTEBIN_MAX_BODY_SIZE = mkOption {
90 default = 1024;
91 type = types.int;
92 description = "Number of bytes to accept for POST requests";
93 };
94
95 WASTEBIN_TITLE = mkOption {
96 default = "wastebin";
97 type = types.str;
98 description = "Overrides the HTML page title";
99 };
100
101 RUST_LOG = mkOption {
102 default = "info";
103 type = types.str;
104 description =
105 ''
106 Influences logging. Besides the typical trace, debug, info etc.
107 keys, you can also set the tower_http key to some log level to get
108 additional information request and response logs.
109 '';
110 };
111 };
112 };
113
114 default = { };
115
116 example = {
117 WASTEBIN_TITLE = "My awesome pastebin";
118 };
119 };
120 };
121
122 config = mkIf cfg.enable
123 {
124 systemd.services.wastebin = {
125 after = [ "network.target" ];
126 wantedBy = [ "multi-user.target" ];
127 environment = mapAttrs (_: v: if isBool v then boolToString v else toString v) cfg.settings;
128 serviceConfig = {
129 CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
130 DevicePolicy = "closed";
131 DynamicUser = true;
132 ExecStart = "${getExe cfg.package}";
133 LockPersonality = true;
134 MemoryDenyWriteExecute = true;
135 PrivateDevices = true;
136 PrivateUsers = true;
137 ProtectClock = true;
138 ProtectControlGroups = true;
139 ProtectHostname = true;
140 ProtectKernelLogs = true;
141 ProtectKernelModules = true;
142 ProtectKernelTunables = true;
143 ProtectProc = "invisible";
144 RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
145 RestrictNamespaces = true;
146 RestrictRealtime = true;
147 SystemCallArchitectures = [ "native" ];
148 SystemCallFilter = [ "@system-service" ];
149 StateDirectory = baseNameOf cfg.stateDir;
150 ReadWritePaths = cfg.stateDir;
151 } // optionalAttrs (cfg.secretFile != null) {
152 EnvironmentFile = cfg.secretFile;
153 };
154 };
155 };
156
157 meta.maintainers = with lib.maintainers; [ pinpox ];
158}