mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-07-13 21:50:33 +03:00
nixos/systemd-chroot: Rename chroot to confinement
Quoting @edolstra from [1]: I don't really like the name "chroot", something like "confine[ment]" or "restrict" seems better. Conceptually we're not providing a completely different filesystem tree but a restricted view of the same tree. I already used "confinement" as a sub-option and I do agree that "chroot" sounds a bit too specific (especially because not *only* chroot is involved). So this changes the module name and its option to use "confinement" instead of "chroot" and also renames the "chroot.confinement" to "confinement.mode". [1]: https://github.com/NixOS/nixpkgs/pull/57519#issuecomment-472855704 Signed-off-by: aszlig <aszlig@nix.build>
This commit is contained in:
parent
ac64ce9945
commit
0ba48f46da
4 changed files with 21 additions and 21 deletions
160
nixos/modules/security/systemd-confinement.nix
Normal file
160
nixos/modules/security/systemd-confinement.nix
Normal file
|
@ -0,0 +1,160 @@
|
|||
{ config, pkgs, lib, ... }:
|
||||
|
||||
let
|
||||
inherit (lib) types;
|
||||
inherit (import ../system/boot/systemd-lib.nix {
|
||||
inherit config pkgs lib;
|
||||
}) mkPathSafeName;
|
||||
in {
|
||||
options.systemd.services = lib.mkOption {
|
||||
type = types.attrsOf (types.submodule ({ name, config, ... }: {
|
||||
options.confinement.enable = lib.mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
If set, all the required runtime store paths for this service are
|
||||
bind-mounted into a <literal>tmpfs</literal>-based <citerefentry>
|
||||
<refentrytitle>chroot</refentrytitle>
|
||||
<manvolnum>2</manvolnum>
|
||||
</citerefentry>.
|
||||
'';
|
||||
};
|
||||
|
||||
options.confinement.packages = lib.mkOption {
|
||||
type = types.listOf (types.either types.str types.package);
|
||||
default = [];
|
||||
description = let
|
||||
mkScOption = optName: "<option>serviceConfig.${optName}</option>";
|
||||
in ''
|
||||
Additional packages or strings with context to add to the closure of
|
||||
the chroot. By default, this includes all the packages from the
|
||||
${lib.concatMapStringsSep ", " mkScOption [
|
||||
"ExecReload" "ExecStartPost" "ExecStartPre" "ExecStop"
|
||||
"ExecStopPost"
|
||||
]} and ${mkScOption "ExecStart"} options.
|
||||
|
||||
<note><para><emphasis role="strong">Only</emphasis> the latter
|
||||
(${mkScOption "ExecStart"}) will be used if
|
||||
${mkScOption "RootDirectoryStartOnly"} is enabled.</para></note>
|
||||
|
||||
<note><para>Also, the store paths listed in <option>path</option> are
|
||||
<emphasis role="strong">not</emphasis> included in the closure as
|
||||
well as paths from other options except those listed
|
||||
above.</para></note>
|
||||
'';
|
||||
};
|
||||
|
||||
options.confinement.withBinSh = lib.mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Whether to symlink <command>dash</command> as
|
||||
<filename>/bin/sh</filename> to the chroot.
|
||||
|
||||
This is useful for some applications, which for example use the
|
||||
<citerefentry>
|
||||
<refentrytitle>system</refentrytitle>
|
||||
<manvolnum>3</manvolnum>
|
||||
</citerefentry> library function to execute commands.
|
||||
'';
|
||||
};
|
||||
|
||||
options.confinement.mode = lib.mkOption {
|
||||
type = types.enum [ "full-apivfs" "chroot-only" ];
|
||||
default = "full-apivfs";
|
||||
description = ''
|
||||
The value <literal>full-apivfs</literal> (the default) sets up
|
||||
private <filename class="directory">/dev</filename>, <filename
|
||||
class="directory">/proc</filename>, <filename
|
||||
class="directory">/sys</filename> and <filename
|
||||
class="directory">/tmp</filename> file systems in a separate user
|
||||
name space.
|
||||
|
||||
If this is set to <literal>chroot-only</literal>, only the file
|
||||
system name space is set up along with the call to <citerefentry>
|
||||
<refentrytitle>chroot</refentrytitle>
|
||||
<manvolnum>2</manvolnum>
|
||||
</citerefentry>.
|
||||
|
||||
<note><para>This doesn't cover network namespaces and is solely for
|
||||
file system level isolation.</para></note>
|
||||
'';
|
||||
};
|
||||
|
||||
config = lib.mkIf config.confinement.enable {
|
||||
serviceConfig = let
|
||||
rootName = "${mkPathSafeName name}-chroot";
|
||||
in {
|
||||
RootDirectory = pkgs.runCommand rootName {} "mkdir \"$out\"";
|
||||
TemporaryFileSystem = "/";
|
||||
MountFlags = lib.mkDefault "private";
|
||||
} // lib.optionalAttrs config.confinement.withBinSh {
|
||||
BindReadOnlyPaths = [ "${pkgs.dash}/bin/dash:/bin/sh" ];
|
||||
} // lib.optionalAttrs (config.confinement.mode == "full-apivfs") {
|
||||
MountAPIVFS = true;
|
||||
PrivateDevices = true;
|
||||
PrivateTmp = true;
|
||||
PrivateUsers = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
};
|
||||
confinement.packages = let
|
||||
startOnly = config.serviceConfig.RootDirectoryStartOnly or false;
|
||||
execOpts = if startOnly then [ "ExecStart" ] else [
|
||||
"ExecReload" "ExecStart" "ExecStartPost" "ExecStartPre" "ExecStop"
|
||||
"ExecStopPost"
|
||||
];
|
||||
execPkgs = lib.concatMap (opt: let
|
||||
isSet = config.serviceConfig ? ${opt};
|
||||
in lib.optional isSet config.serviceConfig.${opt}) execOpts;
|
||||
in execPkgs ++ lib.optional config.confinement.withBinSh pkgs.dash;
|
||||
};
|
||||
}));
|
||||
};
|
||||
|
||||
config.assertions = lib.concatLists (lib.mapAttrsToList (name: cfg: let
|
||||
whatOpt = optName: "The 'serviceConfig' option '${optName}' for"
|
||||
+ " service '${name}' is enabled in conjunction with"
|
||||
+ " 'confinement.enable'";
|
||||
in lib.optionals cfg.confinement.enable [
|
||||
{ assertion = !cfg.serviceConfig.RootDirectoryStartOnly or false;
|
||||
message = "${whatOpt "RootDirectoryStartOnly"}, but right now systemd"
|
||||
+ " doesn't support restricting bind-mounts to 'ExecStart'."
|
||||
+ " Please either define a separate service or find a way to run"
|
||||
+ " commands other than ExecStart within the chroot.";
|
||||
}
|
||||
{ assertion = !cfg.serviceConfig.DynamicUser or false;
|
||||
message = "${whatOpt "DynamicUser"}. Please create a dedicated user via"
|
||||
+ " the 'users.users' option instead as this combination is"
|
||||
+ " currently not supported.";
|
||||
}
|
||||
]) config.systemd.services);
|
||||
|
||||
config.systemd.packages = lib.concatLists (lib.mapAttrsToList (name: cfg: let
|
||||
rootPaths = let
|
||||
contents = lib.concatStringsSep "\n" cfg.confinement.packages;
|
||||
in pkgs.writeText "${mkPathSafeName name}-string-contexts.txt" contents;
|
||||
|
||||
chrootPaths = pkgs.runCommand "${mkPathSafeName name}-chroot-paths" {
|
||||
closureInfo = pkgs.closureInfo { inherit rootPaths; };
|
||||
serviceName = "${name}.service";
|
||||
excludedPath = rootPaths;
|
||||
} ''
|
||||
mkdir -p "$out/lib/systemd/system"
|
||||
serviceFile="$out/lib/systemd/system/$serviceName"
|
||||
|
||||
echo '[Service]' > "$serviceFile"
|
||||
|
||||
while read storePath; do
|
||||
if [ -L "$storePath" ]; then
|
||||
# Currently, systemd can't cope with symlinks in Bind(ReadOnly)Paths,
|
||||
# so let's just bind-mount the target to that location.
|
||||
echo "BindReadOnlyPaths=$(readlink -e "$storePath"):$storePath"
|
||||
elif [ "$storePath" != "$excludedPath" ]; then
|
||||
echo "BindReadOnlyPaths=$storePath"
|
||||
fi
|
||||
done < "$closureInfo/store-paths" >> "$serviceFile"
|
||||
'';
|
||||
in lib.optional cfg.confinement.enable chrootPaths) config.systemd.services);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue