Skip to content
This repository was archived by the owner on Apr 12, 2021. It is now read-only.
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: NixOS/nixpkgs-channels
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: e5e309090803
Choose a base ref
...
head repository: NixOS/nixpkgs-channels
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: eb639a82e018
Choose a head ref
  • 10 commits
  • 10 files changed
  • 8 contributors

Commits on Apr 20, 2020

  1. oq: init at 1.0.2

    Br1ght0ne committed Apr 20, 2020

    Verified

    This commit was signed with the committer’s verified signature.
    edolstra Eelco Dolstra
    Copy the full SHA
    c28f6fc View commit details

Commits on May 4, 2020

  1. nixos/doas: init

    `doas` is a lighter alternative to `sudo` that "provide[s] 95% of the
    features of `sudo` with a fraction of the codebase" [1]. I prefer it to
    `sudo`, so I figured I would add a NixOS module in order for it to be
    easier to use. The module is based off of the existing `sudo` module.
    
    [1] https://github.com/Duncaen/OpenDoas
    cole-h committed May 4, 2020

    Verified

    This commit was signed with the committer’s verified signature.
    edolstra Eelco Dolstra
    Copy the full SHA
    446fb00 View commit details

Commits on May 6, 2020

  1. Copy the full SHA
    f798f07 View commit details

Commits on May 8, 2020

  1. gcc10, gfortran10, gnat10: init at 10.1.0

    Everything is copied as-is from 9 (except version and hash).
    Some platform-specific patches might not apply anymore;
    I'm lazily leaving that for the community to fix.
    vcunat committed May 8, 2020
    Copy the full SHA
    da59984 View commit details

Commits on May 9, 2020

  1. Copy the full SHA
    943b8b6 View commit details

Commits on May 10, 2020

  1. Copy the full SHA
    2e192dc View commit details
  2. Copy the full SHA
    29d0e68 View commit details
  3. Merge pull request #86488 from cole-h/doas

    nixos/doas: init
    adisbladis authored May 10, 2020
    Copy the full SHA
    68ee239 View commit details
  4. Merge pull request #75042 from filalex77/oq-0.2.1

    oq: init at 1.0.2
    etu authored May 10, 2020
    Copy the full SHA
    192c150 View commit details
  5. Merge pull request #87439 from marsam/update-gopass

    gopass: 1.9.0 -> 1.9.1
    andir authored May 10, 2020
    Copy the full SHA
    eb639a8 View commit details
4 changes: 3 additions & 1 deletion nixos/doc/manual/release-notes/rl-2009.xml
Original file line number Diff line number Diff line change
@@ -77,7 +77,9 @@

<itemizedlist>
<listitem>
<para />
<para>
There is a new <xref linkend="opt-security.doas.enable"/> module that provides <command>doas</command>, a lighter alternative to <command>sudo</command> with many of the same features.
</para>
</listitem>
</itemizedlist>

1 change: 1 addition & 0 deletions nixos/modules/module-list.nix
Original file line number Diff line number Diff line change
@@ -200,6 +200,7 @@
./security/rtkit.nix
./security/wrappers/default.nix
./security/sudo.nix
./security/doas.nix
./security/systemd-confinement.nix
./security/tpm2.nix
./services/admin/oxidized.nix
274 changes: 274 additions & 0 deletions nixos/modules/security/doas.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
{ config, lib, pkgs, ... }:

with lib;
let
cfg = config.security.doas;

inherit (pkgs) doas;

mkUsrString = user: toString user;

mkGrpString = group: ":${toString group}";

mkOpts = rule: concatStringsSep " " [
(optionalString rule.noPass "nopass")
(optionalString rule.persist "persist")
(optionalString rule.keepEnv "keepenv")
"setenv { SSH_AUTH_SOCK ${concatStringsSep " " rule.setEnv} }"
];

mkArgs = rule:
if (isNull rule.args) then ""
else if (length rule.args == 0) then "args"
else "args ${concatStringsSep " " rule.args}";

mkRule = rule:
let
opts = mkOpts rule;

as = optionalString (!isNull rule.runAs) "as ${rule.runAs}";

cmd = optionalString (!isNull rule.cmd) "cmd ${rule.cmd}";

args = mkArgs rule;
in
optionals (length cfg.extraRules > 0) [
(
optionalString (length rule.users > 0)
(map (usr: "permit ${opts} ${mkUsrString usr} ${as} ${cmd} ${args}") rule.users)
)
(
optionalString (length rule.groups > 0)
(map (grp: "permit ${opts} ${mkGrpString grp} ${as} ${cmd} ${args}") rule.groups)
)
];
in
{

###### interface

options.security.doas = {

enable = mkOption {
type = with types; bool;
default = false;
description = ''
Whether to enable the <command>doas</command> command, which allows
non-root users to execute commands as root.
'';
};

wheelNeedsPassword = mkOption {
type = with types; bool;
default = true;
description = ''
Whether users of the <code>wheel</code> group must provide a password to
run commands as super user via <command>doas</command>.
'';
};

extraRules = mkOption {
default = [];
description = ''
Define specific rules to be set in the
<filename>/etc/doas.conf</filename> file. More specific rules should
come after more general ones in order to yield the expected behavior.
You can use <code>mkBefore</code> and/or <code>mkAfter</code> to ensure
this is the case when configuration options are merged.
'';
example = literalExample ''
[
# Allow execution of any command by any user in group doas, requiring
# a password and keeping any previously-defined environment variables.
{ groups = [ "doas" ]; noPass = false; keepEnv = true; }
# Allow execution of "/home/root/secret.sh" by user `backup` OR user
# `database` OR any member of the group with GID `1006`, without a
# password.
{ users = [ "backup" "database" ]; groups = [ 1006 ];
cmd = "/home/root/secret.sh"; noPass = true; }
# Allow any member of group `bar` to run `/home/baz/cmd1.sh` as user
# `foo` with argument `hello-doas`.
{ groups = [ "bar" ]; runAs = "foo";
cmd = "/home/baz/cmd1.sh"; args = [ "hello-doas" ]; }
# Allow any member of group `bar` to run `/home/baz/cmd2.sh` as user
# `foo` with no arguments.
{ groups = [ "bar" ]; runAs = "foo";
cmd = "/home/baz/cmd2.sh"; args = [ ]; }
# Allow user `abusers` to execute "nano" and unset the value of
# SSH_AUTH_SOCK, override the value of ALPHA to 1, and inherit the
# value of BETA from the current environment.
{ users = [ "abusers" ]; cmd = "nano";
setEnv = [ "-SSH_AUTH_SOCK" "ALPHA=1" "BETA" ]; }
]
'';
type = with types; listOf (
submodule {
options = {

noPass = mkOption {
type = with types; bool;
default = false;
description = ''
If <code>true</code>, the user is not required to enter a
password.
'';
};

persist = mkOption {
type = with types; bool;
default = false;
description = ''
If <code>true</code>, do not ask for a password again for some
time after the user successfully authenticates.
'';
};

keepEnv = mkOption {
type = with types; bool;
default = false;
description = ''
If <code>true</code>, environment variables other than those
listed in
<citerefentry><refentrytitle>doas</refentrytitle><manvolnum>1</manvolnum></citerefentry>
are kept when creating the environment for the new process.
'';
};

setEnv = mkOption {
type = with types; listOf str;
default = [];
description = ''
Keep or set the specified variables. Variables may also be
removed with a leading '-' or set using
<code>variable=value</code>. If the first character of
<code>value</code> is a '$', the value to be set is taken from
the existing environment variable of the indicated name. This
option is processed after the default environment has been
created.
NOTE: All rules have <code>setenv { SSH_AUTH_SOCK }</code> by
default. To prevent <code>SSH_AUTH_SOCK</code> from being
inherited, add <code>"-SSH_AUTH_SOCK"</code> anywhere in this
list.
'';
};

users = mkOption {
type = with types; listOf (either str int);
default = [];
description = "The usernames / UIDs this rule should apply for.";
};

groups = mkOption {
type = with types; listOf (either str int);
default = [];
description = "The groups / GIDs this rule should apply for.";
};

runAs = mkOption {
type = with types; nullOr str;
default = null;
description = ''
Which user or group the specified command is allowed to run as.
When set to <code>null</code> (the default), all users are
allowed.
A user can be specified using just the username:
<code>"foo"</code>. It is also possible to only allow running as
a specific group with <code>":bar"</code>.
'';
};

cmd = mkOption {
type = with types; nullOr str;
default = null;
description = ''
The command the user is allowed to run. When set to
<code>null</code> (the default), all commands are allowed.
NOTE: It is best practice to specify absolute paths. If a
relative path is specified, only a restricted PATH will be
searched.
'';
};

args = mkOption {
type = with types; nullOr (listOf str);
default = null;
description = ''
Arguments that must be provided to the command. When set to
<code>[]</code>, the command must be run without any arguments.
'';
};
};
}
);
};

extraConfig = mkOption {
type = with types; lines;
default = "";
description = ''
Extra configuration text appended to <filename>doas.conf</filename>.
'';
};
};


###### implementation

config = mkIf cfg.enable {

security.doas.extraRules = [
{
groups = [ "wheel" ];
noPass = !cfg.wheelNeedsPassword;
}
];

security.wrappers = {
doas.source = "${doas}/bin/doas";
};

environment.systemPackages = [
doas
];

security.pam.services.doas = {
allowNullPassword = true;
sshAgentAuth = true;
};

environment.etc."doas.conf" = {
source = pkgs.runCommand "doas-conf"
{
src = pkgs.writeText "doas-conf-in" ''
# To modify this file, set the NixOS options
# `security.doas.extraRules` or `security.doas.extraConfig`. To
# completely replace the contents of this file, use
# `environment.etc."doas.conf"`.
# "root" is allowed to do anything.
permit nopass keepenv root
# extraRules
${concatStringsSep "\n" (lists.flatten (map mkRule cfg.extraRules))}
# extraConfig
${cfg.extraConfig}
'';
preferLocalBuild = true;
}
# Make sure that the doas.conf file is syntactically valid.
"${pkgs.buildPackages.doas}/bin/doas -C $src && cp $src $out";
mode = "0440";
};

};

meta.maintainers = with maintainers; [ cole-h ];
}
1 change: 1 addition & 0 deletions nixos/tests/all-tests.nix
Original file line number Diff line number Diff line change
@@ -68,6 +68,7 @@ in
deluge = handleTest ./deluge.nix {};
dhparams = handleTest ./dhparams.nix {};
dnscrypt-proxy2 = handleTestOn ["x86_64-linux"] ./dnscrypt-proxy2.nix {};
doas = handleTest ./doas.nix {};
docker = handleTestOn ["x86_64-linux"] ./docker.nix {};
oci-containers = handleTestOn ["x86_64-linux"] ./oci-containers.nix {};
docker-edge = handleTestOn ["x86_64-linux"] ./docker-edge.nix {};
83 changes: 83 additions & 0 deletions nixos/tests/doas.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Some tests to ensure doas is working properly.
import ./make-test-python.nix (
{ lib, ... }: {
name = "doas";
meta = with lib.maintainers; {
maintainers = [ cole-h ];
};

machine =
{ ... }:
{
users.groups = { foobar = {}; barfoo = {}; baz = { gid = 1337; }; };
users.users = {
test0 = { isNormalUser = true; extraGroups = [ "wheel" ]; };
test1 = { isNormalUser = true; };
test2 = { isNormalUser = true; extraGroups = [ "foobar" ]; };
test3 = { isNormalUser = true; extraGroups = [ "barfoo" ]; };
test4 = { isNormalUser = true; extraGroups = [ "baz" ]; };
test5 = { isNormalUser = true; };
test6 = { isNormalUser = true; };
test7 = { isNormalUser = true; };
};

security.doas = {
enable = true;
wheelNeedsPassword = false;

extraRules = [
{ users = [ "test1" ]; groups = [ "foobar" ]; }
{ users = [ "test2" ]; noPass = true; setEnv = [ "CORRECT" "HORSE=BATTERY" ]; }
{ groups = [ "barfoo" 1337 ]; noPass = true; }
{ users = [ "test5" ]; noPass = true; keepEnv = true; runAs = "test1"; }
{ users = [ "test6" ]; noPass = true; keepEnv = true; setEnv = [ "-STAPLE" ]; }
{ users = [ "test7" ]; noPass = true; setEnv = [ "-SSH_AUTH_SOCK" ]; }
];
};
};

testScript = ''
with subtest("users in wheel group should have passwordless doas"):
machine.succeed('su - test0 -c "doas -u root true"')
with subtest("test1 user should not be able to use doas without password"):
machine.fail('su - test1 -c "doas -n -u root true"')
with subtest("test2 user should be able to keep some env"):
if "CORRECT=1" not in machine.succeed('su - test2 -c "CORRECT=1 doas env"'):
raise Exception("failed to keep CORRECT")
if "HORSE=BATTERY" not in machine.succeed('su - test2 -c "doas env"'):
raise Exception("failed to setenv HORSE=BATTERY")
with subtest("users in group 'barfoo' shouldn't require password"):
machine.succeed("doas -u test3 doas -n -u root true")
with subtest("users in group 'baz' (GID 1337) shouldn't require password"):
machine.succeed("doas -u test4 doas -n -u root echo true")
with subtest("test5 user should be able to run commands under test1"):
machine.succeed("doas -u test5 doas -n -u test1 true")
with subtest("test5 user should not be able to run commands under root"):
machine.fail("doas -u test5 doas -n -u root true")
with subtest("test6 user should be able to keepenv"):
envs = ["BATTERY=HORSE", "CORRECT=false"]
out = machine.succeed(
'su - test6 -c "BATTERY=HORSE CORRECT=false STAPLE=Tr0ub4dor doas env"'
)
if not all(env in out for env in envs):
raise Exception("failed to keep BATTERY or CORRECT")
if "STAPLE=Tr0ub4dor" in out:
raise Exception("failed to exclude STAPLE")
with subtest("test7 should not have access to SSH_AUTH_SOCK"):
if "SSH_AUTH_SOCK=HOLEY" in machine.succeed(
'su - test7 -c "SSH_AUTH_SOCK=HOLEY doas env"'
):
raise Exception("failed to exclude SSH_AUTH_SOCK")
'';
}
)
Loading