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