[apparmor] RFC: Policy versioning

John Johansen john.johansen at canonical.com
Mon Dec 11 18:33:07 UTC 2017


On 12/11/2017 09:30 AM, Jamie Strandboge wrote:
> On Sun, 2017-12-10 at 03:05 -0800, John Johansen wrote:
>> Currently we have a few problems with policy that must be addressed
>>
> 
> Thanks for this writeup! I'm still processing a lot of it, but there
> were a couple of things I wanted to comment on right away...
> 
>>
>> I. Proposed Solution
> 
> ...
> 
>> 2. Support multiple policy caches
>>
>>   To address problems 3-4 we extend the poliy cache so that we retain
>>   a compiled policy per kernel installed. When a new kernel is
>>   installed we build a new policy cache for it.
> 
> Big +1 on supporting multiple policy caches. It does seem like we would
> want to still compile (versioned!) policy on boot if the policy cache
> does not yet exist to gracefully handle situations where the kernel
> packaging didn't yet do it, no? This would avoid a flag day for
> everyone to adjust their kernel install packaging scripts at the same
> time.
> 
> I'd have to think through the implications for snappy here since the
> kernel is shipped as a snap since nothing is (currently) executed upon
> install. It is possible to run install hooks/etc on this, so the
> proposal is not on its face problematic.
>  

So I am not opposed to having the ability compile policy later on.
It can work but its somewhat racey wrt getting policy in place for
other system services (unless there are modification to ensure
proper depedency), and it doesn't work at all for early services,
nor does it work with the early load we can get with systemd.

If we find our selves in the position where we are compiling policy
on boot, then it is entirely possible a reboot will result in
different confinement, which is not a good or reliable experience
for users.

So as much as possible we want the policy to be compiled before
boot.

>> 3. Standardize policy config dir and files
>>
>>   Problem 5 is addressed by standardizing a config directory and file
>>   layout. New locations must be added to the config dir to inform
>>   apparmor of new policy locations and how they should be handled.
>>
>>   The parser config has proven insufficient so Ubuntu has been
>>   modifying the initscript to manage this which is not a solution
>> that
>>   can be shared across distros, nor does it provide a solution that
>>   works with other parts of apparmor like the tools.
>>
>>   Instead we have a directory in which each new location can drop its
>>   own config, allowing to set its policy and include location cache,
>>   and even compiler options if so desired.
>>
> This makes a lot of sense. Are you envisioning apparmor would handle
> all policy loads? For example, today snapd modifies policy in it own
> area in /var/lib/snapd, outputs cache in /var/cache/apparmor and uses
> its own options. Setting the cache and options is clear from a snapd-
> perspective, what does setting the policy location imply? Or put
> another way, how does this interact with '5', below?
> 

Not necessarily, I fully expect lxd and libvirt to still be dynamically
creating and loading policy. This is merely a way of providing the
tools and initscripts more information so that, they can reload
those policies if so directed but also can say these are managed
by lxd.


>>
>> 4. Limit distros ability to compile policy to the current kernels
>>    feature abi
>>
>>   Along with this Distros will no longer be able to set a default
>>   policy compile that will use the current kernel's abi. This will
>> not
>>   even be supported at the distro level as the project can not afford
>>   to break the feature abi of current policy for kernel developers.
>>
>>   To address this a new tool will be added to extract the kernel
>>   features abi, and tooling will be updated to allow users update a
>>   profiles abi and thus begin development on newer versions.
>> Basically
>>   a per user opt in only approach.
> 
> I'm a little confused by this. Why would it be bad for a distro to
> compile policy to a *versioned* policy cache for the current feature
> abi? I understand wanting to obsolete *un*versioned policy caches.
> 

Because this is effectively equivalent to what got us in trouble
and led to the revert. Whether we like it or not, the ability to
to do this system wide, and at the distro level has to go away
to safe guard our ability to work with upstream.

This is going to have to be a user only per profile opt-in. Yes it
sucks from the distro/profile developer pov, but at this point we
have to be extra paranoid about it.


>>
>> 5. Applications managing policy and unknown profiles
>>
>>   The current solution to problem 6 of having unknown policy and
>>   relying on aa-remove-unknown is more problematic. We are going to
>>   have to break existing behavior to fix it.
>>
>>   Applications that want to manage their own policy are going to have
>>   to register to do so. This will require a new API for applications
>>   to use which could just be a thin layer on top of the policy config
>>   file.
> 
> This sounds fine and is not onerous. The few applications that manage
> their own profiles (eg, libvirt, lxd, snapd, etc) are typically
> following the cutting edge of apparmor development and are already
> making changes to policy for new rules, so a small change to register
> going forward seems ok to me.
> 
> ...
> 
>> 6.1 New variables
>>
>>   The @{abi} or @{version} variable that will be defined as part of
>>   the abi include can be used by the rest of the includes to
>>   selectively include rules that are abi specific
>>
>>   eg. the <abstractions/base> abstraction can do an include on
>>
>>    <abstractions/@{abi}/base>
>>
>>
>>   In addition to the @{abi} variable the parser should make the full
>>   feature set available for finer grained decision making.
>>
>>   if @{features/network/af_unix} {
>>      ...
>>   }
> 
> I like this type of rule.
> 
>> 6.2 Conditional include
>>
> ...
> 
>>   The syntax needs to be decided on, but some suggestions that have
>>   been thrown around in the past are:
>>
>>
>>   * Make style
>>
>>     with a - at the start of the line, in apparmor's case it would be
>>     a special qualifier that for the time being only applies to
>>     includes.
>>
>>     - include <abstractions/@{abi}>
> 
> This reads like it is (part of) a comment. I don't particularly care
> for it. Not sure which of the others I prefer so won't comment further.
> 
> ...
> 
>> 7. Dealing with new policy features on older releases.
>>
>>   Where possible the parser supports downgrading rules. However this
>>   only works for rule types that the parser knows about. To support
>>   newer policy features on older releases the best solution is
>>   dropping the newest version of apparmor into an older release.
>>   However this is not always possible.
>>
>>
>> 7.1 Wrapping rules in conditionals
>>
>>   With the feature set being exported as conditionals it becomes
>>   possible for policy to wrap new feature rules in conditionals.
>>
>>   eg.
>>
>>     if @{features/network/af_unix} {
>>        unix peer=foo,
>>     }
>>
>>   While this addresses the need to do special casing in policy
>>   packaging, it makes policy harder to read.
> 
> Actually, I don't find this particularly hard to read and like the
> syntax quite a bit.
> 
>>
>> 7.2 Supporting unknown rule templates
>>
>>   Instead of wrapping new rule types in conditionals we should extend
>>   policy to support rule templates. Rule templates would allow
>> userspace
>>   to specify patterns for unknown rule types, so that the parser or
>>   tools can parse the rule, and ignore it.
>>
>>   The Rule templates could then be dropped into the abstractions,
>>   as new features are added providing an easy way to update older
>>   userspaces to ignore new rule types.
>>
>>   eg.
>>     if !supports(key) {
>>       template key='key\w.*,'		# yes its overly simple
>>     }
>>
>>   Such rule templates wouldn't completely remove the need for being
>>   able to wrap some policy in conditionals, but it done properly it
>>   should be able to support most cases.
>>
> IMO this would make auditing policy a bit harder since you have to
> either do a preprocess run for auditing (not necessarily a bad thing). 
> 
Any worse than wrapping them in conditionals? Which you then have to
do a preprocessing run to find out if the rule is being applied?
Understand that we already have rules being conditionally
applied/downgraded, it is only whether rules that the parser doesn't
know about have to be wrapped with conditionals.

Think of a packager taking a profile, say for firefox, that works on
xenial back to say trusty and finding the apparmor back there doesn't
support every thing that xenial does so, those rules get wrapped in
conditionals for both trusty and firefox, because the packager doesn't
want to maintain more than one version of the profile.

Is that any cleaner than giving the trusty parser so basic knowlege
to skip the rules it doesn't know about. And yes it would just be
better to SRU the xenial userspace back to trusty and then it
would be able to skip those rules because trusties kernel doesn't
know about them, but we both know what a PITA doing an SRU can be,
and the reality is it isn't going to happen unless there is a
really good reason.

> Mostly though as a policy author I like to group rules together in
> arbitrary ways. For example, if I have an 'ix' rule, I might put the
> file access rules next to it, with the unix rule next. Eg:
> 
>   #
>   # foo rules
>   #
>   /usr/bin/foo ix,
>   # needed by foo for ...
>   /etc/blah r,
>   # foo connects to this for ...
>   unix ...,
> 
>   #
>   # bar rules
>   #
>   /usr/bin/bar ix,
>   # bar connects to this for ...
>   unix ...,
> 
> IIUC, the rule templates put the unix rules somewhere else, outside of
> the context of the need for the rule.
> 

No, no. They are just a way to expand a parsers ability to parse an
unknown rule just enough so it can skip it and keep processing the
rules it does know about.

Think of it as being able to drop a set of rule templates into an
older version apparmor, so it will support newer policy (yes ignoring)
without doing a full SRU, which will still just result in the rule
being dropped unless they are using a newer kernel as well.

The goal is to make it so you won't have to change your policy or
have multiple versions of policy just because your application is
running on systems with different versions of apparmor.




More information about the AppArmor mailing list