From 41419ca2883f7a3294711faf4961d043868e27ef Mon Sep 17 00:00:00 2001 From: euxane Date: Sat, 8 Jun 2024 22:34:13 +0200 Subject: [PATCH 01/11] nixos/fcgiwrap: refactor for multiple instances This allows configuring and starting independent instances of the fgciwrap service, each with their own settings and running user, instead of having to share a global one. I could not use `mkRenamedOptionModule` on the previous options because the aliases conflict with `attrsOf submodule` now defined at `services.fcgiwrap`. This makes this change not backward compatible. --- .../manual/release-notes/rl-2411.section.md | 6 +++ .../modules/services/web-servers/fcgiwrap.nix | 38 +++++++++---------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/nixos/doc/manual/release-notes/rl-2411.section.md b/nixos/doc/manual/release-notes/rl-2411.section.md index 2de4cf4d08af..f59c3d88fdb3 100644 --- a/nixos/doc/manual/release-notes/rl-2411.section.md +++ b/nixos/doc/manual/release-notes/rl-2411.section.md @@ -44,6 +44,12 @@ it is set, instead of the previous hardcoded default of `${networking.hostName}.${security.ipa.domain}`. +- The fcgiwrap module now allows multiple instances running as distinct users. + The option `services.fgciwrap` now takes an attribute set of the + configuration of each individual instance. + This requires migrating any previous configuration keys from + `services.fcgiwrap.*` to `services.fcgiwrap.some-instance.*`. + - `nvimpager` was updated to version 0.13.0, which changes the order of user and nvimpager settings: user commands in `-c` and `--cmd` now override the respective default settings because they are executed later. diff --git a/nixos/modules/services/web-servers/fcgiwrap.nix b/nixos/modules/services/web-servers/fcgiwrap.nix index 3250e9c05ed6..93198622318a 100644 --- a/nixos/modules/services/web-servers/fcgiwrap.nix +++ b/nixos/modules/services/web-servers/fcgiwrap.nix @@ -3,17 +3,15 @@ with lib; let - cfg = config.services.fcgiwrap; + forEachInstance = f: flip mapAttrs' config.services.fcgiwrap (name: cfg: + nameValuePair "fcgiwrap-${name}" (f cfg) + ); + in { - - options = { - services.fcgiwrap = { - enable = mkOption { - type = types.bool; - default = false; - description = "Whether to enable fcgiwrap, a server for running CGI applications over FastCGI."; - }; - + options.services.fcgiwrap = mkOption { + description = "Configuration for fcgiwrap instances."; + default = { }; + type = types.attrsOf (types.submodule ({ config, ... }: { options = { preforkProcesses = mkOption { type = types.int; default = 1; @@ -28,7 +26,7 @@ in { socketAddress = mkOption { type = types.str; - default = "/run/fcgiwrap.sock"; + default = "/run/fcgiwrap-${config._module.args.name}.sock"; example = "1.2.3.4:5678"; description = "Socket address. In case of a UNIX socket, this should be its filesystem path."; }; @@ -44,11 +42,11 @@ in { default = null; description = "Group permissions for the socket."; }; - }; + }; })); }; - config = mkIf cfg.enable { - systemd.services.fcgiwrap = { + config = { + systemd.services = forEachInstance (cfg: { after = [ "nss-user-lookup.target" ]; wantedBy = optional (cfg.socketType != "unix") "multi-user.target"; @@ -60,13 +58,13 @@ in { User = cfg.user; Group = cfg.group; } else { } ); - }; + }); - systemd.sockets = if (cfg.socketType == "unix") then { - fcgiwrap = { - wantedBy = [ "sockets.target" ]; - socketConfig.ListenStream = cfg.socketAddress; + systemd.sockets = forEachInstance (cfg: mkIf (cfg.socketType == "unix") { + wantedBy = [ "sockets.target" ]; + socketConfig = { + ListenStream = cfg.socketAddress; }; - } else { }; + }); }; } From bf2ad6f48c95eea96768cc62dda7c6eb2097cbf4 Mon Sep 17 00:00:00 2001 From: euxane Date: Sat, 8 Jun 2024 22:34:16 +0200 Subject: [PATCH 02/11] nixos/fcgiwrap: adapt consumer modules and tests This also fixes the gitolite-fcgiwrap test by running git through fcgiwrap as the proper user. --- nixos/modules/services/misc/zoneminder.nix | 9 +++------ nixos/modules/services/networking/cgit.nix | 19 +++++++++++-------- .../modules/services/networking/smokeping.nix | 7 +++++-- nixos/tests/gitolite-fcgiwrap.nix | 10 +++++++--- 4 files changed, 26 insertions(+), 19 deletions(-) diff --git a/nixos/modules/services/misc/zoneminder.nix b/nixos/modules/services/misc/zoneminder.nix index d09cd87febff..749d0c174520 100644 --- a/nixos/modules/services/misc/zoneminder.nix +++ b/nixos/modules/services/misc/zoneminder.nix @@ -202,8 +202,7 @@ in { ]; services = { - fcgiwrap = lib.mkIf useNginx { - enable = true; + fcgiwrap.zoneminder = lib.mkIf useNginx { preforkProcesses = cfg.cameras; inherit user group; }; @@ -225,9 +224,7 @@ in { default = true; root = "${pkg}/share/zoneminder/www"; listen = [ { addr = "0.0.0.0"; inherit (cfg) port; } ]; - extraConfig = let - fcgi = config.services.fcgiwrap; - in '' + extraConfig = '' index index.php; location / { @@ -257,7 +254,7 @@ in { fastcgi_param HTTP_PROXY ""; fastcgi_intercept_errors on; - fastcgi_pass ${fcgi.socketType}:${fcgi.socketAddress}; + fastcgi_pass unix:${config.services.fcgiwrap.zoneminder.socketAddress}; } location /cache/ { diff --git a/nixos/modules/services/networking/cgit.nix b/nixos/modules/services/networking/cgit.nix index 0ccbef756812..5acdaa47c3a0 100644 --- a/nixos/modules/services/networking/cgit.nix +++ b/nixos/modules/services/networking/cgit.nix @@ -25,14 +25,14 @@ let regexLocation = cfg: regexEscape (stripLocation cfg); - mkFastcgiPass = cfg: '' + mkFastcgiPass = name: cfg: '' ${if cfg.nginx.location == "/" then '' fastcgi_param PATH_INFO $uri; '' else '' fastcgi_split_path_info ^(${regexLocation cfg})(/.+)$; fastcgi_param PATH_INFO $fastcgi_path_info; '' - }fastcgi_pass unix:${config.services.fcgiwrap.socketAddress}; + }fastcgi_pass unix:${config.services.fcgiwrap."cgit-${name}".socketAddress}; ''; cgitrcLine = name: value: "${name}=${ @@ -165,18 +165,21 @@ in message = "Exactly one of services.cgit.${vhost}.scanPath or services.cgit.${vhost}.repos must be set."; }) cfgs; - services.fcgiwrap.enable = true; + services.fcgiwrap = flip mapAttrs' cfgs (name: cfg: + nameValuePair "cgit-${name}" { + } + ); services.nginx.enable = true; - services.nginx.virtualHosts = mkMerge (mapAttrsToList (_: cfg: { + services.nginx.virtualHosts = mkMerge (mapAttrsToList (name: cfg: { ${cfg.nginx.virtualHost} = { locations = ( genAttrs' [ "cgit.css" "cgit.png" "favicon.ico" "robots.txt" ] - (name: nameValuePair "= ${stripLocation cfg}/${name}" { + (fileName: nameValuePair "= ${stripLocation cfg}/${fileName}" { extraConfig = '' - alias ${cfg.package}/cgit/${name}; + alias ${cfg.package}/cgit/${fileName}; ''; }) ) // { @@ -187,7 +190,7 @@ in GIT_PROJECT_ROOT = mkCgitReposDir cfg; HOME = GIT_PROJECT_ROOT; }; - extraConfig = mkFastcgiPass cfg; + extraConfig = mkFastcgiPass name cfg; }; "${stripLocation cfg}/" = { fastcgiParams = { @@ -196,7 +199,7 @@ in HTTP_HOST = "$server_name"; CGIT_CONFIG = mkCgitrc cfg; }; - extraConfig = mkFastcgiPass cfg; + extraConfig = mkFastcgiPass name cfg; }; }; }; diff --git a/nixos/modules/services/networking/smokeping.nix b/nixos/modules/services/networking/smokeping.nix index 3fb3eac45cc8..d0ec571665fb 100644 --- a/nixos/modules/services/networking/smokeping.nix +++ b/nixos/modules/services/networking/smokeping.nix @@ -337,7 +337,10 @@ in }; # use nginx to serve the smokeping web service - services.fcgiwrap.enable = mkIf cfg.webService true; + services.fcgiwrap.smokeping = mkIf cfg.webService { + user = cfg.user; + group = cfg.user; + }; services.nginx = mkIf cfg.webService { enable = true; virtualHosts."smokeping" = { @@ -349,7 +352,7 @@ in locations."/smokeping.fcgi" = { extraConfig = '' include ${config.services.nginx.package}/conf/fastcgi_params; - fastcgi_pass unix:${config.services.fcgiwrap.socketAddress}; + fastcgi_pass unix:${config.services.fcgiwrap.smokeping.socketAddress}; fastcgi_param SCRIPT_FILENAME ${smokepingHome}/smokeping.fcgi; fastcgi_param DOCUMENT_ROOT ${smokepingHome}; ''; diff --git a/nixos/tests/gitolite-fcgiwrap.nix b/nixos/tests/gitolite-fcgiwrap.nix index abf1db37003a..e51c5da73ae2 100644 --- a/nixos/tests/gitolite-fcgiwrap.nix +++ b/nixos/tests/gitolite-fcgiwrap.nix @@ -24,7 +24,11 @@ import ./make-test-python.nix ( { networking.firewall.allowedTCPPorts = [ 80 ]; - services.fcgiwrap.enable = true; + services.fcgiwrap.gitolite = { + user = "gitolite"; + group = "gitolite"; + }; + services.gitolite = { enable = true; adminPubkey = adminPublicKey; @@ -59,7 +63,7 @@ import ./make-test-python.nix ( fastcgi_param SCRIPT_FILENAME ${pkgs.gitolite}/bin/gitolite-shell; # use Unix domain socket or inet socket - fastcgi_pass unix:/run/fcgiwrap.sock; + fastcgi_pass unix:${config.services.fcgiwrap.gitolite.socketAddress}; ''; }; @@ -82,7 +86,7 @@ import ./make-test-python.nix ( server.wait_for_unit("gitolite-init.service") server.wait_for_unit("nginx.service") - server.wait_for_file("/run/fcgiwrap.sock") + server.wait_for_file("/run/fcgiwrap-gitolite.sock") client.wait_for_unit("multi-user.target") client.succeed( From 022289f2fadb3a3bad83273cd45d8a3e4753991e Mon Sep 17 00:00:00 2001 From: euxane Date: Sat, 8 Jun 2024 22:34:17 +0200 Subject: [PATCH 03/11] nixos/fcgiwrap: group options logically, fix doc Since we're already introducing some backward-incompatible change in the previous commit, let's make the options more tidy, also preparing for the introduction of more options. This also fixes the documentation of the user and group options which are applying to the service's running user, not the socket. --- .../modules/services/web-servers/fcgiwrap.nix | 51 ++++++++++--------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/nixos/modules/services/web-servers/fcgiwrap.nix b/nixos/modules/services/web-servers/fcgiwrap.nix index 93198622318a..e6ebe2317145 100644 --- a/nixos/modules/services/web-servers/fcgiwrap.nix +++ b/nixos/modules/services/web-servers/fcgiwrap.nix @@ -12,35 +12,38 @@ in { description = "Configuration for fcgiwrap instances."; default = { }; type = types.attrsOf (types.submodule ({ config, ... }: { options = { - preforkProcesses = mkOption { + process.prefork = mkOption { type = types.int; default = 1; description = "Number of processes to prefork."; }; - socketType = mkOption { + process.user = mkOption { + type = types.nullOr types.str; + default = null; + description = "User as which this instance of fcgiwrap will be run."; + }; + + process.group = mkOption { + type = types.nullOr types.str; + default = null; + description = "Group as which this instance of fcgiwrap will be run."; + }; + + socket.type = mkOption { type = types.enum [ "unix" "tcp" "tcp6" ]; default = "unix"; description = "Socket type: 'unix', 'tcp' or 'tcp6'."; }; - socketAddress = mkOption { + socket.address = mkOption { type = types.str; default = "/run/fcgiwrap-${config._module.args.name}.sock"; example = "1.2.3.4:5678"; - description = "Socket address. In case of a UNIX socket, this should be its filesystem path."; - }; - - user = mkOption { - type = types.nullOr types.str; - default = null; - description = "User permissions for the socket."; - }; - - group = mkOption { - type = types.nullOr types.str; - default = null; - description = "Group permissions for the socket."; + description = '' + Socket address. + In case of a UNIX socket, this should be its filesystem path. + ''; }; }; })); }; @@ -48,22 +51,22 @@ in { config = { systemd.services = forEachInstance (cfg: { after = [ "nss-user-lookup.target" ]; - wantedBy = optional (cfg.socketType != "unix") "multi-user.target"; + wantedBy = optional (cfg.socket.type != "unix") "multi-user.target"; serviceConfig = { - ExecStart = "${pkgs.fcgiwrap}/sbin/fcgiwrap -c ${builtins.toString cfg.preforkProcesses} ${ - optionalString (cfg.socketType != "unix") "-s ${cfg.socketType}:${cfg.socketAddress}" + ExecStart = "${pkgs.fcgiwrap}/sbin/fcgiwrap -c ${builtins.toString cfg.process.prefork} ${ + optionalString (cfg.socket.type != "unix") "-s ${cfg.socket.type}:${cfg.socket.address}" }"; - } // (if cfg.user != null && cfg.group != null then { - User = cfg.user; - Group = cfg.group; + } // (if cfg.process.user != null && cfg.process.group != null then { + User = cfg.process.user; + Group = cfg.process.group; } else { } ); }); - systemd.sockets = forEachInstance (cfg: mkIf (cfg.socketType == "unix") { + systemd.sockets = forEachInstance (cfg: mkIf (cfg.socket.type == "unix") { wantedBy = [ "sockets.target" ]; socketConfig = { - ListenStream = cfg.socketAddress; + ListenStream = cfg.socket.address; }; }); }; From 8101ae41f8cefce9e518a550881302c4f58a8c5b Mon Sep 17 00:00:00 2001 From: euxane Date: Sat, 8 Jun 2024 22:34:17 +0200 Subject: [PATCH 04/11] nixos/fcgiwrap: adapt consumer modules and tests --- nixos/modules/services/misc/zoneminder.nix | 7 ++++--- nixos/modules/services/networking/cgit.nix | 2 +- nixos/modules/services/networking/smokeping.nix | 6 +++--- nixos/tests/gitolite-fcgiwrap.nix | 6 +++--- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/nixos/modules/services/misc/zoneminder.nix b/nixos/modules/services/misc/zoneminder.nix index 749d0c174520..8db63d538633 100644 --- a/nixos/modules/services/misc/zoneminder.nix +++ b/nixos/modules/services/misc/zoneminder.nix @@ -203,8 +203,9 @@ in { services = { fcgiwrap.zoneminder = lib.mkIf useNginx { - preforkProcesses = cfg.cameras; - inherit user group; + process.prefork = cfg.cameras; + process.user = user; + process.group = group; }; mysql = lib.mkIf cfg.database.createLocally { @@ -254,7 +255,7 @@ in { fastcgi_param HTTP_PROXY ""; fastcgi_intercept_errors on; - fastcgi_pass unix:${config.services.fcgiwrap.zoneminder.socketAddress}; + fastcgi_pass unix:${config.services.fcgiwrap.zoneminder.socket.address}; } location /cache/ { diff --git a/nixos/modules/services/networking/cgit.nix b/nixos/modules/services/networking/cgit.nix index 5acdaa47c3a0..3bfd7fbe44e4 100644 --- a/nixos/modules/services/networking/cgit.nix +++ b/nixos/modules/services/networking/cgit.nix @@ -32,7 +32,7 @@ let fastcgi_split_path_info ^(${regexLocation cfg})(/.+)$; fastcgi_param PATH_INFO $fastcgi_path_info; '' - }fastcgi_pass unix:${config.services.fcgiwrap."cgit-${name}".socketAddress}; + }fastcgi_pass unix:${config.services.fcgiwrap."cgit-${name}".socket.address}; ''; cgitrcLine = name: value: "${name}=${ diff --git a/nixos/modules/services/networking/smokeping.nix b/nixos/modules/services/networking/smokeping.nix index d0ec571665fb..af50ad27e27e 100644 --- a/nixos/modules/services/networking/smokeping.nix +++ b/nixos/modules/services/networking/smokeping.nix @@ -338,8 +338,8 @@ in # use nginx to serve the smokeping web service services.fcgiwrap.smokeping = mkIf cfg.webService { - user = cfg.user; - group = cfg.user; + process.user = cfg.user; + process.group = cfg.user; }; services.nginx = mkIf cfg.webService { enable = true; @@ -352,7 +352,7 @@ in locations."/smokeping.fcgi" = { extraConfig = '' include ${config.services.nginx.package}/conf/fastcgi_params; - fastcgi_pass unix:${config.services.fcgiwrap.smokeping.socketAddress}; + fastcgi_pass unix:${config.services.fcgiwrap.smokeping.socket.address}; fastcgi_param SCRIPT_FILENAME ${smokepingHome}/smokeping.fcgi; fastcgi_param DOCUMENT_ROOT ${smokepingHome}; ''; diff --git a/nixos/tests/gitolite-fcgiwrap.nix b/nixos/tests/gitolite-fcgiwrap.nix index e51c5da73ae2..a4ecf2fc54c4 100644 --- a/nixos/tests/gitolite-fcgiwrap.nix +++ b/nixos/tests/gitolite-fcgiwrap.nix @@ -25,8 +25,8 @@ import ./make-test-python.nix ( networking.firewall.allowedTCPPorts = [ 80 ]; services.fcgiwrap.gitolite = { - user = "gitolite"; - group = "gitolite"; + process.user = "gitolite"; + process.group = "gitolite"; }; services.gitolite = { @@ -63,7 +63,7 @@ import ./make-test-python.nix ( fastcgi_param SCRIPT_FILENAME ${pkgs.gitolite}/bin/gitolite-shell; # use Unix domain socket or inet socket - fastcgi_pass unix:${config.services.fcgiwrap.gitolite.socketAddress}; + fastcgi_pass unix:${config.services.fcgiwrap.gitolite.socket.address}; ''; }; From 3955eaf45015c9dd8a5a59412bf9c5e47b789a65 Mon Sep 17 00:00:00 2001 From: euxane Date: Sat, 8 Jun 2024 22:34:17 +0200 Subject: [PATCH 05/11] nixos/fcgiwrap: improve readability of CLI args --- nixos/modules/services/web-servers/fcgiwrap.nix | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/nixos/modules/services/web-servers/fcgiwrap.nix b/nixos/modules/services/web-servers/fcgiwrap.nix index e6ebe2317145..fc2339730238 100644 --- a/nixos/modules/services/web-servers/fcgiwrap.nix +++ b/nixos/modules/services/web-servers/fcgiwrap.nix @@ -54,9 +54,13 @@ in { wantedBy = optional (cfg.socket.type != "unix") "multi-user.target"; serviceConfig = { - ExecStart = "${pkgs.fcgiwrap}/sbin/fcgiwrap -c ${builtins.toString cfg.process.prefork} ${ - optionalString (cfg.socket.type != "unix") "-s ${cfg.socket.type}:${cfg.socket.address}" - }"; + ExecStart = '' + ${pkgs.fcgiwrap}/sbin/fcgiwrap ${cli.toGNUCommandLineShell {} ({ + c = cfg.process.prefork; + } // (optionalAttrs (cfg.socket.type != "unix") { + s = "${cfg.socket.type}:${cfg.socket.address}"; + }))} + ''; } // (if cfg.process.user != null && cfg.process.group != null then { User = cfg.process.user; Group = cfg.process.group; From 289c1585c2a1f9ff9e159cbcdab664620dc9f7b3 Mon Sep 17 00:00:00 2001 From: euxane Date: Sat, 8 Jun 2024 22:34:17 +0200 Subject: [PATCH 06/11] nixos/fcgiwrap: limit prefork type to positives --- nixos/modules/services/web-servers/fcgiwrap.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nixos/modules/services/web-servers/fcgiwrap.nix b/nixos/modules/services/web-servers/fcgiwrap.nix index fc2339730238..1dc9632e3513 100644 --- a/nixos/modules/services/web-servers/fcgiwrap.nix +++ b/nixos/modules/services/web-servers/fcgiwrap.nix @@ -13,7 +13,7 @@ in { default = { }; type = types.attrsOf (types.submodule ({ config, ... }: { options = { process.prefork = mkOption { - type = types.int; + type = types.ints.positive; default = 1; description = "Number of processes to prefork."; }; From 81f72015f0b96b1227a2de38409049fba0e73aad Mon Sep 17 00:00:00 2001 From: euxane Date: Sat, 8 Jun 2024 23:07:30 +0200 Subject: [PATCH 07/11] nixos/fcgiwrap: add unix socket owner, private by default This adds a few options to properly set the ownership and permissions on UNIX local sockets, set to private by default. Previously, the created UNIX local sockets could be used by any local user. This was especially problematic when fcgiwrap is running as root (the default). --- .../manual/release-notes/rl-2411.section.md | 2 + .../modules/services/web-servers/fcgiwrap.nix | 48 +++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/nixos/doc/manual/release-notes/rl-2411.section.md b/nixos/doc/manual/release-notes/rl-2411.section.md index f59c3d88fdb3..3caa36ab22d8 100644 --- a/nixos/doc/manual/release-notes/rl-2411.section.md +++ b/nixos/doc/manual/release-notes/rl-2411.section.md @@ -49,6 +49,8 @@ configuration of each individual instance. This requires migrating any previous configuration keys from `services.fcgiwrap.*` to `services.fcgiwrap.some-instance.*`. + The ownership and mode of the UNIX sockets created by this service are now + configurable and private by default. - `nvimpager` was updated to version 0.13.0, which changes the order of user and nvimpager settings: user commands in `-c` and `--cmd` now override the diff --git a/nixos/modules/services/web-servers/fcgiwrap.nix b/nixos/modules/services/web-servers/fcgiwrap.nix index 1dc9632e3513..6b633386089f 100644 --- a/nixos/modules/services/web-servers/fcgiwrap.nix +++ b/nixos/modules/services/web-servers/fcgiwrap.nix @@ -45,10 +45,55 @@ in { In case of a UNIX socket, this should be its filesystem path. ''; }; + + socket.user = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + User to be set as owner of the UNIX socket. + Defaults to the process running user. + ''; + }; + + socket.group = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Group to be set as owner of the UNIX socket. + Defaults to the process running group. + ''; + }; + + socket.mode = mkOption { + type = types.nullOr types.str; + default = if config.socket.type == "unix" then "0600" else null; + defaultText = literalExpression '' + if config.socket.type == "unix" then "0600" else null + ''; + description = '' + Mode to be set on the UNIX socket. + Defaults to private to the socket's owner. + ''; + }; }; })); }; config = { + assertions = concatLists (mapAttrsToList (name: cfg: [ + { + assertion = cfg.socket.user != null -> cfg.socket.type == "unix"; + message = "Socket owner can only be set for the UNIX socket type."; + } + { + assertion = cfg.socket.group != null -> cfg.socket.type == "unix"; + message = "Socket owner can only be set for the UNIX socket type."; + } + { + assertion = cfg.socket.mode != null -> cfg.socket.type == "unix"; + message = "Socket mode can only be set for the UNIX socket type."; + } + ]) config.services.fcgiwrap); + systemd.services = forEachInstance (cfg: { after = [ "nss-user-lookup.target" ]; wantedBy = optional (cfg.socket.type != "unix") "multi-user.target"; @@ -71,6 +116,9 @@ in { wantedBy = [ "sockets.target" ]; socketConfig = { ListenStream = cfg.socket.address; + SocketUser = cfg.socket.user; + SocketGroup = cfg.socket.group; + SocketMode = cfg.socket.mode; }; }); }; From c5dc3e203410bc3bfc77182cd8c6955b1bd64cfd Mon Sep 17 00:00:00 2001 From: euxane Date: Sat, 8 Jun 2024 23:07:33 +0200 Subject: [PATCH 08/11] nixos/fcgiwrap: adapt consumer modules and tests --- nixos/modules/services/networking/cgit.nix | 1 + nixos/modules/services/networking/smokeping.nix | 1 + nixos/tests/gitolite-fcgiwrap.nix | 1 + 3 files changed, 3 insertions(+) diff --git a/nixos/modules/services/networking/cgit.nix b/nixos/modules/services/networking/cgit.nix index 3bfd7fbe44e4..640c989aaf4c 100644 --- a/nixos/modules/services/networking/cgit.nix +++ b/nixos/modules/services/networking/cgit.nix @@ -167,6 +167,7 @@ in services.fcgiwrap = flip mapAttrs' cfgs (name: cfg: nameValuePair "cgit-${name}" { + socket = { inherit (config.services.nginx) user group; }; } ); diff --git a/nixos/modules/services/networking/smokeping.nix b/nixos/modules/services/networking/smokeping.nix index af50ad27e27e..a07cde847cf6 100644 --- a/nixos/modules/services/networking/smokeping.nix +++ b/nixos/modules/services/networking/smokeping.nix @@ -340,6 +340,7 @@ in services.fcgiwrap.smokeping = mkIf cfg.webService { process.user = cfg.user; process.group = cfg.user; + socket = { inherit (config.services.nginx) user group; }; }; services.nginx = mkIf cfg.webService { enable = true; diff --git a/nixos/tests/gitolite-fcgiwrap.nix b/nixos/tests/gitolite-fcgiwrap.nix index a4ecf2fc54c4..6e8dae6f72d7 100644 --- a/nixos/tests/gitolite-fcgiwrap.nix +++ b/nixos/tests/gitolite-fcgiwrap.nix @@ -27,6 +27,7 @@ import ./make-test-python.nix ( services.fcgiwrap.gitolite = { process.user = "gitolite"; process.group = "gitolite"; + socket = { inherit (config.services.nginx) user group; }; }; services.gitolite = { From 51b246a1acd4ce4926b8a78e3e7e0e6927d546ff Mon Sep 17 00:00:00 2001 From: euxane Date: Sat, 8 Jun 2024 23:07:33 +0200 Subject: [PATCH 09/11] nixos/fcgiwrap: do not run as root by default Use a dynamic user instead unless one is specified. --- nixos/doc/manual/release-notes/rl-2411.section.md | 2 ++ nixos/modules/services/web-servers/fcgiwrap.nix | 11 ++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/nixos/doc/manual/release-notes/rl-2411.section.md b/nixos/doc/manual/release-notes/rl-2411.section.md index 3caa36ab22d8..eebecf3c5dff 100644 --- a/nixos/doc/manual/release-notes/rl-2411.section.md +++ b/nixos/doc/manual/release-notes/rl-2411.section.md @@ -51,6 +51,8 @@ `services.fcgiwrap.*` to `services.fcgiwrap.some-instance.*`. The ownership and mode of the UNIX sockets created by this service are now configurable and private by default. + Processes also now run as a dynamically allocated user by default instead of + root. - `nvimpager` was updated to version 0.13.0, which changes the order of user and nvimpager settings: user commands in `-c` and `--cmd` now override the diff --git a/nixos/modules/services/web-servers/fcgiwrap.nix b/nixos/modules/services/web-servers/fcgiwrap.nix index 6b633386089f..29ddd39942c6 100644 --- a/nixos/modules/services/web-servers/fcgiwrap.nix +++ b/nixos/modules/services/web-servers/fcgiwrap.nix @@ -21,7 +21,10 @@ in { process.user = mkOption { type = types.nullOr types.str; default = null; - description = "User as which this instance of fcgiwrap will be run."; + description = '' + User as which this instance of fcgiwrap will be run. + Set to `null` (the default) to use a dynamically allocated user. + ''; }; process.group = mkOption { @@ -106,10 +109,12 @@ in { s = "${cfg.socket.type}:${cfg.socket.address}"; }))} ''; - } // (if cfg.process.user != null && cfg.process.group != null then { + } // (if cfg.process.user != null then { User = cfg.process.user; Group = cfg.process.group; - } else { } ); + } else { + DynamicUser = true; + }); }); systemd.sockets = forEachInstance (cfg: mkIf (cfg.socket.type == "unix") { From 2d8626bf0a35659a480c1a92bbd2682625a66e0f Mon Sep 17 00:00:00 2001 From: euxane Date: Sat, 8 Jun 2024 23:08:55 +0200 Subject: [PATCH 10/11] nixos/cgit: configurable user instead of root This allows running cgit instances using dedicated users instead of root. This is now set to "cgit" by default. --- .../manual/release-notes/rl-2411.section.md | 4 ++++ nixos/modules/services/networking/cgit.nix | 21 +++++++++++++++++++ nixos/tests/cgit.nix | 8 +++---- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/nixos/doc/manual/release-notes/rl-2411.section.md b/nixos/doc/manual/release-notes/rl-2411.section.md index eebecf3c5dff..0bf1a1ba93d4 100644 --- a/nixos/doc/manual/release-notes/rl-2411.section.md +++ b/nixos/doc/manual/release-notes/rl-2411.section.md @@ -54,6 +54,10 @@ Processes also now run as a dynamically allocated user by default instead of root. +- `services.cgit` now runs as the cgit user by default instead of root. + This change requires granting access to the repositories to this user or + setting the appropriate one through `services.cgit.some-instance.user`. + - `nvimpager` was updated to version 0.13.0, which changes the order of user and nvimpager settings: user commands in `-c` and `--cmd` now override the respective default settings because they are executed later. diff --git a/nixos/modules/services/networking/cgit.nix b/nixos/modules/services/networking/cgit.nix index 640c989aaf4c..cf4e137ae935 100644 --- a/nixos/modules/services/networking/cgit.nix +++ b/nixos/modules/services/networking/cgit.nix @@ -154,6 +154,18 @@ in type = types.lines; default = ""; }; + + user = mkOption { + description = "User to run the cgit service as."; + type = types.str; + default = "cgit"; + }; + + group = mkOption { + description = "Group to run the cgit service as."; + type = types.str; + default = "cgit"; + }; }; })); }; @@ -165,8 +177,17 @@ in message = "Exactly one of services.cgit.${vhost}.scanPath or services.cgit.${vhost}.repos must be set."; }) cfgs; + users = mkMerge (flip mapAttrsToList cfgs (_: cfg: { + users.${cfg.user} = { + isSystemUser = true; + inherit (cfg) group; + }; + groups.${cfg.group} = { }; + })); + services.fcgiwrap = flip mapAttrs' cfgs (name: cfg: nameValuePair "cgit-${name}" { + process = { inherit (cfg) user group; }; socket = { inherit (config.services.nginx) user group; }; } ); diff --git a/nixos/tests/cgit.nix b/nixos/tests/cgit.nix index 6aed06adefdf..3107e7b964a3 100644 --- a/nixos/tests/cgit.nix +++ b/nixos/tests/cgit.nix @@ -23,7 +23,7 @@ in { nginx.location = "/(c)git/"; repos = { some-repo = { - path = "/srv/git/some-repo"; + path = "/tmp/git/some-repo"; desc = "some-repo description"; }; }; @@ -50,12 +50,12 @@ in { server.fail("curl -fsS http://localhost/robots.txt") - server.succeed("${pkgs.writeShellScript "setup-cgit-test-repo" '' + server.succeed("sudo -u cgit ${pkgs.writeShellScript "setup-cgit-test-repo" '' set -e - git init --bare -b master /srv/git/some-repo + git init --bare -b master /tmp/git/some-repo git init -b master reference cd reference - git remote add origin /srv/git/some-repo + git remote add origin /tmp/git/some-repo date > date.txt git add date.txt git -c user.name=test -c user.email=test@localhost commit -m 'add date' From 3d10deb7a5631e057bb5d84b5a6bd8fdf361a00a Mon Sep 17 00:00:00 2001 From: euxane Date: Sat, 8 Jun 2024 23:08:59 +0200 Subject: [PATCH 11/11] nixos/cgit: fix GIT_PROJECT_ROOT ownership The GIT_PROJECT_ROOT directory is now created at runtime instead of being assembled at build time. This fixes ownership issues which prevented those repositories to be read by users other than root. This also avoids creating symlinks in the nix store pointing to the outside. --- nixos/modules/services/networking/cgit.nix | 41 +++++++++++----------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/nixos/modules/services/networking/cgit.nix b/nixos/modules/services/networking/cgit.nix index cf4e137ae935..de8128ed5a59 100644 --- a/nixos/modules/services/networking/cgit.nix +++ b/nixos/modules/services/networking/cgit.nix @@ -72,25 +72,11 @@ let ${cfg.extraConfig} ''; - mkCgitReposDir = cfg: - if cfg.scanPath != null then - cfg.scanPath - else - pkgs.runCommand "cgit-repos" { - preferLocalBuild = true; - allowSubstitutes = false; - } '' - mkdir -p "$out" - ${ - concatStrings ( - mapAttrsToList - (name: value: '' - ln -s ${escapeShellArg value.path} "$out"/${escapeShellArg name} - '') - cfg.repos - ) - } - ''; + fcgiwrapUnitName = name: "fcgiwrap-cgit-${name}"; + fcgiwrapRuntimeDir = name: "/run/${fcgiwrapUnitName name}"; + gitProjectRoot = name: cfg: if cfg.scanPath != null + then cfg.scanPath + else "${fcgiwrapRuntimeDir name}/repos"; in { @@ -192,6 +178,21 @@ in } ); + systemd.services = flip mapAttrs' cfgs (name: cfg: + nameValuePair (fcgiwrapUnitName name) + (mkIf (cfg.repos != { }) { + serviceConfig.RuntimeDirectory = fcgiwrapUnitName name; + preStart = '' + GIT_PROJECT_ROOT=${escapeShellArg (gitProjectRoot name cfg)} + mkdir -p "$GIT_PROJECT_ROOT" + cd "$GIT_PROJECT_ROOT" + ${concatLines (flip mapAttrsToList cfg.repos (name: repo: '' + ln -s ${escapeShellArg repo.path} ${escapeShellArg name} + ''))} + ''; + } + )); + services.nginx.enable = true; services.nginx.virtualHosts = mkMerge (mapAttrsToList (name: cfg: { @@ -209,7 +210,7 @@ in fastcgiParams = rec { SCRIPT_FILENAME = "${pkgs.git}/libexec/git-core/git-http-backend"; GIT_HTTP_EXPORT_ALL = "1"; - GIT_PROJECT_ROOT = mkCgitReposDir cfg; + GIT_PROJECT_ROOT = gitProjectRoot name cfg; HOME = GIT_PROJECT_ROOT; }; extraConfig = mkFastcgiPass name cfg;