mirror of
https://github.com/ryan4yin/nix-config.git
synced 2026-11-13 12:13:36 +01:00
fix(apparmor): correct the sudo profile comment's conclusion
The abort needs havePrivateMountNs == false AND /nix/store still ro: remountReadOnlyWritable() returns early while the store is writable. Nix <= 2.34 left it rw until reboot; 2.35 remounts in a private namespace, so only a freshly booted host hits it. The repro command also uses an open-only exec 7<..., since cat fails on the read even when the open succeeds. While here, trim the comments and drop the disabled default_deny/nix policies plus the explicit killUnconfinedConfinables=false (nixpkgs default). 82 -> 34 lines.
This commit is contained in:
1 parent
124bcd8490
commit
e9d706aa96
1 file changed
+21
-66
@@ -1,82 +1,37 @@
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
# AppArmor: activate the LSM + load policies. Roll out in complain mode first
|
||||
# (log violations, don't block) so nothing can break; promote profiles to
|
||||
# "enforce" individually once stable. Complain mode is not absolute: opens of
|
||||
# disconnected paths (/proc/self/ns/*, /proc/self/root, ...) are denied even in
|
||||
# complain mode unless the profile sets flags=(attach_disconnected); see the
|
||||
# sudo policy below.
|
||||
# AppArmor in complain mode: log violations instead of blocking.
|
||||
services.dbus.apparmor = "enabled";
|
||||
|
||||
security.apparmor = {
|
||||
enable = true;
|
||||
|
||||
# Do not SIGTERM running unconfined-but-confinable processes yet.
|
||||
# Safe to flip to true later now that no global default-deny profile is active.
|
||||
killUnconfinedConfinables = false;
|
||||
|
||||
# Packages contributing to AppArmor's include path (abstractions).
|
||||
# NOTE: these abstractions are FHS-oriented and reference FHS paths. nixpkgs does NOT
|
||||
# provide a broad FHS->store alias layer (only /run/current-system, /usr/bin/env and
|
||||
# /bin/sh are created), so some referenced paths do not resolve on NixOS and the
|
||||
# resulting profiles are incomplete. They are safe only because they run in complain
|
||||
# mode (log-only, no blocking); see hardening/README.md.
|
||||
# FHS-oriented abstractions, incomplete on NixOS; only safe in complain mode.
|
||||
packages = [ pkgs.apparmor-profiles ];
|
||||
|
||||
policies = {
|
||||
# Global default-deny scaffold. DANGEROUS to enable: `/**` matches every binary and
|
||||
# the empty block allows nothing. Keep disabled until per-app profiles exist.
|
||||
"default_deny" = {
|
||||
state = "disable";
|
||||
profile = "profile default_deny /** { }";
|
||||
};
|
||||
policies.sudo = {
|
||||
state = "complain";
|
||||
profile = ''
|
||||
abi <abi/4.0>,
|
||||
include <tunables/global>
|
||||
|
||||
# Confine sudo; complain mode so a missing rule logs instead of blocking.
|
||||
"sudo" = {
|
||||
state = "complain";
|
||||
profile = ''
|
||||
abi <abi/4.0>,
|
||||
include <tunables/global>
|
||||
|
||||
# NOTE: exec must use the `ix` (inherit) modifier here: abstractions/base
|
||||
# pulls in nested rules like `rix` on specific binaries (via xdg-open etc.),
|
||||
# and AppArmor 5.0's parser rejects merged rules with conflicting exec
|
||||
# modifiers ("profile has merged rule with conflicting x modifiers") when a
|
||||
# broad rule uses Ux over those paths.
|
||||
#
|
||||
# NOTE: `attach_disconnected` is required. Magic links such as
|
||||
# /proc/self/ns/mnt and /proc/self/root have no connected path, so AppArmor
|
||||
# cannot match them against any rule and denies the open with EACCES even in
|
||||
# complain mode. Nix 2.35 (nix-env --set as root) opens /proc/self/ns/mnt in
|
||||
# its saveMountNamespace(), and once that fails the store is never remounted
|
||||
# writable and nixos-rebuild dies with:
|
||||
# cannot remount "/nix/store" writable: not in a private mount namespace
|
||||
# Reproduce the underlying denial with:
|
||||
# aa-exec -p ${pkgs.sudo}/bin/sudo -- cat /proc/self/ns/mnt
|
||||
#
|
||||
# Rolling this out to a machine that already has the old (non-attach)
|
||||
# profile loaded needs one deploy where the *new* system's Nix does not
|
||||
# run the profile switch, e.g.
|
||||
# nixos-rebuild boot --sudo --no-reexec --flake .#<host> --accept-flake-config
|
||||
# (with --no-reexec the current system's Nix, 2.34, runs nix-env --set and
|
||||
# avoids the broken profile). After that generation is active, plain
|
||||
# `just niri` works again and the Nix version is never rolled back.
|
||||
profile ${pkgs.sudo}/bin/sudo flags=(attach_disconnected) {
|
||||
include <abstractions/base>
|
||||
file /** rwlkix,
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
# nix runs unconfined (no restriction); inert, kept disabled.
|
||||
"nix" = {
|
||||
state = "disable";
|
||||
profile = "profile ${config.nix.package}/bin/nix { unconfined, }";
|
||||
};
|
||||
# Confine sudo (complain). `attach_disconnected` lets Nix 2.35's
|
||||
# nixos-rebuild set the profile; `ix` keeps the parser happy.
|
||||
#
|
||||
# /proc/self/ns/mnt has no connected path, so AppArmor denies the open even in
|
||||
# complain mode; Nix 2.35's root nix-env opens it in saveMountNamespace(), and
|
||||
# nixos-rebuild aborts only while /nix/store is ro (<= 2.34 left it rw until
|
||||
# reboot, 2.35 remounts in a namespace).
|
||||
# abstractions/base has nested rix rules, and Ux makes AppArmor 5.0 reject
|
||||
# the merged rules.
|
||||
profile ${pkgs.sudo}/bin/sudo flags=(attach_disconnected) {
|
||||
include <abstractions/base>
|
||||
file /** rwlkix,
|
||||
}
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user