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

# SELinux Enforcing Mode Helper for CentOS 7
#
# Purpose:
#   - Check the current SELinux state
#   - Optionally switch runtime mode to enforcing
#   - Optionally make enforcing persistent in /etc/selinux/config
#   - Verify the resulting configuration
#   - Provide a safe rollback option to permissive mode
#
# Notes:
#   - Run with sudo or as root for mode changes and config updates.
#   - This script makes no destructive changes by default.
#   - It does not relabel files, modify policies, or restart services.
#   - Review AVC denials after switching to enforcing.

usage() {
  cat <<'EOF'
Usage:
  selinux-enforcing-helper.sh [options]

Options:
  --check-only        Show current SELinux state and exit.
  --enforce-now       Set runtime mode to enforcing with setenforce 1.
  --make-persistent   Update /etc/selinux/config to SELINUX=enforcing.
  --rollback-now      Set runtime mode to permissive with setenforce 0.
  --show-config       Print SELINUX settings from /etc/selinux/config.
  --help              Show this help message.

Examples:
  sudo ./selinux-enforcing-helper.sh --check-only
  sudo ./selinux-enforcing-helper.sh --enforce-now --make-persistent
  sudo ./selinux-enforcing-helper.sh --rollback-now
EOF
}

require_command() {
  local cmd="$1"
  if ! command -v "$cmd" >/dev/null 2>&1; then
    echo "Error: required command not found: $cmd" >&2
    exit 1
  fi
}

is_root_or_sudo() {
  if [[ ${EUID:-$(id -u)} -ne 0 ]]; then
    echo "Warning: this script is not running as root. Read-only checks will work, but changes require sudo/root." >&2
  fi
}

show_state() {
  echo "== SELinux status =="
  if command -v sestatus >/dev/null 2>&1; then
    sestatus || true
  else
    echo "sestatus not found"
  fi

  echo
  echo "== Runtime mode =="
  if command -v getenforce >/dev/null 2>&1; then
    getenforce || true
  else
    echo "getenforce not found"
  fi
}

show_config() {
  local cfg="/etc/selinux/config"
  echo "== Config file: $cfg =="
  if [[ -r "$cfg" ]]; then
    grep -E '^(SELINUX|SELINUXTYPE)=' "$cfg" || true
  else
    echo "Cannot read $cfg"
  fi
}

make_persistent() {
  local cfg="/etc/selinux/config"
  local tmp

  if [[ ! -f "$cfg" ]]; then
    echo "Error: $cfg does not exist." >&2
    exit 1
  fi

  if [[ ! -w "$cfg" && ${EUID:-$(id -u)} -ne 0 ]]; then
    echo "Error: write access denied for $cfg. Run with sudo/root." >&2
    exit 1
  fi

  tmp="$(mktemp)"
  cp "$cfg" "$tmp"

  # Update existing SELINUX line or append one if missing.
  if grep -q '^SELINUX=' "$tmp"; then
    sed -i 's/^SELINUX=.*/SELINUX=enforcing/' "$tmp"
  else
    printf '\nSELINUX=enforcing\n' >> "$tmp"
  fi

  # Preserve SELINUXTYPE if present; default targeted is common on CentOS 7.
  if ! grep -q '^SELINUXTYPE=' "$tmp"; then
    printf 'SELINUXTYPE=targeted\n' >> "$tmp"
  fi

  install -m 0644 "$tmp" "$cfg"
  rm -f "$tmp"

  echo "Updated $cfg to SELINUX=enforcing"
}

set_runtime_enforcing() {
  require_command setenforce
  if ! setenforce 1; then
    echo "Error: failed to set runtime mode to enforcing." >&2
    exit 1
  fi
  echo "Runtime mode set to enforcing"
}

set_runtime_permissive() {
  require_command setenforce
  if ! setenforce 0; then
    echo "Error: failed to set runtime mode to permissive." >&2
    exit 1
  fi
  echo "Runtime mode set to permissive"
}

verify_result() {
  echo
  echo "== Verification =="
  if command -v getenforce >/dev/null 2>&1; then
    echo -n "getenforce: "
    getenforce || true
  fi
  if command -v sestatus >/dev/null 2>&1; then
    sestatus | sed -n '1,8p' || true
  fi
  show_config
}

main() {
  local check_only=false
  local enforce_now=false
  local make_persist=false
  local rollback_now=false
  local show_cfg=false

  if [[ $# -eq 0 ]]; then
    usage
    echo
    echo "No action selected. Use --check-only to inspect state or combine actions explicitly."
    exit 0
  fi

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --check-only)
        check_only=true
        ;;
      --enforce-now)
        enforce_now=true
        ;;
      --make-persistent)
        make_persist=true
        ;;
      --rollback-now)
        rollback_now=true
        ;;
      --show-config)
        show_cfg=true
        ;;
      --help|-h)
        usage
        exit 0
        ;;
      *)
        echo "Unknown option: $1" >&2
        usage
        exit 1
        ;;
    esac
    shift
  done

  is_root_or_sudo

  if [[ "$check_only" == true ]]; then
    show_state
    [[ "$show_cfg" == true ]] && show_config
    exit 0
  fi

  show_state
  [[ "$show_cfg" == true ]] && show_config

  if [[ "$make_persist" == true ]]; then
    make_persistent
  fi

  if [[ "$enforce_now" == true ]]; then
    set_runtime_enforcing
  fi

  if [[ "$rollback_now" == true ]]; then
    set_runtime_permissive
  fi

  verify_result

  cat <<'EOF'

Next steps:
  - Test the services that must remain available.
  - Review AVC denials if any service is blocked.
  - Prefer fixing labels, ports, or policy context before disabling enforcement.
EOF
}

main "$@"