Commit 710e189c authored by Gabriel Smith's avatar Gabriel Smith Committed by Bjørn Forsman
Browse files

nixos/ups: run upsmon as non-root user "nutmon" by default

NUT documentation strongly suggests running `upsmon` as a non-root user
with very limited permissions. This includes only read access to
`upsmon.conf`.

Previously the `RUN_AS_USER` setting could be overwritten with
`mkForce`, but this left the system with technically incorrect
permissions on `upsmon.conf` that would prevent reloading. This patch
provides a way to change the user that `upsmon` runs as while
maintaining correct permissions on `upsmon.conf`.

Fixes #318582
parent d4b937da
Loading
Loading
Loading
Loading
+27 −19
Original line number Diff line number Diff line
@@ -74,9 +74,9 @@ let
  };

  installSecrets =
    source: target: secrets:
    source: target: owner: secrets:
    pkgs.writeShellScript "installSecrets.sh" ''
      install -m0600 -D ${source} "${target}"
      install -m0600 -o${owner} -D ${source} "${target}"
      ${lib.concatLines (
        lib.forEach secrets (name: ''
          ${pkgs.replace-secret}/bin/replace-secret \
@@ -327,6 +327,23 @@ let
        description = "Whether to enable `upsmon`.";
      };

      user = lib.mkOption {
        type = lib.types.str;
        default = "nutmon";
        description = ''
          User to run `upsmon` as. `upsmon.conf` will have its owner set to this
          user. If not specified, a default user will be created.
        '';
      };
      group = lib.mkOption {
        type = lib.types.str;
        default = "nutmon";
        description = ''
          Group for the default `nutmon` user. If the default user is created
          and this is not specified, a default group will be created.
        '';
      };

      monitor = lib.mkOption {
        type = with lib.types; attrsOf (submodule monitorOptions);
        default = { };
@@ -344,7 +361,6 @@ let
            MONITOR = <generated from config.power.ups.upsmon.monitor>
            NOTIFYCMD = "''${pkgs.nut}/bin/upssched";
            POWERDOWNFLAG = "/run/killpower";
            RUN_AS_USER = "root";
            SHUTDOWNCMD = "''${pkgs.systemd}/bin/shutdown now";
          }
        '';
@@ -382,7 +398,6 @@ let
        );
        NOTIFYCMD = lib.mkDefault "${pkgs.nut}/bin/upssched";
        POWERDOWNFLAG = lib.mkDefault "/run/killpower";
        RUN_AS_USER = "root"; # TODO: replace 'root' by another username.
        SHUTDOWNCMD = lib.mkDefault "${pkgs.systemd}/bin/shutdown now";
      };
    };
@@ -581,7 +596,7 @@ in
    systemd.services.upsmon =
      let
        secrets = lib.mapAttrsToList (name: monitor: "upsmon_password_${name}") cfg.upsmon.monitor;
        createUpsmonConf = installSecrets upsmonConf "/run/nut/upsmon.conf" secrets;
        createUpsmonConf = installSecrets upsmonConf "/run/nut/upsmon.conf" cfg.upsmon.user secrets;
      in
      {
        enable = cfg.upsmon.enable;
@@ -591,7 +606,7 @@ in
        serviceConfig = {
          Type = "forking";
          ExecStartPre = "${createUpsmonConf}";
          ExecStart = "${pkgs.nut}/sbin/upsmon";
          ExecStart = "${pkgs.nut}/sbin/upsmon -u ${cfg.upsmon.user}";
          ExecReload = "${pkgs.nut}/sbin/upsmon -c reload";
          LoadCredential = lib.mapAttrsToList (
            name: monitor: "upsmon_password_${name}:${monitor.passwordFile}"
@@ -604,7 +619,7 @@ in
    systemd.services.upsd =
      let
        secrets = lib.mapAttrsToList (name: user: "upsdusers_password_${name}") cfg.users;
        createUpsdUsers = installSecrets upsdUsers "/run/nut/upsd.users" secrets;
        createUpsdUsers = installSecrets upsdUsers "/run/nut/upsd.users" "root" secrets;
      in
      {
        enable = cfg.upsd.enable;
@@ -696,18 +711,11 @@ in

    services.udev.packages = [ pkgs.nut ];

    /*
        users.users.nut =
          { uid = 84;
            home = "/var/lib/nut";
            createHome = true;
            group = "nut";
            description = "UPnP A/V Media Server user";
    users.users.nutmon = lib.mkIf (cfg.upsmon.user == "nutmon") {
      isSystemUser = true;
      group = cfg.upsmon.group;
    };

        users.groups."nut" =
          { gid = 84; };
    */
    users.groups.nutmon = lib.mkIf (cfg.upsmon.user == "nutmon" && cfg.upsmon.group == "nutmon") { };

  };
}