172 lines
8.2 KiB
Bash
172 lines
8.2 KiB
Bash
#!/usr/bin/env bash
|
|||
|
|
# =============================================================================
|
||
|
|
# eBdesk SiCABE — Cek konsistensi keystore TLS Trino + password.db ANTAR-HOST
|
||
|
|
#
|
||
|
|
# Dua hal yang WAJIB benar agar klien (Great Expectations / OpenMetadata / dbt)
|
||
|
|
# bisa bicara ke Trino:
|
||
|
|
# 1) keystore TLS (generated/host-<nama>/config/trino/keystore.p12) IDENTIK di
|
||
|
|
# semua host → hasil SATU `--generate` + `--sync`.
|
||
|
|
# 2) password.db punya service account (svc_dbt) & cocok dgn TRINO_SVC_DBT_PASSWORD
|
||
|
|
# di bundle .env (kalau tidak → 401 "Access Denied: Invalid credentials").
|
||
|
|
# Catatan: bcrypt memakai salt acak → berkas password.db TIDAK dibandingkan
|
||
|
|
# byte-per-byte antar-host, melainkan DIVERIFIKASI thd password yang diharapkan.
|
||
|
|
#
|
||
|
|
# Pemakaian:
|
||
|
|
# ./scripts/check-trino-keystore.sh # host ini saja
|
||
|
|
# ./scripts/check-trino-keystore.sh --inventory cluster.p3.yml
|
||
|
|
# ./scripts/check-trino-keystore.sh --expect <sha256-keystore> # CI (exit 1 bila beda)
|
||
|
|
#
|
||
|
|
# Opsi: --host <nama> (paksa host ini) · --user <u> · --dest <dir-remote>
|
||
|
|
# default: user $USER, dest = cermin lokasi repo ini di bawah $HOME
|
||
|
|
# (mis. /home/u/sicabe-core/deploy → ~/sicabe-core/deploy)
|
||
|
|
#
|
||
|
|
# Keluar: 0 = semua OK · 1 = ada BEDA/MISSING · 2 = error (berkas/inventory tak ada)
|
||
|
|
# =============================================================================
|
||
|
|
set -uo pipefail
|
||
|
|
|
||
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||
|
|
cd "${SCRIPT_DIR}"
|
||
|
|
# shellcheck disable=SC1091
|
||
|
|
source "${SCRIPT_DIR}/lib/common.sh" 2>/dev/null || true
|
||
|
|
source "${SCRIPT_DIR}/config/environment.defaults.sh" 2>/dev/null || true
|
||
|
|
if [[ -f "${SCRIPT_DIR}/.env" ]]; then set -a; source "${SCRIPT_DIR}/.env"; set +a; fi
|
||
|
|
|
||
|
|
INV=""; SSH_USER="${USER:-root}"; EXPECT=""; FORCE_HOST=""
|
||
|
|
# Tujuan remote default = cermin lokasi repo ini di bawah $HOME (bebas nama repo:
|
||
|
|
# `sicabe-core` → ~/sicabe-core/deploy). Fallback bila lib/common.sh gagal dimuat.
|
||
|
|
if declare -F sicabe_default_dest >/dev/null 2>&1; then
|
||
|
|
DEST="$(sicabe_default_dest "${SCRIPT_DIR}")"
|
||
|
|
else
|
||
|
|
DEST="~/$(basename "$(dirname "${SCRIPT_DIR}")")/deploy"
|
||
|
|
fi
|
||
|
|
while [[ $# -gt 0 ]]; do
|
||
|
|
case "$1" in
|
||
|
|
--inventory) INV="${2:-}"; shift 2 ;;
|
||
|
|
--user) SSH_USER="${2:-}"; shift 2 ;;
|
||
|
|
--dest) DEST="${2:-}"; shift 2 ;;
|
||
|
|
--expect) EXPECT="${2:-}"; shift 2 ;;
|
||
|
|
--host) FORCE_HOST="${2:-}"; shift 2 ;;
|
||
|
|
-h|--help) sed -n '2,24p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
||
|
|
*) echo "Argumen tidak dikenal: $1" >&2; exit 2 ;;
|
||
|
|
esac
|
||
|
|
done
|
||
|
|
|
||
|
|
C_RED=$'\033[31m'; C_GRN=$'\033[32m'; C_YEL=$'\033[33m'; C_BLD=$'\033[1m'; C_RST=$'\033[0m'
|
||
|
|
|
||
|
|
# ── keystore ─────────────────────────────────────────────────────────────────
|
||
|
|
ks_path() { echo "${SCRIPT_DIR}/generated/host-$1/config/trino/keystore.p12"; }
|
||
|
|
ks_sha() { sha256sum "$1" 2>/dev/null | cut -d' ' -f1; }
|
||
|
|
ks_pw() { sed -n 's/^http-server.https.keystore.key=//p' \
|
||
|
|
"${SCRIPT_DIR}/generated/host-$1/config/trino/config-coordinator.properties" 2>/dev/null | head -1; }
|
||
|
|
ks_fp() { openssl pkcs12 -in "$(ks_path "$1")" -nokeys -passin "pass:$(ks_pw "$1")" 2>/dev/null \
|
||
|
|
| openssl x509 -noout -fingerprint -sha256 2>/dev/null | sed 's/^sha256 Fingerprint=//'; }
|
||
|
|
|
||
|
|
# ── password.db (service account) ────────────────────────────────────────────
|
||
|
|
pw_path() { echo "${SCRIPT_DIR}/generated/host-$1/config/trino/password.db"; }
|
||
|
|
pw_expect() { sed -n 's/^TRINO_SVC_DBT_PASSWORD=//p' \
|
||
|
|
"${SCRIPT_DIR}/generated/host-$1/.env" 2>/dev/null | head -1; }
|
||
|
|
# Status lokal: NOFILE / MISSING / PWBAD / OK
|
||
|
|
pw_verify() { # <host>
|
||
|
|
local f exp; f="$(pw_path "$1")"
|
||
|
|
[[ -f "${f}" ]] || { echo NOFILE; return; }
|
||
|
|
grep -q '^svc_dbt:' "${f}" 2>/dev/null || { echo MISSING; return; }
|
||
|
|
exp="$(pw_expect "$1")"
|
||
|
|
if command -v htpasswd >/dev/null 2>&1 && [[ -n "${exp}" ]]; then
|
||
|
|
htpasswd -vb "${f}" svc_dbt "${exp}" >/dev/null 2>&1 && echo OK || echo PWBAD
|
||
|
|
else
|
||
|
|
echo OK # ada, tapi tak bisa diverifikasi (htpasswd/pw tak ada)
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
# ── host ini ─────────────────────────────────────────────────────────────────
|
||
|
|
detect_self() {
|
||
|
|
local lip d addr; lip="$(hostname -I 2>/dev/null || true)"
|
||
|
|
for d in "${SCRIPT_DIR}"/generated/host-*; do
|
||
|
|
[[ -f "${d}/.env" ]] || continue
|
||
|
|
addr="$(sed -n 's/^SICABE_HOST_ADDRESS=//p' "${d}/.env" | head -1)"
|
||
|
|
[[ -n "${addr}" && " ${lip} " == *" ${addr} "* ]] && { basename "${d}" | sed 's/^host-//'; return 0; }
|
||
|
|
done
|
||
|
|
return 1
|
||
|
|
}
|
||
|
|
|
||
|
|
SELF="${FORCE_HOST}"
|
||
|
|
[[ -z "${SELF}" ]] && SELF="$(detect_self || true)"
|
||
|
|
if [[ -z "${SELF}" ]]; then
|
||
|
|
mapfile -t _dirs < <(ls -d "${SCRIPT_DIR}"/generated/host-* 2>/dev/null)
|
||
|
|
[[ ${#_dirs[@]} -eq 1 ]] && SELF="$(basename "${_dirs[0]}" | sed 's/^host-//')"
|
||
|
|
fi
|
||
|
|
if [[ -z "${SELF}" || ! -f "$(ks_path "${SELF}")" ]]; then
|
||
|
|
echo "${C_RED}Keystore host ini tak ditemukan.${C_RST} Jalankan ./install.sh --generate dulu, atau pakai --host <nama>." >&2
|
||
|
|
exit 2
|
||
|
|
fi
|
||
|
|
|
||
|
|
SELF_SHA="$(ks_sha "$(ks_path "${SELF}")")"
|
||
|
|
EXPECT_PW="$(pw_expect "${SELF}")"
|
||
|
|
echo "${C_BLD}Cek konsistensi keystore + password.db Trino${C_RST}"
|
||
|
|
echo " host ini : ${SELF}"
|
||
|
|
echo " keystore : sha256=${SELF_SHA}"
|
||
|
|
echo " cert : $(ks_fp "${SELF}")"
|
||
|
|
echo " pw svc_dbt: $(pw_verify "${SELF}") (OK/MISSING/PWBAD/NOFILE)"
|
||
|
|
echo ""
|
||
|
|
|
||
|
|
if [[ -n "${EXPECT}" ]]; then
|
||
|
|
if [[ "${SELF_SHA}" == "${EXPECT}" ]]; then
|
||
|
|
echo "${C_GRN}✓ sha256 keystore cocok dengan --expect.${C_RST}"; exit 0
|
||
|
|
fi
|
||
|
|
echo "${C_RED}✗ sha256 keystore BERBEDA dari --expect (${EXPECT}).${C_RST}"; exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [[ -z "${INV}" ]]; then
|
||
|
|
echo " (tanpa --inventory — bandingkan di tiap host, atau pakai --inventory)"
|
||
|
|
exit 0
|
||
|
|
fi
|
||
|
|
|
||
|
|
[[ -f "${INV}" ]] || { echo "${C_RED}Inventory tak ada: ${INV}${C_RST}" >&2; exit 2; }
|
||
|
|
PAIRS="$(python3 "${SCRIPT_DIR}/lib/cluster/inventory.py" hosts "${INV}" 2>/dev/null)" \
|
||
|
|
|| { echo "${C_RED}Gagal membaca inventory: ${INV}${C_RST}" >&2; exit 2; }
|
||
|
|
|
||
|
|
# Verifikasi password.db di host LAIN via SSH (bcrypt salt acak → verifikasi, bukan hash).
|
||
|
|
remote_pw_status() { # <name> <addr>
|
||
|
|
ssh -o ConnectTimeout=8 -o BatchMode=yes "${SSH_USER}@${2}" \
|
||
|
|
"f='${DEST}/generated/host-${1}/config/trino/password.db'
|
||
|
|
[ -f \"\$f\" ] || { echo NOFILE; exit 0; }
|
||
|
|
grep -q '^svc_dbt:' \"\$f\" || { echo MISSING; exit 0; }
|
||
|
|
if command -v htpasswd >/dev/null 2>&1; then htpasswd -vb \"\$f\" svc_dbt '${EXPECT_PW}' >/dev/null 2>&1 && echo OK || echo PWBAD; else echo OK; fi" \
|
||
|
|
2>/dev/null || echo SSHFAIL
|
||
|
|
}
|
||
|
|
pw_icon() { case "$1" in OK) printf '%s%s%s' "${C_GRN}" "$1" "${C_RST}" ;; *) printf '%s%s%s' "${C_RED}" "$1" "${C_RST}" ;; esac; }
|
||
|
|
|
||
|
|
mismatch=0; checked=0
|
||
|
|
while read -r name addr; do
|
||
|
|
[[ -z "${name}" ]] && continue
|
||
|
|
if [[ "${name}" == "${SELF}" ]]; then
|
||
|
|
_ksok=1; _pw="$(pw_verify "${SELF}")"
|
||
|
|
else
|
||
|
|
_rsha="$(ssh -o ConnectTimeout=8 -o BatchMode=yes "${SSH_USER}@${addr}" \
|
||
|
|
"sha256sum ${DEST}/generated/host-${name}/config/trino/keystore.p12 2>/dev/null | cut -d' ' -f1" 2>/dev/null || true)"
|
||
|
|
if [[ -z "${_rsha}" ]]; then _ksok=-1; elif [[ "${_rsha}" == "${SELF_SHA}" ]]; then _ksok=1; else _ksok=0; fi
|
||
|
|
_pw="$(remote_pw_status "${name}" "${addr}")"
|
||
|
|
fi
|
||
|
|
_kslabel="(host ini)"; _kscol=""
|
||
|
|
case "${_ksok}" in
|
||
|
|
1) [[ "${name}" != "${SELF}" ]] && { _kslabel="keystore ✓"; _kscol="${C_GRN}"; } ;;
|
||
|
|
0) _kslabel="keystore ✗ BEDA"; _kscol="${C_RED}"; mismatch=1 ;;
|
||
|
|
-1) _kslabel="keystore ? ssh gagal"; _kscol="${C_YEL}"; mismatch=1 ;;
|
||
|
|
esac
|
||
|
|
[[ "${_pw}" == "OK" ]] || mismatch=1
|
||
|
|
printf ' %-9s %-15s %s%-16s%s pw svc_dbt %s\n' \
|
||
|
|
"${name}" "${addr}" "${_kscol}" "${_kslabel}" "${C_RST}" "$(pw_icon "${_pw}")"
|
||
|
|
checked=$((checked + 1))
|
||
|
|
done <<< "${PAIRS}"
|
||
|
|
|
||
|
|
echo ""
|
||
|
|
if [[ ${mismatch} -eq 0 ]]; then
|
||
|
|
echo "${C_GRN}✓ keystore identik + password.db OK di semua host (${checked} host).${C_RST}"
|
||
|
|
exit 0
|
||
|
|
fi
|
||
|
|
echo "${C_RED}✗ Ada ketidakcocokan (keystore BEDA / password.db MISSING/PWBAD).${C_RST}"
|
||
|
|
echo " Perbaiki (di SATU host — control):"
|
||
|
|
echo " ./install.sh --generate --inventory ${INV} && ./install.sh --sync"
|
||
|
|
echo " lalu di tiap host: ./install.sh --host <nama> (atau recreate Trino & Airflow)"
|
||
|
|
exit 1
|