Skip to content

Commit

Permalink
Revert "apparmor: fix and improve the service"
Browse files Browse the repository at this point in the history
This reverts commit fb6d63f.

I really hope this finally fixes NixOS#99236: evaluation on Hydra.
This time I really did check basically the same commit on Hydra:
https://hydra.nixos.org/eval/1618011

Right now I don't have energy to find what exactly is wrong in the
commit, and it doesn't seem important in comparison to nixos-unstable
channel being stuck on a commit over one week old.
  • Loading branch information
vcunat committed Oct 7, 2020
1 parent 3b0886c commit 420f89c
Show file tree
Hide file tree
Showing 20 changed files with 167 additions and 794 deletions.
2 changes: 1 addition & 1 deletion maintainers/maintainer-list.nix
Expand Up @@ -4251,7 +4251,7 @@
name = "Julien Dehos";
};
julm = {
email = "julm+nixpkgs@sourcephile.fr";
email = "julm+nix@sourcephile.fr";
github = "ju1m";
githubId = 21160136;
name = "Julien Moutinho";
Expand Down
18 changes: 0 additions & 18 deletions nixos/doc/manual/release-notes/rl-2009.xml
Expand Up @@ -1009,24 +1009,6 @@ services.transmission.settings.rpc-bind-address = "0.0.0.0";
to get the previous behavior of listening on all network interfaces.
</para>
</listitem>
<listitem>
<para>
The <literal>security.apparmor</literal> module,
for the <link xlink:href="https://gitlab.com/apparmor/apparmor/-/wikis/Documentation">AppArmor</link>
Mandatory Access Control system,
has been substantialy improved along with related tools,
so that module maintainers can now more easily write AppArmor profiles for NixOS.
The most notable change on the user-side is the new option <xref linkend="opt-security.apparmor.policies"/>,
replacing the previous <literal>profiles</literal> option
to provide a way to disable a profile
and to select whether to confine in enforce mode (default)
or in complain mode (see <literal>journalctl -b --grep apparmor</literal>).
Before enabling this module, either directly
or by importing <literal>&lt;nixpkgs/nixos/modules/profiles/hardened.nix&gt;</literal>,
please be sure to read the documentation of <link linkend="opt-security.apparmor.enable">security.apparmor.enable</link>,
and especially the part about <xref linkend="opt-security.apparmor.killUnconfinedConfinables"/>.
</para>
</listitem>
<listitem>
<para>
With this release <literal>systemd-networkd</literal> (when enabled through <xref linkend="opt-networking.useNetworkd"/>)
Expand Down
34 changes: 0 additions & 34 deletions nixos/modules/config/fonts/fontconfig.nix
Expand Up @@ -448,40 +448,6 @@ in
(mkIf cfg.enable {
environment.systemPackages = [ pkgs.fontconfig ];
environment.etc.fonts.source = "${fontconfigEtc}/etc/fonts/";
security.apparmor.includes."abstractions/fonts" = ''
# fonts.conf
r ${pkg.out}/etc/fonts/fonts.conf,
# fontconfig default config files
r ${pkg.out}/etc/fonts/conf.d/*.conf,
# 00-nixos-cache.conf
r ${cacheConf},
# 10-nixos-rendering.conf
r ${renderConf},
# 50-user.conf
${optionalString cfg.includeUserConf ''
r ${pkg.out}/etc/fonts/conf.d.bak/50-user.conf,
''}
# local.conf (indirect priority 51)
${optionalString (cfg.localConf != "") ''
r ${localConf},
''}
# 52-nixos-default-fonts.conf
r ${defaultFontsConf},
# 53-no-bitmaps.conf
r ${rejectBitmaps},
${optionalString (!cfg.allowType1) ''
# 53-nixos-reject-type1.conf
r ${rejectType1},
''}
'';
})
(mkIf cfg.enable {
fonts.fontconfig.confPackages = [ confPkg ];
Expand Down
7 changes: 0 additions & 7 deletions nixos/modules/config/malloc.nix
Expand Up @@ -87,12 +87,5 @@ in
environment.etc."ld-nix.so.preload".text = ''
${providerLibPath}
'';
security.apparmor.includes = {
"abstractions/base" = ''
r /etc/ld-nix.so.preload,
r ${config.environment.etc."ld-nix.so.preload".source},
mr ${providerLibPath},
'';
};
};
}
1 change: 1 addition & 0 deletions nixos/modules/module-list.nix
Expand Up @@ -186,6 +186,7 @@
./rename.nix
./security/acme.nix
./security/apparmor.nix
./security/apparmor-suid.nix
./security/audit.nix
./security/auditd.nix
./security/ca.nix
Expand Down
49 changes: 49 additions & 0 deletions nixos/modules/security/apparmor-suid.nix
@@ -0,0 +1,49 @@
{ config, lib, pkgs, ... }:
let
cfg = config.security.apparmor;
in
with lib;
{
imports = [
(mkRenamedOptionModule [ "security" "virtualization" "flushL1DataCache" ] [ "security" "virtualisation" "flushL1DataCache" ])
];

options.security.apparmor.confineSUIDApplications = mkOption {
type = types.bool;
default = true;
description = ''
Install AppArmor profiles for commonly-used SUID application
to mitigate potential privilege escalation attacks due to bugs
in such applications.
Currently available profiles: ping
'';
};

config = mkIf (cfg.confineSUIDApplications) {
security.apparmor.profiles = [ (pkgs.writeText "ping" ''
#include <tunables/global>
/run/wrappers/bin/ping {
#include <abstractions/base>
#include <abstractions/consoles>
#include <abstractions/nameservice>
capability net_raw,
capability setuid,
network inet raw,
${pkgs.stdenv.cc.libc.out}/lib/*.so mr,
${pkgs.libcap.lib}/lib/libcap.so* mr,
${pkgs.attr.out}/lib/libattr.so* mr,
${pkgs.iputils}/bin/ping mixr,
#/etc/modules.conf r,
## Site-specific additions and overrides. See local/README for details.
##include <local/bin.ping>
}
'') ];
};

}
241 changes: 51 additions & 190 deletions nixos/modules/security/apparmor.nix
@@ -1,198 +1,59 @@
{ config, lib, pkgs, ... }:

let
inherit (builtins) attrNames head map match readFile;
inherit (lib) types;
inherit (config.environment) etc;
inherit (lib) mkIf mkOption types concatMapStrings;
cfg = config.security.apparmor;
mkDisableOption = name: lib.mkEnableOption name // {
default = true;
example = false;
};
enabledPolicies = lib.filterAttrs (n: p: p.enable) cfg.policies;
in

{
imports = [
(lib.mkRenamedOptionModule [ "security" "virtualization" "flushL1DataCache" ] [ "security" "virtualisation" "flushL1DataCache" ])
(lib.mkRemovedOptionModule [ "security" "apparmor" "confineSUIDApplications" ] "Please use the new options: `security.apparmor.policies.<policy>.enable'.")
(lib.mkRemovedOptionModule [ "security" "apparmor" "profiles" ] "Please use the new option: `security.apparmor.policies'.")
apparmor/includes.nix
apparmor/profiles.nix
];

options = {
security.apparmor = {
enable = lib.mkEnableOption ''the AppArmor Mandatory Access Control system.
If you're enabling this module on a running system,
note that a reboot will be required to activate AppArmor in the kernel.
Also, beware that enabling this module will by default
try to kill unconfined but confinable running processes,
in order to obtain a confinement matching what is declared in the NixOS configuration.
This will happen when upgrading to a NixOS revision
introducing an AppArmor profile for the executable of a running process.
This is because enabling an AppArmor profile for an executable
can only confine new or already confined processes of that executable,
but leaves already running processes unconfined.
Set <link linkend="opt-security.apparmor.killUnconfinedConfinables">killUnconfinedConfinables</link>
to <literal>false</literal> if you prefer to leave those processes running'';
policies = lib.mkOption {
description = ''
AppArmor policies.
'';
type = types.attrsOf (types.submodule ({ name, config, ... }: {
options = {
enable = mkDisableOption "loading of the profile into the kernel";
enforce = mkDisableOption "enforcing of the policy or only complain in the logs";
profile = lib.mkOption {
description = "The policy of the profile.";
type = types.lines;
apply = pkgs.writeText name;
};
};
}));
default = {};
};
includes = lib.mkOption {
type = types.attrsOf types.lines;
default = {};
description = ''
List of paths to be added to AppArmor's searched paths
when resolving <literal>include</literal> directives.
'';
apply = lib.mapAttrs pkgs.writeText;
};
packages = lib.mkOption {
type = types.listOf types.package;
default = [];
description = "List of packages to be added to AppArmor's include path";
};
enableCache = lib.mkEnableOption ''caching of AppArmor policies
in <literal>/var/cache/apparmor/</literal>.
Beware that AppArmor policies almost always contain Nix store paths,
and thus produce at each change of these paths
a new cached version accumulating in the cache'';
killUnconfinedConfinables = mkDisableOption ''killing of processes
which have an AppArmor profile enabled
(in <link linkend="opt-security.apparmor.policies">policies</link>)
but are not confined (because AppArmor can only confine new processes).
Beware that due to a current limitation of AppArmor,
only profiles with exact paths (and no name) can enable such kills'';
};
};

config = lib.mkIf cfg.enable {
assertions = map (policy:
{ assertion = match ".*/.*" policy == null;
message = "`security.apparmor.policies.\"${policy}\"' must not contain a slash.";
# Because, for instance, aa-remove-unknown uses profiles_names_list() in rc.apparmor.functions
# which does not recurse into sub-directories.
}
) (attrNames cfg.policies);

environment.systemPackages = [ pkgs.apparmor-utils ];
environment.etc."apparmor.d".source = pkgs.linkFarm "apparmor.d" (
# It's important to put only enabledPolicies here and not all cfg.policies
# because aa-remove-unknown reads profiles from all /etc/apparmor.d/*
lib.mapAttrsToList (name: p: {inherit name; path=p.profile;}) enabledPolicies ++
lib.mapAttrsToList (name: path: {inherit name path;}) cfg.includes
);
environment.etc."apparmor/parser.conf".text = ''
${if cfg.enableCache then "write-cache" else "skip-cache"}
cache-loc /var/cache/apparmor
Include /etc/apparmor.d
'' +
lib.concatMapStrings (p: "Include ${p}/etc/apparmor.d\n") cfg.packages;
# For aa-logprof
environment.etc."apparmor/apparmor.conf".text = ''
'';
# For aa-logprof
environment.etc."apparmor/severity.db".source = pkgs.apparmor-utils + "/etc/apparmor/severity.db";
environment.etc."apparmor/logprof.conf".text = ''
[settings]
# /etc/apparmor.d/ is read-only on NixOS
profiledir = /var/cache/apparmor/logprof
inactive_profiledir = /etc/apparmor.d/disable
# Use: journalctl -b --since today --grep audit: | aa-logprof
logfiles = /dev/stdin
parser = ${pkgs.apparmor-parser}/bin/apparmor_parser
ldd = ${pkgs.glibc.bin}/bin/ldd
logger = ${pkgs.utillinux}/bin/logger
# customize how file ownership permissions are presented
# 0 - off
# 1 - default of what ever mode the log reported
# 2 - force the new permissions to be user
# 3 - force all perms on the rule to be user
default_owner_prompt = 1
custom_includes = /etc/apparmor.d ${lib.concatMapStringsSep " " (p: "${p}/etc/apparmor.d") cfg.packages}
[qualifiers]
${pkgs.runtimeShell} = icnu
${pkgs.bashInteractive}/bin/sh = icnu
${pkgs.bashInteractive}/bin/bash = icnu
'' + head (match "^.*\\[qualifiers](.*)" # Drop the original [settings] section.
(readFile "${pkgs.apparmor-utils}/etc/apparmor/logprof.conf"));

boot.kernelParams = [ "apparmor=1" "security=apparmor" ];

systemd.services.apparmor = {
after = [
"local-fs.target"
"systemd-journald-audit.socket"
];
before = [ "sysinit.target" ];
wantedBy = [ "multi-user.target" ];
unitConfig = {
Description="Load AppArmor policies";
DefaultDependencies = "no";
ConditionSecurity = "apparmor";
};
# Reloading instead of restarting enables to load new AppArmor profiles
# without necessarily restarting all services which have Requires=apparmor.service
reloadIfChanged = true;
restartTriggers = [
etc."apparmor/parser.conf".source
etc."apparmor.d".source
];
serviceConfig = let
killUnconfinedConfinables = pkgs.writeShellScript "apparmor-kill" ''
set -eu
${pkgs.apparmor-utils}/bin/aa-status --json |
${pkgs.jq}/bin/jq --raw-output '.processes | .[] | .[] | select (.status == "unconfined") | .pid' |
xargs --verbose --no-run-if-empty --delimiter='\n' \
kill
'';
commonOpts = p: "--verbose --show-cache ${lib.optionalString (!p.enforce) "--complain "}${p.profile}";
in {
Type = "oneshot";
RemainAfterExit = "yes";
ExecStartPre = "${pkgs.apparmor-utils}/bin/aa-teardown";
ExecStart = lib.mapAttrsToList (n: p: "${pkgs.apparmor-parser}/bin/apparmor_parser --add ${commonOpts p}") enabledPolicies;
ExecStartPost = lib.optional cfg.killUnconfinedConfinables killUnconfinedConfinables;
ExecReload =
# Add or replace into the kernel profiles in enabledPolicies
# (because AppArmor can do that without stopping the processes already confined).
lib.mapAttrsToList (n: p: "${pkgs.apparmor-parser}/bin/apparmor_parser --replace ${commonOpts p}") enabledPolicies ++
# Remove from the kernel any profile whose name is not
# one of the names within the content of the profiles in enabledPolicies
# (indirectly read from /etc/apparmor.d/*, without recursing into sub-directory).
# Note that this does not remove profiles dynamically generated by libvirt.
[ "${pkgs.apparmor-utils}/bin/aa-remove-unknown" ] ++
# Optionaly kill the processes which are unconfined but now have a profile loaded
# (because AppArmor can only start to confine new processes).
lib.optional cfg.killUnconfinedConfinables killUnconfinedConfinables;
ExecStop = "${pkgs.apparmor-utils}/bin/aa-teardown";
CacheDirectory = [ "apparmor" "apparmor/logprof" ];
CacheDirectoryMode = "0700";
};
};
};

meta.maintainers = with lib.maintainers; [ julm ];
options = {
security.apparmor = {
enable = mkOption {
type = types.bool;
default = false;
description = "Enable the AppArmor Mandatory Access Control system.";
};
profiles = mkOption {
type = types.listOf types.path;
default = [];
description = "List of files containing AppArmor profiles.";
};
packages = mkOption {
type = types.listOf types.package;
default = [];
description = "List of packages to be added to apparmor's include path";
};
};
};

config = mkIf cfg.enable {
environment.systemPackages = [ pkgs.apparmor-utils ];

boot.kernelParams = [ "apparmor=1" "security=apparmor" ];

systemd.services.apparmor = let
paths = concatMapStrings (s: " -I ${s}/etc/apparmor.d")
([ pkgs.apparmor-profiles ] ++ cfg.packages);
in {
after = [ "local-fs.target" ];
before = [ "sysinit.target" ];
wantedBy = [ "multi-user.target" ];
unitConfig = {
DefaultDependencies = "no";
};
serviceConfig = {
Type = "oneshot";
RemainAfterExit = "yes";
ExecStart = map (p:
''${pkgs.apparmor-parser}/bin/apparmor_parser -rKv ${paths} "${p}"''
) cfg.profiles;
ExecStop = map (p:
''${pkgs.apparmor-parser}/bin/apparmor_parser -Rv "${p}"''
) cfg.profiles;
ExecReload = map (p:
''${pkgs.apparmor-parser}/bin/apparmor_parser --reload ${paths} "${p}"''
) cfg.profiles;
};
};
};
}

0 comments on commit 420f89c

Please sign in to comment.