[apparmor] "default"/"system" profile

Steve Beattie steve at nxnw.org
Thu May 16 09:20:41 UTC 2013


On Wed, May 15, 2013 at 05:13:15PM -0700, John Johansen wrote:
> So this is a new attempt to frame the default/init/system profile discussion

Thanks for writing this up (and opening the whole can of worms to begin
with, even if you end up regretting it :) ).

> The goal of the default/system profile is to replace the unconfined profile
> and make it easier to have a default system policy and also to confine
> applications from boot. The unconfined profile has few different properties
>   1. It is the profile that is attached to init as the first process
>   2. It is the target profile for ux/Ux profile transitions
>   3. It is the fallback target for Pux profile transitions
>   4. It is the fallback target for Pix from the unconfined profile. That is
>      to say unconfined does the equivalent of
>        /** pix,
>      for all execs from profiles confined by pix.

Note that that is one interpretation of the existing unconfined
profile's policy, it could also reasonably considered as

  /** pux,

for all execs from profiles that are unconfined. It's a distinction
without a difference in the existing implicit unconfined setup;
however, it has subtle differences when considering unconfined as a
mode for a default policy, or that there may be *multiple* policies
in unconfined mode (and I consider it a possibility that the default
policy may not be one of them, but that may be confusion on my part
on what it means to be in unconfined mode), and what the implications
of that are.

So we should probably document explicitly what unconfined mode is
equivalent to as expressed in apparmor policy, and how it relates to
the default policy. (Even if the documented policy is not the actual
implementation, so long as it's semantically equivalent).

(It also raises the question for me what the bare 'file' keyword is
equivalent to; looking at our apparmor.d(5) man page, I don't see that
made explicit anywhere. It's a little confusing because it's intuitive
what all 'network', 'dbus', or 'capability' access might mean, but
because file access conflates both read/write/exec operations and
policy transitions, it's not straightforward what 'file' means.)

>   5. unconfined is assumed by most policy to be the common system default,
>      though there is nothing in apparmor that requires this as profile
>      transition are determined by profile rules not globally.
>   6. unconfined is the default profile transitioned to when the profile a
>      task is confined by is removed.
>   7. the unconfined profile is exposed on interfaces (historical artifact) as
>        unconfined
>      instead of
>        unconfined (<mode>)

Well, sort of. An enforcing policy was implicitly considered to be
enforcing if no mode was listed in an exposed interface (which is still
the case). The unconfined policy could be considered to be enforcing
(even if it can't actually reject anything). But in the scheme of
things, it's a minor thing anyway.

>   8. the unconfined profile can't be replaced or removed as it is special
>      and not in the profile lists
>   9. the unconfined profile can't be directly transitioned to by
>        /foo/bar px -> unconfined
>  10. the unconfined profile is treated special to bail out of mediation
>      early

Which is really an optimization feature of the current implementation,
and (should) not result in a semantic difference.

>  11. each namespace has its own unconfined profile used to make sure profile
>      attachment occurs from the correct namespace
> 
> 
> To replace unconfined we must be able to address each of these points.
> 
> Fixing 2 & 3:
>   If there is to be a default profile then having a separate unconfined
>   profile that can be used to escape the default profiles confinement is
>   not acceptable. U/ux based transitions need to become transitions to the
>   default profile what ever that is (whether it is unconfined or some other
>   profile).

I think that the distinction you've been drawing between the default
policy and a policy that is unconfined is a good one, and that using
'unconfined' to reference both is confusing. I think it's helpful for
discussion to simply refer to it as 'default_policy', even if that's not
the name we end up using as its (default, sigh) name.

Thus, your statement above be "U/ux based transitions would be
transitions to the default_policy."

(It's a bit unfortunate that our exec modifier U/u is shorthand for
unconfined, and thus continues the conflation.)

> Fixing 4.
>   Nothing to do. Pix is behaving correctly if you are in a different
>   profile than unconfined it is the one that gets inherited if that rule
>   is used
>
> Fixing 5.
>   Nothing to do. Policy should just work. If an author sets up a system
>   confinement then things that are breaking due to not being unconfined
>   need to be address in the modified policy.

To be more explicit for addressing 4 and 5, existing policy should
continue to work semantically as before; that is, a U/ux transition
that before would result in the unconfined state before would result in
a transition to the 'default_policy' which is a policy in unconfined
mode. This is also the policy that init will start with. Or to frame
it differently, mapping the current setup to the new proposal is
like so: the default_policy is a policy named 'unconfined', which
is in unconfined mode (and is the only policy to be in that mode,
modulo apparmor namespace complexities).

What the proposal under discussion gives is the ability for policy
writers to change that behavior.

> Fixing 6:
>   The solution to this is the same as 2, removal of a profile should result
>   in transitioning to the default profile what ever it is (unconfined or
>   otherwise)
> 
> 
> Fixing 7.
>   We can just convert to using
>     <profile> (<mode>)
>   and stop using a bare unconfined, the unconfined profile shows up as
>     unconfined (unconfined)

Should enforcing profiles report that status, or should we continue with
the implicit lack of status mean enforcing?

And again, using a different name makes the distinction clearer, i.e.:

  default_policy (unconfined)


>   this will require some tooling updates to deal with any conditions based
>   on things being "unconfined" but won't out right break tools either
> 
> Fixing 8 & 9
>   We just make it available and maybe special case its removal, replacement.
>   As there must always be a profile on a task
> 
> 
> Fixing 10.
>   We add an unconfined mode, it can be placed on any profile. (DONE)

Can you be explicit and say what it means for a profile to be in
unconfined mode? What are the exec transitions for it (i.e. '/** pix'
or '/** pux')? Can it have additional restrictions in the form of deny
rules, or other modifiers in the form of audit rules as well stricter
(from a path dominance definition) exec transition rules?

> Fixing 11.
>   Nothing to fix, profiles will still need to be per namespace, and tracked
>   per namespace. That means the default/system/init profile that replaces
>   unconfined needs to be per namespace as well. However having a global
>   kernel config that sets the name of the "default" profile for all
>   namespaces may not be correct for cases where a container is being used
>   as a VM. In that case any "default" profile name could be configured as
>   part of the container setup, but defaulting to the system one or the
>   parent namespaces default profile name is an acceptable first pass
>   solution.
> 
>   That is there will need to be work to define this per namespace but that
>   can be done later

AppArmor namespaces throw in a whole additional layer of complexity to
this that I have a hard time thinking about it. That said, given that
in the existing implementation, namespaces have different unconfined
profiles, each namespaces default_policy can be considered an attribute
of that namespace.

I will note that I personally have a myopia around namespaces in that,
because they were grafted on, I tend not to think of them as first class
objects in apparmor.

> Now to the harder point
> 
> Fixing 1. Confining init
>   First up the unconfined profile conflates two issues, confinement of init
>   and the default profile. This is largely because these two issues are not
>   so easily separated. But for the discussion lets tackle each separately
>   and then decide whether they should be combined.

I agree that we're conflating the default_policy (an attribute of
the toplevel system namespace) and the initial_policy that is applied
to init (which is, I grant, going to be the default_policy. At least
*initially*).

>   When the system starts up the kernel attaches a profile to the initial
>   process (init). What exactly init is will vary depending on the system,
>   but it is responsible for bringing the system up. At this point we do
>   not have real context about what init is, just that it is the first
>   process. This first process runs and forks/execs children to do
>   different jobs and they inherit its profile (unconfined). At some point
>   policy is loaded and profile attachments can begin.
> 
>   In a system with an initrd, we can think of init as the early init
>   process that is responsible for doing the tasks required by the initrd
>   (loading modules, etc) before the real init takes over. It is possible to
>   load policy from the initrd, though if doing so it should be only what
>   is required and precompiled. This way policy is in place when the system
>   init is started and policy is correctly applied to the whole system.
> 
>   Loading policy from the initrd is a pita, and it doesn't work systems
>   that don't have an initrd, so it is desirable to be able to trust early
>   boot, and do an early policy load which provides system confinement. This
>   is not currently possible as the unconfined profile can not be replaced,
>   nor can individual tasks have their profile set by an external task.  To
>   be able to confine the processes that are present before policy load
>   occurs (init, and several children) the profile on them must be
>   replaceable. However all these processes share a single profile, because
>   the initial profile has no policy transition rules, and no other profiles
>   are available.

The reason init's confinement is an issue is because in this proposal
we're granting the ability to an administrator to change the default
policy to be more restrictive than straight unconfined (mode). But
I can envision wanting to make the default policy be restrictive in
a way that is inappropriate for init and causes it to break.

>   There are several potential solutions to the problem of confining init
>   and its early children
>     1. Policy load in the initrd (assuming you have an initrd), and having
>        the early initrd init exec into the system init process
>        This is clean and allows policy to be specified separate from the
>        kernel.

This is clean if you're a distribution/OS vendor, and you're
willing to do the work to support it *and* add the flexibility for
an administrator to customize it. Requiring that an administrator
make custom modifications to their initrd (and roll those out across
an enterprise) doesn't really lead to maintainable systems, and will
mean this feature/flexibility is unlikely to be used.

>     2. special case init and children in the kernel code
>        - No, just no.

Agreed.

>     3. provide a basic policy compiled into the kernel. This is like the
>        initrd solution but is built into the kernel. The initrd solution is
>        more flexible but may not be available on some systems
> 
>     4. set the profile on individual tasks, after policy load
>        - this is racy as you are chasing different tasks trying to properly
>          confine tasks with the initial profile
>        - this ability does not currently exist and likely won't for a long
>          while
> 
>     5. provide the ability to replace the initial profile, and accept all
>        tasks under the initial profile will have the same confinement
>        - changing the confinement of the tasks is not really an option as
>          attachments and parent child hierarchy chains aren't reliable
>        - what name to use
>
>     6. Put the initial profile into a "complain" like mode where each child
>        process gets its own child profile and the hierarchy is maintained.
>        - this is somewhat racy as you still need to chase down and replace
>          the dynamic profiles before they fork a child and exec to yet
>          another new profile
>        - do not want complain messages to the log, so new mode?
> 
> 
>   The name of the initial profile is not terribly important as long as it
>   is consistent and conveys the correct meaning. This could be "init",
>   "system", "default", ... (remember at this point we are not yet
>   discussing the default confinement profile). When renaming replacement
>   becomes available this profile can also be renamed as it is replaced,
>   and several profiles (if the hierarchy option is chosen) can be
>   collapsed into a single profile. Also as suggested this initial
>   profile name could easily be defined as a grub boot option, or as part
>   of the Kconfig when a kernel is built
> 
> 
> 
> Now on to the default profile
> 
>   The default/system profile is used when ux/Ux is used or a profile is
>   removed and application confinement falls back to the default.
>   Traditionally this and the init role have been played by the unconfined
>   profile, but it is possible to separate them. The question is how and is
>   it worth it. If the default/system profile is not attached on boot then
>   it is the init profile will be attached to processes on boot and the
>   default profile won't see use until a profile is removed or ux/Ux/pux is
>   encountered. This means the system is confined by the init profile by
>   default.
> 
>   For a finer grained confinement on boot, multiple profiles need to be
>   defined along with their transition rules as discussed above, without
>   that the "init" profile is the default in use by the system after boot,
>   and having a separate default profile that is transitioned to on profile
>   removals is confusing.
> 
>   So without having a more detailed policy defined at boot what can be
>   done?
> 
>   Two profiles could be define at boot (well actually for each namespace),
>   an init and default profile. The init process would start in the "init"
>   profile and at some point the default profile begins to be used. The
>   question is when?
> 
>   a. on every exec until policy is loaded
>      this is equivalent to having the init profile doing pux
>
>      this is problematic unless the init profile has a defined attachment
>      so that it can keep the init processes in the init profile if it
>      re-execs itself. Also note we can not arbitrarily change what this
>      attachment is at boot. It is possible to have the attachment based on
>      a name and change it at boot time, but the kernel will not have the
>      dfa compiler in it so it is not possible to user regexs as part of
>      the attachment.
> 
>   b. define an attachment for init
>      the attachment needs to be fixed so it is not flexible to different
>      systems. While the attachment could be defined as a grub boot option
>      it could not use regexes/globbing (so exactly one name).
> 
>   c. once default is loaded
>      in this case init is aliased to the default profile until a new
>      default profile is loaded, at which point the new default takes over.
> 
>      This is different from a in that all the early processes are confined
>      by the init profile, and default is only different once a new one is
>      loaded. This is also racy as to what processes receive a give
>      confinement unless the policy load is ordered and other boot
>      processes wait on it
> 
>   d. A variation of c
>      Except default doesn't have to be loaded, it will go into effect as
>      soon as the first profile is loaded. I would assume it would start
>      out as just the default (unconfined) {} profile.
> 
>   e. build init, and default as actual profiles and compile into the
>      kernel (I am not found of this)
> 
>      The problem with confining early boot and having init have a different
>      profile is that you need a way to identify which processes should be
>      confined by which profiles. The best way to do this is an early policy
>      load, any other solution is somewhat hackish. Splitting the init and
>      default profiles has some merit but the question is it enough for the
>      extra effort. As without a defined early policy we are very limited
>      in how processes are divided between separate init and default
>      profiles.
>
> Aside: the problem with a default profile with broad attachment specification
>   It is possible to define a fixed broad attachment specification for the
>   original default profile, but there is no point as having init use
>   a specific attachment or pux will have the same results. And the broad
>   attachment breaks pix
> 
>   If the default profile is define like
>     profile default /** {
> 
>     }
> 
>   then it will break the fallback in pix rules, where a profile may want
>   to transition to itself if an application profile is not defined,
>   because there is always a profile defined (ie the p portion of the
>   specification always finds a profile and the ix fallback never happens).

I get what you're saying but the default_policy that U/ux is
*different* than a profile with overly-wide attachment rules. We can
say that U/ux is equivalent to P/px -> default_policy, *except* that
it's not when it comes to Pux (at least not in the existing language
and implementation); there is no P/px -> self, default_policy.


I think the problem with all the above is that you're still conflating
the initial_policy that is applied to init with the default_policy, an
attribute of the 'default' or 'system' namespace. (I grant that I too
didn't fully see the distinction, and didn't consider it an attribute
of the namespace, until halfway through responding to this email.)

So let me try to unconflate them even further:

 - When we initially boot, we have one profile in the system namespace
   (there are no other namespaces at this time), let's name it
   'initial_policy'.

 - This 'initial_policy' starts out in unconfined mode. It can be
   replaced.

 - The default_policy attribute of the system namespace *points to*
   the profile 'initial_policy'. *Initially*. But we could allow
   an administrator to change this attribute after boot to point to
   something besides 'initial_policy', like an administrator defined
   profile named 'my_nifty_default_policy'. This is a different action
   than replacing 'initial_policy'.

   Note that changing the default_policy namespace attribute would not
   change the existing attachment on init and it's children, which have
   'initial_policy' still applied, only future U/ux transitions would
   be affected (though that's one of the reasons why it's important
   to define whether unconfined mode implies '/** pix' or '/** pux').
   Of course, 'initial_policy' can be reloaded/modified as well,
   such that it has '/bin/init ix, /** pux' so that re-execing itself
   will keep 'initial_policy' applied but 'my_nifty_default_policy'
   will get applied to anything else it execs. All this could occur
   before or after your initrd completes.

Does that help at all?

Now I grant that we don't really have anything toolwise that works
on a namespace independently; because they were grafted on, they've
always been tightly coupled to the policies that make them up.

(My only hesitation is the issue cboltz raised, that if you configure
your default_policy too restrictively, you may not be able to complete
booting, and you wouldn't have a kernel command line argument to
tweak to not change the default_policy to 'my_nifty_default_policy'.)

> Choosing a name
> 
>   IFF we choose to split the init and default profile then I think the
>   names should be
>    init and default (or maybe system)
> 
>   otherwise
>     system seems to make sense

I think getting the concepts nailed down first is more important than
the specifics of the names.

-- 
Steve Beattie
<sbeattie at ubuntu.com>
http://NxNW.org/~steve/
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <https://lists.ubuntu.com/archives/apparmor/attachments/20130516/34d0cfb1/attachment-0001.pgp>


More information about the AppArmor mailing list