diff --git a/scripts/export-trino-ca.sh b/scripts/export-trino-ca.sh new file mode 100755 index 0000000..8740a95 --- /dev/null +++ b/scripts/export-trino-ca.sh @@ -0,0 +1,125 @@ +#!/usr/bin/env bash +# ============================================================================= +# export-trino-ca.sh — segarkan pin CA TLS Trino milik panel. +# +# Trino menyajikan sertifikat **self-signed** dari `keystore.p12` +# (http-server.https.keystore.*). Setiap kali keystore diregenerasi — install.sh, +# generate-trino-keystore.sh, atau container Trino dibuat ulang — cert-nya +# berubah, sehingga pin CA panel (`.ca/trino.crt`) jadi basi dan SEMUA panggilan +# Trino HTTPS gagal dengan: +# DEPTH_ZERO_SELF_SIGNED_CERT +# → /api/query mengembalikan 500 "fetch failed" +# → kartu "Data Catalog" di SQL IDE tampil "gagal load catalog" +# +# Script ini mengekspor cert Trino yang berlaku ke file pin, dengan backup, dan +# idempoten (tidak menulis bila sudah sinkron). +# +# Sumber cert: +# 1. endpoint live (default) — via `openssl s_client`; paling akurat karena +# persis cert yang harus dipercaya panel. +# 2. keystore.p12 — set TRINO_KEYSTORE (berguna saat endpoint belum hidup). +# +# Config via env (default cocok dgn deploy sicabe-doc & panel .env): +# TRINO_CA_HOST host:port Trino HTTPS (default: localhost:12443) +# TRINO_CA_SNI SNI server name (default: host dari TRINO_CA_HOST) +# TRINO_CA_FILE file pin CA panel (default: /.ca/trino.crt) +# TRINO_KEYSTORE path keystore.p12 (opsional; bila di-set, dipakai) +# TRINO_KEYSTORE_PASS password keystore (default: trino_ssl_dev) +# +# Contoh: +# ./scripts/export-trino-ca.sh +# TRINO_CA_HOST=trino.example.com:12443 ./scripts/export-trino-ca.sh +# TRINO_KEYSTORE=/path/sicabe-doc/deploy/config/trino/keystore.p12 \ +# ./scripts/export-trino-ca.sh +# +# Setelah dijalankan: RESTART panel (koneksi TLS di-memoize per proses). Agent +# config tidak perlu restart — ia membaca file ini setiap request. +# ============================================================================= +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +TRINO_CA_HOST="${TRINO_CA_HOST:-localhost:12443}" +TRINO_CA_SNI="${TRINO_CA_SNI:-${TRINO_CA_HOST%%:*}}" +TRINO_CA_FILE="${TRINO_CA_FILE:-$REPO_ROOT/.ca/trino.crt}" +TRINO_KEYSTORE="${TRINO_KEYSTORE:-}" +TRINO_KEYSTORE_PASS="${TRINO_KEYSTORE_PASS:-trino_ssl_dev}" + +command -v openssl >/dev/null 2>&1 || { echo "ERROR: openssl tidak ditemukan di PATH" >&2; exit 1; } + +TMP="$(mktemp)" # cert hasil ekspor +LIVE="$(mktemp)" # cert dari endpoint live (untuk cross-check) +trap 'rm -f "$TMP" "$LIVE"' EXIT + +is_pem_cert() { [ -s "$1" ] && grep -q -- "-----BEGIN CERTIFICATE-----" "$1"; } +fp_of() { openssl x509 -in "$1" -noout -fingerprint -sha256 | cut -d= -f2 | tr -d ':'; } + +# ── 1. Ambil cert ──────────────────────────────────────────────────────────── +if [ -n "$TRINO_KEYSTORE" ]; then + [ -f "$TRINO_KEYSTORE" ] || { echo "ERROR: keystore tidak ditemukan: $TRINO_KEYSTORE" >&2; exit 1; } + echo "→ Mengekspor cert dari keystore: $TRINO_KEYSTORE" + # -legacy diperlukan OpenSSL 3 untuk PKCS#12 lama (RC2-40). Bila tidak + # didukung/berbeda, coba tanpa flag tersebut. + if ! openssl pkcs12 -in "$TRINO_KEYSTORE" -nokeys -clcerts \ + -passin "pass:$TRINO_KEYSTORE_PASS" -legacy 2>/dev/null \ + | openssl x509 -outform PEM > "$TMP" 2>/dev/null || ! is_pem_cert "$TMP"; then + openssl pkcs12 -in "$TRINO_KEYSTORE" -nokeys -clcerts \ + -passin "pass:$TRINO_KEYSTORE_PASS" 2>/dev/null \ + | openssl x509 -outform PEM > "$TMP" 2>/dev/null || true + fi +else + echo "→ Mengambil cert dari endpoint live: https://$TRINO_CA_HOST" + echo | openssl s_client -connect "$TRINO_CA_HOST" -servername "$TRINO_CA_SNI" 2>/dev/null \ + | openssl x509 -outform PEM > "$TMP" 2>/dev/null || true +fi + +if ! is_pem_cert "$TMP"; then + echo "ERROR: gagal mengekspor sertifikat (sumber: ${TRINO_KEYSTORE:-https://$TRINO_CA_HOST})" >&2 + [ -z "$TRINO_KEYSTORE" ] && echo " Pastikan Trino hidup & port HTTPS benar (TRINO_CA_HOST)." >&2 + exit 1 +fi + +NEW_FP="$(fp_of "$TMP")" +echo " subject : $(openssl x509 -in "$TMP" -noout -subject | sed 's/^subject=//')" +echo " sha256 : $NEW_FP" + +# ── 2. Tulisan hanya bila berubah (idempoten) ──────────────────────────────── +if [ -f "$TRINO_CA_FILE" ] && [ "$(fp_of "$TRINO_CA_FILE")" = "$NEW_FP" ]; then + echo "✓ $TRINO_CA_FILE sudah sinkron — tidak ada perubahan." + exit 0 +fi + +mkdir -p "$(dirname "$TRINO_CA_FILE")" +if [ -f "$TRINO_CA_FILE" ]; then + BAK="${TRINO_CA_FILE}.bak-$(date +%Y%m%d-%H%M%S)" + cp -p "$TRINO_CA_FILE" "$BAK" + echo "→ Backup pin lama: $BAK" + echo " (fingerprint lama: $(fp_of "$BAK"))" +fi +install -m 600 "$TMP" "$TRINO_CA_FILE" +echo "✓ Pin CA diperbarui: $TRINO_CA_FILE" + +# ── 3. Cross-check dengan endpoint live (bila sumbernya keystore) ──────────── +if [ -n "$TRINO_KEYSTORE" ]; then + if echo | openssl s_client -connect "$TRINO_CA_HOST" -servername "$TRINO_CA_SNI" 2>/dev/null \ + | openssl x509 -outform PEM > "$LIVE" 2>/dev/null && is_pem_cert "$LIVE"; then + if [ "$(fp_of "$LIVE")" = "$NEW_FP" ]; then + echo "✓ Cocok dengan cert yang disajikan https://$TRINO_CA_HOST" + else + echo "WARN: cert keystore BERBEDA dari yang disajikan https://$TRINO_CA_HOST" >&2 + echo " Restart Trino, lalu jalankan ulang script ini." >&2 + exit 1 + fi + else + echo "! https://$TRINO_CA_HOST tidak bisa dihubungi — cross-check dilewati." + fi +fi + +cat <<'EOF' + +Langkah berikutnya: + • RESTART panel (Next.js) — koneksi TLS di-memoize per proses: + kill $(ss -ltnp | grep ':3000' | grep -oE 'pid=[0-9]+' | cut -d= -f2) && npm run dev + • Agent config TIDAK perlu restart (baca file ini setiap request). + • Uji cepat: POST /api/query {"query":"SHOW CATALOGS"} → harus 200. +EOF