at 23.11-pre 6.4 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.spacecookie; 7 8 spacecookieConfig = { 9 listen = { 10 inherit (cfg) port; 11 }; 12 } // cfg.settings; 13 14 format = pkgs.formats.json {}; 15 16 configFile = format.generate "spacecookie.json" spacecookieConfig; 17 18in { 19 imports = [ 20 (mkRenamedOptionModule [ "services" "spacecookie" "root" ] [ "services" "spacecookie" "settings" "root" ]) 21 (mkRenamedOptionModule [ "services" "spacecookie" "hostname" ] [ "services" "spacecookie" "settings" "hostname" ]) 22 ]; 23 24 options = { 25 26 services.spacecookie = { 27 28 enable = mkEnableOption (lib.mdDoc "spacecookie"); 29 30 package = mkOption { 31 type = types.package; 32 default = pkgs.spacecookie; 33 defaultText = literalExpression "pkgs.spacecookie"; 34 example = literalExpression "pkgs.haskellPackages.spacecookie"; 35 description = lib.mdDoc '' 36 The spacecookie derivation to use. This can be used to 37 override the used package or to use another version. 38 ''; 39 }; 40 41 openFirewall = mkOption { 42 type = types.bool; 43 default = false; 44 description = lib.mdDoc '' 45 Whether to open the necessary port in the firewall for spacecookie. 46 ''; 47 }; 48 49 port = mkOption { 50 type = types.port; 51 default = 70; 52 description = lib.mdDoc '' 53 Port the gopher service should be exposed on. 54 ''; 55 }; 56 57 address = mkOption { 58 type = types.str; 59 default = "[::]"; 60 description = lib.mdDoc '' 61 Address to listen on. Must be in the 62 `ListenStream=` syntax of 63 [systemd.socket(5)](https://www.freedesktop.org/software/systemd/man/systemd.socket.html). 64 ''; 65 }; 66 67 settings = mkOption { 68 type = types.submodule { 69 freeformType = format.type; 70 71 options.hostname = mkOption { 72 type = types.str; 73 default = "localhost"; 74 description = lib.mdDoc '' 75 The hostname the service is reachable via. Clients 76 will use this hostname for further requests after 77 loading the initial gopher menu. 78 ''; 79 }; 80 81 options.root = mkOption { 82 type = types.path; 83 default = "/srv/gopher"; 84 description = lib.mdDoc '' 85 The directory spacecookie should serve via gopher. 86 Files in there need to be world-readable since 87 the spacecookie service file sets 88 `DynamicUser=true`. 89 ''; 90 }; 91 92 options.log = { 93 enable = mkEnableOption (lib.mdDoc "logging for spacecookie") 94 // { default = true; example = false; }; 95 96 hide-ips = mkOption { 97 type = types.bool; 98 default = true; 99 description = lib.mdDoc '' 100 If enabled, spacecookie will hide personal 101 information of users like IP addresses from 102 log output. 103 ''; 104 }; 105 106 hide-time = mkOption { 107 type = types.bool; 108 # since we are starting with systemd anyways 109 # we deviate from the default behavior here: 110 # journald will add timestamps, so no need 111 # to double up. 112 default = true; 113 description = lib.mdDoc '' 114 If enabled, spacecookie will not print timestamps 115 at the beginning of every log line. 116 ''; 117 }; 118 119 level = mkOption { 120 type = types.enum [ 121 "info" 122 "warn" 123 "error" 124 ]; 125 default = "info"; 126 description = lib.mdDoc '' 127 Log level for the spacecookie service. 128 ''; 129 }; 130 }; 131 }; 132 133 description = lib.mdDoc '' 134 Settings for spacecookie. The settings set here are 135 directly translated to the spacecookie JSON config 136 file. See 137 [spacecookie.json(5)](https://sternenseemann.github.io/spacecookie/spacecookie.json.5.html) 138 for explanations of all options. 139 ''; 140 }; 141 }; 142 }; 143 144 config = mkIf cfg.enable { 145 assertions = [ 146 { 147 assertion = !(cfg.settings ? user); 148 message = '' 149 spacecookie is started as a normal user, so the setuid 150 feature doesn't work. If you want to run spacecookie as 151 a specific user, set: 152 systemd.services.spacecookie.serviceConfig = { 153 DynamicUser = false; 154 User = "youruser"; 155 Group = "yourgroup"; 156 } 157 ''; 158 } 159 { 160 assertion = !(cfg.settings ? listen || cfg.settings ? port); 161 message = '' 162 The NixOS spacecookie module uses socket activation, 163 so the listen options have no effect. Use the port 164 and address options in services.spacecookie instead. 165 ''; 166 } 167 ]; 168 169 systemd.sockets.spacecookie = { 170 description = "Socket for the Spacecookie Gopher Server"; 171 wantedBy = [ "sockets.target" ]; 172 listenStreams = [ "${cfg.address}:${toString cfg.port}" ]; 173 socketConfig = { 174 BindIPv6Only = "both"; 175 }; 176 }; 177 178 systemd.services.spacecookie = { 179 description = "Spacecookie Gopher Server"; 180 wantedBy = [ "multi-user.target" ]; 181 requires = [ "spacecookie.socket" ]; 182 183 serviceConfig = { 184 Type = "notify"; 185 ExecStart = "${lib.getBin cfg.package}/bin/spacecookie ${configFile}"; 186 FileDescriptorStoreMax = 1; 187 188 DynamicUser = true; 189 190 ProtectSystem = "strict"; 191 ProtectHome = true; 192 PrivateTmp = true; 193 PrivateDevices = true; 194 PrivateMounts = true; 195 PrivateUsers = true; 196 197 ProtectKernelTunables = true; 198 ProtectKernelModules = true; 199 ProtectControlGroups = true; 200 201 CapabilityBoundingSet = ""; 202 NoNewPrivileges = true; 203 LockPersonality = true; 204 RestrictRealtime = true; 205 206 # AF_UNIX for communication with systemd 207 # AF_INET replaced by BindIPv6Only=both 208 RestrictAddressFamilies = "AF_UNIX AF_INET6"; 209 }; 210 }; 211 212 networking.firewall = mkIf cfg.openFirewall { 213 allowedTCPPorts = [ cfg.port ]; 214 }; 215 }; 216}