nixos/preload: init
[NixPkgs.git] / nixos / modules / services / web-servers / stargazer.nix
blob18f57363137cf985039e0dad7f9977a5e7b8d7af
1 { config, lib, pkgs, ... }:
3 let
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}
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}"}
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   ));
36   options.services.stargazer = {
37     enable = lib.mkEnableOption (lib.mdDoc "Stargazer Gemini server");
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     };
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     };
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     };
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     };
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     };
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     };
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     };
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     };
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     };
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     };
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     };
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.
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.
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     };
179     user = lib.mkOption {
180       type = lib.types.str;
181       default = "stargazer";
182       description = lib.mdDoc "User account under which stargazer runs.";
183     };
185     group = lib.mkOption {
186       type = lib.types.str;
187       default = "stargazer";
188       description = lib.mdDoc "Group account under which stargazer runs.";
189     };
190   };
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     };
206     # Create default cert store
207     systemd.tmpfiles.rules = lib.mkIf (cfg.store == /var/lib/gemini/certs) [
208       ''d /var/lib/gemini/certs - "${cfg.user}" "${cfg.group}" -''
209     ];
211     users.users = lib.optionalAttrs (cfg.user == "stargazer") {
212       stargazer = {
213         group = cfg.group;
214         isSystemUser = true;
215       };
216     };
218     users.groups = lib.optionalAttrs (cfg.group == "stargazer") {
219       stargazer = { };
220     };
221   };
223   meta.maintainers = with lib.maintainers; [ gaykitty ];