[apparmor] "default"/"system" profile

John Johansen john.johansen at canonical.com
Wed May 22 07:32:47 UTC 2013


On 05/21/2013 10:57 AM, Steve Beattie wrote:
> On Tue, May 21, 2013 at 12:49:32AM -0700, John Johansen wrote:
>> On 05/21/2013 12:07 AM, Steve Beattie wrote:
>>>> - For all other namespaces
>>>>   - the first profile is the "init" profile, and it is set as the default profile
>>>
>>> The first profile loaded? So profile load order matters here? Is there
>>> a reason not to make this explicit in policy somehow, for non-root
>>
>> yes and no. If a namespace is created as part of a profile load, that
>> profile will be the "init" profile and the default profile until such
>> time as a default profile is loaded.
>>
>> This really isn't any different from kernel boot where the root namespace
>> is seeded with the "initial" profile.
> 
> Sort of. The seeded initial profile for the root namespace is
> synthetically generated (granted, because we have no other choice),
> and the default is not replaced by whichever profile is the first
> one loaded after that, but by an explicit policy choice. Whereas for
> a non-root namespace, you're proposing whichever profile is loaded
> first for that namespace becomes the default, which feels like an
> implicit behavioral artifact of policy load order.
> 
yep

>> Also this make it compatible with what we have today.
> 
> It's not compatible behavior, though, is it? Our existing behavior is
> that when you create a new non-root namespace by loading the first
> policy for that namespace, you also get the 'unconfined' profile as
> the default policy for that namespace.
> 
ah yep, I'm not sure what I was thinking. We will in fact have to do
that for the child namespace as well, so that if a default profile
is not defined we create an unconfined "default" profile.

>>> namespaces? Or are you afraid that the difference between root and
>>> non-root namespace attributes will be too confusing? Do you foresee
>>
>> err, I don't follow? As our namespaces have been set up each is independent
>> of the other, and tasks can't leave or see the higher namespace.
> 
> Sorry, the behavioral distinction I was trying to draw between the
> root namespace and non-root is that, even if we allowed some way in
> policy of specifying an initial profile for namespaces, unless we
> did something like compile the policy into the kernel, it would be
> ignored for the root namespace, because of the realities of booting
> and bootstrapping.  That's not necessarily true for things like
> containers using non-root level apparmor namespaces.
> 
yep

>>> anything besides LXC/containers that would cause an administrator to
>>> specify an initial policy attrribute for a non-root namespace?
>>>
>> The way I proposed things there is no initial policy attribute, just a
>> default profile attribute. In the case of initial namespace setup on
>> boot and setup of a child namespace that attribute points to the only
>> profile until a policy load defines which profile should be the default.
> 
> Ah sorry, I had misconstrued what you were proposing, and thought there
> was an initial attribute. I grant that it really is a bootstrapping
> artifact more than anything.
> 
right it not so much initial policy as, initial default profile for
the boot strap.

we are just providing that profile a nice name so that init et al.
aren't labeled with a profile named "unconfined". Which could honestly
be fine as switching to a new default with a different name would
still work. And if you wanted to confine init you could always
replace the initial "unconfined" profile with a renaming replace
changing it from "unconfined" to "initial" or something else.


>> And yes the initial profile load when setting up a namespace could
>> very well have multiple profiles, and could set the default profile
>> attribute explicitly.
> 
> I would argue that, if we keep the 'first profile in becomes the
> default for non-root namespaces" semantics, we should at the very
> least recommend as best practice always setting the default policy
> explicitly.
> 
right, so thats dead.

>>> When the notion that the default profile should be considered
>>> an attribute of the namespace started becoming apparent to me, I
>>> initially considered expressing it in policy as a mode/flag on the
>>> profile. I decided I didn't like that (and didn't propose it) because
>>> none of our other profile flags operate in the same fashion, either
>>> requiring the flag to be unique across the policies in the namespace,
>>> or specific load ordering if multiple default flags are given.
>>>
>> true
>>
>>> It comes back to my desire to grow namespaces as first class objects
>>> in policy (I have no doubt they are first class in implementation).
>>> Random hand-wavy approximation of a language extension proposal:
>>>
>>>   namespace NAMESPACE_NAME {
>>>     attribute default = my_nifty_default_profile,
>>>   }
>>>
>>> but that's with 15 seconds of what passes for thought on my behalf,
>>> with marginal consideration for consistency, etc.
>>>
>> yeah we are going to have to think about it carefully
> 
> Agreed.
> 
>> right now namespaces are strictly hierarchical and you never reference
>> your namespaces name, as from a profile level policy pov you are always
>> in the root namespace.
> 
> As an aside, what is the current syntax for indicating a sub-sub-level
> namespace?
> 
ah kernel or parser?

the kernel is actually a little more flexible than the parser atm.

It accepts both
  :<ns_name>:<profile>
and
  :<ns_name>://<profile>

it outputs the second form every where


the parser currently only parses (needs to be fixed)
  :<ns_name>:<profile>

the parser also takes the -n flag which allows setting the namespace
on a profile that does not have a namespace defined.

namespace names are hierarchical just like profiles using // to separate
them. So eg.

  :child//grandchild:///usr/bin/firefox//helper

notice in the /// the leading // always binds as a separator to the left
and / becomes the leading / of a path. There is never any ambiguity because
/ is not a valid trailing char (you can not have an attachment on a dir),
and that restriction carried over to the profile name as well.

so yes this is an area that needs some work.

>>>>   - transitions
>>>>     - u/Ux become a transition to the default profile
>>>>     - u/Ux becomes deprecated
>>>>       - we could replace it with d/Dx or something else as part of the policy language
>>>>   - replacement
>>>>     - if the default profile is replaced, its replacement becomes the default
>>>>     - unless another profile within the replacement set specifies it is the default
>>>>     - it is an error for an atomic replacement set to have to profiles marked as being the default profile
>>>
>>> I'd like for our policy language to discourage the latter two points
>>> from being a problem.
>>>
>> sure, if its not marked on the profile that is easier
>>
>> however its still an error to have multiple stanzas declaring different
>> default profiles.
> 
> Absolutely.
> 
>>>>   - removal
>>>>     - removal of profile within the namespace causes a replacement to the namespaces default profile
>>>>     - if the default profile is removed, we could do any of the following
>>>>       - disallow the removal (unless the namespace is being removed)
>>>
>>> I don't care for disallowing the removal.
>>>
>>>>       - set the default profile to unconfined mode, without actually removing it
>>>>       - replace with a profile named "unconfined" that is replaceable, and becomes the new default profile
>>>
>>> I... don't have a strong opinion here, except that I prefer Seth's
>>> proposal to use 'default' over 'unconfined'. It does raise the issue
>>> if we're magically creating profiles, what if a profile by that name
>>> (e.g. "unconfined") already exists in the namespace?
>>>
>> yep its a problem, I don't have a clean solution for it atm
>>
>>> I could also see as another option it reverting to whatever the
>>> initial policy is, though I'm less than enthusiastic about it.
>>>
>> no, the initial policy it self may have been replaced/removed
> 
> For replaced, I was thinking "you asked to replace it, you get what
> you get". For removed, well, we'd still need a fallback, just as if you
> remove all policy.
> 
right but that would require introducing the concept of an initial policy
attribute to the namespace. Do we really want to do that. To me it feels
like at this point neither is terrible clean but at least always go
to the fallback is more consistent

> I *almost* kind of like the 'set the current default profile to
> unconfined' and carry on, for simplicity of behavior sake. I need to
> think more about the implications for reloading policy after such an
> event, however.
> 
well in this case unconfined would be replaceable and renameable so
if you wanted to reconfine those processes you could and you could
have a different name than unconfined.

I'm some what torn on the name as unconfined is the traditional name
but its also a mode, which could cause confusion. I think in this
case default might be the better choice.

>>
>>>>     - if the namespace is removed
>>>>       - all profiles in the namespace, including the current default profile, are replaced by the parent namespaces default profile.
>>>
>>> What is our current behavior for processes in an existing non-root
>>> namespace/profile pair, if the entire namespace is removed? (I guess
>>> if I *really* wanted to know, I'd write a testcase or two.)
>>>
>> It has its profile replaced to the parent namespace's unconfined profile.
> 
> Okay.
> 
>> The other option is to kill all tasks within the namespace that is being
>> removed, but we can't do this directly (well not easily). So it would
>> likely be achieved through some special profile replacement.
> 
> Hopefully, namespace removal that doesn't involve the teardown of
> all processes that have that apparmor namespace applied is an edge
> case. The common cases that I see for namespace removal would be
> things like 'the related container is shutting down', where all
> processes should be ending anyway.
> 
right. I was hoping at this level to leave the process tracking and killing
to other mechanisms. But maybe we should put them into a special kill
profile.

Having a child slip from confined sub namespace to potentially unconfined
feels very wrong.

However if the process is stacking across a namespace its not quite so
bad as the new confinement is the intersection of the stacks profile from
the parent namespace and the default of the parent namespace.

still killing does feel like the correct solution.





More information about the AppArmor mailing list