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