#!/usr/bin/env bash
set -euo pipefail

# Ubuntu AppArmor Profiling Helper Script
#
# Purpose:
#   Provide a safe, repeatable starting point for AppArmor profiling on Ubuntu.
#   This script does NOT modify AppArmor profiles automatically and does NOT put
#   any service into enforce mode. It helps you plan, record, and validate a
#   least-privilege hardening exercise.
#
# What it does:
#   - Validates basic prerequisites
#   - Captures operator-provided context for the target workload
#   - Creates a profiling notes file template
#   - Optionally checks current AppArmor status
#   - Prints next-step guidance for iterative profiling
#
# What it does not do:
#   - It does not edit /etc/apparmor.d/
#   - It does not load profiles
#   - It does not switch profiles into enforce mode
#   - It does not change firewall or system settings
#
# Usage examples:
#   ./apparmor-profile-helper.sh --service my-service --output ./profiling-notes.md
#   ./apparmor-profile-helper.sh --service my-service --check-status
#   ./apparmor-profile-helper.sh --service my-service --profile-path /etc/apparmor.d/usr.sbin.my-service
#
# Suggested workflow:
#   1. Run this script to create a notes file.
#   2. Exercise the application with representative traffic.
#   3. Review denials from journalctl or audit logs.
#   4. Update the profile manually.
#   5. Repeat until normal behavior succeeds.
#
# Notes:
#   - Path-based profiles are sensitive to filesystem changes.
#   - Revalidate after package upgrades, service changes, or new features.

SERVICE_NAME=""
OUTPUT_FILE="./apparmor-profiling-notes.md"
PROFILE_PATH=""
CHECK_STATUS="false"
SHOW_HELP="false"

usage() {
  cat <<'EOF'
Usage:
  apparmor-profile-helper.sh --service NAME [--output FILE] [--profile-path PATH] [--check-status]

Options:
  --service NAME       Target service or application name (required)
  --output FILE        Notes file to create or overwrite (default: ./apparmor-profiling-notes.md)
  --profile-path PATH  Optional AppArmor profile path for reference only
  --check-status       Print current AppArmor status if available
  -h, --help           Show this help message
EOF
}

log() {
  printf '[%s] %s\n' "$(date +'%Y-%m-%d %H:%M:%S')" "$*"
}

require_cmd() {
  local cmd="$1"
  if ! command -v "$cmd" >/dev/null 2>&1; then
    log "WARN: Required command not found: $cmd"
    return 1
  fi
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    --service)
      [[ $# -ge 2 ]] || { echo "ERROR: --service requires a value" >&2; exit 1; }
      SERVICE_NAME="$2"
      shift 2
      ;;
    --output)
      [[ $# -ge 2 ]] || { echo "ERROR: --output requires a value" >&2; exit 1; }
      OUTPUT_FILE="$2"
      shift 2
      ;;
    --profile-path)
      [[ $# -ge 2 ]] || { echo "ERROR: --profile-path requires a value" >&2; exit 1; }
      PROFILE_PATH="$2"
      shift 2
      ;;
    --check-status)
      CHECK_STATUS="true"
      shift
      ;;
    -h|--help)
      SHOW_HELP="true"
      shift
      ;;
    *)
      echo "ERROR: Unknown argument: $1" >&2
      usage
      exit 1
      ;;
  esac
done

if [[ "$SHOW_HELP" == "true" ]]; then
  usage
  exit 0
fi

if [[ -z "$SERVICE_NAME" ]]; then
  echo "ERROR: --service is required" >&2
  usage
  exit 1
fi

# Basic validation of the service label for notes. This is not a security boundary.
if [[ ! "$SERVICE_NAME" =~ ^[A-Za-z0-9._:-]+$ ]]; then
  echo "ERROR: Service name contains unsupported characters" >&2
  exit 1
fi

if [[ -e "$OUTPUT_FILE" ]]; then
  log "Overwriting existing notes file: $OUTPUT_FILE"
fi

mkdir -p "$(dirname "$OUTPUT_FILE")"

if [[ "$CHECK_STATUS" == "true" ]]; then
  if require_cmd aa-status; then
    log "Current AppArmor status:"
    aa-status || true
  else
    log "AppArmor status tool not available (aa-status)."
  fi
fi

PROFILE_LINE="Not provided"
if [[ -n "$PROFILE_PATH" ]]; then
  PROFILE_LINE="$PROFILE_PATH"
fi

cat > "$OUTPUT_FILE" <<EOF
# AppArmor Profiling Notes

## Target workload
- Service name: ${SERVICE_NAME}
- Reference profile path: ${PROFILE_LINE}
- Date created: $(date +'%Y-%m-%d')

## Scope
Describe the exact workload you are profiling. Include:
- Normal startup path
- Steady-state request path
- Maintenance or reload actions
- Any helper binaries or scheduled tasks that are part of normal operation

## Representative test plan
Use this checklist while the service is running in complain or learning mode:
- [ ] Start the service from a clean boot
- [ ] Exercise typical user or client requests
- [ ] Trigger reload or graceful restart behavior
- [ ] Verify log writing works as expected
- [ ] Verify state and cache directories are accessible only where needed
- [ ] Validate certificate renewal or helper execution if applicable
- [ ] Confirm that rare admin-only actions are excluded unless they are part of normal operation

## Denials review log
Record each denial and your decision.

| Time | Path or capability | Expected? | Decision | Notes |
|------|---------------------|-----------|----------|------|
|      |                     |           |          |      |
|      |                     |           |          |      |

## Least-privilege rules to confirm
- [ ] Configuration files are readable only as needed
- [ ] Service state is writable only in owned directories
- [ ] Temporary files are scoped to the service runtime
- [ ] Executable helpers are explicitly approved
- [ ] Unrelated user home directories are not accessible
- [ ] Other services' data directories are not accessible
- [ ] Network access is limited to the service's real use case

## Validation before enforcement
- [ ] Service starts cleanly
- [ ] Core requests succeed
- [ ] Reload/restart path succeeds
- [ ] No unexpected denials remain
- [ ] Profile changes are documented and reviewable
- [ ] Re-test completed after any profile edits

## Rollout notes
- Pilot host:
- Validation date:
- Reviewer:
- Go-live approval:

## Revisit triggers
Re-profile after:
- Package upgrades
- Configuration changes
- New plugins or helper binaries
- Directory layout changes
- Changes to startup, reload, or maintenance workflows
EOF

log "Notes file written: $OUTPUT_FILE"
log "Next steps:"
log "1. Run the service in AppArmor complain or learning mode."
log "2. Exercise normal production-like behavior."
log "3. Review denials from journalctl, audit logs, or AppArmor tooling."
log "4. Update the profile manually and re-test."
log "5. Switch to enforcement only after validation passes."

exit 0