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