fedora: add SELinux validation test suite - #4631
Conversation
Add selinux_validation.py with two test cases for Fedora cloud images: verify_setenforce and verify_selinux_info. Covers mode toggling, audit log verification (MAC_STATUS records), sestatus/avcstat field checks, and edge cases (selinuxfs unmounted, enforce file bind-mounted RO). Sources: https://src.fedoraproject.org/tests/selinux/blob/main/f/libselinux/setenforce https://src.fedoraproject.org/tests/selinux/blob/main/f/policycoreutils/selinux-info Signed-off-by: Bala Konda Reddy M <bala12352@gmail.com>
There was a problem hiding this comment.
Pull request overview
Adds a new Fedora-focused SELinux validation test suite under lisa/microsoft/testsuites/fedora/, intended to validate SELinux mode switching and basic SELinux tooling behavior on Fedora cloud images.
Changes:
- Introduces
FedoraSELinuxValidationwith averify_setenforcetest that toggles SELinux mode and validates corresponding audit events plus edge cases. - Adds
verify_selinux_infocoverage forgetenforce,sestatus(including-b/-v), andavcstatoutput validation.
Suppressed comments (1)
lisa/microsoft/testsuites/fedora/selinux_validation.py:160
- Magic-number timeout
30should have an inline comment explaining why this value is appropriate, since it directly impacts test runtime/flakiness.
check_mac_status_enforcing1,
timeout_message=(
"MAC_STATUS record enforcing=1 old_enforcing=0"
" not found in audit log"
),
timeout=30,
)
| if backup.strip(): | ||
| node.execute( | ||
| f"echo '{backup}' > /tmp/audit_backup && auditctl -R /tmp/audit_backup", | ||
| shell=True, sudo=True, no_error_log=True | ||
| ) | ||
| node.execute("rm -f /tmp/audit_backup", sudo=True, no_error_log=True) |
| Tests switching between SELinux enforcing and permissive modes, | ||
| validates mode changes are reflected in the enforce file and | ||
| audit logs, and verifies error handling for edge cases. | ||
| """, |
| Tests that getenforce accurately reports mode changes, sestatus | ||
| displays all required policy fields and contexts, and avcstat | ||
| shows live AVC cache statistics. | ||
| """, |
| # auditd must be active on boot; not started manually | ||
| self._wait_for_service_active(node, "auditd", timeout=60) | ||
|
|
| node.execute( | ||
| f"mount -t selinuxfs none {self._SELINUX_FS_MOUNT}", | ||
| sudo=True, | ||
| ) | ||
| node.log.info("Edge case: selinuxfs unmounted passed") |
| node.execute("touch /var/tmp/selinux_enforce_bind_test", sudo=True) | ||
| node.execute("chattr +i /var/tmp/selinux_enforce_bind_test", sudo=True) | ||
| node.execute( | ||
| f"mount --bind /var/tmp/selinux_enforce_bind_test" | ||
| f" {self._SELINUX_FS_MOUNT}/enforce", | ||
| sudo=True, | ||
| ) |
| # Trigger a filesystem access to ensure AVC lookups increment between runs | ||
| node.execute("ls /etc/passwd", no_error_log=True) | ||
| avcstat2 = node.execute("avcstat") | ||
| assert_that(avcstat2.stdout).described_as( | ||
| "Two consecutive avcstat outputs must differ (AVC cache is active)" | ||
| ).is_not_equal_to(avcstat1.stdout) |
Apply Black auto-formatting and rename local variable _CON to _con to fix pep8-naming N806 (uppercase variable in function scope). Signed-off-by: Bala Konda Reddy M <bala12352@gmail.com>
|
Adding @jeremycline @nmeyerhans |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
Suppressed comments (8)
lisa/microsoft/testsuites/fedora/selinux_validation.py:60
- The audit rules restore path embeds the full (potentially multi-line)
backupstring into a single-quoted shell command (echo '{backup}' ...). If the saved rules contain a single quote, the command breaks (and can also alter what gets restored). Escaping the payload before building the shell command makes the restore deterministic.
def _restore_audit_rules(self, node: Node, backup: str) -> None:
"""Restore audit rules from backup."""
node.execute("auditctl -D", sudo=True, no_error_log=True)
if backup.strip():
node.execute(
f"echo '{backup}' > /tmp/audit_backup && auditctl -R /tmp/audit_backup",
shell=True,
sudo=True,
no_error_log=True,
)
node.execute("rm -f /tmp/audit_backup", sudo=True, no_error_log=True)
lisa/microsoft/testsuites/fedora/selinux_validation.py:223
- The "enforce file locked" edge-case relies on
touch,chattr +i, and themount --bindsucceeding, but their exit codes aren't checked. If any of these steps fails, subsequent assertions can become misleading (or the edge-case validation can accidentally exercise the normal path).
node.execute("touch /var/tmp/selinux_enforce_bind_test", sudo=True)
node.execute("chattr +i /var/tmp/selinux_enforce_bind_test", sudo=True)
node.execute(
f"mount --bind /var/tmp/selinux_enforce_bind_test"
f" {self._SELINUX_FS_MOUNT}/enforce",
sudo=True,
)
lisa/microsoft/testsuites/fedora/selinux_validation.py:403
- Asserting
avcstatstdout differs between two consecutive runs can be flaky: the counters may not change deterministically from a singlels /etc/passwd(or the change may be too small/fast to observe). A short bounded retry makes the check more reliable while keeping runtime low.
# Trigger a filesystem access to ensure AVC lookups increment between runs
node.execute("ls /etc/passwd", no_error_log=True)
avcstat2 = node.execute("avcstat")
assert_that(avcstat2.stdout).described_as(
"Two consecutive avcstat outputs must differ (AVC cache is active)"
).is_not_equal_to(avcstat1.stdout)
lisa/microsoft/testsuites/fedora/selinux_validation.py:126
- The
timeout=60here is a test-behavior magic number; adding a brief rationale comment makes it easier to maintain and tune without guessing later.
self._wait_for_service_active(node, "auditd", timeout=60)
lisa/microsoft/testsuites/fedora/selinux_validation.py:162
- This
timeout=30is a test-behavior magic number; please add a short inline rationale so future maintainers know why 30s is expected/acceptable for audit records to appear.
timeout=30,
lisa/microsoft/testsuites/fedora/selinux_validation.py:86
_assert_cmd_fails_with_errorcurrently treats a non-zero exit code as sufficient even whenerror_keywordsare provided. For the edge-case checks that are supposed to validate a specific failure reason (e.g., "SELinux is disabled"), this can produce false positives where the command fails for an unrelated reason but the test still passes.
result = node.execute(cmd, shell=True, sudo=True, no_error_log=True)
combined = (result.stdout + result.stderr).lower()
has_error = (
any(kw in combined for kw in error_keywords) or result.exit_code != 0
)
assert_that(has_error).described_as(f"{desc} (got: {combined[:200]})").is_true()
lisa/microsoft/testsuites/fedora/selinux_validation.py:369
- The SELinux context regex is too restrictive: valid contexts can include an MLS/MCS range and categories (e.g.
s0-s0:c0.c1023). Limiting the pattern to:s\d+risks false failures on otherwise-correct Fedora images.
_con = r"[a-z_]+_u:[a-z_]+_r:[a-z_]+_t:s\d+"
lisa/microsoft/testsuites/fedora/selinux_validation.py:63
- The default timeout value is a test-behavior control and should be documented inline (per project review guidelines on magic numbers) so future changes keep waits bounded and intentional.
This issue also appears on line 162 of the same file.
def _wait_for_service_active(
self, node: Node, service: str, timeout: int = 30
) -> None:
Description
Add selinux_validation.py with two test cases for Fedora cloud images: verify_setenforce and verify_selinux_info. Covers mode toggling, audit log verification (MAC_STATUS records), sestatus/avcstat field checks, and edge cases (selinuxfs unmounted, enforce file bind-mounted RO).
Sources:
https://src.fedoraproject.org/tests/selinux/blob/main/f/libselinux/setenforce https://src.fedoraproject.org/tests/selinux/blob/main/f/policycoreutils/selinux-info
Related Issue
Type of Change
Checklist
Test Validation
Key Test Cases:
verify_selinux_info|verify_setenforce
Impacted LISA Features:
Tested Azure Marketplace Images:
Test Results
2026-08-04 01:54:22.970[140166918605760][INFO] lisa.RootRunner ________________________________________
2026-08-04 01:54:22.970[140166918605760][INFO] lisa.RootRunner FedoraSELinuxValidation.verify_setenforce: PASSED
2026-08-04 01:54:22.970[140166918605760][INFO] lisa.RootRunner FedoraSELinuxValidation.verify_selinux_info: PASSED
2026-08-04 01:54:22.970[140166918605760][INFO] lisa.RootRunner test result summary
2026-08-04 01:54:22.970[140166918605760][INFO] lisa.RootRunner TOTAL : 2
2026-08-04 01:54:22.970[140166918605760][INFO] lisa.RootRunner QUEUED : 0
2026-08-04 01:54:22.970[140166918605760][INFO] lisa.RootRunner ASSIGNED : 0
2026-08-04 01:54:22.970[140166918605760][INFO] lisa.RootRunner RUNNING : 0
2026-08-04 01:54:22.970[140166918605760][INFO] lisa.RootRunner FAILED : 0
2026-08-04 01:54:22.971[140166918605760][INFO] lisa.RootRunner PASSED : 2
2026-08-04 01:54:22.971[140166918605760][INFO] lisa.RootRunner SKIPPED : 0
2026-08-04 01:54:22.973[140166918605760][INFO] lisa.notifier[Html] report: /lisa/runtime/log/20260804/20260804-014713-049/lisa.html
2026-08-04 01:54:22.982[140166918605760][INFO] lisa. completed in 429.937 sec