nixos/please: init module

Co-authored-by: Cole Helbling <cole.e.helbling@outlook.com>

Changed files
+201
nixos
doc
manual
from_md
release-notes
release-notes
modules
tests
pkgs
tools
security
please
+7
nixos/doc/manual/from_md/release-notes/rl-2211.section.xml
···
</listitem>
<listitem>
<para>
+
<link xlink:href="https://github.com/edneville/please">Please</link>,
+
a Sudo clone written in Rust. Available as
+
<link linkend="opt-security.please.enable">security.please</link>
+
</para>
+
</listitem>
+
<listitem>
+
<para>
<link xlink:href="https://github.com/messagebird/sachet/">Sachet</link>,
an SMS alerting tool for the Prometheus Alertmanager.
Available as
+2
nixos/doc/manual/release-notes/rl-2211.section.md
···
- [HBase cluster](https://hbase.apache.org/), a distributed, scalable, big data store. Available as [services.hadoop.hbase](options.html#opt-services.hadoop.hbase.enable).
+
- [Please](https://github.com/edneville/please), a Sudo clone written in Rust. Available as [security.please](#opt-security.please.enable)
+
- [Sachet](https://github.com/messagebird/sachet/), an SMS alerting tool for the Prometheus Alertmanager. Available as [services.prometheus.sachet](#opt-services.prometheus.sachet.enable).
- [infnoise](https://github.com/leetronics/infnoise), a hardware True Random Number Generator dongle.
+1
nixos/modules/module-list.nix
···
./security/pam.nix
./security/pam_usb.nix
./security/pam_mount.nix
+
./security/please.nix
./security/polkit.nix
./security/rngd.nix
./security/rtkit.nix
+122
nixos/modules/security/please.nix
···
+
{ config, lib, pkgs, ... }:
+
+
with lib;
+
+
let
+
cfg = config.security.please;
+
ini = pkgs.formats.ini { };
+
in
+
{
+
options.security.please = {
+
enable = mkEnableOption (mdDoc ''
+
please, a Sudo clone which allows a users to execute a command or edit a
+
file as another user
+
'');
+
+
package = mkOption {
+
type = types.package;
+
default = pkgs.please;
+
defaultText = literalExpression "pkgs.please";
+
description = mdDoc ''
+
Which package to use for {command}`please`.
+
'';
+
};
+
+
wheelNeedsPassword = mkOption {
+
type = types.bool;
+
default = true;
+
description = lib.mdDoc ''
+
Whether users of the `wheel` group must provide a password to run
+
commands or edit files with {command}`please` and
+
{command}`pleaseedit` respectively.
+
'';
+
};
+
+
settings = mkOption {
+
type = ini.type;
+
default = { };
+
example = {
+
jim_run_any_as_root = {
+
name = "jim";
+
type = "run";
+
target = "root";
+
rule = ".*";
+
require_pass = false;
+
};
+
jim_edit_etc_hosts_as_root = {
+
name = "jim";
+
type = "edit";
+
target = "root";
+
rule = "/etc/hosts";
+
editmode = 644;
+
require_pass = true;
+
};
+
};
+
description = mdDoc ''
+
Please configuration. Refer to
+
<https://github.com/edneville/please/blob/master/please.ini.md> for
+
details.
+
'';
+
};
+
};
+
+
config = mkIf cfg.enable {
+
security.wrappers =
+
let
+
owner = "root";
+
group = "root";
+
setuid = true;
+
in
+
{
+
please = {
+
source = "${cfg.package}/bin/please";
+
inherit owner group setuid;
+
};
+
pleaseedit = {
+
source = "${cfg.package}/bin/pleaseedit";
+
inherit owner group setuid;
+
};
+
};
+
+
security.please.settings = rec {
+
# The "wheel" group is allowed to do anything by default but this can be
+
# overridden.
+
wheel_run_as_any = {
+
type = "run";
+
group = true;
+
name = "wheel";
+
target = ".*";
+
rule = ".*";
+
require_pass = cfg.wheelNeedsPassword;
+
};
+
wheel_edit_as_any = wheel_run_as_any // { type = "edit"; };
+
wheel_list_as_any = wheel_run_as_any // { type = "list"; };
+
};
+
+
environment = {
+
systemPackages = [ cfg.package ];
+
+
etc."please.ini".source = ini.generate "please.ini"
+
(cfg.settings // (rec {
+
# The "root" user is allowed to do anything by default and this cannot
+
# be overridden.
+
root_run_as_any = {
+
type = "run";
+
name = "root";
+
target = ".*";
+
rule = ".*";
+
require_pass = false;
+
};
+
root_edit_as_any = root_run_as_any // { type = "edit"; };
+
root_list_as_any = root_run_as_any // { type = "list"; };
+
}));
+
};
+
+
security.pam.services.please = {
+
sshAgentAuth = true;
+
usshAuth = true;
+
};
+
+
meta.maintainers = with maintainers; [ azahi ];
+
};
+
}
+1
nixos/tests/all-tests.nix
···
plasma5 = handleTest ./plasma5.nix {};
plasma5-systemd-start = handleTest ./plasma5-systemd-start.nix {};
plausible = handleTest ./plausible.nix {};
+
please = handleTest ./please.nix {};
pleroma = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./pleroma.nix {};
plikd = handleTest ./plikd.nix {};
plotinus = handleTest ./plotinus.nix {};
+66
nixos/tests/please.nix
···
+
import ./make-test-python.nix ({ lib, ... }:
+
{
+
name = "please";
+
meta.maintainers = with lib.maintainers; [ azahi ];
+
+
nodes.machine =
+
{ ... }:
+
{
+
users.users = with lib; mkMerge [
+
(listToAttrs (map
+
(n: nameValuePair n { isNormalUser = true; })
+
(genList (x: "user${toString x}") 6)))
+
{
+
user0.extraGroups = [ "wheel" ];
+
}
+
];
+
+
security.please = {
+
enable = true;
+
wheelNeedsPassword = false;
+
settings = {
+
user2_run_true_as_root = {
+
name = "user2";
+
target = "root";
+
rule = "/run/current-system/sw/bin/true";
+
require_pass = false;
+
};
+
user4_edit_etc_hosts_as_root = {
+
name = "user4";
+
type = "edit";
+
target = "root";
+
rule = "/etc/hosts";
+
editmode = 644;
+
require_pass = false;
+
};
+
};
+
};
+
};
+
+
testScript = ''
+
with subtest("root: can run anything by default"):
+
machine.succeed('please true')
+
with subtest("root: can edit anything by default"):
+
machine.succeed('EDITOR=cat pleaseedit /etc/hosts')
+
+
with subtest("user0: can run as root because it's in the wheel group"):
+
machine.succeed('su - user0 -c "please -u root true"')
+
with subtest("user1: cannot run as root because it's not in the wheel group"):
+
machine.fail('su - user1 -c "please -u root true"')
+
+
with subtest("user0: can edit as root"):
+
machine.succeed('su - user0 -c "EDITOR=cat pleaseedit /etc/hosts"')
+
with subtest("user1: cannot edit as root"):
+
machine.fail('su - user1 -c "EDITOR=cat pleaseedit /etc/hosts"')
+
+
with subtest("user2: can run 'true' as root"):
+
machine.succeed('su - user2 -c "please -u root true"')
+
with subtest("user3: cannot run 'true' as root"):
+
machine.fail('su - user3 -c "please -u root true"')
+
+
with subtest("user4: can edit /etc/hosts"):
+
machine.succeed('su - user4 -c "EDITOR=cat pleaseedit /etc/hosts"')
+
with subtest("user5: cannot edit /etc/hosts"):
+
machine.fail('su - user5 -c "EDITOR=cat pleaseedit /etc/hosts"')
+
'';
+
})
+2
pkgs/tools/security/please/default.nix
···
installManPage man/*
'';
+
passthru.tests = { inherit (nixosTests) please; };
+
meta = with lib; {
description = "A polite regex-first sudo alternative";
longDescription = ''