diff --git a/hardening/apparmor/default.nix b/hardening/apparmor/default.nix index 409550fc..f75b6ce3 100644 --- a/hardening/apparmor/default.nix +++ b/hardening/apparmor/default.nix @@ -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 , + include - # Confine sudo; complain mode so a missing rule logs instead of blocking. - "sudo" = { - state = "complain"; - profile = '' - abi , - include - - # 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 .# --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 - 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 + file /** rwlkix, + } + ''; }; }; }