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 "$@"