Course outline

Linux Security / Advanced: constrained design and verified change / Lesson 17

Verify security changes with discriminating evidence

Build meaningful allowed and denied cases, preserve execution scope, and review effective change rather than checklist completion.

About 4 min readContent reviewed
On this page

Lesson goals

  • Check that a proposed policy preserves useful work and rejects excess authority.
  • Distinguish planned cases, executed local models, and actual platform verification.

Verify the protected outcome the change promises

A hardening change should connect to a concrete claim, such as the report worker reading public schedules while being unable to manage host accounts. Test useful allowed behavior and benign denied alternatives together. A service that never starts can satisfy many denial checks while failing its purpose. Choose the actual enforcement boundary for each claim: policy reasoning, configuration interpretation, loaded state, or observed kernel/application behavior. The course executes only the policy model. It does not run scanners, security benchmarks, permission tests, services, or administrative commands against this host.

Use explicit outcomes that expose a wrong grant

The first program checks a fictional authority allowlist against two legitimate and two forbidden tasks. Expected values are stated independently in the fixture. If manage-host were added to the allowed set, the negative case would fail instead of accepting any successful operation. This gives the oracle discriminating power, but it still does not verify actual Linux credentials or sandbox behavior. Treat the labels as requirements and assign real enforcement evidence separately. Avoid a model whose expected values call the same policy helper, because a shared defect could make expected and actual agree circularly.

python Check a bounded workload authority matrix
allowed = {"read-public-schedules", "write-derived-summaries"}
cases = [("read-public-schedules", True), ("write-derived-summaries", True), ("read-attendee-contacts", False), ("manage-host", False)]
for operation, expected in cases:
    assert (operation in allowed) is expected
assert len(cases) == 4
print("legitimate and excess-authority cases discriminate the policy")
Expected output
legitimate and excess-authority cases discriminate the policy

The complete program compares four synthetic labels with a fixed set. It reads no file, invokes no capability, and operates no account or service. The result is policy evidence only and cannot establish operating-system enforcement.

Compare intended and actually recorded checks

A green summary may omit the one negative case that protects the boundary. The second program detects a missing mandatory-policy denial in a declared execution inventory. It executes none of those named checks. A real verification record must include actual outputs, fixture identity, environment, source revision, and limitations. Keep skipped cases and their reasons visible. A test count or coverage score does not show that the right operation, subject, resource, and side effect were asserted. Evidence completeness and individual test correctness are separate review questions.

python Check a fictional verification inventory for omissions
intended = {"public-input-allowed", "scoped-output-allowed", "mandatory-policy-denial"}
recorded = {"public-input-allowed", "scoped-output-allowed"}
missing = sorted(intended - recorded)
assert missing == ["mandatory-policy-denial"]
assert not intended <= recorded
print("a green subset leaves the required denial check pending")
Expected output
a green subset leaves the required denial check pending

The complete program compares two invented inventories. It runs no named permission or mandatory-policy case and inspects no CI or host. The missing item remains a planned requirement rather than a failed or successful actual test.

Review the narrow correction and adjacent dependencies

If a service restriction breaks a legitimate dependency, identify the exact resource and proposed correction rather than removing all controls. Review neighboring callers, writable paths, inherited resources, and recovery assumptions affected by the change. A real configuration may have includes, overrides, package defaults, and version-dependent behavior that a simple text model does not interpret. Do not claim effective change because a file was edited. Actual syntax, loaded state, and behavior checks require separate authorization and environment support. Preserve the old symptom and the evidence that the correction addresses its cause.

Report what passed and what remains unexecuted

A useful change report states the intended property, actual local results, real platform results if any, unresolved boundaries, accepted exceptions, and accountable next action. The examples here establish only synthetic authority and inventory relationships. They do not test systemd, AppArmor, nftables, SSH, package trust, storage, or kernel enforcement. That limitation should stay near the relevant claim rather than disappear in a broad green statement. Broaden checks when a new change, failure, or shared mechanism justifies it, and stop unrelated repetition once the relevant evidence is sufficient for the stated decision.

Common errors and fixes

Only denials pass because useful work is broken

Why it happens
The positive workload requirement is omitted.
How to fix it
Pair allowed work with denied alternatives.
Prevent it next time
Review security and functionality as one protected contract.

A test inventory is called executed evidence

Why it happens
Named checks are mistaken for observed results.
How to fix it
Attach actual outputs and preserve planned/skipped status.
Prevent it next time
Compare intended and recorded case populations.

Editing text proves a live security change

Why it happens
Loaded state and runtime behavior are unknown.
How to fix it
Separate source/configuration review from actual enforcement.
Prevent it next time
Record version and integration limitations.

Check your understanding

1. Why is a positive report-worker case necessary in a least-privilege test?

Show worked solution

It prevents a deny-everything policy or nonstarting service from satisfying the security suite while making the product unusable. Assert the exact legitimate inputs/outputs and the denied excess authority. The local labels still need separate actual enforcement evidence.

2. The mandatory-policy case was never recorded. Can the change claim that layer passed?

Show worked solution

No. Keep the case pending and identify the required authorized observation. The two successful local workload cases do not establish AppArmor attachment or denial. A precise report preserves both the useful evidence and its incomplete platform coverage.

Key takeaways

  • Discriminating positive and negative cases support useful security.
  • Planned checks and executed outputs are different evidence.
  • Configuration changes need effective-state verification before runtime claims.

Official references