PR #56255 broke services.nginx.user = "root"; -- first discovered in https://github.com/NixOS/nixpkgs/pull/56255#issuecomment-608048848.
root, so the nginx master process ran as root, and then the worker processes ran as an unprivileged user (user nginx nginx here, or nobody by default if thta line is not given).nginx, including the master process.When you change the default back to root (e.g. because your setup needs the master process to be root, or for replicate the setup as it was in 19.09) using services.nginx.user = "root";, it does not work out of the box.
That is because the module now assumes that nginx will run only as 1 user, and it sets file system permissions that way. But nginx running as root makes nginx drop privileges to nobody or the configured user username usergroup, and the file system permissions are not set up correctly for that as shown in https://github.com/NixOS/nixpkgs/pull/56255#issuecomment-608048848.
FYI this is how I'm handling the situation with httpd:
https://github.com/NixOS/nixpkgs/blob/92e9009172a8ae5d26a5ddc6eb0b2a8cbb394637/nixos/modules/services/web-servers/apache-httpd/default.nix#L686..L693
The sysadmin should override serviceConfig.User and serviceConfig.Group to set the master process user and group, while the user and group options set the effective user and group.
The way the tmpfile rules are currently written is that the ${cfg.group} group doesn't have write, only read access to ${cfg.stateDir}. So it doesn't really work well with running the nginx master process as root, and then drop permissions to a regular user. I assume you also don't want to run worker processes as root either :laughing:…
I'm not sure how to properly approach this problem. Can you elaborate a bit on what's your usecase as running the master process as root?
@nh2 are you able to test this configuration out and report back:
services.nginx.user = "nginx"; # the default
services.nginx.group = "nginx"; # the default
systemd.services.ngnix.serviceConfig.User = lib.mkForce "root";
systemd.services.ngnix.serviceConfig.Group = lib.mkForce "root";
Please take special consideration to look over your logs directory (under ${stateDir}) and the permissions there. I'm not familiar on what user/group permissions the nginx process assigns to log files when it runs with root as the master process... possibly a minor tweak to the tmpfiles will be required, but let me know what happens with your test.
If this works we need to simply add documentation on how to run with root as the master process, as I have explained above.
This issue has been mentioned on NixOS Discourse. There might be relevant details there:
https://discourse.nixos.org/t/go-no-go-meeting-nixos-20-03-markhor/6495/19
Just from the go/no-go meeting, I approved of changes being backported to 20.03
I've gone ahead and tested the above configuration snippet. I had to make a few tweaks and ended up with this as the required configuration to get nginx to run with root as the master process but nginx as the child processes:
services.nginx.appendConfig = let cfg = config.services.nginx; in ''user ${cfg.user} ${cfg.group};'';
systemd.services.ngnix.serviceConfig.User = lib.mkForce "root";
@nh2 can you please describe your requirement to run the master process as root? I'm not familiar with nginx so I have a hard time judging whether your case is an edge case or not.
@flokli depending on the answer from @nh2 we need to make a decision about whether we need to simply improve documentation, or improve documentation and modify the module code to make this use case better supported. I'm currently leaning toward this being a documentation only thing, but let's wait to hear what @nh2 has to say.
@aanderse I think your suggestion is good, I will try it today for our setup and report back.
If it works, then I agree that it is sufficient to document your approach in the release notes (and perhaps as a description in one of the config options (probably the one for .user is best).
On my use case, it is:
services.nginx.appendConfig = let cfg = config.services.nginx; in ''user ${cfg.user} ${cfg.group};''; systemd.services.ngnix.serviceConfig.User = lib.mkForce "root";
@aanderse What an evil typo! ngnix -> nginx :fire:
NixOS does not complain when options on non-existent systemd services are set (should it?) -- that took a while to figure out :grin:
@aanderse I have checked your approach now on my staging deployment; it works (with the typo removed). What we should add to the changelog:
services.nginx.appendConfig = let cfg = config.services.nginx; in ''user ${cfg.user} ${cfg.group};'';
systemd.services.nginx.serviceConfig.User = lib.mkForce "root";
@nh2 glad to hear. Are you able to make a PR and document this as described?
Thanks! :tada:
master PR: #84960
We should cherry-pick it on release-20.03 after merge.
@aanderse Thanks for finding the best solution!