New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AppArmor: Support for loading a pre-compiled profile #15905
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks OK, but a couple of comments
src/core/apparmor-setup.c
Outdated
|
||
r = read_one_line_file("/proc/self/attr/apparmor/current", ¤t_profile); | ||
if (r < 0) { | ||
if (errno == ENOENT) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
error is returned in r, not errno, i.e. if (r == -ENOENT) …
src/core/apparmor-setup.c
Outdated
if (errno == ENOENT) { | ||
r = read_one_line_file("/proc/self/attr/current", ¤t_profile); | ||
if (r < 0) { | ||
log_warning_errno(errno, "Failed to get the current AppArmor profile of systemd from /proc/self/attr/current: %m"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as above
src/core/apparmor-setup.c
Outdated
r = read_one_line_file("/proc/self/attr/current", ¤t_profile); | ||
if (r < 0) { | ||
log_warning_errno(errno, "Failed to get the current AppArmor profile of systemd from /proc/self/attr/current: %m"); | ||
return 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why check these two paths btw? can you add a comment, please?
src/core/apparmor-setup.c
Outdated
r = aa_kernel_interface_load_policy_from_fd(interface, fd); | ||
if (r < 0) { | ||
log_warning_errno(errno, "Failed to load AppArmor profile from file %s into the kernel: %m", profile_file); | ||
aa_kernel_interface_unref(interface); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instead of unreffing this explicitly in each exit path, please use DEFINE_TRIVIAL_CLEANUP_FUNC() and then use _cleanup_(…)
on the variable to automatically destroy the object when the function is left.
src/core/apparmor-setup.c
Outdated
log_error("Failed to change to AppArmor profile " APPARMOR_PROFILE_NAME ". Please ensure that the profile file %s contains a profile with that name.", profile_file); | ||
} else { | ||
log_error_errno(errno, "Failed to change to AppArmor profile " APPARMOR_PROFILE_NAME ": %m"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as per out coding style, no {} around single-line if blocks:
src/core/apparmor-setup.c
Outdated
|
||
#define APPARMOR_PROFILE_NAME "systemd" | ||
#define APPARMOR_PROFILE_ETC_PATH "/etc/systemd/profile.apparmor" | ||
#define APPARMOR_PROFILE_USR_PATH "/usr/lib/systemd/profile.apparmor" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
quite frankly I don't see the point of these defines. The latter two are used exactly once, and it would be more readable to just specify them literally where they are used. And the former is simple enough to not result in typos if repeated, hence I don't think it warrants a macro here..
I mean, macros are great to abbreviate stuff, to lower the risk of making typos, to make things configurable. But neither of those reasons apply here, hence please don't add the extra indirection that these macros are.
src/core/apparmor-setup.c
Outdated
|
||
int mac_apparmor_setup(void) { | ||
#if HAVE_APPARMOR | ||
int r = -1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why assign -1 here? it gets overwritten right away
Are you an apparmor upstream dev btw? If not I think it would make sense to get a review from someone involved in aa upstream, to get a blessing. Can you cc a suitable person from the aa team? Or @xnox do you know the right person to CC? The thing is simply that I don't know AA, and given I am a Fedora guy can't test it really, so I'd like a set of eyes on this that knows that stuff. |
I did not see the comments in the commit itself. I will change the commits again. |
@jdstrand can you please review this? |
I would feel better if securityfs was mounted, apparmor_parser invoke to compile profile for systemd, and then it loaded and switched to. The paths |
We can add /var/cache/apparmor/$apparmor-abi/usr.lib.systemd.systemd to the list of paths instead of /etc/... or before or after it. I will have to find out how to read the ABI version from the kernel. Running apparmor_parser is something I do not want to do, because there is no standardized way to do it. Distributions with AppArmor support (as Ubuntu does) may have the same layout of files for profiles but others may not. We would force those users to have the same layout which may not fit there needs. Using a pre-compiled file allows everyone to create that file in the way as they wish. They can even distribute it in /usr with their systemd package (as I do). I am not sure what you mean by securitfyfs being mounted or not. The profil is loaded using a file in securityfs. So it has to be mounted anyway in all cases. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks great, one more minor point
Shelling out to external binaries (that's what this apparmor_parser thing is, no?) is something I really don't like. We generally want to be more than a bunch of superglued shell scripts, but if we keep invoking all kinds of external binaries in order we just become that... |
apparmor_parser is an application to read text profile, compile them into binary profiles and load those into the kernel. so yes, it is an external binary. |
looks good by me now. Would be good to get at least a superficial blessing from some upstream AA folks, but if that never materializes we can merge this as it is, too. let's give them a few days though |
I will ask for a comment on their irc channel. |
I did request jdstrand to review this. (AA upstream). |
I understand that calling external binaries in core/main.c is undesirable. It's just I don't see how the current naming scheme of a single precompiled Thus I'd still rather see loading precompiled binary profile from Without doing this, I don't see how one can store a precompiled profile for the next boot using the new kernel, whilst preserving capability of booting using fallback/old kernel, when the two have different sets of apparmor features. See https://gitlab.com/apparmor/apparmor/-/blob/master/libraries/libapparmor/src/features.c A precompiled profile is per-kernel, and is not universal. |
Then I am out of options here. I will not propose a patch that calls apparmor_parser and compiles the profile. |
Makes sense. Wouldn't including |
i am not too fond of the idea to shell out to the aa profile compiler. I think that's something the RPM/DEB can do whenever kernel/aa policy is updated. But supporting a mode where the kernel realease (or that aa feature mask thing you found) is included in the profile path makes a lot of sense. I'd first look for the version with the feature mask in the name, and then fall back to the generic ones. After all there appears to be a strong usecase for just using a plain name, for example if you have kernel+initrd+rootfs all immutable and pre-built in a verifiable fashion (secureboot/verity/…) and thus kernel+init+rootfs are always updated together anyway and each kernel has a matching rootfs anyway... |
I am not sure, but I don't think so. It is common for AppArmor to distinguish the feature set of a particular kernel with the feature hash. I think, systemd should stick to that method, if we want to distinguish between different feature sets at all. And after all, it is just one call of a function of a library we have to use anyway.
This is also my intention. I expect this feature to be used by people who build their own images. dm_verity, fs_verity or ima/evm can be used to sign the compiled profile file. This is, why I also search for the file in /usr so that it can be installed by the package manager or an image building tool. Also searching for the profile file in /var/cache/apparmor will enable Ubuntu and other general purpose distributions to start systemd with a profile applied too. @xnox Is that ok for you? If so, I will update the patch accordingly. |
While @xnox tagged me, I'm going to defer to @jrjohansen (AppArmor lead) on this since he'll have the most up to date information on libapparmor (eg, to avoid calling out to apparmor_parser) as well as upstream AppArmor's thoughts on full system policy, policy from initrd, etc. |
src/core/apparmor-setup.c
Outdated
FOREACH_STRING(profile_file, "/etc/systemd/profile.apparmor", "/usr/lib/systemd/profile.apparmor") { | ||
fd = open(profile_file, O_RDONLY | O_CLOEXEC); | ||
if (fd < 0) { | ||
if (errno == ENOENT) | ||
continue; | ||
log_warning_errno(errno, "Failed to open binary AppArmor profile file %s, ignoring: %m", profile_file); | ||
} else | ||
break; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have concerns here both of these locations are outside of apparmor regular cache management, which means they will require manual update to keep from being stale. Also neither of these take into account that policy can be compiled differently for different kernel versions. This can lead to the following cases
- Everything working because the policy features matches up well with the kernel.
- The kernel rejecting policy because it uses an abi, or feature set it doesn't know how to handle.
- The kernel accepting the policy but it doesn't enforce everything it could. The kernel is backwards compatible to older abis, but when using a single cache file and multiple kernels the policy cache has to be compiled to the lowest common denominator.
It would be far better if we can standardize this to work within regular apparmor policy compiles and loads, which would mean we could have the cache being updated when policy is updated and also different cache files for the different kernel versions.
src/core/apparmor-setup.c
Outdated
r = aa_kernel_interface_new(&interface, NULL, NULL); | ||
if (r < 0) { | ||
log_warning_errno(errno, "Failed to get a new AppArmor kernel interface: %m"); | ||
return 0; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is going to fail if securityfs or apparmorfs are not premounted. Basically there is a hidden dependency here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mount_setup_early ensures that api filesystems like securitfy are mounted. It is called before in main.c before mac_apparmor_setup. The comment in mount_setup_early explicitly mentions this as a requirement for SELinux so it should be ok for AppArmor too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, we do have "basic" filesystems mounted already directly by systemd mount_setup_early, it handles things like dev, proc, sys, securityfs, for selinux, apparmor, smack, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, src/core/mount-setup.c only covers securityfs + smackfs. selinuxf is mounted by selinux code. apparmorfs is currently not covered, didn't know this was a thing. Maybe this PR should carry another commit mounting it too, if that's necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its new and hasn't been merged yet. Once it lands we can work on a patch for that.
securityfs or apparmorfs have to be pre mounted to be able to load the policy regardless, so there is a hidden dependency that is not being taken care of here. As for the apparmor_parser it isn't needed as long as you are grabbing the correct policy cache file. But ...
as you point out the current paths don't do that, they share the precompiled cache between kernel versions and don't get updated when policy is compiled. |
you can use libapparmor
I completely agree that the apparmor_parser (policy compiler) should not be called from here. However the current scheme is problematic. Yes different distros may specify different locations, or even multiple locations. But then should we standardize on way to specify the locations and everybody can work with that?
Ideally the code should be able to try to resolve this dependency and not have it be hidden. |
you don't need to call out to the parser, the code already exists in libapparmor, see man aa_policy_cache_replace_all |
unfortunately no, but there are ways to get at which cache via library routines. |
yes this is fine, and something we have been working towards, you are just front running us a bit. |
@poettering The version of libapparmor for the CI checks do not seem to have support for the aa_features_id function. It has been introduced to libapparmor about two years ago in commit https://gitlab.com/apparmor/apparmor/-/commit/8d9c904174ceb29b17984e61b94fcd72aaa753bc and released with AppArmor 2.13. I have an updated version that lets meson check for aa_features_id, but this means, that my code will not compiled with the CI checks. I am not sure if that is ok with you. |
src/core/apparmor-setup.c
Outdated
r = strv_extendf(&profile_files, "/etc/systemd/profile.apparmor.%s", feature_hash); | ||
if (r < 0) | ||
return log_oom(); | ||
r = strv_extendf(&profile_files, "/usr/lib/systemd/profile.apparmor.%s", feature_hash); | ||
if (r < 0) | ||
return log_oom(); | ||
r = strv_extend(&profile_files, "/etc/systemd/profile.apparmor"); | ||
if (r < 0) | ||
return log_oom(); | ||
r = strv_extend(&profile_files, "/usr/lib/systemd/profile.apparmor"); | ||
if (r < 0) | ||
return log_oom(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would drop all of these, in favor of only loading from /var/cache/apparmor
.
It is the correct and the default location, which all prebuilt / immutable images should have, as well as any "package" (deb, rpm, etc) based systems too.
Why do we still need loading from /etc/systemd, /usr/lib/systemd, if loading from /var/cache is there? Note this is binary profile, rather than the textual representation of the profile.
Otherwise the search paths look fine, and ordering is fine. I just don't think we need them at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am ok with that. I will just maintain a small patch privately that adds the other directories back.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, but /var is a no-go for early boot stuff like this, see other comment, sorry.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(The other directories make a lot more sense to me than the /var path...)
I very much agree with that sentiment, but only to some point: I think /etc should be shipped empty, and populated minimally on first boot (or every boot, on entirely stateless systems), but not necessarily be empty forever. Because ultimately you need configuration/state somewhere, and /etc is for that just fine. Because if it would be entirely empty all the time and you just have /cache then you basically just renamed "etc" to "cache" and what good does that do?
It can be in either. The idea is that people who built immutable images that are updated as a whole put it int /usr/lib. — And distros who are updated with classic packages and where additional packages may drop in new hwdb files any time rebuild it on package update time (or whenever else the user drops in a hwdb file) and that file is then placed in /etc. Or to say this differently: data generated from data in /etc must also be in /etc and cannot be in /usr. |
That way you generate stuff into /usr that is from data in /etc potentially. that's just weird. And people who drop in additional hwdb files into /etc and then rebuild the thing will be disconnected from your distros hwdb rebuilds. As long as Debian does updates with "apt" it should stick the file in /etc, since it incorporates stuff from all kinds of dirs, including /etc. If Debian however decides to become an immutable-/usr-only distro, then it should do the "--usr" thing, and disallow people to drop in their own hwdb files in /etc. |
I was more thinking If However, if and when customisation appear in
Ack, did not know that. |
bionic-i386 & bionic-ppc64el builds somehow ran against old apparmor, will re-trigger that in a bit. |
Right now systemd seems to be failing to compile when libapparmor is too old with:
I think it would make sense to bump up the dependency in "BuildDepends" in https://salsa.debian.org/systemd-team/systemd/-/blob/upstream-ci/debian/control#L27 should probably be updated as well. |
@evverx i am aware of that and have a new commit waiting here until it has been decided what path should be used. i can push the commit if you want, but it will still contain the same path. |
No, we should not update debian/control just yet. As I mentioned above the bionic amd64, arm64, s390x built against the updated apparmor, whilst for unknonw reason to me i386 ppc64el did not. Although the ppa used for testing has the up to date apparmor. I with there was a simple "redeliver" button for the checks. As hunting through the webhook history is not nice. |
Yeah, i386 & ppc64el tests are now pending again, awaiting results. |
Agreed. I think it would be better to wait until it's settled.
Since it's the "upstream-ci" branch I think it should be OK to update it once the PR is merged. |
but that will not magically install a newer version of apparmor =) can you please explain what you hope to achieve by changing the packaging there? It does not need any changes. |
bionic-i386 => most tests passed, and upstream test timedout. From my point of view that's acceptable to merge, there are no regressions identified. |
The idea is to make it clear that the package won't build without the latest version of libapparmor.
I'm not sure I understand what is the point of having the "upstream-ci" branch then if it isn't supposed to be used to keep track of build dependencies? |
On 6/2/20 1:24 AM, YmrDtnJu wrote:
@evverx <https://github.com/evverx> i am aware of that and have a new commit waiting here until it has been decided what path should be used. i can push the commit if you want, but it will still contain the same path.
I talked it over with @jdstrand and given the constraints and history /etc/apparmor/earlypolicy/ seem like the best place. I am fine with hard coding the location for now, and us following up after with a patch to make it configurable via apparmor's pkg-config.
… —
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub <#15905 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AH4SSHVVPGUONHRNBTPYLUTRUSZKFANCNFSM4NJLEUOA>.
|
yes, I can update the upstream-ci minimum apparmor version. I'd like to wait until this PR is merged, in case there are any more changes. And currently for this PR, apparmor 2.13 is the right minimum version to use, correct? Based on earlier statement:
|
Sounds perfect to me! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the NULL thing matters
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See unresolved conversations in the Files Changed tab.
…up time Let systemd load a set of pre-compiled AppArmor profile files from a policy cache at /etc/apparmor/earlypolicy. Maintenance of that policy cache must be done outside of systemd. After successfully loading the profiles systemd will attempt to change to a profile named systemd. If systemd is already confined in a profile, it will not load any profile files and will not attempt to change it's profile. If anything goes wrong, systemd will only log failures. It will not fail to start.
New commit with all changes done. Please check. Currently, systemd will not load the profile if it is already confined in a profile. Should we change that and just skip the change to the systemd profile but stil load the policy cache? |
Hmm, maybe. @jrjohansen? |
been a week. I think we are good to merge this. if there's left to fix after all we can handle that as follow-up PR of course. |
Let systemd load a pre-compiled AppArmor profile file from either
/etc/systemd/profile.apparmor if that file exists or
/usr/lib/systemd/profile.apparmor. If neither file exists, systemd will not
load anything and will also not log this at all.
The pre-compiled profile file needs to contain a profile with name systemd.
systemd will change to that profile after loading the file. If the file does
not contain a profile with that name, systemd will log an error.
If systemd is already confined in a profile, it will not load the profile file
and not change to any profile.
If anything goes wrong, systemd will only log errors or warnings. It will not
fail to boot.
Things that may need to be discussed:
A pre-compiled AppArmor profile file can be created using the --ofile option of apparmor_parser.
The idea behind this is, that the kernel does not provide any method of loading a profile file at startup. One has to use an initrd to load the profile file and let start systemd with the profile. If you don't have an initrd (e.g. for VMs that don't need one) you cannot confine systemd itself in an AppArmor profile. With this patch it is possible to do so.