Project 3: Custom Application Policy Module Builder
Write a full SELinux policy module for a custom daemon, including types, transitions, and file contexts.
Quick Reference
| Attribute | Value |
|---|---|
| Difficulty | Level 3: Advanced |
| Time Estimate | 2-3 weeks |
| Main Programming Language | Policy language (TE + M4) |
| Alternative Programming Languages | Python (for generation tooling), C |
| Coolness Level | Level 4 |
| Business Potential | 2 |
| Prerequisites | Projects 1-2, SELinux tooling, service management basics |
| Key Topics | TE rules, domain transitions, policy modules, file contexts |
1. Learning Objectives
By completing this project, you will:
- Define new SELinux types for a custom application and its data.
- Create domain transitions and entrypoints for a systemd-managed service.
- Build and load a policy module using the SELinux toolchain.
- Label application files and directories with custom types.
- Validate least privilege by iterating on AVC denials.
2. All Theory Needed (Per-Concept Breakdown)
SELinux Policy Language and Type Enforcement
Fundamentals
SELinux policy is a declarative language that specifies which source types can access which target types under which object classes and permissions. The dominant model in most distributions is Type Enforcement (TE). An allow rule in TE looks like allow source_t target_t:class { perms }; and it is the core unit of access control. Policies also define types, attributes, roles, and constraints. For a custom application, you need to define a process type (domain), file types for data and logs, and allow rules that connect them. Understanding the structure of a policy module and how rules are interpreted is essential to writing a correct and minimal policy.
Deep Dive into the concept
The SELinux policy language is compiled into a binary policy that the kernel loads and enforces. Policies are built from multiple modules that are layered together, which means your custom policy module must integrate with the base policy. In practice, you define a type for your domain (e.g., myapp_t), a type for your executable (myapp_exec_t), and additional types for data (myapp_data_t) and logs (myapp_log_t). The domain is associated with the executable via a type_transition rule, and the executable type must be marked as an entrypoint for the domain. Without the entrypoint permission, the transition will not occur.
Type enforcement relies on attributes to simplify policy. For example, files_type marks types that can appear on files, while domain marks process types. You can attach your custom types to existing attributes to inherit policy conventions, but you must do so carefully to avoid unintended access. The policy language also supports interfaces and macros (via M4) that encapsulate common patterns. The sepolicy generate tool produces a scaffolding that uses these interfaces, which can accelerate development. However, blindly using macros can grant more permissions than required. The correct approach is to start minimal, run the application, capture AVC denials, and iteratively add the specific allows needed.
Constraints and neverallow rules further restrict access. They are evaluated after allow rules, and they can prevent unsafe permissions even if you add them. This is a safety net but can be surprising. If you attempt to grant access that violates a constraint, module compilation may fail or the rule may be ignored. Understanding these constraints is part of responsible policy development. Use sesearch and seinfo to inspect existing policy and ensure your module aligns with it.
Because SELinux is a kernel-enforced MAC, your policy decisions are final. Overly permissive rules can defeat the protection you are trying to build. Least privilege is not just a principle here; it is a debugging strategy. If you grant only what the app needs and verify it through AVCs, you build an auditable, minimal policy. This iterative method also makes policies maintainable, as each rule corresponds to a real access pattern. Your policy module is, therefore, not just code; it is documentation of the application’s security contract.
Additional operational notes on SELinux Policy Language and Type Enforcement: In real systems, this concept interacts with policy versions, distribution defaults, and local overrides. Always record the exact policy version and runtime toggles when diagnosing behavior, because the same action can be allowed on one host and denied on another. When you change configuration related to this concept, capture before/after evidence (labels, logs, and outcomes) so you can justify the change, detect regressions, and roll it back if needed. Treat every tweak as a hypothesis: change one variable, re-run the same action, and compare results against a known baseline. This makes debugging repeatable and keeps your fixes defensible.
From a design perspective, treat SELinux Policy Language and Type Enforcement as an invariant: define what success means, which data proves it, and what failure looks like. Build tooling that supports dry-run mode and deterministic fixtures so you can validate behavior without risking production. This also makes the concept teachable to others. Finally, connect the concept to security and performance trade-offs: overly broad changes reduce security signal, while overly strict changes create operational friction. Good designs surface these trade-offs explicitly so operators can make safe decisions.
How this fit on projects
TE rules appear in §3.2 and §4.4, and they are validated in §6.2. This concept is the core of P02-avc-denial-analyzer-auto-fixer.md and P08-selinux-policy-diff-tool.md.
Definitions & key terms
- type -> named label used in TE
- allow rule -> grants access between types
- attribute -> grouping of types for policy convenience
- interface -> macro that encapsulates common policy patterns
- neverallow -> rule that forbids access regardless of allow rules
Mental model diagram
myapp_t --allow--> myapp_data_t:file { read write }
How it works (step-by-step, with invariants and failure modes)
- Define types for domain and files.
- Attach attributes (domain, files_type) if appropriate.
- Write allow rules for required access.
- Compile the policy module.
- Load module and validate behavior.
Invariants: allow rules are type-based; no access without explicit allow. Failure modes: rule too broad, constraint violations, missing attributes.
Minimal concrete example
type myapp_t;
allow myapp_t myapp_log_t:file { append write getattr };
Common misconceptions
- “A single allow rule is enough.” -> Real apps need permissions across multiple classes.
- “Macros always grant minimal access.” -> They can be broad if used carelessly.
Check-your-understanding questions
- Why are
neverallowrules important? - How do attributes simplify policy?
- What is the risk of overusing macros?
Check-your-understanding answers
- They enforce global security invariants and prevent unsafe permissions.
- They let you apply rules to groups of types instead of each type individually.
- Macros can add permissions you did not intend, weakening least privilege.
Real-world applications
- Confining custom daemons in enterprises.
- Hardening vendor software with custom policies.
Where you’ll apply it
- This project: §3.2, §4.2, §4.4, §5.10 Phase 2, §6.2.
- Also used in: P08-selinux-policy-diff-tool.md.
References
- “SELinux by Example” (Mayer et al.), policy language chapters
- “SELinux Notebook” policy reference
Key insights
SELinux policy is a precise contract: every allow rule should correspond to a real need.
Summary
Type Enforcement rules define access. Writing minimal allow rules is the foundation of safe SELinux policy.
Homework/Exercises to practice the concept
- Write a minimal allow rule for reading a config file.
- Identify an attribute you could attach your custom type to.
- Find a
neverallowrule in the base policy.
Solutions to the homework/exercises
allow myapp_t myapp_conf_t:file { read getattr open };files_typefor file labels,domainfor process types.- Use
sesearch --neverallowto locate constraints.
Domain Transitions and Entrypoints
Fundamentals
Domain transitions ensure that a process runs in the correct SELinux domain when it executes a specific binary. The transition is defined by a type_transition rule and an entrypoint permission on the binary’s type. Without these, the process will remain in its parent domain, which is often too permissive. For a custom application, you must define the executable type (myapp_exec_t) and a transition from init_t or systemd_t into your custom domain (myapp_t). This is essential for confinement and is one of the first things to verify during debugging.
Deep Dive into the concept
A domain transition is a policy rule that maps from a source domain and an executable type to a new process domain. It is evaluated when a process calls execve on a binary. The rule looks like type_transition source_t myapp_exec_t:process myapp_t; and is accompanied by allow source_t myapp_exec_t:file { read execute entrypoint };. The entrypoint permission is what authorizes the binary as a valid transition target. Without it, SELinux will not transition even if the type_transition is declared. This is a common error when hand-writing policy.
Systemd complicates this slightly because many services are spawned by systemd (or init) which run in init_t or systemd_t domains. If your policy does not allow that domain to execute your binary as an entrypoint, the service will run in the caller domain. You can verify domain transitions by checking ps -eZ after starting the service. If your daemon is running in init_t, your transition rules are missing or the binary label is wrong.
There are also role transitions and MLS constraints that can affect transitions, especially on MLS systems. In targeted policy, these are less visible, but you should still ensure your types have the correct role associations. Another nuance is that some transitions are confined by domain_auto_trans or other macros. These macros set up the entrypoint, type_transition, and sometimes additional permissions in one step. If you use a macro, understand what it expands to by inspecting the generated policy. This helps you avoid unnecessary permissions.
From a debugging perspective, transition errors manifest as policy failures that look unrelated. If the service runs in the wrong domain, every subsequent access check will be evaluated against the wrong set of rules, causing a cascade of denials or unexpected allowances. That is why your policy builder must validate transitions early and make them explicit in the module documentation.
Operational expansion for Domain Transitions and Entrypoints: In real systems, the behavior you observe is the product of policy, labels, and runtime state. That means your investigation workflow must be repeatable. Start by documenting the exact inputs (contexts, paths, users, domains, ports, and the action attempted) and the exact outputs (audit events, error codes, and any policy query results). Then, replay the same action after each change so you can attribute cause and effect. When the concept touches multiple subsystems, isolate variables: change one label, one boolean, or one rule at a time. This reduces confusion and prevents accidental privilege creep. Use staging environments or fixtures to test fixes before deploying them widely, and always keep a rollback path ready.
To deepen understanding, connect Domain Transitions and Entrypoints to adjacent concepts: how it affects policy decisions, how it appears in logs, and how it changes operational risk. Build small verification scripts that assert the expected outcome and fail loudly if the outcome diverges. Over time, these scripts become a regression suite for your SELinux posture. Finally, treat the concept as documentation-worthy: write down the invariants it guarantees, the constraints it imposes, and the exact evidence that proves it works. This makes future debugging faster and creates a shared mental model for teams.
How this fit on projects
TE rules appear in §3.2 and §4.4, and they are validated in §6.2. This concept is the core of P02-avc-denial-analyzer-auto-fixer.md and P08-selinux-policy-diff-tool.md.
Further depth on Domain Transitions and Entrypoints: In production environments, this concept is shaped by policy versions, automation layers, and distro-specific defaults. To keep reasoning consistent, capture a minimal evidence bundle every time you analyze behavior: the policy name/version, the exact labels or contexts involved, the command that triggered the action, and the resulting audit event. If the same action yields different decisions on two hosts, treat that as a signal that a hidden variable changed (boolean state, module priority, label drift, or category range). This disciplined approach prevents trial-and-error debugging and makes your conclusions defensible.
Operationally, build a short checklist for Domain Transitions and Entrypoints: verify prerequisites, verify labels or mappings, verify policy query results, then run the action and confirm the expected audit outcome. Track metrics that reflect stability, such as the count of denials per hour, the number of unique denial keys, or the fraction of hosts in compliance. When you must change behavior, apply the smallest change that can be verified (label fix before boolean, boolean before policy). Document the rollback path and include a post-change validation step so the system returns to a known-good state.
Definitions & key terms
- type -> named label used in TE
- allow rule -> grants access between types
- attribute -> grouping of types for policy convenience
- interface -> macro that encapsulates common policy patterns
- neverallow -> rule that forbids access regardless of allow rules
Mental model diagram
myapp_t --allow--> myapp_data_t:file { read write }
How it works (step-by-step, with invariants and failure modes)
- Define types for domain and files.
- Attach attributes (domain, files_type) if appropriate.
- Write allow rules for required access.
- Compile the policy module.
- Load module and validate behavior.
Invariants: allow rules are type-based; no access without explicit allow. Failure modes: rule too broad, constraint violations, missing attributes.
Minimal concrete example
type myapp_t;
allow myapp_t myapp_log_t:file { append write getattr };
Common misconceptions
- “A single allow rule is enough.” -> Real apps need permissions across multiple classes.
- “Macros always grant minimal access.” -> They can be broad if used carelessly.
Check-your-understanding questions
- Why are
neverallowrules important? - How do attributes simplify policy?
- What is the risk of overusing macros?
Check-your-understanding answers
- They enforce global security invariants and prevent unsafe permissions.
- They let you apply rules to groups of types instead of each type individually.
- Macros can add permissions you did not intend, weakening least privilege.
Real-world applications
- Confining custom daemons in enterprises.
- Hardening vendor software with custom policies.
Where you’ll apply it
- This project: §3.2, §4.2, §4.4, §5.10 Phase 2, §6.2.
- Also used in: P08-selinux-policy-diff-tool.md.
References
- “SELinux by Example” (Mayer et al.), policy language chapters
- “SELinux Notebook” policy reference
Key insights
SELinux policy is a precise contract: every allow rule should correspond to a real need.
Summary
Type Enforcement rules define access. Writing minimal allow rules is the foundation of safe SELinux policy.
Homework/Exercises to practice the concept
- Write a minimal allow rule for reading a config file.
- Identify an attribute you could attach your custom type to.
- Find a
neverallowrule in the base policy.
Solutions to the homework/exercises
allow myapp_t myapp_conf_t:file { read getattr open };files_typefor file labels,domainfor process types.- Use
sesearch --neverallowto locate constraints.
Policy Module Build and Deployment Pipeline
Fundamentals
Policy modules are compiled units that can be loaded into the SELinux policy store without rebuilding the entire base policy. The typical workflow is to write .te and .fc files, compile them with checkmodule and semodule_package, and load them with semodule -i. For quick iteration, the SELinux development Makefile automates this. Understanding this pipeline ensures your policy is built correctly and can be installed, updated, or removed cleanly. It also supports versioning and rollback, which are essential in production.
Deep Dive into the concept
SELinux policy compilation is a multi-step process because the human-readable policy language must be transformed into a binary representation. checkmodule compiles the .te file into a .mod file, which contains the module in an intermediate format. semodule_package bundles the .mod with file contexts (.fc) into a .pp package. Finally, semodule -i loads the package into the policy store. This pipeline ensures that syntax errors or constraint violations are caught at build time. When using /usr/share/selinux/devel/Makefile, you can simply run make -f ... myapp.pp and it will call the correct tools.
Module priority is another operational detail. SELinux supports module priorities that determine which rules override others. If you define a type in a module with a lower priority than a conflicting definition in another module, the policy store will resolve conflicts according to priority. Most custom modules use priority 100 or the default. In this project, you should avoid name collisions and keep types unique to your app to prevent conflicts.
Deployment is also about lifecycle. semodule -i installs, semodule -r removes, and semodule -l lists installed modules. Your project should include a Makefile target for install/uninstall so that testers can reproduce steps. When you update a policy, the AVC cache is invalidated and the new policy becomes active immediately. This is good for iterative development but means you should be cautious when testing on production systems. Always test in a VM first, and include a rollback plan.
Another subtle point is that file context rules are part of the policy package but are not applied automatically to existing files. After installing a module, you must run restorecon on the relevant paths. Many failures during policy testing are caused by forgetting this step. Your implementation guide should include these commands explicitly.
Operational expansion for tart the service and check the domain.: In real systems, the behavior you observe is the product of policy, labels, and runtime state. That means your investigation workflow must be repeatable. Start by documenting the exact inputs (contexts, paths, users, domains, ports, and the action attempted) and the exact outputs (audit events, error codes, and any policy query results). Then, replay the same action after each change so you can attribute cause and effect. When the concept touches multiple subsystems, isolate variables: change one label, one boolean, or one rule at a time. This reduces confusion and prevents accidental privilege creep. Use staging environments or fixtures to test fixes before deploying them widely, and always keep a rollback path ready.
To deepen understanding, connect tart the service and check the domain. to adjacent concepts: how it affects policy decisions, how it appears in logs, and how it changes operational risk. Build small verification scripts that assert the expected outcome and fail loudly if the outcome diverges. Over time, these scripts become a regression suite for your SELinux posture. Finally, treat the concept as documentation-worthy: write down the invariants it guarantees, the constraints it imposes, and the exact evidence that proves it works. This makes future debugging faster and creates a shared mental model for teams.
How this fit on projects
Domain transitions are part of §3.2 and verified in §3.7. They are also relevant in P01-selinux-context-explorer-visualizer.md.
Further depth on tart the service and check the domain.: In production environments, this concept is shaped by policy versions, automation layers, and distro-specific defaults. To keep reasoning consistent, capture a minimal evidence bundle every time you analyze behavior: the policy name/version, the exact labels or contexts involved, the command that triggered the action, and the resulting audit event. If the same action yields different decisions on two hosts, treat that as a signal that a hidden variable changed (boolean state, module priority, label drift, or category range). This disciplined approach prevents trial-and-error debugging and makes your conclusions defensible.
Operationally, build a short checklist for tart the service and check the domain.: verify prerequisites, verify labels or mappings, verify policy query results, then run the action and confirm the expected audit outcome. Track metrics that reflect stability, such as the count of denials per hour, the number of unique denial keys, or the fraction of hosts in compliance. When you must change behavior, apply the smallest change that can be verified (label fix before boolean, boolean before policy). Document the rollback path and include a post-change validation step so the system returns to a known-good state.
Definitions & key terms
- type_transition -> rule mapping exec to new domain
- entrypoint -> permission granting executable as transition target
- exec type -> label on the binary
- source domain -> domain executing the binary
Mental model diagram
init_t --exec(myapp_exec_t)--> myapp_t
How it works (step-by-step, with invariants and failure modes)
- Service manager in
init_texecutes/usr/sbin/myapp. - SELinux checks
entrypointpermissions onmyapp_exec_t. - If allowed, apply
type_transitionand assignmyapp_t. - Process runs in
myapp_tand policy rules apply.
Invariants: binary must be labeled correctly; transition rule must exist. Failure modes: mislabeled binary, missing entrypoint, missing type_transition.
Minimal concrete example
type myapp_exec_t;
type myapp_t;
init_daemon_domain(myapp_t, myapp_exec_t)
Common misconceptions
- “Labeling the binary is enough.” -> You still need type_transition/entrypoint rules.
- “Systemd uses a different transition system.” -> It still relies on SELinux transitions.
Check-your-understanding questions
- What happens if the binary label is wrong?
- Why is
entrypointpermission required? - How do you verify a transition occurred?
Check-your-understanding answers
- The process stays in the parent domain, often
init_t. - It marks the binary as a valid transition target to prevent arbitrary exec.
- Use
ps -eZto confirm the process domain.
Real-world applications
- Confining web servers, database daemons, and custom services.
- Preventing privilege escalation via unconfined domains.
Where you’ll apply it
- This project: §3.2, §3.7, §5.10 Phase 1.
- Also used in: P01-selinux-context-explorer-visualizer.md, P11-selinux-kernel-module-inspector.md.
References
- “SELinux by Example” (Mayer et al.), domain transitions
- Red Hat SELinux Guide, service domains
Key insights
If the transition is wrong, all subsequent policy reasoning is wrong.
Summary
Domain transitions are the gateway to confinement. They must be explicit and verified.
Homework/Exercises to practice the concept
- Label a test binary and create a transition into a custom domain.
- Verify transition with
ps -eZ. - Break the label and observe the effect.
Solutions to the homework/exercises
- Use
semanage fcontextto label and definetype_transitionin policy. - Start the service and check the domain.
restoreconto correct the label and confirm the transition is restored.
3. Project Specification
3.1 What You Will Build
A complete SELinux policy module for a custom daemon named myapp, including:
- A process domain
myapp_t - An executable type
myapp_exec_t - File types for configuration, data, and logs
- A systemd transition into
myapp_t - A reproducible build and install process
Excluded features:
- Full distribution policy packaging
- GUI management tools
3.2 Functional Requirements
- Types: Define process, exec, config, data, and log types.
- Transitions: Implement a domain transition from
init_t/systemd_t. - Allow Rules: Grant minimal permissions needed for startup and operation.
- File Contexts: Provide
.fcmappings for all app paths. - Packaging: Provide Makefile targets to build/install/remove.
3.3 Non-Functional Requirements
- Security: Enforce least privilege and avoid broad type attributes.
- Reliability: Service must start and run without AVC denials.
- Maintainability: Policy should be readable and documented.
3.4 Example Usage / Output
$ make -f /usr/share/selinux/devel/Makefile myapp.pp
$ sudo semodule -i myapp.pp
$ sudo restorecon -Rv /usr/sbin/myapp /var/lib/myapp /var/log/myapp
$ sudo systemctl start myapp
$ ps -eZ | grep myapp
system_u:system_r:myapp_t:s0 2441 ? 00:00:00 myapp
3.5 Data Formats / Schemas / Protocols
Policy files:
myapp.te(types, allow rules, transitions)myapp.fc(file context regex mappings)
3.6 Edge Cases
- Service runs in
init_tbecause binary mislabeled - Missing file context rules for
/opt/myapp - AVC denials for socket access or PID files
3.7 Real World Outcome
3.7.1 How to Run (Copy/Paste)
make -f /usr/share/selinux/devel/Makefile myapp.pp
sudo semodule -i myapp.pp
sudo restorecon -Rv /usr/sbin/myapp /var/lib/myapp /var/log/myapp
sudo systemctl restart myapp
3.7.2 Golden Path Demo (Deterministic)
Use a fixture systemd unit and test files under /var/lib/myapp. Verify that ps -eZ shows myapp_t and that ausearch -m avc -c myapp -ts recent returns no denials.
3.7.3 CLI Transcript (Success and Failure)
$ sudo systemctl start myapp
$ ps -eZ | grep myapp
system_u:system_r:myapp_t:s0 2441 ? 00:00:00 myapp
Exit code: 0
$ sudo semodule -r myapp
$ sudo systemctl start myapp
Job failed: see journalctl -xe
Exit code: 1
4. Solution Architecture
4.1 High-Level Design
+-------------+ +----------------+ +-------------------+
| myapp_exec | --> | Domain Transition| --> | myapp_t (process) |
+-------------+ +----------------+ +-------------------+
| |
v v
myapp_data_t, myapp_log_t allow rules
4.2 Key Components
| Component | Responsibility | Key Decisions |
|---|---|---|
| Types | Define domain and file types | Use dedicated types for logs/data |
| Transitions | Ensure service runs in myapp_t | Use init_daemon_domain macro |
| Allow Rules | Grant least privilege | Add permissions via AVC feedback |
| File Contexts | Label app paths | Use regex for nonstandard paths |
4.3 Data Structures (No Full Code)
# myapp.te
module myapp 1.0;
require {
type init_t;
}
# types
# allow rules
4.4 Algorithm Overview
Key Algorithm: Iterative Policy Tightening
- Run service in enforcing mode.
- Collect AVC denials with
ausearch. - Add minimal allow rules or fix labels.
- Repeat until denials are resolved.
Complexity Analysis:
- Time: O(n) denials per iteration
- Space: O(1) incremental additions
5. Implementation Guide
5.1 Development Environment Setup
sudo dnf install -y selinux-policy-devel policycoreutils-devel
5.2 Project Structure
myapp-policy/
├── myapp.te
├── myapp.fc
├── Makefile
└── README.md
5.3 The Core Question You’re Answering
“How do I confine a new service with only the permissions it truly needs?”
5.4 Concepts You Must Understand First
- TE allow rule structure and type definitions.
- Domain transitions and entrypoints.
- Module build and install pipeline.
5.5 Questions to Guide Your Design
- What files does the app need to access, and with which permissions?
- Which domain should the service run under?
- How will you verify the policy is minimal?
5.6 Thinking Exercise
List every file or socket the service touches on startup. For each, decide whether to label it or allow access.
5.7 The Interview Questions They’ll Ask
- “What is a type_transition rule?”
- “Why should log files have a dedicated type?”
- “How do you iterate on a policy safely?”
5.8 Hints in Layers
Hint 1: Use sepolicy generate for a scaffold
Hint 2: Start with minimal permissions and add only what AVCs show
Hint 3: Verify domain with ps -eZ after each change
5.9 Books That Will Help
| Topic | Book | Chapter |
|---|---|---|
| Policy language | “SELinux by Example” | Ch. 3-4 |
| Labeling | “SELinux System Administration” | Labeling chapter |
| Services | “The Linux Programming Interface” | Daemons and services |
5.10 Implementation Phases
Phase 1: Foundation (3-4 days)
Goals:
- Define types and file contexts.
- Create a policy scaffold.
Tasks:
- Label your binary and directories.
- Use
sepolicy generateor write a basic.tefile.
Checkpoint: Module compiles successfully.
Phase 2: Core Functionality (1 week)
Goals:
- Implement transitions and minimal allow rules.
- Run in enforcing mode and fix denials.
Tasks:
- Start service, collect AVCs.
- Add precise allow rules and relabel paths.
Checkpoint: Service runs with zero AVCs.
Phase 3: Polish & Edge Cases (3-4 days)
Goals:
- Document policy reasoning.
- Add uninstall and rollback steps.
Tasks:
- Add README and risk notes.
- Provide
make uninstalltarget.
Checkpoint: Policy can be installed and removed cleanly.
5.11 Key Implementation Decisions
| Decision | Options | Recommendation | Rationale |
|---|---|---|---|
| Logging type | var_log_t vs custom |
custom (myapp_log_t) |
Avoids broad permissions |
| Transition macro | init_daemon_domain vs manual |
macro + review | Faster and safer |
| Policy iteration | permissive domain vs enforcing | enforcing with AVC review | Avoids masked errors |
6. Testing Strategy
6.1 Test Categories
| Category | Purpose | Examples |
|---|---|---|
| Policy Tests | Validate transitions | ps -eZ domain checks |
| Integration Tests | Service operations | read/write log, create PID file |
| Edge Case Tests | Missing labels | unlabeled data dir |
6.2 Critical Test Cases
- Service starts and runs in
myapp_t. - Logs are written only to
myapp_log_t. - Removing the module causes a predictable failure.
6.3 Test Data
fixtures/app_data/
fixtures/app_log/
7. Common Pitfalls & Debugging
7.1 Frequent Mistakes
| Pitfall | Symptom | Solution |
|---|---|---|
Missing file context for /opt/myapp |
Service denied access | Add .fc rule and relabel |
| Transition missing | Process runs in init_t |
Add type_transition + entrypoint |
| Overbroad allow rules | Policy too permissive | Remove and narrow rules |
7.2 Debugging Strategies
- Use
ausearch -m avc -c myappafter every change. - Verify labels with
ls -Zandmatchpathcon.
7.3 Performance Traps
- Too many allow rules can slow policy evaluation slightly; keep them minimal.
8. Extensions & Challenges
8.1 Beginner Extensions
- Add a policy documentation section with rationale.
- Include a
make uninstalltarget.
8.2 Intermediate Extensions
- Add separate types for cache vs data.
- Support an optional network port with a custom port type.
8.3 Advanced Extensions
- Write a policy interface for reuse by other modules.
- Add MLS/MCS constraints if applicable.
9. Real-World Connections
9.1 Industry Applications
- Confining internal services and compliance workloads.
- Auditing vendor applications for least privilege.
9.2 Related Open Source Projects
- SELinux reference policy (as a model for structure).
- setools (
sesearch,seinfo) for policy analysis.
9.3 Interview Relevance
- Policy design and least privilege reasoning.
- Understanding of SELinux transitions and module deployment.
10. Resources
10.1 Essential Reading
- “SELinux by Example” (policy chapters)
- “SELinux System Administration” (labeling and troubleshooting)
10.2 Video Resources
- SELinux policy writing workshop sessions
10.3 Tools & Documentation
checkmodule,semodule,sepolicy generate
10.4 Related Projects in This Series
11. Self-Assessment Checklist
11.1 Understanding
- I can explain TE rule shape and how types interact.
- I can explain domain transitions and entrypoints.
- I can explain the policy build pipeline.
11.2 Implementation
- Module installs and removes cleanly.
- Service runs in
myapp_t. - No AVC denials remain in enforcing mode.
11.3 Growth
- I can justify each allow rule in the policy.
- I documented decisions and trade-offs.
12. Submission / Completion Criteria
Minimum Viable Completion:
- Custom domain and file types exist.
- Service runs in correct domain.
Full Completion:
- All required resources are labeled and accessed without denials.
- Policy module documented and reproducible.
Excellence (Going Above & Beyond):
- Policy interface for reuse and MLS-compatible constraints.
13 Additional Content Rules (Hard Requirements)
13.1 Determinism
- Use a fixed test service and fixtures for reproducible AVCs.
13.2 Outcome Completeness
- Include success and failure paths in Real World Outcome.
13.3 Cross-Linking
- Link to related projects and relevant sections.
13.4 No Placeholder Text
- All sections include concrete guidance.