16.2.3.5 Security Label Restriction
A focused guide to Security Label Restriction, connecting core concepts with practical Docker and container operations.
A security label restriction refers specifically to SELinux's security context labeling system blocking a container's access to a file or resource because the label attached to that resource does not match what the container's own security context is permitted to access, a more granular and specific concept than simply confirming SELinux is enforcing, requiring understanding of the actual label types involved to resolve correctly rather than only the blunt instrument of toggling enforcement on or off.
Understanding security contexts
Every file, process, and resource under SELinux carries a security context, a structured label with several components, and a container's own process operates under a specific context type that determines what other contexts it is permitted to interact with:
ls -Z /srv/app/data
unconfined_u:object_r:user_home_t:s0 data
ps -eZ | grep my-api
system_u:system_r:container_t:s0 1234 ? node server.js
The container process runs under container_t, while the bind-mounted directory carries user_home_t, a type SELinux's policy does not permit container_t to access by default, which is the actual, specific cause of a denial in this scenario, distinct from a generic "SELinux is blocking something" diagnosis.
The container_file_t type and why it matters
SELinux policy generally permits container processes to access content labeled with container-specific types, most commonly container_file_t, which is why Docker's :z and :Z mount flags work: they relabel the bind-mounted content to this permitted type rather than requiring a deeper policy change:
docker run -v /srv/app/data:/app/data:z my-api
ls -Z /srv/app/data
system_u:object_r:container_file_t:s0 data
After this relabeling, the directory's content carries a type the container's own context is permitted to access, which resolves the denial without requiring any custom policy work or enforcement mode change at all.
Persistent labeling outside of mount-time flags
For content that should retain its container-accessible label permanently, independent of how it happens to be mounted on any given run, semanage fcontext combined with restorecon applies a persistent labeling rule rather than relying on Docker's mount-time relabeling each time:
semanage fcontext -a -t container_file_t "/srv/app/data(/.*)?"
restorecon -Rv /srv/app/data
This is useful specifically for a directory that might also be accessed directly by host-level tools or other processes outside of Docker's own mount-time relabeling, ensuring the label remains correctly set regardless of how or by what the directory is subsequently accessed.
Diagnosing the specific denial with audit logs
Rather than guessing which label mismatch is responsible, SELinux's own audit log records the exact source and target contexts involved in a specific denial, which is the most direct way to confirm precisely what needs to change:
ausearch -m avc -ts recent
type=AVC msg=audit(...): avc: denied { read } for pid=1234 comm="node"
scontext=system_u:system_r:container_t:s0
tcontext=unconfined_u:object_r:user_home_t:s0
tclass=file
This entry shows exactly which source context (container_t) was denied access to exactly which target context (user_home_t), which is precise enough to either apply the correct mount flag, a persistent labeling rule, or, where genuinely necessary, construct a custom policy module permitting this specific interaction.
Generating a custom policy module when relabeling is not appropriate
In cases where relabeling the target resource to a container-accessible type is not appropriate, perhaps because other, non-containerized processes also need to access it under their own expected label, a custom SELinux policy module can grant the specific access pattern without changing the resource's label at all:
ausearch -m avc -ts recent | audit2allow -M my_container_policy
semodule -i my_container_policy.pp
audit2allow generates a policy module directly from the recorded denial, granting exactly the access pattern that was actually attempted and blocked; this should be reviewed before installing, since it grants precisely what was denied without independently verifying whether that access is actually appropriate to grant, rather than just resolving the immediate error.
Container-specific SELinux types beyond container_file_t
Beyond the general-purpose container_file_t, more specialized container-related types exist for specific purposes, such as types governing network access for confined containers, and a denial involving one of these more specialized types points toward a different, more specific kind of restriction than a simple file access issue:
ausearch -m avc -ts recent | grep svirt
A denial referencing a type prefixed with svirt (the broader virtualization and container security label family SELinux uses) but not specifically container_file_t is worth investigating with attention to exactly which specialized type and operation is involved, since the fix may require a different specific policy adjustment than the file-labeling approach described above.
Verifying a fix resolves the denial completely
After applying a labeling or policy fix, confirming the original operation now succeeds, and checking that no new, different denial has appeared in its place, verifies the fix is complete rather than having only partially addressed a multi-step operation that involves more than one access pattern:
docker exec my-api touch /app/data/test-write
ausearch -m avc -ts recent
An empty or unrelated audit log result after retrying the original failing operation is the clearest confirmation that the specific security label restriction has been fully resolved.
Common mistakes
- Disabling SELinux enforcement entirely to resolve a specific, narrow label restriction that a targeted mount flag or policy adjustment would have addressed without sacrificing the broader security benefit.
- Not checking the audit log for the specific source and target contexts involved, attempting a fix based on guesswork rather than the precise, available evidence.
- Relabeling a shared resource to a container-accessible type without considering whether other, non-containerized processes also depend on its original label for their own access.
- Installing an
audit2allow-generated policy module without reviewing what access it actually grants, rather than treating it as a verified, deliberate security decision. - Assuming a single fix resolved a multi-step operation completely without checking whether a subsequent, different denial appears for a later step in the same operation.
Security label restrictions under SELinux are resolved most effectively by reading the audit log for the exact source and target contexts involved, applying the most narrowly scoped fix available, mount-time relabeling, persistent semanage fcontext rules, or a custom policy module, rather than reaching for a broad enforcement toggle that resolves the symptom while discarding the security benefit the labeling system was providing.