#!/bin/bash # 运行方法: # step1: chmod +x run.sh demo_shell.sh # step2: sudo sh run.sh set -e WORK_DIR=$(cd "$(dirname "$0")" && pwd) cd "${WORK_DIR}" if [ "$(id -u)" -ne 0 ];then echo "please run with sudo: sudo sh run.sh" exit 1 fi ELF_BIN="${WORK_DIR}/demo_app" SCRIPT_FILE="${WORK_DIR}/demo_shell.sh" PROFILE_ELF_NAME="home.fnzhang.aa_demo.demo_app" PROFILE_SCRIPT_NAME="home.fnzhang.aa_demo.demo_shell" PROFILE_ELF="/etc/apparmor.d/${PROFILE_ELF_NAME}" PROFILE_SCRIPT="/etc/apparmor.d/${PROFILE_SCRIPT_NAME}" REPORT="${WORK_DIR}/report.txt" > "${REPORT}" log(){ echo "$1" echo "$1" >> "${REPORT}" } echo "========================================" echo "AppArmor Demo start, workdir: ${WORK_DIR}" echo "========================================" # ========== 【前置清理:解决上次残留冲突】 ========== log "" log "===== Pre‑cleanup: remove leftover profile =====" # 卸载内核中可能残留的旧 profile(-R 按 profile 名卸载) apparmor_parser -R "${PROFILE_ELF}" 2>/dev/null || true apparmor_parser -R "${PROFILE_SCRIPT}" 2>/dev/null || true # 清掉 /etc/apparmor.d 下旧文件(含上次失败残留的错误 profile) rm -f "${PROFILE_ELF}" "${PROFILE_SCRIPT}" # 注意:不使用 systemctl reload apparmor —— 它会重载全部 200+ 个 profile, # 触发内核 printk ratelimit(kauditd callbacks suppressed), # 把随后 demo 真正的 DENIED 审计事件一并丢弃。 log "pre‑cleanup done" # ========== Step1 compile C elf ========== log "" log "===== Step1 compile C elf =====" gcc "${WORK_DIR}/demo_app.c" -o "${ELF_BIN}" chmod +x "${ELF_BIN}" log "compile done: ${ELF_BIN}" # ========== Step2 validate profile syntax ========== log "" log "===== Step2 validate profile syntax =====" apparmor_parser -Q "${WORK_DIR}/profile.elf" && log "profile.elf syntax OK" apparmor_parser -Q "${WORK_DIR}/profile.script" && log "profile.script syntax OK" # ========== Step3 install profile ========== log "" log "===== Step3 install profile =====" cp "${WORK_DIR}/profile.elf" "${PROFILE_ELF}" cp "${WORK_DIR}/profile.script" "${PROFILE_SCRIPT}" apparmor_parser -r -W "${PROFILE_ELF}" || { rm -f "${PROFILE_ELF}"; log "ERROR: elf profile load failed"; exit 1; } apparmor_parser -r -W "${PROFILE_SCRIPT}" || { rm -f "${PROFILE_SCRIPT}"; log "ERROR: script profile load failed"; exit 1; } log "profile installed & enforce enabled" # 等待内核 audit ratelimit 窗口恢复,避免 DENIED 事件被 printk ratelimit 吞掉 sleep 6 # ========== Step4 run elf demo_app ========== log "" log "===== Step4 run elf demo_app =====" rm -f /tmp/demo_elf.log /tmp/demo_forbidden.log "${ELF_BIN}" 2>&1 | tee -a "${REPORT}" || log "(demo_app returned non-zero / was killed - expected)" log "-- verify /tmp/demo_elf.log exists (allowed write):" ls -l /tmp/demo_elf.log 2>&1 | tee -a "${REPORT}" # ========== Step5 run shell script ========== log "" log "===== Step5 run shell script =====" rm -f /tmp/demo_sh.log /tmp/demo_forbidden.log "${SCRIPT_FILE}" 2>&1 | tee -a "${REPORT}" || log "(demo_shell.sh returned non-zero / was killed - expected)" log "-- verify /tmp/demo_sh.log exists (allowed write):" ls -l /tmp/demo_sh.log 2>&1 | tee -a "${REPORT}" # ========== Step6 aa-status snapshot ========== log "" log "===== Step6 aa-status snapshot =====" aa-status --json 2>/dev/null | python3 -c " import json,sys d = json.load(sys.stdin) for name, mode in sorted(d.get('profiles', {}).items()): if 'demo_app' in name or 'demo_shell' in name: print(f' {name}: {mode}') " | tee -a "${REPORT}" # ========== Step7 check deny evidences ========== log "" log "===== Step7 AppArmor DENIED logs =====" # 等 ratelimit 窗口过去再收集(sleep 6 已在 Step3 后做过,这里直接查) dmesg 2>/dev/null | grep -iE 'apparmor=\"DENIED\"' | grep -iE 'demo_app|demo_shell' | tail -10 | tee -a "${REPORT}" \ || journalctl -k -b --no-pager 2>/dev/null | grep -iE 'apparmor=\"DENIED\"' | grep -iE 'demo_app|demo_shell' | tail -10 | tee -a "${REPORT}" if [ -z "$(dmesg 2>/dev/null | grep 'apparmor=\"DENIED\"' | grep -iE 'demo_app|demo_shell')" ]; then log "(提示: 若此处为空, 可能被内核 printk ratelimit 抑制; 显式 deny 规则本身不产生日志)" fi # ========== Step8 clean resources ========== log "" log "===== Step8 clean resources =====" apparmor_parser -R "${PROFILE_ELF}" 2>/dev/null || true apparmor_parser -R "${PROFILE_SCRIPT}" 2>/dev/null || true rm -f "${PROFILE_ELF}" "${PROFILE_SCRIPT}" rm -f "${ELF_BIN}" rm -f /tmp/demo_elf.log /tmp/demo_sh.log /tmp/demo_forbidden.log log "" log "========================================" log "Demo finished, report file: ${REPORT}"