#!/usr/bin/env bash # #region docker.certs [C:3] [TYPE Module] [SEMANTICS docker,certs,ssl,ca,trust] # @BRIEF Shared certificate installation functions for all containers. # @LAYER Infrastructure # @PRE CERTS_PATH points to mounted /opt/certs directory (may be empty or absent). # @POST Corporate CA certificates installed into system trust store and NSS DB. # @INVARIANT server.crt, server.key, server.p12, *.key, *.p12, *.pfx are never imported as CA. set -euo pipefail # ── normalize_cert – PEM/DER detection + conversion ────────────────── normalize_cert() { local input="$1" local output="$2" if openssl x509 -in "$input" -inform PEM -noout 2>/dev/null; then cp "$input" "$output" return 0 fi if openssl x509 -in "$input" -inform DER -noout 2>/dev/null; then openssl x509 -in "$input" -inform DER -out "$output" -outform PEM 2>/dev/null return 0 fi return 1 } # ── install_all_certs – single unified entry point ─────────────────── install_all_certs() { local certs_dir="${CERTS_PATH:-/opt/certs}" if [ ! -d "$certs_dir" ]; then echo "[certs] CERTS_PATH=${certs_dir} не существует – пропускаем" return 0 fi local target="/usr/local/share/ca-certificates/custom" mkdir -p "$target" echo "[certs] Устанавливаем корпоративные сертификаты из ${certs_dir}..." local installed=0 skipped=0 invalid=0 for f in "$certs_dir"/*; do [ -f "$f" ] || continue local name name="$(basename "$f")" # Skip non-CA server/private files case "$(echo "$name" | tr '[:upper:]' '[:lower:]')" in server.crt|server.key|server.pem|*.key|*.p12|*.pfx) echo "[certs] ⏭️ пропускаем: ${name} (server/private файл)" skipped=$((skipped + 1)) continue ;; esac # Accept .crt, .pem, .cer, .der case "$(echo "$name" | tr '[:upper:]' '[:lower:]')" in *.crt|*.pem|*.cer|*.der) # valid cert extension ;; *) echo "[certs] ⚠️ неизвестный формат: ${name} – пропускаем" skipped=$((skipped + 1)) continue ;; esac local out_name="${name%.*}.crt" local out_file="${target}/${out_name}" if normalize_cert "$f" "$out_file"; then echo "[certs] ✅ ${name} (→ ${out_name})" installed=$((installed + 1)) else echo "[certs] ❌ невалидный сертификат: ${name}" invalid=$((invalid + 1)) fi done if [ "$installed" -eq 0 ]; then echo "[certs] Нет CA-сертификатов для установки" return 0 fi echo "[certs] Установлено: ${installed}, пропущено: ${skipped}, невалидно: ${invalid}" # Update system CA store if command -v update-ca-certificates &>/dev/null; then echo "[certs] Обновляем системное хранилище CA-сертификатов..." update-ca-certificates --fresh 2>&1 | sed 's/^/[certs] /' fi # Create hash symlinks for OpenSSL capath echo "[certs] Создаём хеш-симлинки..." local sym_count=0 for cert_file in "$target"/*.crt; do [ -f "$cert_file" ] || continue local cert_name cert_name="$(basename "$cert_file" .crt)" local hash_val hash_val="$(openssl x509 -in "$cert_file" -noout -hash 2>/dev/null || true)" if [ -n "$hash_val" ]; then local suffix=0 local link_path="/etc/ssl/certs/${hash_val}.${suffix}" while [ -L "$link_path" ]; do if [ "$(readlink "$link_path")" = "$cert_file" ]; then break 2 fi suffix=$((suffix + 1)) link_path="/etc/ssl/certs/${hash_val}.${suffix}" done if [ ! -L "$link_path" ]; then ln -sf "$cert_file" "$link_path" sym_count=$((sym_count + 1)) fi fi done echo "[certs] Создано хеш-симлинок: ${sym_count}" } # ── install_ca_to_nss – NSS DB for Chromium/Playwright ────────────── install_ca_to_nss() { if ! command -v certutil &>/dev/null; then return 0 fi local target="/usr/local/share/ca-certificates/custom" if [ ! -d "$target" ] || [ -z "$(ls -A "$target" 2>/dev/null)" ]; then return 0 fi local nssdb="${HOME}/.pki/nssdb" mkdir -p "$nssdb" local nss_count=0 echo "[certs] Импортируем сертификаты в NSS DB..." for cert_file in "$target"/*.crt; do [ -f "$cert_file" ] || continue local cert_name cert_name="$(basename "$cert_file" .crt)" certutil -A -d "sql:${nssdb}" -t "C,," -n "custom-${cert_name}" -i "$cert_file" 2>/dev/null && nss_count=$((nss_count + 1)) || true done echo "[certs] Импортировано в NSS: ${nss_count}" } # ── skip_server_files – remove server certs from CA dir ────────────── skip_server_files_filter() { # Used by frontend to skip server.crt from being installed as CA local certs_dir="${1:-/opt/certs}" [ -d "$certs_dir" ] || return 0 for f in "$certs_dir"/*.crt "$certs_dir"/*.pem; do [ -f "$f" ] || continue case "$(basename "$f" | tr '[:upper:]' '[:lower:]')" in server.crt|server.key) continue ;; esac echo "$f" done } # #endregion docker.certs