Unverified Commit 3bd4ec61 authored by Sandro Jäckel's avatar Sandro Jäckel Committed by GitHub
Browse files

nixos/stargazer: module bug fix and hardening (#294795)

parents d85bcd15 be1336d8
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -289,6 +289,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:

  ```
+57 −0
Original line number Diff line number Diff line
@@ -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,48 @@ in
        # User and group
        User = cfg.user;
        Group = cfg.group;
        AmbientCapabilities = lib.mkIf cfg.allowCgiUser [
          "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" ];
      };
    };

+26 −1
Original line number Diff line number Diff line
@@ -117,16 +117,41 @@ 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)
    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)
  '';
}