Skip to content

RFE Audit Container ID

Richard Guy Briggs edited this page Apr 5, 2019 · 3 revisions

Feature Description

Containers are a userspace concept. The kernel knows nothing of them.

The Linux audit system needs a way to be able to track the container provenance of events and actions. Audit needs the kernel's help to do this.

Motivations:

  • Filter unwanted, irrelevant or unimportant messages before they fill queue so important messages don't get lost. This is a certification requirement.

  • Make security claims about containers, require tracking of actions within those containers to ensure compliance with established security policies.

  • Route messages from events to local audit daemon instance or host audit daemon instance

  • Tried nsIDs, but insufficient for efficient filtering, routing, tracking

Since the concept of a container is entirely a userspace concept, a registration from the userspace container orchestration system initiates this. This will define a point in time and a set of resources associated with a particular container with an audit container identifier.

A container orchestrator will be able to register an audit container identifier with a specific process to indicate that it and its descendants are part of a specific container. Events attributable to actions in those processes will be marked as such in the audit logs.

Feature Design

The registration is a u64 representing the audit container identifier written to a special file in a pseudo filesystem (/proc/, since a PID tree already exists) representing a process that will become a parent process in that container. This write might place restrictions on mount namespaces required to define a container, or at least careful checking of namespaces in the kernel to verify permissions of the orchestrator so it can't change its own audit container identifier. A bind mount of nsfs may be necessary in the container orchestrator's mount namespace. This write can only happen once per process.

Note: The justification for using a u64 is that it minimizes the information printed in every audit record, reducing bandwidth and limits comparisons to a single u64 which will be faster and less error-prone.

Require CAP_AUDIT_CONTROL to be able to carry out the registration. At that time, record the target container's user-supplied audit container identifier along with a target container's parent process (which may become the target container's "init" process) process ID (referenced from the initial PID namespace) in a new record AUDIT_CONTAINER_ID with a qualifying op=$action field.

Issue a new auxilliary record AUDIT_CONTAINER for each valid audit container identifier present on an auditable action or event with the field name contid= accompanied by an operation field op=.

Forked and cloned processes inherit their parent's audit container identifier, referenced via the process' task_struct. Since the audit container identifier is inherited rather than written, it can still be written once. This will prevent tampering while allowing nesting. (This can be implemented with an internal settable flag upon registration that does not get copied across a fork/clone.)

Mimic setns(2) and return an error if the process has already initiated threading or forked since this registration should happen before the process execution is started by the orchestrator and hence should not yet have any threads or children. If this is deemed overly restrictive, switch all of the target's threads and children to the new audit container identifier.

Trust the orchestrator to judiciously use and restrict CAP_AUDIT_CONTROL.

When a container ceases to exist because the last process in that container has exited log the fact to balance the registration action. (This is likely needed for certification accountability.)

At this point it appears unnecessary to add a container session identifier since this is all tracked from loginuid and sessionid to communicate with the container orchestrator to spawn an additional session into an existing container which would be logged. It can be added at a later date without breaking API should it be deemed necessary.

The audit container identifier will need to be reaped from all implicated namespaces upon the destruction of a container.

Provide a new field AUDIT_CONTID to specify an audit container identifier to use for filtering events.

Since a process in a container could potentially signal the audit daemon (reconfig, terminate, roll log, resume), that audit container identifier information should be made available to the audit daemon to report the full provenance of the signal. It is not possible to add it to the existing audit_signal_info struct without causing a kABI change. Introduce a new audit message type AUDIT_SIGNAL_INFO2 using a new audit_sig_info2 struct to be able to transfer this information from kernel to userspace.

#define AUDIT_SIGNAL_INFO2 1021 /* auditd signal sender info */

struct audit_sig_info2 { uid_t uid; pid_t pid; uint64_t cid; char ctx[0]; };

Development Tasks

  • Implement the audit container identifier registration process including reporting the initiation of a container and reporting the audit container identifier involved in audit events.
  • Implement an audit filter to select or exclude events based on specific audit container identifier.
  • Implement a new message type to convey the audit container identifier of the task signalling the daemon.
  • Implement namespace tagging and reporting of audit container identifier lists potentially involved based on process namespace membership.
  • Implement userspace changes to support the three above.
  • Develop an automated acceptance test to verify all of the above.
  • Develop documentation to support the above.

Functional Testing and Verification

  • A registration when audit is disabled should fail with ENOPROTOOPT.
  • A registration of AUDIT_CID_UNSET (-1) should fail with EINVAL.
  • A registration without CAP_AUDIT_CONTROL should fail with EPERM.
  • A registration to a process with children should fail with ECHILD.
  • A registration to a process with threads should fail with EALREADY.
  • A registration to a process that has already been registered (not inherited) should fail with EEXIST.
  • The child of a process with an audit container identifier should have inherited its audit container identifier.
  • A process with an audit container identifier that signals the audit daemon should have that audit container identifier reported in the signal report.
  • A packet that triggers a NETFILTER_PKT event in a net namespace should have one CONTAINER record for each unique audit container identifier represented by processes in that net namespace.

Example Usage

A container orchestrator will designate a process it just created with PID 1234 to belong to a container whose audit container identifier is to be 123456 before that process has started running:

echo 123456 > /proc/1234/audit_containerid

Filtering on a particular audit container identifier would be accomplished with:

auditctl -a exit,always -F containerid=$containerid

Searching for events involving a particular audit container identifier would be accomplished with:

ausearch --containerid 123456

Example Audit Records

Upon registration of a container for task 1234 with audit container identifier 123456 a record such a record would be produced:

type=CONTAINER_ID msg=audit(1519903238.968:261): op=set pid=596 uid=0 subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 auid=0 tty=pts0 ses=1 opid=596 old-contid=18446744073709551615 contid=123456 res=1

When an audit event happens on a process that has been registered to be part of a container or a process that has inherited that audit container identifier, an auxiliary record is added to the event records:

type=CONTAINER msg=audit(1519924845.499:257): op=task contid=123456

Bugzilla and Issue Trackers