Tomcat CIS fixes

Use below script for applying CIS fixes, hardening

/middleware/cis-tomcat9-harden.sh /bin/bash -c 'CATALINA_HOME=/middleware/tomcat CATALINA_BASE=/middleware/tomcat TOMCAT_ADMIN_USER=mwadmin TOMCAT_GROUP=mwadmin'
#!/usr/bin/env bash
# CIS Apache Tomcat 9 Benchmark (v1.2.0) hardening script
# Customized for /middleware/tomcat and mwadmin
# Idempotent; creates backups; xmlstarlet preferred, robust Perl fallback for multiline <Connector>.
set -euo pipefail

# ===== Fixed defaults (override via env if needed) =====
CATALINA_HOME="${CATALINA_HOME:-/middleware/tomcat}"
CATALINA_BASE="${CATALINA_BASE:-/middleware/tomcat}"
TOMCAT_ADMIN_USER="${TOMCAT_ADMIN_USER:-mwadmin}"
TOMCAT_GROUP="${TOMCAT_GROUP:-mwadmin}"
BACKUP_DIR="${BACKUP_DIR:-/var/backups/tomcat-cis}"
SERVER_HEADER="${SERVER_HEADER:-Hidden}"

# Feature toggles
REMOVE_SAMPLES="${REMOVE_SAMPLES:-1}"
REMOVE_MANAGER="${REMOVE_MANAGER:-1}"
DISABLE_AJP="${DISABLE_AJP:-1}"
DISABLE_SHUTDOWN_PORT="${DISABLE_SHUTDOWN_PORT:-1}"
RANDOMIZE_SHUTDOWN_CMD="${RANDOMIZE_SHUTDOWN_CMD:-0}"
DISABLE_TRACE="${DISABLE_TRACE:-1}"
XPOWERED_BY_FALSE="${XPOWERED_BY_FALSE:-1}"
FORCE_PERMS_ONLY="${FORCE_PERMS_ONLY:-0}"
DRY_RUN="${DRY_RUN:-0}"
TEST_MODE="${TEST_MODE:-0}"

# Derived paths
CONF_DIR="$CATALINA_BASE/conf"
WEBAPPS_DIR="$CATALINA_BASE/webapps"
LOGS_DIR="$CATALINA_BASE/logs"
TEMP_DIR="$CATALINA_BASE/temp"
BIN_DIR="$CATALINA_HOME/bin"
STAMP="$(date +%Y%m%d-%H%M%S)"
xmlstarlet_bin="$(command -v xmlstarlet || true)"
perl_bin="$(command -v perl || true)"

CONF_FILES=(
  "$CONF_DIR/server.xml" "$CONF_DIR/web.xml" "$CONF_DIR/context.xml"
  "$CONF_DIR/logging.properties" "$CONF_DIR/catalina.properties"
  "$CONF_DIR/catalina.policy" "$CONF_DIR/tomcat-users.xml" "$CONF_DIR/jaspic-providers.xml"
)

say(){ echo "[*] $*"; }
warn(){ echo "[!] $*" >&2; }
die(){ echo "[x] $*" >&2; exit 1; }

need_root(){
  if [ "$TEST_MODE" = "1" ]; then say "TEST_MODE=1: skipping root check"; return; fi
  [ "$(id -u)" -eq 0 ] || die "Run as root."
}
check_paths(){
  [ -d "$CATALINA_HOME" ] || die "CATALINA_HOME not found: $CATALINA_HOME"
  [ -d "$CATALINA_BASE" ] || die "CATALINA_BASE not found: $CATALINA_BASE"
  [ -d "$CONF_DIR" ] || die "Tomcat conf dir not found: $CONF_DIR"
}
mkbackup(){
  if [ "$DRY_RUN" = "1" ]; then say "DRY-RUN: would create backups in $BACKUP_DIR/$STAMP"; return; fi
  mkdir -p "$BACKUP_DIR/$STAMP"
  for f in "${CONF_FILES[@]}"; do [ -f "$f" ] && cp -a "$f" "$BACKUP_DIR/$STAMP/"; done
  say "Backups saved to $BACKUP_DIR/$STAMP"
}
write_file(){ [ "$DRY_RUN" = "1" ] && say "DRY-RUN: would write $1" || printf "%s" "$2" >"$1"; }

# ===== XML helpers =====
# Multiline-safe attribute set on ALL <Connector ...>
xml_set_attr_all_connectors(){
  local file="$1" attr="$2" val="$3"
  if [ "$DRY_RUN" = "1" ]; then say "DRY-RUN: set @$attr=\"$val\" for all <Connector> in $file"; return; fi
  if [ -n "$xmlstarlet_bin" ]; then
    "$xmlstarlet_bin" ed -P -L -u "//Connector/@$attr" -v "$val" "$file" \
      || "$xmlstarlet_bin" ed -P -L -s "//Connector[not(@$attr)]" -t attr -n "$attr" -v "$val" "$file"
    return
  fi
  # Perl fallback: remove existing attr in each Connector tag, then add once before '>'
  if [ -z "$perl_bin" ]; then
    die "Perl not found and xmlstarlet not installed; install one of them for multiline-safe edits."
  fi
  "$perl_bin" -0777 -i -pe '
    s/(<Connector\b[^>]*?)\s'"$attr"'="[^"]*"([^>]*>)/$1$2/sg;
    s/(<Connector\b[^>]*?)>/$1 '"$attr"'="'"$val"'">/sg;
  ' "$file"
}
xml_add_server_header_all_connectors(){ xml_set_attr_all_connectors "$1" "server" "$2"; }
xml_set_allowTrace_false(){ xml_set_attr_all_connectors "$1" "allowTrace" "false"; }
xml_set_xpoweredBy_false(){ xml_set_attr_all_connectors "$1" "xpoweredBy" "false"; }

xml_disable_shutdown_port_or_randomize_cmd(){
  local file="$1"
  if [ "$DRY_RUN" = "1" ]; then
    [ "$DISABLE_SHUTDOWN_PORT" = "1" ] && say "DRY-RUN: set <Server port='-1'> in $file" || \
    { [ "$RANDOMIZE_SHUTDOWN_CMD" = "1" ] && say "DRY-RUN: set random shutdown token in $file"; }
    return
  fi
  if [ -n "$xmlstarlet_bin" ]; then
    if [ "$DISABLE_SHUTDOWN_PORT" = "1" ]; then
      "$xmlstarlet_bin" ed -P -L -u "/Server/@port" -v "-1" "$file"
    elif [ "$RANDOMIZE_SHUTDOWN_CMD" = "1" ]; then
      local t; t="$(tr -dc 'A-Za-z0-9' </dev/urandom | head -c 32)"
      "$xmlstarlet_bin" ed -P -L -u "/Server/@shutdown" -v "$t" "$file"
    fi
  else
    if [ "$DISABLE_SHUTDOWN_PORT" = "1" ]; then
      sed -i -E 's#(<Server[^>]*port=)"[^"]*"#\1"-1"#' "$file"
    elif [ "$RANDOMIZE_SHUTDOWN_CMD" = "1" ]; then
      local t; t="$(tr -dc 'A-Za-z0-9' </dev/urandom | head -c 32)"
      sed -i -E "s#(<Server[^>]*shutdown=)\"[^\"]*\"#\1\"$t\"#" "$file"
    fi
  fi
}

add_global_error_page_and_trace_block(){
  local webxml="$CONF_DIR/web.xml" err_path="${GLOBAL_ERROR_PAGE:-/error.html}"
  if [ -d "$WEBAPPS_DIR/ROOT" ] && [ ! -f "$WEBAPPS_DIR/ROOT${err_path}" ]; then
    say "Creating $WEBAPPS_DIR/ROOT${err_path}"
    write_file "$WEBAPPS_DIR/ROOT${err_path}" "<!doctype html><html><head><meta charset=\"utf-8\"><title>Error</title></head><body><h1>Request cannot be completed</h1></body></html>"
    [ "$DRY_RUN" = "1" ] || { chown "$TOMCAT_ADMIN_USER:$TOMCAT_GROUP" "$WEBAPPS_DIR/ROOT${err_path}" || true; chmod 640 "$WEBAPPS_DIR/ROOT${err_path}" || true; }
  fi
  if ! grep -q "<exception-type>java.lang.Throwable</exception-type>" "$webxml"; then
    say "Adding <error-page> for Throwable to $webxml (CIS 2.5)"
    if [ "$DRY_RUN" != "1" ]; then
      cp -a "$webxml" "$webxml.$STAMP.bak"
      awk -v snip="  <error-page>\n    <exception-type>java.lang.Throwable</exception-type>\n    <location>${err_path}</location>\n  </error-page>" '
        /<\/web-app>/ && !x { print snip; x=1 } { print }' "$webxml" >"$webxml.new" && mv "$webxml.new" "$webxml"
    fi
  fi
  if [ "$DISABLE_TRACE" = "1" ] && ! grep -q "<http-method>TRACE</http-method>" "$webxml"; then
    say "Adding security-constraint to block TRACE in $webxml (CIS 2.6)"
    if [ "$DRY_RUN" != "1" ]; then
      cp -a "$webxml" "$webxml.$STAMP.trbak"
      awk -v snip="  <security-constraint>\n    <web-resource-collection>\n      <web-resource-name>restricted methods</web-resource-name>\n      <url-pattern>/*</url-pattern>\n      <http-method>TRACE</http-method>\n    </web-resource-collection>\n  </security-constraint>" '
        /<\/web-app>/ && !y { print snip; y=1 } { print }' "$webxml" >"$webxml.new2" && mv "$webxml.new2" "$webxml"
    fi
  fi
}

fix_permissions(){
  say "Setting secure ownership/permissions (CIS 4.x)"
  if [ "$DRY_RUN" != "1" ]; then chown -R "$TOMCAT_ADMIN_USER:$TOMCAT_GROUP" "$CATALINA_HOME" "$CATALINA_BASE" || true; fi
  chmod g-w,o-rwx "$CATALINA_HOME" || true
  chmod g-w,o-rwx "$CATALINA_BASE" || true
  chmod o-rwx "$LOGS_DIR" || true
  chmod o-rwx "$TEMP_DIR" || true
  chmod g-w,o-rwx "$BIN_DIR" || true
  chmod g-w,o-rwx "$WEBAPPS_DIR" || true
  chmod g-w,o-rwx "$CONF_DIR" || true
  for f in "$CONF_DIR"/{catalina.properties,catalina.policy,context.xml,logging.properties,server.xml,tomcat-users.xml,jaspic-providers.xml}; do
    [ -f "$f" ] || continue
    [ "$DRY_RUN" = "1" ] || { chown "$TOMCAT_ADMIN_USER:$TOMCAT_GROUP" "$f" || true; }
    chmod 600 "$f" || true
  done
  if [ -f "$CONF_DIR/web.xml" ]; then
    [ "$DRY_RUN" = "1" ] || { chown "$TOMCAT_ADMIN_USER:$TOMCAT_GROUP" "$CONF_DIR/web.xml" || true; }
    chmod 400 "$CONF_DIR/web.xml" || true
  fi
}

remove_extraneous(){
  if [ "$REMOVE_SAMPLES" = "1" ]; then
    for d in docs examples ROOT; do [ -d "$WEBAPPS_DIR/$d" ] && { say "Removing $WEBAPPS_DIR/$d (CIS 1.1)"; [ "$DRY_RUN" = "1" ] || rm -rf "$WEBAPPS_DIR/$d"; }; done
  else say "Skip removing sample apps (REMOVE_SAMPLES=0)"; fi
  if [ "$REMOVE_MANAGER" = "1" ]; then
    for d in manager host-manager; do [ -d "$WEBAPPS_DIR/$d" ] && { say "Removing $WEBAPPS_DIR/$d (CIS 10.2/10.3)"; [ "$DRY_RUN" = "1" ] || rm -rf "$WEBAPPS_DIR/$d"; }; done
  fi
}

disable_unused_connectors(){
  local f="$CONF_DIR/server.xml"; [ -f "$f" ] || return
  if [ "$DISABLE_AJP" = "1" ]; then
    say "Disabling AJP Connector if present (CIS 1.2)"
    if grep -q '<Connector[^>]*protocol="AJP' "$f"; then
      if [ "$DRY_RUN" != "1" ]; then cp -a "$f" "$f.$STAMP.ajp.bak"; sed -i -E 's#([[:space:]]*)<Connector([^>]*protocol="AJP[^"]*"[^>]*)>#\1<!-- Connector\2 -->#g' "$f"; fi
    fi
  else say "Skip disabling AJP (DISABLE_AJP=0)"; fi
}

connector_headers_and_trace(){
  local f="$CONF_DIR/server.xml"; [ -f "$f" ] || return
  [ "$FORCE_PERMS_ONLY" = "1" ] && { say "FORCE_PERMS_ONLY=1 -> skipping XML edits"; return; }
  say "Setting non-revealing server header on all Connectors (CIS 2.7)"
  xml_add_server_header_all_connectors "$f" "$SERVER_HEADER"
  if [ "$XPOWERED_BY_FALSE" = "1" ]; then
    say "Ensuring xpoweredBy=\"false\" on all Connectors (CIS 2.4)"
    xml_set_xpoweredBy_false "$f"
  fi
  if [ "$DISABLE_TRACE" = "1" ]; then
    say "Ensuring allowTrace=\"false\" on all Connectors (CIS 2.6)"
    xml_set_allowTrace_false "$f"
  fi
}

shutdown_port_hardening(){
  local f="$CONF_DIR/server.xml"; [ -f "$f" ] || return
  [ "$FORCE_PERMS_ONLY" = "1" ] && return
  if [ "$DISABLE_SHUTDOWN_PORT" = "1" ] || [ "$RANDOMIZE_SHUTDOWN_CMD" = "1" ]; then
    say "Hardening shutdown configuration (CIS 3.1 / 3.2)"
    xml_disable_shutdown_port_or_randomize_cmd "$f"
  else
    say "Note: consider DISABLE_SHUTDOWN_PORT=1 or RANDOMIZE_SHUTDOWN_CMD=1 (CIS 3.x)"
  fi
}

sed -i -E -e 's#(<Connector[^>]*)(>)#\1 server="Hidden" xpoweredBy="false" allowTrace="false"\2#g' /middleware/tomcat/conf/server.xml

tighten_webxml_and_errorpage(){ [ "$FORCE_PERMS_ONLY" = "1" ] && return; [ -f "$CONF_DIR/web.xml" ] || return; add_global_error_page_and_trace_block; }

final_notes(){
  cat <<'EOF'
[✓] CIS Tomcat hardening steps applied (where enabled).
Next:
  1) Review server.xml for app-specific needs (ports, TLS, proxy).
  2) Start Tomcat and validate apps (headers, TRACE blocked).
  3) Consider optional CIS items (Realms/LDAP, TLS-only).
Backups in BACKUP_DIR/TIMESTAMP.
EOF
}

# ===== Main =====
need_root
check_paths
mkbackup
remove_extraneous
disable_unused_connectors
connector_headers_and_trace
shutdown_port_hardening
tighten_webxml_and_errorpage
fix_permissions
final_notes

sed -i -E -e 's#(<Connector[^>]*)(>)#\1 server="Hidden" xpoweredBy="false" allowTrace="false"\2#g' /middleware/tomcat/conf/server.xml

Use below for scanning

sudo bash ./standardize_tomcat.sh –current /home/prasad/apache-tomcat-9.0.108 –target /middleware/tomcat –user mwadmin –group mwadmin –service-name tomcat9 –create-systemd

#!/usr/bin/env bash
# cis-tomcat9-scan.sh (robust)
# Read-only scanner for key CIS Apache Tomcat 9 controls.
# Defaults: CATALINA_HOME/BASE=/middleware/tomcat, mwadmin:mwadmin.
# Prints PASS/WARN/FAIL for each check and exits non-zero only at the very end if any FAILs.

set -uo pipefail
LC_ALL=C

# ---- Defaults (override via env) ----
CATALINA_HOME="${CATALINA_HOME:-/middleware/tomcat}"
CATALINA_BASE="${CATALINA_BASE:-/middleware/tomcat}"
CONF_DIR="${CONF_DIR:-$CATALINA_BASE/conf}"
WEBAPPS_DIR="${WEBAPPS_DIR:-$CATALINA_BASE/webapps}"
LOGS_DIR="${LOGS_DIR:-$CATALINA_BASE/logs}"
TEMP_DIR="${TEMP_DIR:-$CATALINA_BASE/temp}"
BIN_DIR="${BIN_DIR:-$CATALINA_HOME/bin}"
TOMCAT_ADMIN_USER="${TOMCAT_ADMIN_USER:-mwadmin}"
TOMCAT_GROUP="${TOMCAT_GROUP:-mwadmin}"

SERVER_XML="$CONF_DIR/server.xml"
WEB_XML="$CONF_DIR/web.xml"

GREEN=$'\033[0;32m'; RED=$'\033[0;31m'; YEL=$'\033[1;33m'; NC=$'\033[0m'
say()  { echo "[*] $*"; }
pass() { printf "%s  - %s\n" "${GREEN}PASS${NC}" "$1"; }
fail() { printf "%s  - %s\n" "${RED}FAIL${NC}" "$1"; }
warn() { printf "%s  - %s\n" "${YEL}WARN${NC}" "$1"; }

RESULTS=()
add_res() { RESULTS+=("$1|$2"); }  # "PASS|desc" / "FAIL|desc" / "WARN|desc"

need_paths() {
  for d in "$CATALINA_HOME" "$CATALINA_BASE" "$CONF_DIR" "$WEBAPPS_DIR"; do
    if [ ! -d "$d" ]; then echo "Missing required path: $d"; exit 2; fi
  done
  [ -f "$SERVER_XML" ] || { echo "Missing $SERVER_XML"; exit 2; }
  [ -f "$WEB_XML" ] || { echo "Missing $WEB_XML"; exit 2; }
}

# ---- Helpers ----
strip_comments() {
  # Remove <!-- ... --> (single or multi-line) without depending on awk features
  sed -e ':a' -e 's/<!--[^-]*-[^-]*-*>//g; /<!--/ {N; ba};' "$1" 2>/dev/null || cat "$1"
}

# Return 1 if all <Connector ...> tags match attr regex, else 0
has_connector_attr_all() {
  local attr_re="$1" plain connectors total count
  plain="$(strip_comments "$SERVER_XML")"
  connectors="$(printf "%s" "$plain" | grep -o '<Connector[^>]*>' 2>/dev/null || true)"
  total=$(printf "%s" "$connectors" | grep -c '<Connector' 2>/dev/null || echo 0)
  [ "$total" -gt 0 ] || { echo 0; return; }
  count=$(printf "%s" "$connectors" | grep -E -c "$attr_re" 2>/dev/null || echo 0)
  [ "$count" -eq "$total" ] && echo 1 || echo 0
}

# Return 0/1 if any connector matches regex
has_connector_attr_any() {
  local attr_re="$1" plain connectors
  plain="$(strip_comments "$SERVER_XML")"
  connectors="$(printf "%s" "$plain" | grep -o '<Connector[^>]*>' 2>/dev/null || true)"
  printf "%s" "$connectors" | grep -E -q "$attr_re" 2>/dev/null && echo 1 || echo 0
}

# stat helpers
perm_octal() { stat -c '%a' "$1" 2>/dev/null || echo ""; }
owner_user() { stat -c '%U' "$1" 2>/dev/null || echo ""; }
owner_group(){ stat -c '%G' "$1" 2>/dev/null || echo ""; }

# ---- Checks ----
check_samples_removed() {
  local ok=1; for d in docs examples ROOT; do [ -d "$WEBAPPS_DIR/$d" ] && ok=0; done
  [ "$ok" -eq 1 ] && add_res PASS "Sample apps removed (docs/examples/ROOT)" \
                  || add_res FAIL "Sample apps present under $WEBAPPS_DIR"
}
check_manager_removed() {
  local ok=1; for d in manager host-manager; do [ -d "$WEBAPPS_DIR/$d" ] && ok=0; done
  [ "$ok" -eq 1 ] && add_res PASS "Admin apps removed (manager/host-manager)" \
                  || add_res WARN "manager/host-manager present (restrict or remove)"
}
check_ajp_disabled() {
  local plain; plain="$(strip_comments "$SERVER_XML")"
  if echo "$plain" | grep -E -q '<Connector[^>]*protocol="AJP' 2>/dev/null; then
    add_res FAIL "AJP Connector active in server.xml"
  else
    add_res PASS "AJP Connector disabled/not present"
  fi
}
check_shutdown_port() {
  local plain token; plain="$(strip_comments "$SERVER_XML")"
  if echo "$plain" | grep -q '<Server[^>]*port="-1"' 2>/dev/null; then
    add_res PASS "Shutdown port disabled (port=-1)"; return
  fi
  token="$(printf "%s" "$plain" | sed -n 's/.*<Server[^>]*shutdown="\([^"]*\)".*/\1/p' 2>/dev/null | head -n1)"
  if [ -z "${token:-}" ]; then add_res FAIL "Shutdown port enabled and token not set"
  elif [ "$token" = "SHUTDOWN" ]; then add_res FAIL "Shutdown port enabled with default token (SHUTDOWN)"
  else add_res WARN "Shutdown port enabled but token appears randomized"
  fi
}
check_server_header() {
  if [ "$(has_connector_attr_all 'server=\"[^\"]+\"')" = "1" ]; then
    if [ "$(has_connector_attr_any 'server=\"[^"]*(T|t)omcat|server=\"[^"]*(C|c)oyote')" = "1" ]; then
      add_res FAIL "Connector server header reveals Tomcat/Coyote"
    else
      add_res PASS "Connector server header set to non-revealing value"
    fi
  else
    add_res FAIL "One or more Connectors missing server=\"...\" attribute"
  fi
}
check_xpoweredby_false() {
  [ "$(has_connector_attr_all 'xpoweredBy=\"false\"')" = "1" ] \
    && add_res PASS "xpoweredBy=\"false\" on all Connectors" \
    || add_res FAIL "xpoweredBy not false on all Connectors"
}
check_allowtrace_false() {
  [ "$(has_connector_attr_all 'allowTrace=\"false\"')" = "1" ] \
    && add_res PASS "allowTrace=\"false\" on all Connectors" \
    || add_res FAIL "TRACE not disabled on all Connectors"
}
check_webxml_errorpage() {
  grep -q '<exception-type>java\.lang\.Throwable</exception-type>' "$WEB_XML" 2>/dev/null \
    && add_res PASS "Global error-page for Throwable present (no stack traces)" \
    || add_res FAIL "Global error-page for Throwable missing in web.xml"
}
check_webxml_trace_block() {
  grep -q '<http-method>TRACE</http-method>' "$WEB_XML" 2>/dev/null \
    && add_res PASS "TRACE blocked via web.xml security-constraint" \
    || add_res FAIL "No TRACE block in web.xml"
}

check_dir_mode_no_world() {
  local d="$1" label="$2" p; p="$(perm_octal "$d")"
  [ -n "$p" ] || { add_res FAIL "$label permissions unreadable"; return; }
  local g="${p:1:1}" o="${p:2:1}"
  if [[ "$o" == 0 && ! "$g" =~ [2367] ]]; then add_res PASS "$label permissions OK ($p)"
  else add_res FAIL "$label permissions too open ($p)"; fi
}
check_file_mode_max() {
  local f="$1" max="$2" label="$3" p; [ -f "$f" ] || { add_res PASS "$label not present (OK)"; return; }
  p="$(perm_octal "$f")"; [ -n "$p" ] || { add_res FAIL "$label permissions unreadable"; return; }
  if [ "$max" = "600" ]; then
    local g="${p:1:1}" o="${p:2:1}"
    [[ "$g" == 0 && "$o" == 0 ]] && add_res PASS "$label mode ≤600 ($p)" \
                                 || add_res FAIL "$label mode too open ($p), expected ≤600"
  elif [ "$max" = "400" ]; then
    [ "$p" = "400" ] && add_res PASS "$label mode 400" \
                     || add_res FAIL "$label mode $p, expected 400"
  else add_res WARN "Unknown mode rule for $label (max=$max)"; fi
}
check_conf_file_owners() {
  local f="$1" label="$2"; [ -f "$f" ] || { add_res PASS "$label not present (OK)"; return; }
  local u g; u="$(owner_user "$f")"; g="$(owner_group "$f")"
  if [ "$u" = "$TOMCAT_ADMIN_USER" ] && [ "$g" = "$TOMCAT_GROUP" ]; then
    add_res PASS "$label owner $u:$g"
  else
    add_res WARN "$label owned by $u:$g (expected $TOMCAT_ADMIN_USER:$TOMCAT_GROUP)"
  fi
}

run_checks() {
  say "Scanning Tomcat at $CATALINA_BASE"

  check_samples_removed
  check_manager_removed
  check_ajp_disabled
  check_shutdown_port
  check_server_header
  check_xpoweredby_false
  check_allowtrace_false
  check_webxml_errorpage
  check_webxml_trace_block

  check_dir_mode_no_world "$CATALINA_HOME" "CATALINA_HOME"
  check_dir_mode_no_world "$CATALINA_BASE" "CATALINA_BASE"
  check_dir_mode_no_world "$CONF_DIR" "conf/"
  check_dir_mode_no_world "$WEBAPPS_DIR" "webapps/"
  check_dir_mode_no_world "$BIN_DIR" "bin/"
  [ -d "$LOGS_DIR" ] && check_dir_mode_no_world "$LOGS_DIR" "logs/"
  [ -d "$TEMP_DIR" ] && check_dir_mode_no_world "$TEMP_DIR" "temp/"

  check_file_mode_max "$CONF_DIR/catalina.properties"   600 "catalina.properties"
  check_file_mode_max "$CONF_DIR/catalina.policy"       600 "catalina.policy"
  check_file_mode_max "$CONF_DIR/context.xml"           600 "context.xml"
  check_file_mode_max "$CONF_DIR/logging.properties"    600 "logging.properties"
  check_file_mode_max "$CONF_DIR/server.xml"            600 "server.xml"
  check_file_mode_max "$CONF_DIR/tomcat-users.xml"      600 "tomcat-users.xml"
  check_file_mode_max "$CONF_DIR/jaspic-providers.xml"  600 "jaspic-providers.xml"
  check_file_mode_max "$CONF_DIR/web.xml"               400 "web.xml"

  for f in "$CONF_DIR"/*.properties "$CONF_DIR"/*.xml "$CONF_DIR"/*.policy; do
    [ -e "$f" ] || continue
    check_conf_file_owners "$f" "$(basename "$f")"
  done
}

print_report() {
  echo; echo "========== CIS Tomcat Scan Report =========="
  local fails=0 warns=0 passes=0
  for r in "${RESULTS[@]}"; do
    IFS='|' read -r status desc <<<"$r"
    case "$status" in
      PASS) pass "$desc"; ((passes++));;
      FAIL) fail "$desc"; ((fails++));;
      WARN) warn "$desc"; ((warns++));;
    esac
  done
  echo "--------------------------------------------"
  echo -e "Total: $((passes+warns+fails))  ${GREEN}PASS:${passes}${NC}  ${YEL}WARN:${warns}${NC}  ${RED}FAIL:${fails}${NC}"
  echo "============================================"
  [ "$fails" -eq 0 ] || exit 1
}

main() { need_paths; run_checks; print_report; }
main "$@"
Subscribe
Notify of
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments