This is an automated email from the ASF dual-hosted git repository.

ramackri pushed a commit to branch RANGER-5712
in repository https://gitbox.apache.org/repos/asf/ranger.git

commit 9203c3c8e599351a28e70ad996f6b56824cda1c0
Author: ramk <[email protected]>
AuthorDate: Thu Jul 30 15:02:53 2026 +0530

    RANGER-5712: Add Kerberos audit client config for Ozone docker E2E
    
    Configure OM keytab JAAS for audit-ingestor Kerberos outbound auth and
    disable SPIFFE audit headers so static+Kerberos ingestor testing works.
    Add verify-ozone-pr1116-e2e.sh with hdfs policy-enforced audit trigger.
---
 .../ozone/ranger-ozone-plugin-install.properties   |  22 +-
 .../scripts/ozone/verify-ozone-pr1116-e2e.sh       | 569 +++++++++++++++++++++
 plugin-ozone/conf/ranger-ozone-audit-changes.cfg   |   2 +
 .../conf/ranger-ozone-security-changes.cfg         |   9 +-
 4 files changed, 593 insertions(+), 9 deletions(-)

diff --git 
a/dev-support/ranger-docker/scripts/ozone/ranger-ozone-plugin-install.properties
 
b/dev-support/ranger-docker/scripts/ozone/ranger-ozone-plugin-install.properties
index e101d57d3..82494ef51 100644
--- 
a/dev-support/ranger-docker/scripts/ozone/ranger-ozone-plugin-install.properties
+++ 
b/dev-support/ranger-docker/scripts/ozone/ranger-ozone-plugin-install.properties
@@ -52,14 +52,14 @@ XAAUDIT.SOLR.ZOOKEEPER=NONE
 XAAUDIT.SOLR.FILE_SPOOL_DIR=/var/log/ozone/audit/solr/spool
 XAAUDIT.SOLR.USE_INMEMORY_JAAS_CFG=false
 
-XAAUDIT.JAAS.CLIENT.LOGIN_MODULE_NAME=%EMPTY%
-XAAUDIT.JAAS.CLIENT.LOGIN_MODULE_CONTROL_FLAG=%EMPTY%
-XAAUDIT.JAAS.CLIENT.OPTION.USE_KEY_TAB=%EMPTY%
-XAAUDIT.JAAS.CLIENT.OPTION.STORE_KEY=%EMPTY%
-XAAUDIT.JAAS.CLIENT.OPTION.USE_TICKET_CACHE=%EMPTY%
-XAAUDIT.JAAS.CLIENT.OPTION.SERVICE_NAME=%EMPTY%
-XAAUDIT.JAAS.CLIENT.OPTION.KEY_TAB=%EMPTY%
-XAAUDIT.JAAS.CLIENT.OPTION.PRINCIPAL=%EMPTY%
+XAAUDIT.JAAS.CLIENT.LOGIN_MODULE_NAME=com.sun.security.auth.module.Krb5LoginModule
+XAAUDIT.JAAS.CLIENT.LOGIN_MODULE_CONTROL_FLAG=required
+XAAUDIT.JAAS.CLIENT.OPTION.USE_KEY_TAB=true
+XAAUDIT.JAAS.CLIENT.OPTION.STORE_KEY=true
+XAAUDIT.JAAS.CLIENT.OPTION.USE_TICKET_CACHE=true
+XAAUDIT.JAAS.CLIENT.OPTION.SERVICE_NAME=om
+XAAUDIT.JAAS.CLIENT.OPTION.KEY_TAB=/etc/keytabs/om.keytab
+XAAUDIT.JAAS.CLIENT.OPTION.PRINCIPAL=om/[email protected]
 
 XAAUDIT.ELASTICSEARCH.ENABLE=false
 XAAUDIT.ELASTICSEARCH.URL=NONE
@@ -91,6 +91,12 @@ XAAUDIT.AMAZON_CLOUDWATCH.LOG_STREAM_PREFIX=NONE
 XAAUDIT.AMAZON_CLOUDWATCH.FILE_SPOOL_DIR=NONE
 XAAUDIT.AMAZON_CLOUDWATCH.REGION=NONE
 
+# Audit Server Kerberos outbound (plugin -> ingestor static+Kerberos E2E)
+XAAUDIT.AUDITSERVER.AUTHN.SPIFFE.ENABLED=false
+XAAUDIT.AUDITSERVER.AUTHN.SPIFFE.HEADER=X-Spiffe-Id
+XAAUDIT.AUDITSERVER.AUTHN.SPIFFE.VALUE=%EMPTY%
+XAAUDIT.AUDITSERVER.AUTHN.SPIFFE.FILE=%EMPTY%
+
 SSL_KEYSTORE_FILE_PATH=/etc/hadoop/conf/ranger-plugin-keystore.jks
 SSL_KEYSTORE_PASSWORD=myKeyFilePassword
 SSL_TRUSTSTORE_FILE_PATH=/etc/hadoop/conf/ranger-plugin-truststore.jks
diff --git a/dev-support/ranger-docker/scripts/ozone/verify-ozone-pr1116-e2e.sh 
b/dev-support/ranger-docker/scripts/ozone/verify-ozone-pr1116-e2e.sh
new file mode 100755
index 000000000..177387313
--- /dev/null
+++ b/dev-support/ranger-docker/scripts/ozone/verify-ozone-pr1116-e2e.sh
@@ -0,0 +1,569 @@
+#!/bin/bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Apply PR #1116 (RANGER-5712) Ozone Kerberos docker fix to a running stack,
+# verify PolicyRefresher policy/role download (Kerberos secureMode), optional
+# ozone audit E2E (ingestor static+Kerberos mode), then revert ozone files.
+#
+# Does NOT permanently modify audit-ingestor SPIFFE / admin header auth; audit
+# phase switches ingestor to static+Kerberos temporarily and restores after.
+#
+# Usage:
+#   RANGER_DOCKER_STACK=/path/to/ranger-docker 
./scripts/ozone/verify-ozone-pr1116-e2e.sh apply
+#   ./scripts/ozone/verify-ozone-pr1116-e2e.sh verify
+#   ./scripts/ozone/verify-ozone-pr1116-e2e.sh audit-e2e
+#   ./scripts/ozone/verify-ozone-pr1116-e2e.sh revert
+#   ./scripts/ozone/verify-ozone-pr1116-e2e.sh run
+
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+PR_DOCKER_DIR="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+PR_REPO="$(cd "${PR_DOCKER_DIR}/../.." && pwd)"
+
+RANGER_DOCKER_STACK="${RANGER_DOCKER_STACK:-/Users/ramk/rangerRelease/ranger/dev-support/ranger-docker}"
+REVERT_AFTER_TEST="${REVERT_AFTER_TEST:-false}"
+RUN_AUDIT_E2E="${RUN_AUDIT_E2E:-true}"
+REVERT_INGESTOR_AFTER_AUDIT="${REVERT_INGESTOR_AFTER_AUDIT:-true}"
+SKIP_POLICY_CREATE="${SKIP_POLICY_CREATE:-false}"
+OZONE_E2E_WAIT_SEC="${OZONE_E2E_WAIT_SEC:-45}"
+
+STATE_DIR="${RANGER_DOCKER_STACK}/.ozone-pr1116-e2e"
+BACKUP_DIR="${STATE_DIR}/backup"
+STATE_FILE="${STATE_DIR}/state.env"
+INGESTOR_ENV_BACKUP="${STATE_DIR}/ingestor-env.backup"
+
+COMPOSE=( -f docker-compose.ranger.yml -f docker-compose.ranger-ozone.yml )
+AUDIT_COMPOSE=(
+  -f docker-compose.ranger.yml
+  -f docker-compose.ranger-hadoop.yml
+  -f docker-compose.ranger-kafka.yml
+  -f docker-compose.ranger-audit-server.yml
+)
+
+TEST_POLICY_NAME="${TEST_POLICY_NAME:-pr1116-test-policy}"
+TEST_ROLE_NAME="${TEST_ROLE_NAME:-pr1116_test_role}"
+TEST_VOLUME="${TEST_VOLUME:-testvol-pr1116}"
+AUDIT_BUCKET="${AUDIT_BUCKET:-audit-bucket}"
+AUDIT_MARKER="${AUDIT_MARKER:-pr1116-audit-marker}"
+
+PR_GIT_REF="${PR_GIT_REF:-RANGER-5712}"
+
+die() { echo "ERROR: $*" >&2; exit 1; }
+
+pr_file() {
+  local rel="$1"
+  local path="${PR_DOCKER_DIR}/${rel#dev-support/ranger-docker/}"
+  if [ -f "${path}" ]; then
+    echo "${path}"
+    return 0
+  fi
+  git -C "${PR_REPO}" show 
"${PR_GIT_REF}:dev-support/ranger-docker/${rel#dev-support/ranger-docker/}" 
2>/dev/null \
+    || die "missing ${rel} on branch ${PR_GIT_REF}"
+}
+
+pr_file_to() {
+  local rel="$1" dest="$2"
+  local src
+  src="$(pr_file "${rel}")"
+  if [ -f "${src}" ]; then
+    cp "${src}" "${dest}"
+  else
+    printf '%s' "${src}" > "${dest}"
+  fi
+}
+
+usage() {
+  cat <<EOF
+Usage: $(basename "$0") <apply|verify|audit-e2e|revert-ingestor|revert|run>
+
+  apply           Apply PR #1116 ozone docker files, recreate om/scm/datanode
+  verify          Kerberos + PolicyRefresher policy/role download
+  audit-e2e       Ingestor static+Kerberos, ozone audit -> kafka -> solr
+  revert-ingestor Restore ingestor dynamic+SPIFFE (if audit phase interrupted)
+  revert          Restore backed-up ozone docker files
+  run             apply + verify + audit-e2e
+EOF
+}
+
+require_stack() {
+  [ -d "${RANGER_DOCKER_STACK}" ] || die "RANGER_DOCKER_STACK not found: 
${RANGER_DOCKER_STACK}"
+  [ -f "${RANGER_DOCKER_STACK}/.env" ] || die "missing 
${RANGER_DOCKER_STACK}/.env"
+}
+
+require_pr_files() {
+  git -C "${PR_REPO}" rev-parse --verify "${PR_GIT_REF}" >/dev/null 2>&1 || \
+    die "git ref ${PR_GIT_REF} not found in ${PR_REPO}"
+  pr_file docker-compose.ranger-ozone.yml >/dev/null
+  pr_file scripts/ozone/docker-config >/dev/null
+  pr_file scripts/ozone/ranger-ozone-setup.sh >/dev/null
+}
+
+save_state() {
+  mkdir -p "${STATE_DIR}"
+  cat > "${STATE_FILE}" <<EOF
+BACKUP_DIR=${BACKUP_DIR}
+APPLIED_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ)
+PR_REPO=${PR_REPO}
+RANGER_DOCKER_STACK=${RANGER_DOCKER_STACK}
+EOF
+}
+
+load_state() {
+  [ -f "${STATE_FILE}" ] || die "no apply state at ${STATE_FILE}; run apply 
first"
+  # shellcheck source=/dev/null
+  source "${STATE_FILE}"
+}
+
+backup_stack_files() {
+  mkdir -p "${BACKUP_DIR}/scripts/ozone"
+  cp "${RANGER_DOCKER_STACK}/docker-compose.ranger-ozone.yml" "${BACKUP_DIR}/"
+  cp "${RANGER_DOCKER_STACK}/Dockerfile.ranger-ozone" "${BACKUP_DIR}/" 
2>/dev/null || true
+  cp "${RANGER_DOCKER_STACK}/scripts/ozone/docker-config" 
"${BACKUP_DIR}/scripts/ozone/"
+  cp "${RANGER_DOCKER_STACK}/scripts/ozone/ranger-ozone-setup.sh" 
"${BACKUP_DIR}/scripts/ozone/"
+  cp 
"${RANGER_DOCKER_STACK}/scripts/ozone/ranger-ozone-plugin-install.properties" 
"${BACKUP_DIR}/scripts/ozone/"
+  echo "Backed up ozone docker files to ${BACKUP_DIR}"
+}
+
+apply_pr_files() {
+  echo "Applying PR #1116 ozone docker files -> ${RANGER_DOCKER_STACK}"
+  pr_file_to docker-compose.ranger-ozone.yml \
+    "${RANGER_DOCKER_STACK}/docker-compose.ranger-ozone.yml"
+  pr_file_to Dockerfile.ranger-ozone \
+    "${RANGER_DOCKER_STACK}/Dockerfile.ranger-ozone" 2>/dev/null || true
+  pr_file_to scripts/ozone/docker-config \
+    "${RANGER_DOCKER_STACK}/scripts/ozone/docker-config"
+  pr_file_to scripts/ozone/ranger-ozone-setup.sh \
+    "${RANGER_DOCKER_STACK}/scripts/ozone/ranger-ozone-setup.sh"
+  pr_file_to scripts/ozone/ranger-ozone-plugin-install.properties \
+    
"${RANGER_DOCKER_STACK}/scripts/ozone/ranger-ozone-plugin-install.properties"
+}
+
+strip_ozone_plugin_spiffe_props() {
+  local 
sec="${RANGER_DOCKER_STACK}/dist/ranger-${OZONE_PLUGIN_VERSION:-3.0.0-SNAPSHOT}-ozone-plugin/conf/ranger-ozone-security.xml"
+  [ -f "${sec}" ] || return 0
+  python3 - "${sec}" <<'PY'
+import sys
+import xml.etree.ElementTree as ET
+path = sys.argv[1]
+tree = ET.parse(path)
+root = tree.getroot()
+drop = {
+    'ranger.ozone.authn.header.enabled',
+    'ranger.ozone.authn.header.spiffe',
+    'ranger.ozone.authn.spiffe.value',
+}
+for prop in list(root.findall('property')):
+    name = prop.find('name')
+    if name is not None and name.text in drop:
+        root.remove(prop)
+tree.write(path, encoding='unicode', xml_declaration=False)
+print('[verify-ozone-pr1116] stripped SPIFFE props from ozone plugin 
security.xml')
+PY
+}
+
+# Ozone 2.1.x: avoid WadlAutoDiscoverable ClassCastException (not part of PR 
#1116).
+apply_ozone_211_jersey_workaround() {
+  local stack="${RANGER_DOCKER_STACK}"
+  # shellcheck source=/dev/null
+  source "${stack}/.env"
+  local ozone_lib="${stack}/downloads/ozone-${OZONE_VERSION}/share/ozone/lib"
+  local disabled="${ozone_lib}/.ranger-jersey-workaround"
+  [ -d "${ozone_lib}" ] || return 0
+  mkdir -p "${disabled}"
+  for jar in "${ozone_lib}"/jersey-server-*.jar; do
+    [ -f "${jar}" ] || continue
+    mv "${jar}" "${disabled}/"
+    echo "  quarantined host $(basename "${jar}") for PolicyRefresher Jersey 
workaround"
+  done
+}
+
+ensure_ozone_keytabs() {
+  local stack="${RANGER_DOCKER_STACK}"
+  mkdir -p "${stack}/dist/keytabs/om" "${stack}/dist/keytabs/scm" 
"${stack}/dist/keytabs/datanode"
+  for spec in "om:om.keytab:om/om.rangernw" "scm:scm.keytab:scm/scm.rangernw" 
"datanode:dn.keytab:dn/datanode.rangernw"; do
+    local dir file principal
+    IFS=: read -r dir file principal <<< "${spec}"
+    if [ ! -s "${stack}/dist/keytabs/${dir}/${file}" ]; then
+      echo "  provisioning keytab dist/keytabs/${dir}/${file} from ranger-kdc"
+      docker exec ranger-kdc kadmin.local -q "addprinc -randkey 
${principal}@EXAMPLE.COM" 2>/dev/null || true
+      docker exec ranger-kdc kadmin.local -q "xst -k /tmp/${file} 
${principal}@EXAMPLE.COM"
+      docker cp "ranger-kdc:/tmp/${file}" 
"${stack}/dist/keytabs/${dir}/${file}"
+      chmod 600 "${stack}/dist/keytabs/${dir}/${file}"
+    fi
+  done
+}
+
+recreate_ozone_only() {
+  local stack="${RANGER_DOCKER_STACK}"
+  cd "${stack}"
+  # shellcheck source=/dev/null
+  source .env
+  export OZONE_VERSION
+  rm -f "downloads/ozone-${OZONE_VERSION}/.setupDone"
+  pr_file_to scripts/ozone/ozone-plugin-docker-setup.sh \
+    "${stack}/scripts/ozone/ozone-plugin-docker-setup.sh"
+  chmod +x "${stack}/scripts/ozone/ozone-plugin-docker-setup.sh"
+  ./scripts/ozone/ozone-plugin-docker-setup.sh
+  strip_ozone_plugin_spiffe_props
+  apply_ozone_211_jersey_workaround
+  echo "Recreating om/scm/datanode (admin + audit-ingestor untouched)..."
+  docker compose "${COMPOSE[@]}" up -d --no-deps --force-recreate --build om 
scm datanode
+}
+
+ingestor_env_snapshot() {
+  docker inspect ranger-audit-ingestor \
+    --format '{{range .Config.Env}}{{println .}}{{end}}' \
+    | grep -E 
'^RANGER_AUDIT_(PARTITION_PLAN_ADMIN_ENABLED|INGESTOR_SPIFFE_ENABLED)=' \
+    > "${INGESTOR_ENV_BACKUP}" 2>/dev/null || true
+}
+
+restart_ingestor_static_kerberos() {
+  local stack="${RANGER_DOCKER_STACK}"
+  cd "${stack}"
+  # shellcheck source=/dev/null
+  source .env
+  ingestor_env_snapshot
+  echo "== Switching audit-ingestor to static partition + Kerberos inbound =="
+  RANGER_AUDIT_PARTITION_PLAN_ADMIN_ENABLED=false \
+  RANGER_AUDIT_INGESTOR_SPIFFE_ENABLED=false \
+  KERBEROS_ENABLED=true \
+    docker compose "${AUDIT_COMPOSE[@]}" up -d --no-deps --force-recreate 
ranger-audit-ingestor
+  sleep 25
+  docker exec ranger-audit-ingestor python3 -c "
+import xml.etree.ElementTree as ET
+r=ET.parse('/opt/ranger/audit-ingestor/conf/ranger-audit-ingestor-site.xml').getroot()
+for n in 
['ranger.audit.ingestor.partition.plan.admin.enabled','ranger.audit.ingestor.authn.header.enabled','ranger.audit.ingestor.kerberos.enabled']:
+  for p in r.findall('property'):
+    if p.find('name') is not None and p.find('name').text==n:
+      print(n,'=',p.find('value').text)
+"
+}
+
+restart_ingestor_dynamic_spiffe() {
+  local stack="${RANGER_DOCKER_STACK}"
+  cd "${stack}"
+  # shellcheck source=/dev/null
+  source .env
+  echo "== Restoring audit-ingestor dynamic partition + SPIFFE inbound =="
+  RANGER_AUDIT_PARTITION_PLAN_ADMIN_ENABLED=true \
+  RANGER_AUDIT_INGESTOR_SPIFFE_ENABLED=true \
+  KERBEROS_ENABLED=true \
+    docker compose "${AUDIT_COMPOSE[@]}" up -d --no-deps --force-recreate 
ranger-audit-ingestor
+  sleep 25
+}
+
+verify_kdc_fix() {
+  echo "== KDC / Kerberos startup =="
+  local om_logs
+  om_logs="$(docker logs ozone-om 2>&1 || true)"
+  if grep -qE 'Setting up kerberos|KDC ISSUER|krb5:8081' <<< "${om_logs}"; then
+    echo "FAIL: ozone-om still shows KDC krb5:8081 wait messages"
+    return 1
+  fi
+  echo "PASS: no KDC krb5:8081 wait in ozone-om logs"
+  if docker inspect ozone-om --format '{{range 
.Mounts}}{{.Destination}}{{"\n"}}{{end}}' | grep -qx '/etc/keytabs'; then
+    echo "PASS: /etc/keytabs mounted on ozone-om"
+  else
+    echo "FAIL: /etc/keytabs not mounted on ozone-om (PR #1116 compose not 
applied?)"
+    return 1
+  fi
+  if grep -qE 'Login successful for user om/|Ozone Manager login successful' 
<<< "${om_logs}"; then
+    echo "PASS: OM Kerberos login succeeded"
+  else
+    echo "FAIL: OM Kerberos login not found — PolicyRefresher will use 
secureMode=false"
+    grep -iE 'Login|keytab|Kerberos|UGI' <<< "${om_logs}" | tail -5 || true
+    return 1
+  fi
+}
+
+create_test_policy_and_role() {
+  [ "${SKIP_POLICY_CREATE}" = "true" ] && return 0
+  echo "== Create test policy/role in Ranger Admin =="
+  local policy_json role_json policy_id role_id
+  policy_json=$(cat <<EOF
+{"isEnabled":true,"service":"dev_ozone","name":"${TEST_POLICY_NAME}","policyType":0,
+ 
"resources":{"volume":{"values":["${TEST_VOLUME}"],"isExcludes":false,"isRecursive":false},
+  
"bucket":{"values":["${AUDIT_BUCKET}"],"isExcludes":false,"isRecursive":false}},
+ 
"policyItems":[{"accesses":[{"type":"read","isAllowed":true},{"type":"list","isAllowed":true}],"users":["hdfs"]}],
+ "serviceType":"ozone"}
+EOF
+)
+  policy_id=$(docker exec ranger curl -s -u admin:rangerR0cks! \
+    -H 'Content-Type: application/json' -X POST \
+    'http://localhost:6080/service/public/v2/api/policy' \
+    -d "${policy_json}" | python3 -c "import sys,json; 
print(json.load(sys.stdin).get('id',''))")
+  
role_json="{\"name\":\"${TEST_ROLE_NAME}\",\"isEnabled\":true,\"users\":[],\"groups\":[],\"roles\":[]}"
+  role_id=$(docker exec ranger curl -s -u admin:rangerR0cks! \
+    -H 'Content-Type: application/json' -X POST \
+    "http://localhost:6080/service/public/v2/api/roles?serviceName=dev_ozone"; \
+    -d "${role_json}" | python3 -c "import sys,json; 
print(json.load(sys.stdin).get('id',''))")
+  echo "  created policy id=${policy_id} role id=${role_id}"
+  echo "TEST_POLICY_ID=${policy_id}" >> "${STATE_FILE}"
+  echo "TEST_ROLE_ID=${role_id}" >> "${STATE_FILE}"
+}
+
+verify_plugin_policy_pull() {
+  echo "== Plugin PolicyRefresher (Kerberos secure download) =="
+  sleep "${OZONE_E2E_WAIT_SEC}"
+  local om_logs
+  om_logs="$(docker logs ozone-om 2>&1 || true)"
+  if grep -q 'WadlAutoDiscoverable cannot be cast' <<< "${om_logs}"; then
+    echo "FAIL: Jersey ClassCastException in PolicyRefresher (Ozone 2.1.x 
classpath issue)"
+    return 1
+  fi
+  if grep -q 'Unauthenticated access not allowed' <<< "${om_logs}"; then
+    echo "FAIL: PolicyRefresher still hitting unauthenticated admin endpoints 
(secureMode=false?)"
+    grep -E 'PolicyRefresher|RangerAdminRESTClient|secureMode' <<< 
"${om_logs}" | tail -10
+    return 1
+  fi
+  if docker logs ozone-om 2>&1 | tail -150 | grep -qE 'secureMode=true.*Error 
getting policies|secureMode=true.*Error getting Roles'; then
+    echo "FAIL: secureMode=true but policy/role download errors remain"
+    return 1
+  fi
+  local pv rv
+  pv=$(docker exec ozone-om python3 -c "import json; 
print(json.load(open('/etc/ranger/dev_ozone/policycache/ozone_dev_ozone.json')).get('policyVersion',-1))"
 2>/dev/null || echo "-1")
+  rv=$(docker exec ozone-om python3 -c "import json; 
print(json.load(open('/etc/ranger/dev_ozone/policycache/ozone_dev_ozone_roles.json')).get('roleVersion',-1))"
 2>/dev/null || echo "-1")
+  echo "  policycache policyVersion=${pv} roleVersion=${rv}"
+  if [ "${pv}" != "-1" ] && [ "${pv}" != "?" ]; then
+    echo "PASS: policies downloaded (policyVersion=${pv})"
+  else
+    echo "FAIL: policyVersion still -1"
+    return 1
+  fi
+  if [ "${rv}" != "-1" ]; then
+    echo "PASS: roles downloaded (roleVersion=${rv})"
+  else
+    echo "WARN: roleVersion=-1 (role resolution may be disabled in plugin 
options)"
+  fi
+  if [ "${SKIP_POLICY_CREATE}" != "true" ]; then
+    if docker exec ozone-om grep -q "${TEST_POLICY_NAME}" 
/etc/ranger/dev_ozone/policycache/ozone_dev_ozone.json 2>/dev/null; then
+      echo "PASS: test policy in plugin policycache"
+    else
+      echo "WARN: test policy not yet in policycache (may need another poll 
cycle)"
+    fi
+  fi
+}
+
+wait_for_om_kerberos() {
+  echo "== Waiting for OM Kerberos login =="
+  local i om_logs
+  for i in $(seq 1 60); do
+    om_logs="$(docker logs ozone-om 2>&1 || true)"
+    if grep -qE 'Login successful for user om/|Ozone Manager login successful' 
<<< "${om_logs}"; then
+      echo "PASS: OM ready (${i}*5s)"
+      return 0
+    fi
+    if grep -q 'Waiting for SCM' <<< "${om_logs}" && [ "${i}" -lt 60 ]; then
+      sleep 5
+      continue
+    fi
+    sleep 5
+  done
+  echo "FAIL: OM Kerberos login timed out"
+  docker logs ozone-om 2>&1 | tail -15
+  return 1
+}
+
+trigger_ozone_audit() {
+  echo "== Trigger policy-enforced Ozone audit (hdfs -> OM -> ingestor) =="
+  local audit_vol="${TEST_VOLUME}" audit_bucket="${AUDIT_BUCKET:-audit-bucket}"
+  docker exec ozone-om bash -c "
+    set -e
+    export KRB5_CONFIG=/etc/krb5.conf
+    kinit -kt /etc/keytabs/om.keytab om/[email protected]
+    /opt/hadoop/bin/ozone sh volume create ${audit_vol} 2>/dev/null || true
+    /opt/hadoop/bin/ozone sh bucket create ${audit_vol}/${audit_bucket} 
2>/dev/null || true
+  " || echo "WARN: om setup for audit volume failed"
+
+  docker cp ranger-hadoop:/etc/keytabs/hdfs.keytab /tmp/ozone-audit-hdfs.keytab
+  docker cp /tmp/ozone-audit-hdfs.keytab ozone-om:/tmp/hdfs.keytab
+  docker exec ozone-om bash -c "
+    set -e
+    export KRB5_CONFIG=/etc/krb5.conf
+    kinit -kt /tmp/hdfs.keytab hdfs/[email protected]
+    /opt/hadoop/bin/ozone sh bucket info ${audit_vol}/${audit_bucket}
+    /opt/hadoop/bin/ozone sh key list ${audit_vol}/${audit_bucket} 2>/dev/null 
|| true
+    /opt/hadoop/bin/ozone sh volume info ${audit_vol}
+  " || echo "WARN: hdfs ozone operation failed (check policy + OM health)"
+  rm -f /tmp/ozone-audit-hdfs.keytab
+}
+
+verify_ozone_audit_pipeline() {
+  echo "== Ozone audit pipeline (ingestor -> kafka -> solr dispatcher -> solr) 
=="
+  local found=0 i
+  for i in $(seq 1 18); do
+    if docker logs ranger-audit-ingestor 2>&1 | tail -200 | grep -qiE 
"dev_ozone|${TEST_VOLUME}|${AUDIT_MARKER}"; then
+      found=1
+      break
+    fi
+    sleep 5
+  done
+  if [ "${found}" = "1" ]; then
+    echo "PASS: audit-ingestor received dev_ozone traffic"
+  else
+    echo "WARN: no dev_ozone lines in ingestor logs yet"
+  fi
+  if docker exec ranger-kafka /opt/kafka/bin/kafka-console-consumer.sh \
+      --bootstrap-server localhost:9092 \
+      --topic ranger_audits \
+      --from-beginning \
+      --max-messages 3 \
+      --timeout-ms 15000 2>/dev/null | grep -q 'dev_ozone'; then
+    echo "PASS: dev_ozone audit events on Kafka topic ranger_audits"
+  else
+    echo "WARN: could not read dev_ozone from Kafka (Kerberos/SASL may block 
console consumer)"
+  fi
+  if docker logs ranger-audit-dispatcher-solr 2>&1 | tail -100 | grep -qi 
'dev_ozone'; then
+    echo "PASS: solr dispatcher processed dev_ozone audits"
+  else
+    echo "WARN: no dev_ozone in solr dispatcher logs yet"
+  fi
+  if docker exec ranger-solr curl -sf 
"http://localhost:8983/solr/ranger_audits/select?q=repo:dev_ozone&rows=1&sort=evtTime+desc&wt=json";
 \
+      | python3 -c "import sys,json; d=json.load(sys.stdin); 
n=d.get('response',{}).get('numFound',0); print(n); exit(0 if n>0 else 1)" 
2>/dev/null; then
+    echo "PASS: dev_ozone audits indexed in Solr"
+  else
+    echo "WARN: no dev_ozone audits found in Solr yet"
+  fi
+  if docker exec ranger curl -sf -u admin:rangerR0cks! \
+      "http://localhost:6080/service/plugins/policies/service/dev_ozone"; 
>/dev/null; then
+    echo "PASS: Ranger Admin still reachable after audit test"
+  else
+    echo "WARN: Ranger Admin policy API check failed"
+  fi
+}
+
+verify_admin_audit_untouched() {
+  echo "== Admin + audit-ingestor health =="
+  local ai_status ranger_status
+  ai_status=$(docker inspect ranger-audit-ingestor --format '{{if 
.State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' 
2>/dev/null || echo missing)
+  ranger_status=$(docker inspect ranger --format '{{.State.Status}}' 
2>/dev/null || echo missing)
+  echo "  ranger: ${ranger_status}"
+  echo "  ranger-audit-ingestor: ${ai_status}"
+  [ "${ranger_status}" = "running" ] || die "ranger container not running"
+}
+
+cleanup_test_artifacts() {
+  # shellcheck source=/dev/null
+  [ -f "${STATE_FILE}" ] && source "${STATE_FILE}" || true
+  if [ -n "${TEST_POLICY_ID:-}" ] && [ "${TEST_POLICY_ID}" != "" ]; then
+    docker exec ranger curl -s -u admin:rangerR0cks! -X DELETE \
+      "http://localhost:6080/service/public/v2/api/policy/${TEST_POLICY_ID}"; 
>/dev/null || true
+  fi
+  if [ -n "${TEST_ROLE_ID:-}" ] && [ "${TEST_ROLE_ID}" != "" ]; then
+    docker exec ranger curl -s -u admin:rangerR0cks! -X DELETE \
+      "http://localhost:6080/service/public/v2/api/roles/${TEST_ROLE_ID}"; 
>/dev/null || true
+  fi
+}
+
+cmd_apply() {
+  require_stack
+  require_pr_files
+  backup_stack_files
+  save_state
+  apply_pr_files
+  ensure_ozone_keytabs
+  recreate_ozone_only
+  wait_for_om_kerberos
+  verify_admin_audit_untouched
+  echo "Apply complete. Run: $0 verify"
+}
+
+cmd_verify() {
+  require_stack
+  load_state
+  wait_for_om_kerberos || true
+  verify_kdc_fix
+  create_test_policy_and_role
+  verify_plugin_policy_pull
+  verify_admin_audit_untouched
+  echo ""
+  echo "Policy/role verify complete."
+}
+
+cmd_audit_e2e() {
+  require_stack
+  load_state
+  restart_ingestor_static_kerberos
+  trigger_ozone_audit
+  verify_ozone_audit_pipeline
+  if [ "${REVERT_INGESTOR_AFTER_AUDIT}" = "true" ]; then
+    restart_ingestor_dynamic_spiffe
+    docker exec ranger-audit-ingestor python3 -c "
+import xml.etree.ElementTree as ET
+r=ET.parse('/opt/ranger/audit-ingestor/conf/ranger-audit-ingestor-site.xml').getroot()
+for n in 
['ranger.audit.ingestor.partition.plan.admin.enabled','ranger.audit.ingestor.authn.header.enabled']:
+  for p in r.findall('property'):
+    if p.find('name') is not None and p.find('name').text==n:
+      print('restored', n,'=',p.find('value').text)
+"
+  fi
+  echo ""
+  echo "Audit E2E phase complete."
+}
+
+cmd_revert_ingestor() {
+  restart_ingestor_dynamic_spiffe
+}
+
+cmd_revert() {
+  require_stack
+  load_state
+  [ -d "${BACKUP_DIR}" ] || die "backup missing: ${BACKUP_DIR}"
+  echo "Restoring ozone files from ${BACKUP_DIR}"
+  cp "${BACKUP_DIR}/docker-compose.ranger-ozone.yml" "${RANGER_DOCKER_STACK}/"
+  [ -f "${BACKUP_DIR}/Dockerfile.ranger-ozone" ] && \
+    cp "${BACKUP_DIR}/Dockerfile.ranger-ozone" "${RANGER_DOCKER_STACK}/"
+  cp "${BACKUP_DIR}/scripts/ozone/docker-config" 
"${RANGER_DOCKER_STACK}/scripts/ozone/"
+  cp "${BACKUP_DIR}/scripts/ozone/ranger-ozone-setup.sh" 
"${RANGER_DOCKER_STACK}/scripts/ozone/"
+  cp "${BACKUP_DIR}/scripts/ozone/ranger-ozone-plugin-install.properties" 
"${RANGER_DOCKER_STACK}/scripts/ozone/"
+  cleanup_test_artifacts
+  recreate_ozone_only
+  rm -rf "${STATE_DIR}"
+  verify_admin_audit_untouched
+  echo "Revert complete."
+}
+
+cmd_run() {
+  cmd_apply
+  cmd_verify
+  if [ "${RUN_AUDIT_E2E}" = "true" ]; then
+    cmd_audit_e2e
+  fi
+  if [ "${REVERT_AFTER_TEST}" = "true" ]; then
+    cmd_revert
+  else
+    echo ""
+    echo "PR ozone changes left applied. Revert with: $0 revert"
+  fi
+}
+
+main() {
+  local cmd="${1:-}"
+  case "${cmd}" in
+    apply)            cmd_apply ;;
+    verify)           cmd_verify ;;
+    audit-e2e)        cmd_audit_e2e ;;
+    revert-ingestor)  cmd_revert_ingestor ;;
+    revert)           cmd_revert ;;
+    run)              cmd_run ;;
+    -h|--help|help|"") usage ;;
+    *) die "unknown command: ${cmd}" ;;
+  esac
+}
+
+main "$@"
diff --git a/plugin-ozone/conf/ranger-ozone-audit-changes.cfg 
b/plugin-ozone/conf/ranger-ozone-audit-changes.cfg
index 619f48d77..9edf9175d 100644
--- a/plugin-ozone/conf/ranger-ozone-audit-changes.cfg
+++ b/plugin-ozone/conf/ranger-ozone-audit-changes.cfg
@@ -87,3 +87,5 @@ xasecure.audit.destination.log4j.logger                
%XAAUDIT.LOG4J.DESTINATIO
 xasecure.audit.destination.auditserver                     
%XAAUDIT.AUDITSERVER.ENABLE%          mod create-if-not-exists
 xasecure.audit.destination.auditserver.url                 
%XAAUDIT.AUDITSERVER.URL%             mod create-if-not-exists
 xasecure.audit.destination.auditserver.batch.filespool.dir 
%XAAUDIT.AUDITSERVER.FILE_SPOOL_DIR%  mod create-if-not-exists
+# Audit ingestor SPIFFE header config prefix (optional explicit binding)
+xasecure.audit.destination.auditserver.authn.header.config.prefix ranger.ozone 
mod create-if-not-exists
diff --git a/plugin-ozone/conf/ranger-ozone-security-changes.cfg 
b/plugin-ozone/conf/ranger-ozone-security-changes.cfg
index 30eae3ec9..41713dada 100644
--- a/plugin-ozone/conf/ranger-ozone-security-changes.cfg
+++ b/plugin-ozone/conf/ranger-ozone-security-changes.cfg
@@ -25,4 +25,11 @@ ranger.plugin.ozone.policy.rest.ssl.config.file    
%COMPONENT_INSTALL_DIR_NAME%/
 ranger.plugin.ozone.policy.pollIntervalMs          30000                       
              mod create-if-not-exists
 ranger.plugin.ozone.policy.cache.dir               %POLICY_CACHE_FILE_PATH%    
              mod create-if-not-exists
 ranger.policy.rest.client.connection.timeoutMs    120000                       
                                                 mod create-if-not-exists
-ranger.policy.rest.client.read.timeoutMs                  30000                
                                                         mod 
create-if-not-exists
\ No newline at end of file
+ranger.policy.rest.client.read.timeoutMs                  30000                
                                                         mod 
create-if-not-exists
+#
+# Audit ingestor SPIFFE header auth (see XAAUDIT.AUDITSERVER.AUTHN.SPIFFE.* in 
install.properties)
+#
+ranger.ozone.authn.header.enabled %XAAUDIT.AUDITSERVER.AUTHN.SPIFFE.ENABLED% 
mod create-if-not-exists
+ranger.ozone.authn.header.spiffe %XAAUDIT.AUDITSERVER.AUTHN.SPIFFE.HEADER% mod 
create-if-not-exists
+ranger.ozone.authn.spiffe.value %XAAUDIT.AUDITSERVER.AUTHN.SPIFFE.VALUE% mod 
create-if-not-exists
+ranger.ozone.authn.spiffe.file %XAAUDIT.AUDITSERVER.AUTHN.SPIFFE.FILE% mod 
create-if-not-exists
\ No newline at end of file

Reply via email to