Skip to content

Commit bf18da0

Browse files
test: collect SELinux AVC denials during rpm onboarding tests
1 parent ad6b178 commit bf18da0

3 files changed

Lines changed: 166 additions & 0 deletions

File tree

test/fmf/plans/rpm-e2e.fmf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ prepare:
2929

3030
- how: install
3131
package:
32+
- audit
33+
- policycoreutils-python-utils
3234
- golang
3335
- make
3436
- openssl

test/rpm/test-onboarding-deferred-rendezvous.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ run_test() {
3030
log_info "Setting hostnames"
3131
set_hostnames
3232

33+
log_info "Checking SELinux status"
34+
getenforce 2>/dev/null || log_warn "getenforce not available"
35+
check_go_fdo_server_permissive
36+
ensure_auditd_running
37+
mark_avc_start
38+
3339
log_info "Start services (manufacturer, owner) — rendezvous is intentionally delayed"
3440
start_service_manufacturer
3541
start_service_owner

test/rpm/utils.sh

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,14 +394,172 @@ remove_files() {
394394
cleanup_home_dirs
395395
}
396396

397+
# ---------------------------------------------------------------------------
398+
# SELinux AVC collection
399+
# ---------------------------------------------------------------------------
400+
401+
selinux_reports_dir="${base_dir}/selinux"
402+
selinux_avc_raw="${selinux_reports_dir}/avc-raw.txt"
403+
selinux_avc_report="${selinux_reports_dir}/avc-report.txt"
404+
selinux_audit2allow_te="${selinux_reports_dir}/go_fdo_server_avc.te"
405+
directories+=("${selinux_reports_dir}")
406+
407+
# Timestamp set just before services start; used to scope ausearch to this run.
408+
_avc_start_timestamp=""
409+
410+
# Record the start of the AVC collection window.
411+
mark_avc_start() {
412+
_avc_start_timestamp="$(date '+%m/%d/%Y %H:%M:%S')"
413+
log_info "AVC collection window starts at: ${_avc_start_timestamp}"
414+
}
415+
416+
# Warn if go_fdo_server_t is not in permissive mode (non-fatal).
417+
check_go_fdo_server_permissive() {
418+
if ! command -v semanage &>/dev/null; then
419+
log_warn "semanage not found; cannot verify go_fdo_server_t permissive status"
420+
return 0
421+
fi
422+
if semanage permissive -l 2>/dev/null | grep -q 'go_fdo_server_t'; then
423+
log_info "go_fdo_server_t is in permissive mode"
424+
return 0
425+
fi
426+
if [ "$(getenforce 2>/dev/null)" = "Permissive" ]; then
427+
log_info "SELinux is globally permissive"
428+
return 0
429+
fi
430+
log_warn "go_fdo_server_t is NOT in permissive mode; AVCs may block services"
431+
}
432+
433+
# Ensure auditd is running so that AVC records reach the audit log.
434+
ensure_auditd_running() {
435+
if systemctl is-active --quiet auditd 2>/dev/null; then
436+
log_info "auditd is running"
437+
return 0
438+
fi
439+
log_warn "auditd is not running; attempting to start it"
440+
sudo systemctl start auditd || log_warn "Could not start auditd; AVC collection may be incomplete"
441+
}
442+
443+
# Collect all AVC records for go-fdo-server since _avc_start_timestamp.
444+
collect_avcs() {
445+
log_info "Collecting AVC denials for go-fdo-server"
446+
mkdir -p "${selinux_reports_dir}"
447+
448+
if ! command -v ausearch &>/dev/null; then
449+
log_warn "ausearch not found; skipping AVC collection"
450+
echo "ausearch not available" >"${selinux_avc_raw}"
451+
return
452+
fi
453+
454+
local ausearch_args=("-m" "avc" "--comm" "go-fdo-server")
455+
[ -z "${_avc_start_timestamp}" ] || ausearch_args+=("-ts" "${_avc_start_timestamp}")
456+
457+
sudo ausearch "${ausearch_args[@]}" >"${selinux_avc_raw}" 2>&1 || {
458+
local rc=$?
459+
if [ "${rc}" -eq 1 ]; then
460+
echo "No AVC denials found for go-fdo-server" >"${selinux_avc_raw}"
461+
log_info "No AVC denials found (policy may already be complete)"
462+
return 0
463+
fi
464+
log_warn "ausearch exited with code ${rc}"
465+
}
466+
467+
local avc_count
468+
avc_count=$(grep -c 'type=AVC' "${selinux_avc_raw}" 2>/dev/null || echo 0)
469+
log_info "Found ${avc_count} AVC denial(s)"
470+
}
471+
472+
# Generate a draft TE policy snippet from the collected AVC records.
473+
generate_audit2allow_report() {
474+
log_info "Generating audit2allow report"
475+
476+
if ! command -v audit2allow &>/dev/null; then
477+
log_warn "audit2allow not found; skipping TE generation"
478+
return
479+
fi
480+
481+
if [ ! -s "${selinux_avc_raw}" ] || grep -q "^No AVC\|^ausearch not" "${selinux_avc_raw}"; then
482+
log_info "No AVCs to process with audit2allow"
483+
echo "(no AVC denials to report)" >"${selinux_audit2allow_te}"
484+
return
485+
fi
486+
487+
{
488+
echo "# Draft SELinux rules generated from go-fdo-server AVC denials"
489+
echo "# Test run: $(date -u --iso-8601=seconds)"
490+
echo "# Policy domain: go_fdo_server_t"
491+
echo "#"
492+
echo "# These rules were produced by audit2allow and must be reviewed"
493+
echo "# before being added to the upstream selinux-policy package."
494+
echo ""
495+
} >"${selinux_audit2allow_te}"
496+
497+
audit2allow -i "${selinux_avc_raw}" >>"${selinux_audit2allow_te}" 2>&1 ||
498+
log_warn "audit2allow exited with a non-zero status"
499+
}
500+
501+
# Print a human-readable AVC summary to stdout and save it as an artefact.
502+
report_avcs() {
503+
{
504+
echo "================================================================"
505+
echo " go-fdo-server SELinux AVC Denial Report"
506+
echo " Generated: $(date -u --iso-8601=seconds)"
507+
echo "================================================================"
508+
echo ""
509+
if [ ! -s "${selinux_avc_raw}" ]; then
510+
echo "No AVC data collected."
511+
elif grep -q "^No AVC\|^ausearch not" "${selinux_avc_raw}"; then
512+
echo "No AVC denials were recorded during this test run."
513+
else
514+
echo "--- Raw AVC denial records (from ausearch) ---"
515+
echo ""
516+
cat "${selinux_avc_raw}"
517+
echo ""
518+
echo "--- audit2allow suggested rules ---"
519+
echo ""
520+
cat "${selinux_audit2allow_te}" 2>/dev/null || echo "(audit2allow output not available)"
521+
fi
522+
echo ""
523+
echo "================================================================"
524+
} | tee "${selinux_avc_report}"
525+
526+
log_info "AVC report saved to: ${selinux_avc_report}"
527+
log_info "Draft TE file saved to: ${selinux_audit2allow_te}"
528+
}
529+
530+
# Override start_services to record the AVC start timestamp and check SELinux
531+
# status before any service is launched.
532+
start_services() {
533+
log_info "Checking SELinux status"
534+
getenforce 2>/dev/null || log_warn "getenforce not available"
535+
check_go_fdo_server_permissive
536+
ensure_auditd_running
537+
mark_avc_start
538+
539+
log_info "Adding hostnames to '/etc/hosts'"
540+
set_hostnames
541+
log_info "Starting Services"
542+
for service in "${services[@]}"; do
543+
log " ⚙ Starting service ${service} "
544+
start_service "${service}"
545+
log_success
546+
done
547+
}
548+
397549
on_failure() {
398550
trap - ERR
551+
collect_avcs
552+
generate_audit2allow_report
553+
report_avcs
399554
save_logs
400555
stop_services
401556
test_fail
402557
}
403558

404559
cleanup() {
560+
collect_avcs
561+
generate_audit2allow_report
562+
report_avcs
405563
[ ! -v "PACKIT_COPR_RPMS" ] || save_logs
406564
stop_services
407565
unset_hostnames

0 commit comments

Comments
 (0)