at 23.11-pre 6.7 kB view raw
1{ config, lib, pkgs, ... }: 2 3let 4 cfg = config.services.stargazer; 5 globalSection = '' 6 listen = ${lib.concatStringsSep " " cfg.listen} 7 connection-logging = ${lib.boolToString cfg.connectionLogging} 8 log-ip = ${lib.boolToString cfg.ipLog} 9 log-ip-partial = ${lib.boolToString cfg.ipLogPartial} 10 request-timeout = ${toString cfg.requestTimeout} 11 response-timeout = ${toString cfg.responseTimeout} 12 13 [:tls] 14 store = ${toString cfg.store} 15 organization = ${cfg.certOrg} 16 gen-certs = ${lib.boolToString cfg.genCerts} 17 regen-certs = ${lib.boolToString cfg.regenCerts} 18 ${lib.optionalString (cfg.certLifetime != "") "cert-lifetime = ${cfg.certLifetime}"} 19 20 ''; 21 genINI = lib.generators.toINI { }; 22 configFile = pkgs.writeText "config.ini" (lib.strings.concatStrings ( 23 [ globalSection ] ++ (lib.lists.forEach cfg.routes (section: 24 let 25 name = section.route; 26 params = builtins.removeAttrs section [ "route" ]; 27 in 28 genINI 29 { 30 "${name}" = params; 31 } + "\n" 32 )) 33 )); 34in 35{ 36 options.services.stargazer = { 37 enable = lib.mkEnableOption (lib.mdDoc "Stargazer Gemini server"); 38 39 listen = lib.mkOption { 40 type = lib.types.listOf lib.types.str; 41 default = [ "0.0.0.0" ] ++ lib.optional config.networking.enableIPv6 "[::0]"; 42 defaultText = lib.literalExpression ''[ "0.0.0.0" ] ++ lib.optional config.networking.enableIPv6 "[::0]"''; 43 example = lib.literalExpression ''[ "10.0.0.12" "[2002:a00:1::]" ]''; 44 description = lib.mdDoc '' 45 Address and port to listen on. 46 ''; 47 }; 48 49 connectionLogging = lib.mkOption { 50 type = lib.types.bool; 51 default = true; 52 description = lib.mdDoc "Whether or not to log connections to stdout."; 53 }; 54 55 ipLog = lib.mkOption { 56 type = lib.types.bool; 57 default = false; 58 description = lib.mdDoc "Log client IP addresses in the connection log."; 59 }; 60 61 ipLogPartial = lib.mkOption { 62 type = lib.types.bool; 63 default = false; 64 description = lib.mdDoc "Log partial client IP addresses in the connection log."; 65 }; 66 67 requestTimeout = lib.mkOption { 68 type = lib.types.int; 69 default = 5; 70 description = lib.mdDoc '' 71 Number of seconds to wait for the client to send a complete 72 request. Set to 0 to disable. 73 ''; 74 }; 75 76 responseTimeout = lib.mkOption { 77 type = lib.types.int; 78 default = 0; 79 description = lib.mdDoc '' 80 Number of seconds to wait for the client to send a complete 81 request and for stargazer to finish sending the response. 82 Set to 0 to disable. 83 ''; 84 }; 85 86 store = lib.mkOption { 87 type = lib.types.path; 88 default = /var/lib/gemini/certs; 89 description = lib.mdDoc '' 90 Path to the certificate store on disk. This should be a 91 persistent directory writable by Stargazer. 92 ''; 93 }; 94 95 certOrg = lib.mkOption { 96 type = lib.types.str; 97 default = "stargazer"; 98 description = lib.mdDoc '' 99 The name of the organization responsible for the X.509 100 certificate's /O name. 101 ''; 102 }; 103 104 genCerts = lib.mkOption { 105 type = lib.types.bool; 106 default = true; 107 description = lib.mdDoc '' 108 Set to false to disable automatic certificate generation. 109 Use if you want to provide your own certs. 110 ''; 111 }; 112 113 regenCerts = lib.mkOption { 114 type = lib.types.bool; 115 default = true; 116 description = lib.mdDoc '' 117 Set to false to turn off automatic regeneration of expired certificates. 118 Use if you want to provide your own certs. 119 ''; 120 }; 121 122 certLifetime = lib.mkOption { 123 type = lib.types.str; 124 default = ""; 125 description = lib.mdDoc '' 126 How long certs generated by Stargazer should live for. 127 Certs live forever by default. 128 ''; 129 example = lib.literalExpression "\"1y\""; 130 }; 131 132 routes = lib.mkOption { 133 type = lib.types.listOf 134 (lib.types.submodule { 135 freeformType = with lib.types; attrsOf (nullOr 136 (oneOf [ 137 bool 138 int 139 float 140 str 141 ]) // { 142 description = "INI atom (null, bool, int, float or string)"; 143 }); 144 options.route = lib.mkOption { 145 type = lib.types.str; 146 description = lib.mdDoc "Route section name"; 147 }; 148 }); 149 default = [ ]; 150 description = lib.mdDoc '' 151 Routes that Stargazer should server. 152 153 Expressed as a list of attribute sets. Each set must have a key `route` 154 that becomes the section name for that route in the stargazer ini cofig. 155 The remaining keys and values become the parameters for that route. 156 157 [Refer to upstream docs for other params](https://git.sr.ht/~zethra/stargazer/tree/main/item/doc/stargazer.ini.5.txt) 158 ''; 159 example = lib.literalExpression '' 160 [ 161 { 162 route = "example.com"; 163 root = "/srv/gemini/example.com" 164 } 165 { 166 route = "example.com:/man"; 167 root = "/cgi-bin"; 168 cgi = true; 169 } 170 { 171 route = "other.org~(.*)"; 172 redirect = "gemini://example.com"; 173 rewrite = "\1"; 174 } 175 ] 176 ''; 177 }; 178 179 user = lib.mkOption { 180 type = lib.types.str; 181 default = "stargazer"; 182 description = lib.mdDoc "User account under which stargazer runs."; 183 }; 184 185 group = lib.mkOption { 186 type = lib.types.str; 187 default = "stargazer"; 188 description = lib.mdDoc "Group account under which stargazer runs."; 189 }; 190 }; 191 192 config = lib.mkIf cfg.enable { 193 systemd.services.stargazer = { 194 description = "Stargazer gemini server"; 195 after = [ "network.target" ]; 196 wantedBy = [ "multi-user.target" ]; 197 serviceConfig = { 198 ExecStart = "${pkgs.stargazer}/bin/stargazer ${configFile}"; 199 Restart = "always"; 200 # User and group 201 User = cfg.user; 202 Group = cfg.group; 203 }; 204 }; 205 206 # Create default cert store 207 system.activationScripts.makeStargazerCertDir = 208 lib.optionalAttrs (cfg.store == /var/lib/gemini/certs) '' 209 mkdir -p /var/lib/gemini/certs 210 chown -R ${cfg.user}:${cfg.group} /var/lib/gemini/certs 211 ''; 212 213 users.users = lib.optionalAttrs (cfg.user == "stargazer") { 214 stargazer = { 215 group = cfg.group; 216 isSystemUser = true; 217 }; 218 }; 219 220 users.groups = lib.optionalAttrs (cfg.group == "stargazer") { 221 stargazer = { }; 222 }; 223 }; 224 225 meta.maintainers = with lib.maintainers; [ gaykitty ]; 226}