From 77430d388d03dea21c65813b4c48a346a9c7b79f Mon Sep 17 00:00:00 2001 From: gaykitty <126119280+gaykitty@users.noreply.github.com> Date: Sun, 10 Mar 2024 15:08:11 -0400 Subject: [PATCH 1/2] nixos/stargazer: add allowCgiUser to make cgi-user option work Previously the cgi-user option in stargazer was broken in this module because stargazer didn't have CAP_SETUID and CAP_SETGID. cgi-user tells stargazer to run cgi processes as a different user. I added an option allowCgiUser that give stargazer these capabilities when enabled. I made this an option because access to those syscalls greatly increases the damage a RCE bug in stargazer could do. So they should only be enabled if needed. --- .../services/web-servers/stargazer.nix | 19 ++++++++++++ nixos/tests/web-servers/stargazer.nix | 29 ++++++++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/nixos/modules/services/web-servers/stargazer.nix b/nixos/modules/services/web-servers/stargazer.nix index da39c8172c8bf..b8374313723da 100644 --- a/nixos/modules/services/web-servers/stargazer.nix +++ b/nixos/modules/services/web-servers/stargazer.nix @@ -83,6 +83,21 @@ in ''; }; + allowCgiUser = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + When enabled, the stargazer process will be given `CAP_SETGID` + and `CAP_SETUID` so that it can run cgi processes as a different + user. This is required if the `cgi-user` option is used for a route. + Note that these capabilities could allow privilege escalation so be + careful. For that reason, this is disabled by default. + + You will need to create the user mentioned `cgi-user` if it does not + already exist. + ''; + }; + store = lib.mkOption { type = lib.types.path; default = /var/lib/gemini/certs; @@ -206,6 +221,10 @@ in # User and group User = cfg.user; Group = cfg.group; + AmbientCapabilities = lib.mkIf cfg.allowCgiUser [ + "CAP_SETGID" + "CAP_SETUID" + ]; }; }; diff --git a/nixos/tests/web-servers/stargazer.nix b/nixos/tests/web-servers/stargazer.nix index 52bc93af17194..70a9fee456f12 100644 --- a/nixos/tests/web-servers/stargazer.nix +++ b/nixos/tests/web-servers/stargazer.nix @@ -117,16 +117,43 @@ in }; }; }; + cgiTestServer = { ... }: { + users.users.cgi = { + isSystemUser = true; + group = "cgi"; + }; + users.groups.cgi = { }; + services.stargazer = { + enable = true; + connectionLogging = false; + requestTimeout = 1; + allowCgiUser = true; + routes = [ + { + route = "localhost:/cgi-bin"; + root = "${test_env}/test_data"; + cgi = true; + cgi-timeout = 5; + cgi-user = "cgi"; + } + ]; + }; + }; }; testScript = { nodes, ... }: '' geminiserver.wait_for_unit("scgi_server") geminiserver.wait_for_open_port(1099) geminiserver.wait_for_unit("stargazer") - geminiserver.wait_for_open_port(1965) + geminiserver.wait_for_unit("stargazer") + cgiTestServer.wait_for_open_port(1965) + cgiTestServer.wait_for_open_port(1965) with subtest("stargazer test suite"): response = geminiserver.succeed("sh -c 'cd ${test_env}; ${test_script}/bin/test'") print(response) + with subtest("stargazer cgi-user test"): + response = cgiTestServer.succeed("sh -c 'cd ${test_env}; ${test_script}/bin/test --checks CGIVars'") + print(response) ''; } From be1336d8b84ba89607268bedcd6f5ed0c4030c5c Mon Sep 17 00:00:00 2001 From: gaykitty <126119280+gaykitty@users.noreply.github.com> Date: Sun, 10 Mar 2024 15:22:32 -0400 Subject: [PATCH 2/2] nixos/stargazer: harden systemd service --- .../manual/release-notes/rl-2411.section.md | 6 +++ .../services/web-servers/stargazer.nix | 38 +++++++++++++++++++ nixos/tests/web-servers/stargazer.nix | 2 - 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/nixos/doc/manual/release-notes/rl-2411.section.md b/nixos/doc/manual/release-notes/rl-2411.section.md index 29e29afd80acc..849be73761334 100644 --- a/nixos/doc/manual/release-notes/rl-2411.section.md +++ b/nixos/doc/manual/release-notes/rl-2411.section.md @@ -235,6 +235,12 @@ for `stateVersion` ≥ 24.11. (It was previously using SQLite for structured data and the filesystem for blobs). +- The `stargazer` service has been hardened to improve security, but these + changes make break certain setups, particularly around traditional CGI. + + - The `stargazer.allowCgiUser` option has been added, enabling + Stargazer's `cgi-user` option to work, which was previously broken. + - The `shiori` service now requires an HTTP secret value `SHIORI_HTTP_SECRET_KEY` to be provided via environment variable. The nixos module therefore, now provides an environmentFile option: ``` diff --git a/nixos/modules/services/web-servers/stargazer.nix b/nixos/modules/services/web-servers/stargazer.nix index b8374313723da..249fd30bf6001 100644 --- a/nixos/modules/services/web-servers/stargazer.nix +++ b/nixos/modules/services/web-servers/stargazer.nix @@ -225,6 +225,44 @@ in "CAP_SETGID" "CAP_SETUID" ]; + + # Hardening + UMask = "0077"; + PrivateTmp = true; + ProtectHome = true; + ProtectSystem = "full"; + ProtectClock = true; + ProtectHostname = true; + ProtectControlGroups = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + PrivateDevices = true; + NoNewPrivileges = true; + RestrictSUIDSGID = true; + PrivateMounts = true; + MemoryDenyWriteExecute = true; + LockPersonality = true; + RestrictRealtime = true; + RemoveIPC = true; + CapabilityBoundingSet = [ + "~CAP_SYS_PTRACE" + "~CAP_SYS_ADMIN" + "~CAP_SETPCAP" + "~CAP_SYS_TIME" + "~CAP_SYS_PACCT" + "~CAP_SYS_TTY_CONFIG " + "~CAP_SYS_CHROOT" + "~CAP_SYS_BOOT" + "~CAP_NET_ADMIN" + ] ++ lib.lists.optional (!cfg.allowCgiUser) [ + "~CAP_SETGID" + "~CAP_SETUID" + ]; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "~@cpu-emulation @debug @keyring @mount @obsolete" ] + ++ lib.lists.optional (!cfg.allowCgiUser) [ "@privileged @setuid" ]; }; }; diff --git a/nixos/tests/web-servers/stargazer.nix b/nixos/tests/web-servers/stargazer.nix index 70a9fee456f12..b687f2046a044 100644 --- a/nixos/tests/web-servers/stargazer.nix +++ b/nixos/tests/web-servers/stargazer.nix @@ -145,8 +145,6 @@ in geminiserver.wait_for_unit("scgi_server") geminiserver.wait_for_open_port(1099) geminiserver.wait_for_unit("stargazer") - geminiserver.wait_for_unit("stargazer") - cgiTestServer.wait_for_open_port(1965) cgiTestServer.wait_for_open_port(1965) with subtest("stargazer test suite"):