Copy nixos-container module from nixpkgs
This commit is contained in:
1113
modules/user-jail-containers.nix
Normal file
1113
modules/user-jail-containers.nix
Normal file
File diff suppressed because it is too large
Load Diff
199
modules/user-jails.nix
Normal file
199
modules/user-jails.nix
Normal file
@@ -0,0 +1,199 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
let
|
||||
extraUserOpts = {
|
||||
options.jail = {
|
||||
enable = lib.mkEnableOption "";
|
||||
# TODO: it's not possible to configure this with the current container module
|
||||
# chrootPath = lib.mkOption {
|
||||
# type = with lib.types; nullOr path;
|
||||
# default = null;
|
||||
# example = "/run/users/<uid>/root-mnt";
|
||||
# description = ''
|
||||
# '';
|
||||
# };
|
||||
|
||||
# Bind global nix store instead of a separate isolated one.
|
||||
bindGlobalNixStore = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
description = ''
|
||||
Whether to bindmount the global nix store into the user jail.
|
||||
Note that this will expose any content in the nix store downloaded by
|
||||
other users as well.
|
||||
'';
|
||||
default = false;
|
||||
example = true;
|
||||
};
|
||||
|
||||
allowNetworking = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
description = ''
|
||||
Whether to bridge the user's private network namespace to the main namespace
|
||||
so the user can establish network connections outwards from the host machine.
|
||||
'';
|
||||
default = true;
|
||||
example = false;
|
||||
};
|
||||
|
||||
useGlobalNetworking = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
description = ''
|
||||
Whether to let the user be a part of the main network namespace.
|
||||
'';
|
||||
default = false;
|
||||
example = true;
|
||||
};
|
||||
|
||||
# TODO: not sure if systemd exposes this as an option? In either case, the
|
||||
# nix socket is bindmounted in, so I think the IPC namespace is global
|
||||
# by default
|
||||
# useGlobalIPC = lib.mkOption {
|
||||
# type = lib.types.bool;
|
||||
# description = ''
|
||||
# Whether to let the user be a part of the main IPC namespace.
|
||||
# '';
|
||||
# default = false;
|
||||
# example = true;
|
||||
# };
|
||||
|
||||
useGlobalUsers = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
description = ''
|
||||
Whether to let the user be a part of the main user namespace.
|
||||
'';
|
||||
default = false;
|
||||
example = true;
|
||||
};
|
||||
|
||||
# NOTE: I believe this is inherently disabled
|
||||
# useGlobalPIDs = lib.mkOption {
|
||||
# type = lib.types.bool;
|
||||
# description = ''
|
||||
# Whether to let the user be a part of the main pid namespace.
|
||||
# '';
|
||||
# default = false;
|
||||
# example = true;
|
||||
# };
|
||||
|
||||
# TODO: Let the user configure their own timezone
|
||||
# useGlobalUTS = lib.mkOption {
|
||||
# type = lib.types.bool;
|
||||
# description = ''
|
||||
# Whether to let the user be a part of the main UTS namespace.
|
||||
# '';
|
||||
# default = false;
|
||||
# example = true;
|
||||
# };
|
||||
|
||||
extraBindPaths = lib.mkOption {
|
||||
type = with lib.types; listOf str;
|
||||
description = ''
|
||||
Additional paths to bindmount into the user's jail.
|
||||
'';
|
||||
default = [ ];
|
||||
example = [ ];
|
||||
};
|
||||
|
||||
extraBindReadOnlyPaths = lib.mkOption {
|
||||
type = with lib.types; listOf str;
|
||||
description = ''
|
||||
Additional paths to bindmount as readonly into the user's jail.
|
||||
'';
|
||||
default = [ ];
|
||||
example = [ ];
|
||||
};
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
options = {
|
||||
users.users' = lib.mkOption {
|
||||
default = { };
|
||||
type = with lib.types; attrsOf (submodule extraUserOpts);
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
assertions = [{
|
||||
assertion = config.boot.enableContainers;
|
||||
message = "Machine needs to be able to run containers in order to create user jails";
|
||||
}]
|
||||
# NOTE: needed for uid mapping to work correctly?
|
||||
++ lib.mapAttrsToList (k: _: {
|
||||
assertion = config.users.users.${k}.uid != null;
|
||||
message = "All jailed users need a deterministic uid, missing for user '${k}'";
|
||||
}) (lib.filterAttrs (_: v: v.jail.enable) config.users.users');
|
||||
|
||||
users.users = lib.pipe config.users.users' [
|
||||
(lib.filterAttrs (_: v: v.jail.enable))
|
||||
(lib.mapAttrs' (k: v: {
|
||||
name = k;
|
||||
value.group = k;
|
||||
}))
|
||||
];
|
||||
|
||||
users.groups = lib.pipe config.users.users' [
|
||||
(lib.filterAttrs (_: v: v.jail.enable))
|
||||
(lib.mapAttrs' (k: v: {
|
||||
name = k;
|
||||
value = { gid = config.users.users.${k}.uid; };
|
||||
}))
|
||||
];
|
||||
|
||||
containers = lib.mapAttrs' (k: v: {
|
||||
name = "user-jail-${k}";
|
||||
value = {
|
||||
# TODO: don't linger unless users.users.linger = true;
|
||||
autoStart = true;
|
||||
|
||||
ephemeral = true;
|
||||
privateNetwork = !v.jail.useGlobalNetworking;
|
||||
privateUsers = if v.jail.useGlobalUsers then "no" else "pick";
|
||||
extraFlags = [
|
||||
# TODO: add support for bindmount arguments instead of hacking it in here
|
||||
# "--bind=${config.users.users.${k}.home}:${config.users.users.${k}.home}:rbind,idmap"
|
||||
"--bind=${config.users.users.${k}.home}:${config.users.users.${k}.home}"
|
||||
# TODO: add support for bind-user in nixos-container module
|
||||
# "--bind-user=${k}"
|
||||
];
|
||||
config = {
|
||||
system.stateVersion = config.system.stateVersion;
|
||||
networking.hostName = config.networking.hostName;
|
||||
# NOTE: seemingly not needed with --bind-user.
|
||||
users.users.${k} = config.users.users.${k};
|
||||
users.groups.${config.users.users.${k}.group} = config.users.groups.${config.users.users.${k}.group};
|
||||
};
|
||||
};
|
||||
}) (lib.filterAttrs (_: v: v.jail.enable) config.users.users');
|
||||
|
||||
systemd.services = lib.mapAttrs' (k: v: {
|
||||
name = "container@user-jail-${k}";
|
||||
value = {
|
||||
# unitConfig.RequiresMountsFor = lib.mapAttrsToList (k: v: if v.hostPath != null then v.hostPath else k) config.containers."user-jail-${k}".bindMounts;
|
||||
};
|
||||
}) (lib.filterAttrs (_: v: v.jail.enable) config.users.users');
|
||||
|
||||
services.openssh.extraConfig = lib.pipe config.users.users' [
|
||||
(lib.filterAttrs (_: v: v.jail.enable))
|
||||
(lib.mapAttrsToList (k: v: ''
|
||||
Match User ${k}
|
||||
ForceCommand 'machinectl' --quiet shell '${k}@user-jail-${k}' $SSH_ORIGINAL_COMMAND
|
||||
''))
|
||||
lib.concatStrings
|
||||
];
|
||||
|
||||
security.polkit.enable = true;
|
||||
security.polkit.extraConfig = ''
|
||||
polkit.addRule(function(action, subject) {
|
||||
if (
|
||||
action.id === "org.freedesktop.machine1.shell"
|
||||
&& action.lookup("user") === subject.user
|
||||
&& action.lookup("machine") === "user-jail-" + subject.user
|
||||
) {
|
||||
return polkit.Result.YES;
|
||||
}
|
||||
});
|
||||
'';
|
||||
|
||||
# TODO: use pam module to maybe stop the container upon closing the connection
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user