* add support for systemd, OpenRC, SysVinit, and Dinit * add automatic init-system detection through CMake * make libsystemd optional for non-systemd builds * add native service definitions for all supported init systems * add Gentoo ebuild and Alpine APKBUILD packaging support * publish the official BastionGuard source repository * update the README with supported distributions, init systems, repository information, and build documentation
1618 lines
60 KiB
Bash
1618 lines
60 KiB
Bash
#!/bin/bash
|
|
# /usr/share/BastionGuard/data/scripts/install-ca-system.sh
|
|
|
|
|
|
set -euo pipefail
|
|
|
|
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
|
|
|
# Unica identità della CA usata dal proxy, dai trust store e dai database NSS.
|
|
CA_NICKNAME="BastionGuard Intercept CA"
|
|
CA_FILE_BASENAME="BastionGuard-Intercept-CA"
|
|
CA_DEFAULT_SUBJECT="/O=BastionGuard/CN=BastionGuard Intercept CA"
|
|
CA_VALIDITY_DAYS="${BASTIONGUARD_CA_VALIDITY_DAYS:-3650}"
|
|
CA_RENEW_BEFORE_DAYS="${BASTIONGUARD_CA_RENEW_BEFORE_DAYS:-30}"
|
|
|
|
[[ "$CA_VALIDITY_DAYS" =~ ^[0-9]+$ ]] && (( CA_VALIDITY_DAYS >= 365 )) || {
|
|
echo "[BastionGuard] ❌ BASTIONGUARD_CA_VALIDITY_DAYS deve essere un intero >= 365" >&2
|
|
exit 1
|
|
}
|
|
[[ "$CA_RENEW_BEFORE_DAYS" =~ ^[0-9]+$ ]] || {
|
|
echo "[BastionGuard] ❌ BASTIONGUARD_CA_RENEW_BEFORE_DAYS deve essere un intero >= 0" >&2
|
|
exit 1
|
|
}
|
|
|
|
log() { echo "[BastionGuard] $*"; }
|
|
warn() { echo "[BastionGuard] ⚠️ $*" >&2; }
|
|
err() { echo "[BastionGuard] ❌ $*" >&2; }
|
|
|
|
have_cmd() { command -v "$1" >/dev/null 2>&1; }
|
|
|
|
# ── Utente/home reali + layout CA utente ─────────────────────────────────────
|
|
# Regola: anche se questo helper gira come root (pkexec/sudo), i file privati
|
|
# della CA devono vivere nella HOME dell'utente grafico/reale e devono restare
|
|
# posseduti da quell'utente. Root serve solo per copiare la CA pubblica nei
|
|
# trust store di sistema.
|
|
|
|
get_real_uid() {
|
|
# pkexec espone l'UID del chiamante; sudo espone SUDO_UID. Sono più
|
|
# affidabili di USER/HOME, perché in root diventano spesso root:/root.
|
|
if [[ -n "${PKEXEC_UID:-}" && "${PKEXEC_UID:-}" =~ ^[0-9]+$ && "${PKEXEC_UID}" != "0" ]]; then
|
|
printf '%s' "$PKEXEC_UID"
|
|
return 0
|
|
fi
|
|
if [[ -n "${SUDO_UID:-}" && "${SUDO_UID:-}" =~ ^[0-9]+$ && "${SUDO_UID}" != "0" ]]; then
|
|
printf '%s' "$SUDO_UID"
|
|
return 0
|
|
fi
|
|
|
|
local u=""
|
|
if [[ -n "${SUDO_USER:-}" && "${SUDO_USER:-}" != "root" ]] && id "$SUDO_USER" >/dev/null 2>&1; then
|
|
id -u "$SUDO_USER"
|
|
return 0
|
|
fi
|
|
|
|
u="$(logname 2>/dev/null || true)"
|
|
if [[ -n "$u" && "$u" != "root" ]] && id "$u" >/dev/null 2>&1; then
|
|
id -u "$u"
|
|
return 0
|
|
fi
|
|
|
|
for cand in "${PKEXEC_USER:-}" "${USER:-}" "${LOGNAME:-}"; do
|
|
if [[ -n "${cand:-}" && "$cand" != "root" ]] && id "$cand" >/dev/null 2>&1; then
|
|
id -u "$cand"
|
|
return 0
|
|
fi
|
|
done
|
|
|
|
return 1
|
|
}
|
|
|
|
get_user_for_uid() {
|
|
local uid="$1"
|
|
[[ -n "$uid" ]] || return 1
|
|
getent passwd "$uid" | cut -d: -f1
|
|
}
|
|
|
|
get_group_for_uid() {
|
|
local uid="$1"
|
|
[[ -n "$uid" ]] || return 1
|
|
getent passwd "$uid" | cut -d: -f4 | xargs -r getent group | cut -d: -f1
|
|
}
|
|
|
|
get_home_for_uid() {
|
|
local uid="$1"
|
|
[[ -n "$uid" ]] || return 1
|
|
getent passwd "$uid" | cut -d: -f6
|
|
}
|
|
|
|
path_under() {
|
|
local child="$1"
|
|
local parent="$2"
|
|
[[ -n "$child" && -n "$parent" ]] || return 1
|
|
child="$(readlink -m -- "$child")"
|
|
parent="$(readlink -m -- "$parent")"
|
|
[[ "$child" == "$parent" || "$child" == "$parent"/* ]]
|
|
}
|
|
|
|
selinux_status() {
|
|
if have_cmd getenforce; then
|
|
getenforce 2>/dev/null || printf 'Disabled'
|
|
elif [[ -r /sys/fs/selinux/enforce ]]; then
|
|
if [[ "$(cat /sys/fs/selinux/enforce 2>/dev/null || echo 0)" == "1" ]]; then
|
|
printf 'Enforcing'
|
|
else
|
|
printf 'Permissive'
|
|
fi
|
|
else
|
|
printf 'Disabled'
|
|
fi
|
|
}
|
|
|
|
selinux_enabled() {
|
|
local s
|
|
s="$(selinux_status 2>/dev/null || printf 'Disabled')"
|
|
[[ "$s" != "Disabled" ]]
|
|
}
|
|
|
|
selinux_enforcing() {
|
|
[[ "$(selinux_status 2>/dev/null || printf 'Disabled')" == "Enforcing" ]]
|
|
}
|
|
|
|
restore_selinux_context() {
|
|
# Fedora/RHEL hanno SELinux attivo di default: dopo install/chown/mkdir
|
|
# ripristiniamo sempre i label standard. Su distro senza SELinux non è fatale.
|
|
if ! have_cmd restorecon; then
|
|
if selinux_enabled; then
|
|
warn "SELinux è attivo ma restorecon non è disponibile; installare policycoreutils"
|
|
fi
|
|
return 0
|
|
fi
|
|
|
|
local p
|
|
for p in "$@"; do
|
|
[[ -n "${p:-}" && -e "$p" ]] || continue
|
|
if ! restorecon -RF "$p" >/dev/null 2>&1; then
|
|
selinux_enforcing && warn "restorecon fallito su: $p"
|
|
fi
|
|
done
|
|
}
|
|
|
|
fix_selinux_system_trust_contexts() {
|
|
selinux_enabled || return 0
|
|
restore_selinux_context \
|
|
/etc/pki/ca-trust \
|
|
/etc/pki/ca-trust/source \
|
|
/etc/pki/ca-trust/source/anchors \
|
|
/etc/pki/tls \
|
|
/etc/pki/nssdb \
|
|
/usr/share/pki/nssdb \
|
|
/etc/ssl/certs \
|
|
/usr/share/ca-certificates \
|
|
/usr/local/share/ca-certificates \
|
|
/etc/ca-certificates 2>/dev/null || true
|
|
}
|
|
|
|
fix_selinux_user_browser_contexts() {
|
|
selinux_enabled || return 0
|
|
[[ -n "${REAL_HOME:-}" ]] || return 0
|
|
restore_selinux_context \
|
|
"$REAL_HOME/.pki" \
|
|
"$REAL_HOME/.mozilla" \
|
|
"$REAL_HOME/.config/google-chrome" \
|
|
"$REAL_HOME/.config/chromium" \
|
|
"$REAL_HOME/.config/BraveSoftware" \
|
|
"$REAL_HOME/.config/vivaldi" \
|
|
"$REAL_HOME/.config/opera" \
|
|
"$REAL_HOME/.config/microsoft-edge" \
|
|
"$REAL_HOME/.config/thorium" \
|
|
"$REAL_HOME/.config/ungoogled-chromium" \
|
|
"$REAL_HOME/.var/app" \
|
|
"$REAL_HOME/snap" 2>/dev/null || true
|
|
}
|
|
|
|
check_fedora_selinux_requirements() {
|
|
if distro_is "fedora" || distro_is "rhel" || distro_is "centos"; then
|
|
selinux_enabled && log "SELinux rilevato: $(selinux_status)" || true
|
|
have_cmd update-ca-trust || warn "Fedora/RHEL: update-ca-trust non trovato"
|
|
have_cmd certutil || warn "Fedora/RHEL: certutil non trovato; installare nss-tools per import NSS/browser"
|
|
if selinux_enabled && ! have_cmd restorecon; then
|
|
warn "Fedora/RHEL: restorecon non trovato; installare policycoreutils"
|
|
fi
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
run_as_real_user() {
|
|
[[ -n "${REAL_USER:-}" && -n "${REAL_HOME:-}" ]] || return 1
|
|
|
|
local runtime_dir=""
|
|
local bus_addr=""
|
|
if [[ -n "${REAL_UID:-}" && -d "/run/user/${REAL_UID}" ]]; then
|
|
runtime_dir="/run/user/${REAL_UID}"
|
|
[[ -S "${runtime_dir}/bus" ]] && bus_addr="unix:path=${runtime_dir}/bus"
|
|
fi
|
|
|
|
if have_cmd runuser; then
|
|
local env_args=(
|
|
"HOME=$REAL_HOME"
|
|
"USER=$REAL_USER"
|
|
"LOGNAME=$REAL_USER"
|
|
"XDG_DATA_HOME=$REAL_HOME/.local/share"
|
|
"XDG_CONFIG_HOME=$REAL_HOME/.config"
|
|
)
|
|
[[ -n "$runtime_dir" ]] && env_args+=("XDG_RUNTIME_DIR=$runtime_dir")
|
|
[[ -n "$bus_addr" ]] && env_args+=("DBUS_SESSION_BUS_ADDRESS=$bus_addr")
|
|
|
|
runuser -u "$REAL_USER" -- env "${env_args[@]}" "$@"
|
|
elif have_cmd su; then
|
|
local env_cmd
|
|
env_cmd="HOME=$(printf '%q' "$REAL_HOME") USER=$(printf '%q' "$REAL_USER") LOGNAME=$(printf '%q' "$REAL_USER") XDG_DATA_HOME=$(printf '%q' "$REAL_HOME/.local/share") XDG_CONFIG_HOME=$(printf '%q' "$REAL_HOME/.config")"
|
|
[[ -n "$runtime_dir" ]] && env_cmd="$env_cmd XDG_RUNTIME_DIR=$(printf '%q' "$runtime_dir")"
|
|
[[ -n "$bus_addr" ]] && env_cmd="$env_cmd DBUS_SESSION_BUS_ADDRESS=$(printf '%q' "$bus_addr")"
|
|
su -s /bin/sh - "$REAL_USER" -c "$env_cmd $(printf '%q ' "$@")"
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
REAL_UID="$(get_real_uid || true)"
|
|
REAL_USER=""
|
|
REAL_GROUP=""
|
|
REAL_HOME=""
|
|
|
|
if [[ -n "$REAL_UID" ]]; then
|
|
REAL_USER="$(get_user_for_uid "$REAL_UID" || true)"
|
|
REAL_GROUP="$(get_group_for_uid "$REAL_UID" || true)"
|
|
REAL_HOME="$(get_home_for_uid "$REAL_UID" || true)"
|
|
fi
|
|
|
|
if [[ -z "$REAL_GROUP" && -n "$REAL_USER" ]]; then
|
|
REAL_GROUP="$REAL_USER"
|
|
fi
|
|
|
|
if [[ -n "$REAL_USER" && -n "$REAL_HOME" ]]; then
|
|
log "Utente reale: $REAL_USER ($REAL_HOME)"
|
|
else
|
|
warn "Impossibile determinare utente/home reali; userò solo path di sistema"
|
|
fi
|
|
|
|
USER_BG_DIR=""
|
|
USER_CA_DIR=""
|
|
if [[ -n "$REAL_HOME" ]]; then
|
|
USER_BG_DIR="$REAL_HOME/.local/share/BastionGuard"
|
|
USER_CA_DIR="$USER_BG_DIR/certs"
|
|
fi
|
|
|
|
fix_user_ca_permissions() {
|
|
[[ -n "${REAL_USER:-}" && -n "${REAL_GROUP:-}" && -n "${USER_BG_DIR:-}" && -n "${USER_CA_DIR:-}" ]] || return 0
|
|
[[ -d "$USER_BG_DIR" ]] || return 0
|
|
|
|
# Ripara anche directory intermedie create in precedenza da root.
|
|
chown -R "$REAL_USER:$REAL_GROUP" "$USER_BG_DIR" 2>/dev/null || true
|
|
chmod 700 "$USER_BG_DIR" 2>/dev/null || true
|
|
[[ -d "$USER_CA_DIR" ]] && chmod 700 "$USER_CA_DIR" 2>/dev/null || true
|
|
|
|
if [[ -d "$USER_CA_DIR" ]]; then
|
|
find "$USER_CA_DIR" -type d -exec chmod 700 {} + 2>/dev/null || true
|
|
find "$USER_CA_DIR" -type f \( -name '*.key' -o -name '*.key.pem' \) -exec chmod 600 {} + 2>/dev/null || true
|
|
find "$USER_CA_DIR" -type f \( -name '*.crt' -o -name '*.crt.pem' -o -name '*.pem' \) ! -name '*.key.pem' -exec chmod 644 {} + 2>/dev/null || true
|
|
fi
|
|
|
|
restore_selinux_context "$USER_BG_DIR"
|
|
}
|
|
|
|
ensure_user_ca_dir() {
|
|
[[ -n "${REAL_USER:-}" && -n "${REAL_GROUP:-}" && -n "${USER_CA_DIR:-}" ]] || return 0
|
|
|
|
# Crea prima da root, poi corregge ownership/mode: funziona anche se
|
|
# ~/.local/share/BastionGuard era già stato creato male da root.
|
|
mkdir -p "$USER_CA_DIR"
|
|
fix_user_ca_permissions
|
|
}
|
|
|
|
# Il path della CA può essere passato come argomento, ma non deve mai cadere
|
|
# accidentalmente sotto /root quando conosciamo l'utente reale.
|
|
if [[ -n "${USER_CA_DIR:-}" ]]; then
|
|
ensure_user_ca_dir
|
|
fi
|
|
|
|
if [[ $# -ge 1 && -n "${1:-}" ]]; then
|
|
CA_SRC="$(readlink -m -- "$1")"
|
|
|
|
# Caso tipico del bug: il chiamante, essendo root, ha espanso HOME=/root.
|
|
# In quel caso correggiamo verso la home reale invece di generare certs root-owned.
|
|
if [[ -n "${REAL_HOME:-}" ]] && path_under "$CA_SRC" "/root"; then
|
|
warn "Path CA sotto /root rilevato ($CA_SRC); uso la home reale dell'utente"
|
|
CA_SRC="$USER_CA_DIR/intercept-ca.crt.pem"
|
|
fi
|
|
else
|
|
if [[ -n "${USER_CA_DIR:-}" ]]; then
|
|
CA_SRC="$USER_CA_DIR/intercept-ca.crt.pem"
|
|
else
|
|
CA_SRC="/etc/BastionGuard/certs/intercept-ca.crt.pem"
|
|
fi
|
|
fi
|
|
|
|
CA_DIR="$(dirname "$CA_SRC")"
|
|
if [[ -n "${REAL_HOME:-}" ]] && path_under "$CA_DIR" "$REAL_HOME"; then
|
|
mkdir -p "$CA_DIR"
|
|
[[ -n "${REAL_USER:-}" && -n "${REAL_GROUP:-}" ]] && chown -R "$REAL_USER:$REAL_GROUP" "$CA_DIR" 2>/dev/null || true
|
|
chmod 700 "$CA_DIR" 2>/dev/null || true
|
|
restore_selinux_context "$CA_DIR"
|
|
# Se è il percorso canonico di BastionGuard, ripara anche l'albero completo.
|
|
ensure_user_ca_dir
|
|
elif [[ "$CA_DIR" == /etc/* ]]; then
|
|
mkdir -p "$CA_DIR"
|
|
else
|
|
mkdir -p "$CA_DIR"
|
|
[[ -n "${REAL_USER:-}" && -n "${REAL_GROUP:-}" ]] && chown "$REAL_USER:$REAL_GROUP" "$CA_DIR" 2>/dev/null || true
|
|
fi
|
|
|
|
[ -f "$CA_SRC" ] || warn "CA non trovata: $CA_SRC — verrà generata automaticamente"
|
|
# ── Legge /etc/os-release in modo sicuro ──────────────────────────────────────
|
|
ID=""
|
|
ID_LIKE=""
|
|
[ -f /etc/os-release ] && . /etc/os-release
|
|
|
|
distro_is() {
|
|
[[ "${ID:-}" == "$1" ]] && return 0
|
|
[[ "${ID_LIKE:-}" == *"$1"* ]] && return 0
|
|
return 1
|
|
}
|
|
|
|
check_fedora_selinux_requirements
|
|
|
|
# ── Valida il certificato sorgente ────────────────────────────────────────────
|
|
validate_ca() {
|
|
if ! openssl x509 -in "$CA_SRC" -noout >/dev/null 2>&1; then
|
|
err "Certificato non valido o non leggibile: $CA_SRC"
|
|
exit 1
|
|
fi
|
|
if ! openssl x509 -in "$CA_SRC" -noout -text 2>/dev/null | grep -q "CA:TRUE"; then
|
|
err "Il certificato non ha Basic Constraints CA:TRUE"
|
|
exit 1
|
|
fi
|
|
if ! openssl x509 -in "$CA_SRC" -noout -text 2>/dev/null \
|
|
| grep -A3 -i "X509v3 Key Usage" \
|
|
| grep -qiE "Certificate Sign|keyCertSign"; then
|
|
err "Il certificato non permette la firma di certificati (keyCertSign)"
|
|
exit 1
|
|
fi
|
|
if ! openssl verify -CAfile "$CA_SRC" "$CA_SRC" >/dev/null 2>&1; then
|
|
err "La CA non è autofirmata correttamente: $CA_SRC"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
|
|
# ── Pulizia vecchie CA BastionGuard ──────────────────────────────────────────
|
|
# Obiettivo: prima di installare/importare la CA corrente, rimuovere copie
|
|
# precedenti dai trust store di sistema e dai database NSS. Non tocca mai
|
|
# CA_SRC né la chiave privata: elimina solo vecchie copie pubbliche installate.
|
|
ca_fingerprint_sha256() {
|
|
local cert="$1"
|
|
[[ -f "$cert" ]] || return 1
|
|
openssl x509 -in "$cert" -noout -fingerprint -sha256 2>/dev/null \
|
|
| sed 's/^sha256 Fingerprint=//; s/^SHA256 Fingerprint=//'
|
|
}
|
|
|
|
current_ca_fingerprint_sha256() {
|
|
ca_fingerprint_sha256 "$CA_SRC"
|
|
}
|
|
|
|
remove_old_ca_file_if_needed() {
|
|
local cert="$1"
|
|
[[ -n "$cert" && -f "$cert" ]] || return 0
|
|
|
|
# Non eliminare mai il certificato sorgente usato dal proxy.
|
|
if [[ "$(readlink -m -- "$cert")" == "$(readlink -m -- "$CA_SRC")" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
# Tocca soltanto certificati che dichiarano BastionGuard/intercept nel subject
|
|
# o nell'issuer. In questo modo non rimuove CA estranee dal trust store.
|
|
if ! openssl x509 -in "$cert" -noout >/dev/null 2>&1; then
|
|
return 0
|
|
fi
|
|
if ! openssl x509 -in "$cert" -noout -subject -issuer 2>/dev/null \
|
|
| grep -qi 'BastionGuard'; then
|
|
return 0
|
|
fi
|
|
|
|
log "Rimuovo copia CA BastionGuard preesistente: $cert"
|
|
rm -f -- "$cert" 2>/dev/null || warn "Impossibile rimuovere vecchia CA: $cert"
|
|
}
|
|
|
|
purge_old_system_ca_copies() {
|
|
[[ -f "$CA_SRC" ]] || return 0
|
|
log "Pulizia vecchie CA BastionGuard dai trust store di sistema…"
|
|
|
|
local candidates=()
|
|
candidates+=(
|
|
"/usr/local/share/ca-certificates/BastionGuard-ca.crt"
|
|
"/usr/local/share/ca-certificates/BastionGuard-ca.pem"
|
|
"/usr/local/share/ca-certificates/BastionGuard-CA.crt"
|
|
"/usr/local/share/ca-certificates/BastionGuard-CA.pem"
|
|
"/usr/local/share/ca-certificates/BastionGuard Intercept CA.crt"
|
|
"/usr/local/share/ca-certificates/BastionGuard Intercept CA.pem"
|
|
"/usr/share/ca-certificates/local/BastionGuard-ca.crt"
|
|
"/usr/share/ca-certificates/local/BastionGuard-ca.pem"
|
|
"/usr/share/ca-certificates/local/BastionGuard-CA.crt"
|
|
"/usr/share/ca-certificates/local/BastionGuard-CA.pem"
|
|
"/usr/share/ca-certificates/local/BastionGuard Intercept CA.crt"
|
|
"/usr/share/ca-certificates/local/BastionGuard Intercept CA.pem"
|
|
"/etc/pki/ca-trust/source/anchors/BastionGuard-ca.pem"
|
|
"/etc/pki/ca-trust/source/anchors/BastionGuard-ca.crt"
|
|
"/etc/pki/ca-trust/source/anchors/BastionGuard-CA.pem"
|
|
"/etc/pki/ca-trust/source/anchors/BastionGuard-CA.crt"
|
|
"/etc/pki/ca-trust/source/anchors/BastionGuard Intercept CA.pem"
|
|
"/etc/pki/ca-trust/source/anchors/BastionGuard Intercept CA.crt"
|
|
"/etc/ca-certificates/trust-source/anchors/BastionGuard-ca.crt"
|
|
"/etc/ca-certificates/trust-source/anchors/BastionGuard-ca.pem"
|
|
"/etc/ca-certificates/trust-source/anchors/BastionGuard-CA.crt"
|
|
"/etc/ca-certificates/trust-source/anchors/BastionGuard-CA.pem"
|
|
"/etc/pki/trust/anchors/BastionGuard-ca.pem"
|
|
"/etc/pki/trust/anchors/BastionGuard-ca.crt"
|
|
"/etc/pki/trust/anchors/BastionGuard-CA.pem"
|
|
"/etc/pki/trust/anchors/BastionGuard-CA.crt"
|
|
"/usr/share/ca-certificates/bastionguard/BastionGuard-ca.pem"
|
|
"/usr/share/ca-certificates/bastionguard/BastionGuard-ca.crt"
|
|
"/usr/share/ca-certificates/bastionguard/BastionGuard-CA.pem"
|
|
"/usr/share/ca-certificates/bastionguard/BastionGuard-CA.crt"
|
|
)
|
|
|
|
local f
|
|
for f in "${candidates[@]}"; do
|
|
remove_old_ca_file_if_needed "$f"
|
|
done
|
|
|
|
# Cerca anche copie residue con nomi BastionGuard/intercept nelle directory
|
|
# dei trust store, senza scandire l'intero filesystem.
|
|
local roots=(
|
|
/usr/local/share/ca-certificates
|
|
/usr/share/ca-certificates/local
|
|
/etc/pki/ca-trust/source/anchors
|
|
/etc/ca-certificates/trust-source/anchors
|
|
/etc/pki/trust/anchors
|
|
/usr/share/ca-certificates/bastionguard
|
|
)
|
|
local root
|
|
for root in "${roots[@]}"; do
|
|
[[ -d "$root" ]] || continue
|
|
while IFS= read -r -d '' f; do
|
|
remove_old_ca_file_if_needed "$f"
|
|
done < <(find "$root" -maxdepth 1 -type f \
|
|
\( -iname '*bastionguard*.crt' -o -iname '*bastionguard*.pem' -o -iname '*intercept*.crt' -o -iname '*intercept*.pem' \) \
|
|
-print0 2>/dev/null || true)
|
|
done
|
|
|
|
# Pulisce le entry duplicate/obsolete su Debian-like; l'entry corretta
|
|
# verrà riscritta da install_debian_style().
|
|
[[ -f /etc/ca-certificates.conf ]] && sed -i '/[Bb]astion[Gg]uard\|[Ii]ntercept/d' /etc/ca-certificates.conf || true
|
|
|
|
fix_selinux_system_trust_contexts
|
|
}
|
|
|
|
nss_delete_matching_nicknames_root() {
|
|
local prefix="$1"
|
|
[[ -n "$prefix" ]] || return 0
|
|
|
|
local nick line
|
|
|
|
# Nickname noti.
|
|
for nick in "$CA_NICKNAME" "BastionGuard-CA" "BastionGuard CA" "BastionGuard-ca"; do
|
|
certutil -D -d "$prefix" -n "$nick" >/dev/null 2>&1 || true
|
|
done
|
|
|
|
# Nickname imprevisti ma riconoscibili in certutil -L.
|
|
while IFS= read -r line; do
|
|
[[ "$line" =~ [Bb]astion[Gg]uard ]] || continue
|
|
[[ "$line" =~ ^Certificate ]] && continue
|
|
[[ "$line" =~ ^-+ ]] && continue
|
|
nick="$(printf '%s\n' "$line" | sed -E 's/[[:space:]]+[A-Za-z,]+$//; s/[[:space:]]+$//')"
|
|
[[ -n "$nick" ]] || continue
|
|
certutil -D -d "$prefix" -n "$nick" >/dev/null 2>&1 || true
|
|
done < <(certutil -L -d "$prefix" 2>/dev/null || true)
|
|
}
|
|
|
|
nss_delete_matching_nicknames_user() {
|
|
local prefix="$1"
|
|
[[ -n "$prefix" ]] || return 0
|
|
[[ -n "${REAL_USER:-}" && -n "${REAL_HOME:-}" ]] || return 0
|
|
|
|
local nick line
|
|
|
|
# Nickname noti.
|
|
for nick in "$CA_NICKNAME" "BastionGuard-CA" "BastionGuard CA" "BastionGuard-ca"; do
|
|
run_as_real_user certutil -D -d "$prefix" -n "$nick" >/dev/null 2>&1 || true
|
|
done
|
|
|
|
# Nickname imprevisti ma riconoscibili in certutil -L.
|
|
while IFS= read -r line; do
|
|
[[ "$line" =~ [Bb]astion[Gg]uard ]] || continue
|
|
[[ "$line" =~ ^Certificate ]] && continue
|
|
[[ "$line" =~ ^-+ ]] && continue
|
|
nick="$(printf '%s\n' "$line" | sed -E 's/[[:space:]]+[A-Za-z,]+$//; s/[[:space:]]+$//')"
|
|
[[ -n "$nick" ]] || continue
|
|
run_as_real_user certutil -D -d "$prefix" -n "$nick" >/dev/null 2>&1 || true
|
|
done < <(run_as_real_user certutil -L -d "$prefix" 2>/dev/null || true)
|
|
}
|
|
|
|
close_running_browsers() {
|
|
# I browser tengono spesso aperti/lockati i DB NSS dei profili. Se restano
|
|
# in esecuzione, la rimozione delle vecchie CA o l'import della nuova CA può
|
|
# fallire o risultare visibile solo al riavvio. Per questo li chiudiamo in
|
|
# modo controllato prima dell'import.
|
|
[[ -n "${REAL_UID:-}" ]] || return 0
|
|
|
|
local patterns=(
|
|
'(^|/)(firefox|firefox-bin)([[:space:]]|$)'
|
|
'(^|/)(librewolf|librewolf-bin)([[:space:]]|$)'
|
|
'(^|/)(waterfox|waterfox-bin)([[:space:]]|$)'
|
|
'(^|/)(floorp|floorp-bin)([[:space:]]|$)'
|
|
'(^|/)(zen|zen-bin|zen-browser)([[:space:]]|$)'
|
|
'(^|/)(mullvadbrowser|mullvad-browser)([[:space:]]|$)'
|
|
'(^|/)(chromium|chromium-browser)([[:space:]]|$)'
|
|
'(^|/)(google-chrome|google-chrome-stable|google-chrome-beta|google-chrome-unstable|chrome)([[:space:]]|$)'
|
|
'(^|/)(brave|brave-browser|brave-browser-beta|brave-browser-nightly)([[:space:]]|$)'
|
|
'(^|/)(vivaldi|vivaldi-bin|vivaldi-snapshot)([[:space:]]|$)'
|
|
'(^|/)(opera|opera-browser|opera-beta|opera-developer)([[:space:]]|$)'
|
|
'(^|/)(microsoft-edge|microsoft-edge-beta|microsoft-edge-dev|msedge)([[:space:]]|$)'
|
|
'(^|/)(thorium|thorium-browser|ungoogled-chromium|epiphany|yandex-browser)([[:space:]]|$)'
|
|
'(org\.mozilla\.firefox|org\.chromium\.Chromium|com\.google\.Chrome|com\.google\.ChromeDev|com\.brave\.Browser|com\.vivaldi\.Vivaldi|com\.opera\.Opera|com\.microsoft\.Edge|io\.gitlab\.librewolf-community|net\.waterfox\.waterfox|one\.ablaze\.floorp|io\.github\.ungoogled_software\.ungoogled_chromium|com\.github\.Eloston\.UngoogledChromium|org\.gnome\.Epiphany)'
|
|
)
|
|
|
|
local pattern found=0
|
|
for pattern in "${patterns[@]}"; do
|
|
if pgrep -u "$REAL_UID" -f -- "$pattern" >/dev/null 2>&1; then
|
|
found=1
|
|
break
|
|
fi
|
|
done
|
|
|
|
[[ "$found" -eq 1 ]] || return 0
|
|
|
|
warn "Browser in esecuzione rilevati: li chiudo automaticamente prima dell'import certificati"
|
|
|
|
for pattern in "${patterns[@]}"; do
|
|
pkill -TERM -u "$REAL_UID" -f -- "$pattern" >/dev/null 2>&1 || true
|
|
done
|
|
|
|
local i still_running
|
|
for i in $(seq 1 20); do
|
|
still_running=0
|
|
for pattern in "${patterns[@]}"; do
|
|
if pgrep -u "$REAL_UID" -f -- "$pattern" >/dev/null 2>&1; then
|
|
still_running=1
|
|
break
|
|
fi
|
|
done
|
|
[[ "$still_running" -eq 0 ]] && { log "✅ Browser chiusi"; return 0; }
|
|
sleep 0.5
|
|
done
|
|
|
|
warn "Alcuni processi browser sono ancora attivi: forzo la chiusura"
|
|
for pattern in "${patterns[@]}"; do
|
|
pkill -KILL -u "$REAL_UID" -f -- "$pattern" >/dev/null 2>&1 || true
|
|
done
|
|
sleep 1
|
|
}
|
|
# ── Mantiene una sola coppia CA/chiave canonica ───────────────────────────────
|
|
# La CA del proxy è sempre CA_SRC; la chiave canonica è intercept-ca.key.pem
|
|
# accanto al certificato. Lo script rigenera il certificato quando è assente,
|
|
# corrotto, non-CA, non autofirmato, non corrisponde alla chiave oppure è
|
|
# scaduto/in scadenza. Tutti i trust store ricevono poi esattamente CA_SRC.
|
|
canonical_ca_key_path() {
|
|
local dir base
|
|
dir="$(dirname "$CA_SRC")"
|
|
base="$(basename "$CA_SRC")"
|
|
base="${base%.crt.pem}"
|
|
base="${base%.crt}"
|
|
base="${base%.pem}"
|
|
printf '%s/%s.key.pem' "$dir" "$base"
|
|
}
|
|
|
|
pubkey_fingerprint_from_cert() {
|
|
openssl x509 -in "$1" -pubkey -noout 2>/dev/null \
|
|
| openssl pkey -pubin -outform DER 2>/dev/null \
|
|
| sha256sum | awk '{print $1}'
|
|
}
|
|
|
|
pubkey_fingerprint_from_key() {
|
|
openssl pkey -in "$1" -pubout -outform DER 2>/dev/null \
|
|
| sha256sum | awk '{print $1}'
|
|
}
|
|
|
|
certificate_has_expected_subject() {
|
|
openssl x509 -in "$CA_SRC" -noout -subject -nameopt RFC2253 2>/dev/null \
|
|
| grep -Fq "CN=${CA_NICKNAME}"
|
|
}
|
|
|
|
certificate_is_usable_ca() {
|
|
[[ -f "$CA_SRC" ]] || return 1
|
|
openssl x509 -in "$CA_SRC" -noout >/dev/null 2>&1 || return 1
|
|
openssl x509 -in "$CA_SRC" -noout -text 2>/dev/null | grep -q 'CA:TRUE' || return 1
|
|
openssl x509 -in "$CA_SRC" -noout -text 2>/dev/null \
|
|
| grep -A3 -i 'X509v3 Key Usage' \
|
|
| grep -qiE 'Certificate Sign|keyCertSign' || return 1
|
|
openssl verify -CAfile "$CA_SRC" "$CA_SRC" >/dev/null 2>&1 || return 1
|
|
return 0
|
|
}
|
|
|
|
generate_ca_certificate_with_key() {
|
|
local key="$1"
|
|
local tmp_ext=""
|
|
|
|
if openssl req -new -x509 \
|
|
-key "$key" \
|
|
-out "$CA_SRC" \
|
|
-days "$CA_VALIDITY_DAYS" \
|
|
-subj "$CA_DEFAULT_SUBJECT" \
|
|
-addext 'basicConstraints=critical,CA:TRUE,pathlen:0' \
|
|
-addext 'keyUsage=critical,keyCertSign,cRLSign' \
|
|
-addext 'subjectKeyIdentifier=hash' \
|
|
-addext 'authorityKeyIdentifier=keyid:always' \
|
|
>/dev/null 2>&1; then
|
|
return 0
|
|
fi
|
|
|
|
# Compatibilità con OpenSSL senza -addext.
|
|
tmp_ext="$(mktemp /tmp/bastionguard-ca.XXXXXX.cnf)"
|
|
cat > "$tmp_ext" <<'EOF_CA_CONF'
|
|
[req]
|
|
distinguished_name = req_dn
|
|
x509_extensions = v3_ca
|
|
prompt = no
|
|
|
|
[req_dn]
|
|
O = BastionGuard
|
|
CN = BastionGuard Intercept CA
|
|
|
|
[v3_ca]
|
|
basicConstraints = critical, CA:TRUE, pathlen:0
|
|
keyUsage = critical, keyCertSign, cRLSign
|
|
subjectKeyIdentifier = hash
|
|
authorityKeyIdentifier = keyid:always
|
|
EOF_CA_CONF
|
|
|
|
if ! openssl req -new -x509 \
|
|
-key "$key" \
|
|
-out "$CA_SRC" \
|
|
-days "$CA_VALIDITY_DAYS" \
|
|
-config "$tmp_ext" \
|
|
>/dev/null 2>&1; then
|
|
rm -f "$tmp_ext"
|
|
return 1
|
|
fi
|
|
rm -f "$tmp_ext"
|
|
}
|
|
|
|
check_and_fix_key_mismatch() {
|
|
log "Controllo CA canonica, chiave, validità ed estensioni…"
|
|
|
|
local canonical_key
|
|
canonical_key="$(canonical_ca_key_path)"
|
|
mkdir -p "$(dirname "$canonical_key")"
|
|
|
|
# Recupera una chiave legacy soltanto se quella canonica non esiste.
|
|
if [[ ! -f "$canonical_key" ]]; then
|
|
local candidate
|
|
for candidate in \
|
|
"$(dirname "$CA_SRC")/intercept-ca.key" \
|
|
"$(dirname "$CA_SRC")/BastionGuard-CA.key.pem" \
|
|
"$(dirname "$CA_SRC")/BastionGuard-CA.key" \
|
|
/etc/BastionGuard/certs/intercept-ca.key.pem \
|
|
/etc/BastionGuard/certs/intercept-ca.key \
|
|
/etc/BastionGuard/certs/BastionGuard-CA.key.pem \
|
|
/etc/BastionGuard/certs/BastionGuard-CA.key
|
|
do
|
|
[[ -f "$candidate" ]] || continue
|
|
if openssl pkey -in "$candidate" -noout >/dev/null 2>&1; then
|
|
install -m 0600 "$candidate" "$canonical_key"
|
|
log "Chiave legacy copiata nel percorso canonico: $canonical_key"
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Se non esiste una chiave valida, crea una nuova coppia canonica.
|
|
if [[ ! -f "$canonical_key" ]] || ! openssl pkey -in "$canonical_key" -noout >/dev/null 2>&1; then
|
|
[[ -f "$canonical_key" ]] && mv -f "$canonical_key" "${canonical_key}.invalid.$(date +%Y%m%d%H%M%S)" || true
|
|
log "Genero nuova chiave privata RSA 4096: $canonical_key"
|
|
openssl genrsa -out "$canonical_key" 4096 >/dev/null 2>&1 \
|
|
|| { err "Generazione chiave RSA fallita"; exit 1; }
|
|
chmod 600 "$canonical_key"
|
|
|
|
[[ -f "$CA_SRC" ]] && cp -a "$CA_SRC" "${CA_SRC}.bak.$(date +%Y%m%d%H%M%S)" || true
|
|
generate_ca_certificate_with_key "$canonical_key" \
|
|
|| { err "Impossibile generare la CA canonica"; exit 1; }
|
|
log "✅ Nuova coppia CA/chiave generata ($CA_VALIDITY_DAYS giorni)"
|
|
else
|
|
local regenerate=0 reason=""
|
|
|
|
if ! certificate_is_usable_ca; then
|
|
regenerate=1
|
|
reason="certificato assente, corrotto o privo delle corrette estensioni CA"
|
|
else
|
|
local cert_fp key_fp
|
|
cert_fp="$(pubkey_fingerprint_from_cert "$CA_SRC" || true)"
|
|
key_fp="$(pubkey_fingerprint_from_key "$canonical_key" || true)"
|
|
|
|
if [[ -z "$cert_fp" || -z "$key_fp" || "$cert_fp" != "$key_fp" ]]; then
|
|
regenerate=1
|
|
reason="certificato e chiave privata non coincidono"
|
|
elif ! certificate_has_expected_subject; then
|
|
regenerate=1
|
|
reason="subject CA non canonico"
|
|
elif ! openssl x509 -in "$CA_SRC" -checkend "$((CA_RENEW_BEFORE_DAYS * 86400))" -noout >/dev/null 2>&1; then
|
|
regenerate=1
|
|
reason="certificato scaduto o in scadenza entro ${CA_RENEW_BEFORE_DAYS} giorni"
|
|
fi
|
|
fi
|
|
|
|
if (( regenerate )); then
|
|
warn "$reason: rigenero il certificato usando la chiave canonica"
|
|
[[ -f "$CA_SRC" ]] && cp -a "$CA_SRC" "${CA_SRC}.bak.$(date +%Y%m%d%H%M%S)" || true
|
|
generate_ca_certificate_with_key "$canonical_key" \
|
|
|| { err "Impossibile rigenerare la CA canonica"; exit 1; }
|
|
log "✅ Certificato CA rigenerato ($CA_VALIDITY_DAYS giorni)"
|
|
else
|
|
log "✅ CA e chiave canoniche valide — nessuna rigenerazione necessaria"
|
|
fi
|
|
fi
|
|
|
|
chmod 600 "$canonical_key"
|
|
chmod 644 "$CA_SRC"
|
|
[[ -n "${REAL_USER:-}" && -n "${REAL_GROUP:-}" ]] \
|
|
&& chown "$REAL_USER:$REAL_GROUP" "$canonical_key" "$CA_SRC" 2>/dev/null || true
|
|
fix_user_ca_permissions
|
|
validate_ca
|
|
|
|
local cert_fp key_fp
|
|
cert_fp="$(pubkey_fingerprint_from_cert "$CA_SRC")"
|
|
key_fp="$(pubkey_fingerprint_from_key "$canonical_key")"
|
|
[[ "$cert_fp" == "$key_fp" ]] \
|
|
|| { err "Verifica finale fallita: CA e chiave non coincidono"; exit 1; }
|
|
|
|
log "CA sorgente: $CA_SRC"
|
|
log "Fingerprint SHA-256: $(current_ca_fingerprint_sha256)"
|
|
}
|
|
|
|
# ── Helper Debian ─────────────────────────────────────────────────────────────
|
|
install_debian_style() {
|
|
local dst="$1"
|
|
local conf_entry="$2"
|
|
|
|
SYSTEM_CA_DST="$dst"
|
|
mkdir -p "$(dirname "$dst")"
|
|
|
|
rm -f \
|
|
/usr/local/share/ca-certificates/BastionGuard-ca.crt \
|
|
/usr/local/share/ca-certificates/BastionGuard-ca.pem \
|
|
/usr/local/share/ca-certificates/BastionGuard-CA.crt \
|
|
/usr/local/share/ca-certificates/BastionGuard-CA.pem \
|
|
/usr/share/ca-certificates/local/BastionGuard-ca.crt \
|
|
/usr/share/ca-certificates/local/BastionGuard-ca.pem \
|
|
/usr/share/ca-certificates/local/BastionGuard-CA.crt \
|
|
/usr/share/ca-certificates/local/BastionGuard-CA.pem
|
|
|
|
install -m 0644 "$CA_SRC" "$dst"
|
|
restore_selinux_context "$dst" "$(dirname "$dst")" /etc/ca-certificates /usr/share/ca-certificates /usr/local/share/ca-certificates
|
|
|
|
sed -i '/[Bb]astion[Gg]uard/d' /etc/ca-certificates.conf || true
|
|
echo "$conf_entry" >> /etc/ca-certificates.conf
|
|
|
|
log "Entry registrata in /etc/ca-certificates.conf: $conf_entry"
|
|
update-ca-certificates
|
|
}
|
|
|
|
|
|
# ── Policy SELinux locale per BastionGuard-cef.service ───────────────────────
|
|
# BastionGuard-cef.service è un servizio systemd utente, quindi gira come utente
|
|
# normale ma può comunque essere bloccato da SELinux su Fedora/RHEL mentre
|
|
# legge/scrive la propria CA e cache in home.
|
|
# Questa sezione crea una policy locale mirata al dominio SELinux effettivo
|
|
# dell'utente/processo e limita i permessi all'albero ~/.local/share/BastionGuard.
|
|
CEF_USER_SERVICE_NAME="BastionGuard-cef.service"
|
|
CEF_SERVICE_COMM="bastionguard-cef"
|
|
CEF_SERVICE_COMM_TRUNC="bastionguard-ce"
|
|
CEF_SERVICE_NAME_GLOB="BastionGuard-cef"
|
|
CEF_POLICY_MODULE="bastionguard_cef_local"
|
|
CEF_DATA_TYPE="bastionguard_cef_home_t"
|
|
|
|
regex_escape() {
|
|
printf '%s' "$1" | sed -E 's/[][().^$*+?{}|\\]/\\&/g'
|
|
}
|
|
|
|
selinux_type_from_context() {
|
|
awk -F: '{print $3}' <<<"$1"
|
|
}
|
|
|
|
selinux_type_name_valid() {
|
|
local t="$1"
|
|
[[ "$t" =~ ^[A-Za-z0-9_]+$ ]] || return 1
|
|
[[ "$t" == *_t ]] || return 1
|
|
}
|
|
|
|
detect_bastionguard_cef_source_domains() {
|
|
local d label comm rest raw svc_lc comm_lc rest_lc
|
|
svc_lc="${CEF_SERVICE_NAME_GLOB,,}"
|
|
|
|
# Dominio SELinux della sessione utente. Per un servizio systemd --user
|
|
# avviato come utente normale, spesso è lo stesso dominio del processo.
|
|
if [[ -n "${REAL_USER:-}" ]] && label="$(run_as_real_user id -Z 2>/dev/null || true)"; then
|
|
d="$(selinux_type_from_context "$label" 2>/dev/null || true)"
|
|
selinux_type_name_valid "$d" && printf '%s\n' "$d"
|
|
fi
|
|
|
|
# Dominio reale del processo BastionGuard-cef, se è già avviato.
|
|
# Nota: Linux tronca comm a 15 caratteri; bastionguard-cef può comparire
|
|
# come bastionguard-ce nelle AVC e in ps -o comm.
|
|
if have_cmd ps; then
|
|
while read -r label comm rest; do
|
|
[[ -n "${label:-}" ]] || continue
|
|
comm_lc="${comm,,}"
|
|
rest_lc="${rest,,}"
|
|
if [[ "$comm_lc" == *"${CEF_SERVICE_COMM,,}"* || \
|
|
"$comm_lc" == *"${CEF_SERVICE_COMM_TRUNC,,}"* || \
|
|
"$rest_lc" == *"${CEF_SERVICE_COMM,,}"* || \
|
|
"$rest_lc" == *"${svc_lc}"* ]]; then
|
|
d="$(selinux_type_from_context "$label" 2>/dev/null || true)"
|
|
selinux_type_name_valid "$d" && printf '%s\n' "$d"
|
|
fi
|
|
done < <(ps -eZ -o label=,comm=,args= 2>/dev/null || true)
|
|
fi
|
|
|
|
# Domini sorgente comparsi nelle denial recenti del servizio/processo.
|
|
if have_cmd ausearch; then
|
|
raw="$(
|
|
{
|
|
ausearch -m AVC,USER_AVC -ts recent -c "$CEF_SERVICE_COMM" 2>/dev/null || true
|
|
ausearch -m AVC,USER_AVC -ts recent -c "$CEF_SERVICE_COMM_TRUNC" 2>/dev/null || true
|
|
ausearch -m AVC,USER_AVC -ts recent 2>/dev/null \
|
|
| grep -iE 'bastionguard-ce|BastionGuard-cef' || true
|
|
} | sort -u
|
|
)"
|
|
while IFS= read -r d; do
|
|
selinux_type_name_valid "$d" && printf '%s\n' "$d"
|
|
done < <(
|
|
printf '%s\n' "$raw" \
|
|
| grep -oE 'scontext=[^ ]+' \
|
|
| sed -E 's/^scontext=([^:]+:){2}([^:]+):.*/\2/' \
|
|
| sort -u || true
|
|
)
|
|
fi
|
|
}
|
|
|
|
write_bastionguard_cef_base_te() {
|
|
local te="$1"; shift
|
|
local domains=("$@")
|
|
local d
|
|
|
|
{
|
|
echo "module ${CEF_POLICY_MODULE} 1.0;"
|
|
echo
|
|
echo "require {"
|
|
for d in "${domains[@]}"; do
|
|
echo " type ${d};"
|
|
done
|
|
echo " type user_home_dir_t;"
|
|
echo " type user_home_t;"
|
|
echo " attribute file_type;"
|
|
echo " attribute user_home_type;"
|
|
echo " class dir { add_name create getattr ioctl lock open read remove_name rmdir search setattr write };"
|
|
echo " class file { append create getattr ioctl lock map open read rename setattr unlink write };"
|
|
echo " class lnk_file { create getattr read rename setattr unlink write };"
|
|
echo " class sock_file { create getattr setattr unlink write };"
|
|
echo " class fifo_file { create getattr ioctl lock open read setattr unlink write };"
|
|
echo "}"
|
|
echo
|
|
echo "type ${CEF_DATA_TYPE};"
|
|
echo "typeattribute ${CEF_DATA_TYPE} file_type;"
|
|
echo "typeattribute ${CEF_DATA_TYPE} user_home_type;"
|
|
echo
|
|
for d in "${domains[@]}"; do
|
|
echo "# Permessi sui dati BastionGuard dell'utente per ${d}."
|
|
echo "allow ${d} ${CEF_DATA_TYPE}:dir { add_name create getattr ioctl lock open read remove_name rmdir search setattr write };"
|
|
echo "allow ${d} ${CEF_DATA_TYPE}:file { append create getattr ioctl lock map open read rename setattr unlink write };"
|
|
echo "allow ${d} ${CEF_DATA_TYPE}:lnk_file { create getattr read rename setattr unlink write };"
|
|
echo "allow ${d} ${CEF_DATA_TYPE}:sock_file { create getattr setattr unlink write };"
|
|
echo "allow ${d} ${CEF_DATA_TYPE}:fifo_file { create getattr ioctl lock open read setattr unlink write };"
|
|
echo "allow ${d} user_home_dir_t:dir { getattr open read search };"
|
|
echo "allow ${d} user_home_t:dir { getattr open read search };"
|
|
echo
|
|
done
|
|
} > "$te"
|
|
}
|
|
|
|
compile_and_install_selinux_te() {
|
|
local te="$1"
|
|
local base="$2"
|
|
checkmodule -M -m -o "${base}.mod" "$te"
|
|
semodule_package -o "${base}.pp" -m "${base}.mod"
|
|
semodule -i "${base}.pp"
|
|
}
|
|
|
|
install_bastionguard_cef_observed_avc_policy() {
|
|
# Facoltativo: se ci sono già denial recenti, crea un secondo modulo solo
|
|
# dalle AVC del servizio/processo BastionGuard-cef. È mirato alle denial reali osservate.
|
|
selinux_enabled || return 0
|
|
have_cmd ausearch || return 0
|
|
have_cmd audit2allow || return 0
|
|
|
|
local tmpd avc base
|
|
tmpd="$(mktemp -d /tmp/bastionguard-cef-avc.XXXXXX)"
|
|
avc="$tmpd/${CEF_SERVICE_COMM}.avc"
|
|
base="bastionguard_cef_observed"
|
|
|
|
{
|
|
ausearch -m AVC,USER_AVC -ts recent -c "$CEF_SERVICE_COMM" --raw 2>/dev/null || true
|
|
ausearch -m AVC,USER_AVC -ts recent -c "$CEF_SERVICE_COMM_TRUNC" --raw 2>/dev/null || true
|
|
ausearch -m AVC,USER_AVC -ts recent --raw 2>/dev/null \
|
|
| grep -iE 'bastionguard-ce|BastionGuard-cef' || true
|
|
} | sort -u > "$avc"
|
|
|
|
if ! grep -qE 'avc:.*denied|type=AVC|type=USER_AVC' "$avc" 2>/dev/null; then
|
|
rm -rf "$tmpd"
|
|
log "Nessuna AVC denial recente per $CEF_USER_SERVICE_NAME / $CEF_SERVICE_COMM"
|
|
return 0
|
|
fi
|
|
|
|
if (
|
|
cd "$tmpd" && \
|
|
audit2allow -M "$base" -i "$avc" >/dev/null && \
|
|
semodule -i "${base}.pp"
|
|
); then
|
|
log "✅ Modulo SELinux da AVC recenti installato: ${base}"
|
|
else
|
|
warn "Impossibile generare/installare modulo da AVC recenti per $CEF_USER_SERVICE_NAME"
|
|
fi
|
|
rm -rf "$tmpd"
|
|
}
|
|
|
|
install_bastionguard_cef_selinux_policy() {
|
|
selinux_enabled || return 0
|
|
distro_is "fedora" || distro_is "rhel" || distro_is "centos" || return 0
|
|
[[ -n "${REAL_USER:-}" && -n "${REAL_HOME:-}" && -n "${REAL_GROUP:-}" ]] || return 0
|
|
|
|
local missing=0 cmd
|
|
for cmd in checkmodule semodule_package semodule semanage restorecon; do
|
|
if ! have_cmd "$cmd"; then
|
|
warn "SELinux policy $CEF_USER_SERVICE_NAME: comando mancante: $cmd"
|
|
missing=1
|
|
fi
|
|
done
|
|
if [[ "$missing" -eq 1 ]]; then
|
|
warn "Fedora/RHEL: installare checkpolicy policycoreutils-python-utils policycoreutils per la policy di $CEF_USER_SERVICE_NAME"
|
|
return 0
|
|
fi
|
|
|
|
local domains_raw domains=() d tmpd te home_re data_re
|
|
domains_raw="$(detect_bastionguard_cef_source_domains | sort -u || true)"
|
|
if [[ -z "$domains_raw" ]]; then
|
|
warn "Nessun dominio SELinux rilevato per $CEF_USER_SERVICE_NAME; avvia il servizio utente una volta e rilancia lo script se SELinux blocca"
|
|
install_bastionguard_cef_observed_avc_policy
|
|
return 0
|
|
fi
|
|
|
|
while IFS= read -r d; do
|
|
[[ -n "$d" ]] && domains+=("$d")
|
|
done <<< "$domains_raw"
|
|
|
|
log "Domini SELinux per $CEF_USER_SERVICE_NAME: ${domains[*]}"
|
|
|
|
tmpd="$(mktemp -d /tmp/bastionguard-cef-selinux.XXXXXX)"
|
|
te="$tmpd/${CEF_POLICY_MODULE}.te"
|
|
write_bastionguard_cef_base_te "$te" "${domains[@]}"
|
|
|
|
if (
|
|
cd "$tmpd" && compile_and_install_selinux_te "$te" "$CEF_POLICY_MODULE"
|
|
); then
|
|
log "✅ Policy SELinux base installata: $CEF_POLICY_MODULE"
|
|
else
|
|
warn "Compilazione/installazione policy SELinux base fallita per $CEF_USER_SERVICE_NAME"
|
|
rm -rf "$tmpd"
|
|
install_bastionguard_cef_observed_avc_policy
|
|
return 0
|
|
fi
|
|
rm -rf "$tmpd"
|
|
|
|
home_re="$(regex_escape "$REAL_HOME")"
|
|
data_re="${home_re}/\.local/share/BastionGuard(/.*)?"
|
|
|
|
semanage fcontext -a -t "$CEF_DATA_TYPE" "$data_re" 2>/dev/null \
|
|
|| semanage fcontext -m -t "$CEF_DATA_TYPE" "$data_re" 2>/dev/null \
|
|
|| warn "Impossibile registrare fcontext SELinux per $REAL_HOME/.local/share/BastionGuard"
|
|
|
|
mkdir -p "$REAL_HOME/.local/share/BastionGuard"
|
|
chown -R "$REAL_USER:$REAL_GROUP" "$REAL_HOME/.local/share/BastionGuard" 2>/dev/null || true
|
|
chmod 700 "$REAL_HOME/.local/share/BastionGuard" 2>/dev/null || true
|
|
restore_selinux_context "$REAL_HOME/.local/share/BastionGuard"
|
|
|
|
install_bastionguard_cef_observed_avc_policy
|
|
|
|
log "✅ Policy SELinux $CEF_USER_SERVICE_NAME completata"
|
|
}
|
|
|
|
# ── Gestione servizio systemd utente BastionGuard-cef.service ────────────────
|
|
user_systemctl() {
|
|
[[ -n "${REAL_USER:-}" && -n "${REAL_UID:-}" ]] || return 1
|
|
[[ -d "/run/user/${REAL_UID}" ]] || return 1
|
|
run_as_real_user systemctl --user "$@"
|
|
}
|
|
|
|
bastionguard_cef_user_service_exists() {
|
|
local state=""
|
|
state="$(user_systemctl show "$CEF_USER_SERVICE_NAME" -p LoadState --value 2>/dev/null || true)"
|
|
[[ -n "$state" && "$state" != "not-found" ]] && return 0
|
|
user_systemctl status "$CEF_USER_SERVICE_NAME" >/dev/null 2>&1 && return 0
|
|
return 1
|
|
}
|
|
|
|
restart_bastionguard_cef_user_service_if_active() {
|
|
[[ -n "${REAL_USER:-}" && -n "${REAL_UID:-}" ]] || return 0
|
|
if ! bastionguard_cef_user_service_exists; then
|
|
log "Servizio utente $CEF_USER_SERVICE_NAME non trovato o bus utente non disponibile: skip restart"
|
|
return 0
|
|
fi
|
|
|
|
user_systemctl daemon-reload >/dev/null 2>&1 || true
|
|
|
|
if user_systemctl is-active --quiet "$CEF_USER_SERVICE_NAME"; then
|
|
log "Riavvio servizio utente $CEF_USER_SERVICE_NAME per ricaricare CA/policy…"
|
|
if user_systemctl restart "$CEF_USER_SERVICE_NAME"; then
|
|
log "✅ Servizio utente riavviato: $CEF_USER_SERVICE_NAME"
|
|
else
|
|
warn "Restart di $CEF_USER_SERVICE_NAME fallito; prova manualmente: systemctl --user restart $CEF_USER_SERVICE_NAME"
|
|
fi
|
|
else
|
|
log "Servizio utente $CEF_USER_SERVICE_NAME non attivo: non lo avvio automaticamente"
|
|
fi
|
|
}
|
|
|
|
# ── 1. Installa nel trust store di sistema ────────────────────────────────────
|
|
check_and_fix_key_mismatch
|
|
fix_user_ca_permissions
|
|
close_running_browsers
|
|
purge_old_system_ca_copies
|
|
|
|
installed_sys=false
|
|
SYSTEM_CA_DST=""
|
|
|
|
|
|
if distro_is "debian" || distro_is "ubuntu"; then
|
|
log "Rilevato: Debian/Ubuntu"
|
|
install_debian_style \
|
|
"/usr/share/ca-certificates/local/${CA_FILE_BASENAME}.crt" \
|
|
"local/${CA_FILE_BASENAME}.crt"
|
|
log "✅ CA installata (Debian/Ubuntu)"
|
|
installed_sys=true
|
|
|
|
elif distro_is "fedora" || distro_is "rhel" || distro_is "centos"; then
|
|
log "Rilevato: Fedora/RHEL/CentOS"
|
|
DST="/etc/pki/ca-trust/source/anchors/${CA_FILE_BASENAME}.pem"
|
|
SYSTEM_CA_DST="$DST"
|
|
mkdir -p "$(dirname "$DST")"
|
|
install -m 0644 "$CA_SRC" "$DST"
|
|
# Su Fedora/RHEL il label SELinux errato su /etc/pki/ca-trust può impedire
|
|
# lettura/estrazione corretta in ambienti enforcing. Ripristina prima e dopo.
|
|
restore_selinux_context "$DST" "$(dirname "$DST")" /etc/pki/ca-trust
|
|
update-ca-trust extract
|
|
fix_selinux_system_trust_contexts
|
|
log "✅ CA installata (Fedora/RHEL)"
|
|
installed_sys=true
|
|
|
|
elif distro_is "arch" || distro_is "archlinux"; then
|
|
log "Rilevato: Arch Linux"
|
|
DST="/etc/ca-certificates/trust-source/anchors/${CA_FILE_BASENAME}.crt"
|
|
SYSTEM_CA_DST="$DST"
|
|
mkdir -p "$(dirname "$DST")"
|
|
install -m 0644 "$CA_SRC" "$DST"
|
|
if have_cmd update-ca-trust; then
|
|
update-ca-trust extract
|
|
elif have_cmd trust; then
|
|
trust extract-compat
|
|
else
|
|
err "Arch Linux: né update-ca-trust né trust sono disponibili"
|
|
exit 1
|
|
fi
|
|
log "✅ CA installata (Arch Linux)"
|
|
installed_sys=true
|
|
|
|
elif distro_is "opensuse" || distro_is "suse"; then
|
|
log "Rilevato: openSUSE/SLES"
|
|
DST="/etc/pki/trust/anchors/${CA_FILE_BASENAME}.pem"
|
|
SYSTEM_CA_DST="$DST"
|
|
mkdir -p "$(dirname "$DST")"
|
|
install -m 0644 "$CA_SRC" "$DST"
|
|
update-ca-certificates
|
|
log "✅ CA installata (openSUSE)"
|
|
installed_sys=true
|
|
|
|
elif distro_is "alpine"; then
|
|
log "Rilevato: Alpine Linux"
|
|
DST="/usr/local/share/ca-certificates/${CA_FILE_BASENAME}.crt"
|
|
SYSTEM_CA_DST="$DST"
|
|
mkdir -p "$(dirname "$DST")"
|
|
install -m 0644 "$CA_SRC" "$DST"
|
|
update-ca-certificates
|
|
log "✅ CA installata (Alpine)"
|
|
installed_sys=true
|
|
|
|
elif distro_is "void"; then
|
|
log "Rilevato: Void Linux"
|
|
DST="/usr/share/ca-certificates/bastionguard/${CA_FILE_BASENAME}.pem"
|
|
SYSTEM_CA_DST="$DST"
|
|
mkdir -p "$(dirname "$DST")"
|
|
install -m 0644 "$CA_SRC" "$DST"
|
|
CONF="/etc/ca-certificates/update.d/bastionguard.conf"
|
|
mkdir -p "$(dirname "$CONF")"
|
|
echo "bastionguard/${CA_FILE_BASENAME}.pem" > "$CONF"
|
|
update-ca-certificates
|
|
log "✅ CA installata (Void Linux)"
|
|
installed_sys=true
|
|
fi
|
|
|
|
if ! $installed_sys; then
|
|
warn "Distro non riconosciuta (ID='${ID:-?}', ID_LIKE='${ID_LIKE:-?}') — fallback"
|
|
|
|
if have_cmd trust; then
|
|
DST="/etc/ca-certificates/trust-source/anchors/${CA_FILE_BASENAME}.crt"
|
|
SYSTEM_CA_DST="$DST"
|
|
mkdir -p "$(dirname "$DST")"
|
|
install -m 0644 "$CA_SRC" "$DST"
|
|
trust extract-compat
|
|
log "✅ CA installata (fallback: trust)"
|
|
installed_sys=true
|
|
|
|
elif have_cmd update-ca-trust; then
|
|
DST="/etc/pki/ca-trust/source/anchors/${CA_FILE_BASENAME}.pem"
|
|
SYSTEM_CA_DST="$DST"
|
|
mkdir -p "$(dirname "$DST")"
|
|
install -m 0644 "$CA_SRC" "$DST"
|
|
restore_selinux_context "$DST" "$(dirname "$DST")" /etc/pki/ca-trust
|
|
update-ca-trust extract
|
|
fix_selinux_system_trust_contexts
|
|
log "✅ CA installata (fallback: update-ca-trust)"
|
|
installed_sys=true
|
|
|
|
elif have_cmd update-ca-certificates; then
|
|
install_debian_style \
|
|
"/usr/share/ca-certificates/local/${CA_FILE_BASENAME}.crt" \
|
|
"local/${CA_FILE_BASENAME}.crt"
|
|
log "✅ CA installata (fallback: update-ca-certificates)"
|
|
installed_sys=true
|
|
fi
|
|
fi
|
|
|
|
if ! $installed_sys; then
|
|
err "Nessun metodo di installazione trovato per questa distro"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -z "$SYSTEM_CA_DST" || ! -f "$SYSTEM_CA_DST" ]]; then
|
|
err "Copia CA di sistema non trovata dopo l'installazione"
|
|
exit 1
|
|
fi
|
|
if [[ "$(ca_fingerprint_sha256 "$SYSTEM_CA_DST" || true)" != "$(current_ca_fingerprint_sha256 || true)" ]]; then
|
|
err "La CA nel trust store di sistema non coincide con la CA usata dal proxy"
|
|
exit 1
|
|
fi
|
|
log "✅ Fingerprint CA di sistema verificata: $SYSTEM_CA_DST"
|
|
|
|
# Ripristina label SELinux sui trust store toccati, senza rendere obbligatorio SELinux.
|
|
fix_selinux_system_trust_contexts
|
|
fix_user_ca_permissions
|
|
|
|
# ── validate_ca dopo installazione sistema ───────────────────────────────────
|
|
validate_ca
|
|
|
|
# ── 2. NSS system-wide ────────────────────────────────────────────────────────
|
|
if have_cmd certutil; then
|
|
for db in /etc/pki/nssdb /usr/share/pki/nssdb; do
|
|
[ -d "$db" ] || continue
|
|
prefix="dbm:"
|
|
[ -f "$db/cert9.db" ] && prefix="sql:"
|
|
nss_delete_matching_nicknames_root "${prefix}${db}"
|
|
if certutil -A -d "${prefix}${db}" -n "$CA_NICKNAME" -t "CT,," -i "$CA_SRC" 2>/dev/null; then
|
|
nss_sys_fp="$(certutil -L -d "${prefix}${db}" -n "$CA_NICKNAME" -a 2>/dev/null \
|
|
| openssl x509 -noout -fingerprint -sha256 2>/dev/null \
|
|
| sed 's/^sha256 Fingerprint=//; s/^SHA256 Fingerprint=//' || true)"
|
|
if [[ "$nss_sys_fp" == "$(current_ca_fingerprint_sha256)" ]]; then
|
|
log "✅ NSS system verificato: $db"
|
|
else
|
|
warn "NSS system $db: fingerprint diversa dopo l'import"
|
|
fi
|
|
else
|
|
warn "NSS system $db: certutil fallito (non fatale)"
|
|
fi
|
|
restore_selinux_context "$db"
|
|
done
|
|
else
|
|
warn "certutil non trovato — NSS system-wide non aggiornato"
|
|
fi
|
|
|
|
# ── 3. NSS utente ─────────────────────────────────────────────────────────────
|
|
nss_db_prefix() {
|
|
[[ -f "$1/cert9.db" ]] && printf 'sql:%s' "$1" || printf 'dbm:%s' "$1"
|
|
}
|
|
|
|
nss_user_ca_fingerprint() {
|
|
local prefix="$1"
|
|
run_as_real_user certutil -L -d "$prefix" -n "$CA_NICKNAME" -a 2>/dev/null \
|
|
| openssl x509 -noout -fingerprint -sha256 2>/dev/null \
|
|
| sed 's/^sha256 Fingerprint=//; s/^SHA256 Fingerprint=//'
|
|
}
|
|
|
|
verify_nss_user_ca() {
|
|
local prefix="$1"
|
|
local expected actual
|
|
expected="$(current_ca_fingerprint_sha256 || true)"
|
|
actual="$(nss_user_ca_fingerprint "$prefix" || true)"
|
|
[[ -n "$expected" && "$actual" == "$expected" ]]
|
|
}
|
|
|
|
import_nss_db() {
|
|
local db="$1"
|
|
[[ -d "$db" ]] || return 0
|
|
[[ -f "$db/cert9.db" || -f "$db/cert8.db" ]] || return 0
|
|
|
|
local prefix
|
|
prefix="$(nss_db_prefix "$db")"
|
|
|
|
nss_delete_matching_nicknames_user "$prefix"
|
|
if run_as_real_user certutil -A -d "$prefix" -n "$CA_NICKNAME" -t "CT,," -i "$CA_SRC" >/dev/null 2>&1; then
|
|
if verify_nss_user_ca "$prefix"; then
|
|
log "✅ NSS utente verificato: $db"
|
|
else
|
|
warn "NSS utente $db: fingerprint diversa dopo l'import"
|
|
fi
|
|
else
|
|
warn "NSS utente $db: import fallito (non fatale)"
|
|
fi
|
|
[[ -n "${REAL_USER:-}" && -n "${REAL_GROUP:-}" ]] && chown -R "$REAL_USER:$REAL_GROUP" "$db" 2>/dev/null || true
|
|
restore_selinux_context "$db"
|
|
}
|
|
|
|
ensure_nssdb() {
|
|
local db="$1"
|
|
[[ -n "${REAL_USER:-}" ]] || return 0
|
|
|
|
if [[ ! -d "$db" ]]; then
|
|
log "Creo directory NSS DB: $db"
|
|
mkdir -p "$db"
|
|
chown "$REAL_USER:$REAL_GROUP" "$db"
|
|
chmod 700 "$db"
|
|
restore_selinux_context "$db"
|
|
fi
|
|
|
|
if [[ ! -f "$db/cert9.db" && ! -f "$db/cert8.db" ]]; then
|
|
log "Inizializzo NSS DB: $db"
|
|
if run_as_real_user certutil -N -d "sql:$db" --empty-password 2>/dev/null; then
|
|
log "✅ NSS DB creato: $db"
|
|
chown -R "$REAL_USER:$REAL_GROUP" "$db"
|
|
restore_selinux_context "$db"
|
|
else
|
|
if run_as_real_user certutil -N -d "dbm:$db" --empty-password 2>/dev/null; then
|
|
log "✅ NSS DB (dbm) creato: $db"
|
|
chown -R "$REAL_USER:$REAL_GROUP" "$db"
|
|
restore_selinux_context "$db"
|
|
else
|
|
warn "Impossibile creare NSS DB: $db"
|
|
return 1
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
import_firefox_profile_dir() {
|
|
local prof="$1"
|
|
[[ -d "$prof" ]] || return 0
|
|
|
|
if [[ ! -f "$prof/cert9.db" && ! -f "$prof/cert8.db" ]]; then
|
|
log "Inizializzo NSS DB nel profilo Firefox: $prof"
|
|
if run_as_real_user certutil -N -d "sql:$prof" --empty-password >/dev/null 2>&1; then
|
|
:
|
|
elif run_as_real_user certutil -N -d "dbm:$prof" --empty-password >/dev/null 2>&1; then
|
|
:
|
|
else
|
|
warn "Impossibile inizializzare NSS DB nel profilo: $prof"
|
|
return 1
|
|
fi
|
|
chown -R "$REAL_USER:$REAL_GROUP" "$prof" 2>/dev/null || true
|
|
restore_selinux_context "$prof"
|
|
fi
|
|
|
|
import_nss_db "$prof"
|
|
}
|
|
|
|
firefox_imported_seen=""
|
|
|
|
already_imported_firefox_profile() {
|
|
local prof="$1"
|
|
case "
|
|
$firefox_imported_seen
|
|
" in
|
|
*"
|
|
$prof
|
|
"*) return 0 ;;
|
|
*) return 1 ;;
|
|
esac
|
|
}
|
|
|
|
mark_imported_firefox_profile() {
|
|
local prof="$1"
|
|
firefox_imported_seen="${firefox_imported_seen}
|
|
$prof"
|
|
}
|
|
|
|
import_firefox_profile_once() {
|
|
local prof="$1"
|
|
[[ -n "$prof" ]] || return 0
|
|
[[ -d "$prof" ]] || return 0
|
|
|
|
if already_imported_firefox_profile "$prof"; then
|
|
return 0
|
|
fi
|
|
|
|
mark_imported_firefox_profile "$prof"
|
|
import_firefox_profile_dir "$prof"
|
|
}
|
|
|
|
import_firefox_root() {
|
|
local root="$1"
|
|
local found=0
|
|
local ini="$root/profiles.ini"
|
|
[[ -d "$root" ]] || return 0
|
|
|
|
firefox_imported_seen=""
|
|
|
|
log "Scansione profili Firefox in: $root"
|
|
|
|
# 1. Sezioni [Install...] con Default=...
|
|
if [[ -f "$ini" ]]; then
|
|
while IFS= read -r relpath; do
|
|
[[ -n "$relpath" ]] || continue
|
|
local prof="$root/$relpath"
|
|
if [[ -d "$prof" ]]; then
|
|
found=1
|
|
log "Profilo Firefox da sezione [Install*] Default=: $prof"
|
|
import_firefox_profile_once "$prof"
|
|
fi
|
|
done < <(
|
|
awk -F= '
|
|
BEGIN { in_install=0 }
|
|
/^\[Install[^]]*\]/ { in_install=1; next }
|
|
/^\[/ { in_install=0; next }
|
|
in_install && $1=="Default" {
|
|
gsub(/\r/,"",$2)
|
|
print $2
|
|
}
|
|
' "$ini" 2>/dev/null || true
|
|
)
|
|
fi
|
|
|
|
# 2. Profili [ProfileN] con Default=1
|
|
if [[ -f "$ini" ]]; then
|
|
while IFS='|' read -r is_relative path; do
|
|
[[ -n "$path" ]] || continue
|
|
local prof=""
|
|
if [[ "$is_relative" == "1" ]]; then
|
|
prof="$root/$path"
|
|
else
|
|
prof="$path"
|
|
fi
|
|
if [[ -d "$prof" ]]; then
|
|
found=1
|
|
log "Profilo Firefox da profiles.ini (Default=1): $prof"
|
|
import_firefox_profile_once "$prof"
|
|
fi
|
|
done < <(
|
|
awk -F= -v ROOT="$root" '
|
|
BEGIN {
|
|
in_profile=0
|
|
is_default=0
|
|
path=""
|
|
is_relative=1
|
|
}
|
|
|
|
/^\[Profile[0-9]+\]/ {
|
|
if (in_profile && is_default == 1 && path != "") {
|
|
printf "%s|%s\n", is_relative, path
|
|
}
|
|
in_profile=1
|
|
is_default=0
|
|
path=""
|
|
is_relative=1
|
|
next
|
|
}
|
|
|
|
/^\[/ {
|
|
if (in_profile && is_default == 1 && path != "") {
|
|
printf "%s|%s\n", is_relative, path
|
|
}
|
|
in_profile=0
|
|
next
|
|
}
|
|
|
|
in_profile && $1=="Default" {
|
|
gsub(/\r/,"",$2)
|
|
is_default=$2
|
|
next
|
|
}
|
|
|
|
in_profile && $1=="Path" {
|
|
gsub(/\r/,"",$2)
|
|
path=$2
|
|
next
|
|
}
|
|
|
|
in_profile && $1=="IsRelative" {
|
|
gsub(/\r/,"",$2)
|
|
is_relative=$2
|
|
next
|
|
}
|
|
|
|
END {
|
|
if (in_profile && is_default == 1 && path != "") {
|
|
printf "%s|%s\n", is_relative, path
|
|
}
|
|
}
|
|
' "$ini" 2>/dev/null || true
|
|
)
|
|
fi
|
|
|
|
# 3. Forza tutti i *.default-release
|
|
while IFS= read -r -d '' prof; do
|
|
found=1
|
|
log "Profilo Firefox forzato (*.default-release): $prof"
|
|
import_firefox_profile_once "$prof"
|
|
done < <(
|
|
find "$root" -mindepth 1 -maxdepth 1 -type d -name '*.default-release' -print0 2>/dev/null || true
|
|
)
|
|
|
|
# 4. Fallback finale su profili comuni
|
|
while IFS= read -r -d '' prof; do
|
|
found=1
|
|
log "Profilo Firefox fallback: $prof"
|
|
import_firefox_profile_once "$prof"
|
|
done < <(
|
|
find "$root" -mindepth 1 -maxdepth 1 -type d \
|
|
\( \
|
|
-name '*.default' -o \
|
|
-name '*.default-esr' -o \
|
|
-name '*.default-release' -o \
|
|
-name '*.profile' -o \
|
|
-name '*.release' -o \
|
|
-name '*.esr' \
|
|
\) \
|
|
-print0 2>/dev/null || true
|
|
)
|
|
|
|
if [[ "$found" -eq 0 ]]; then
|
|
warn "Nessun profilo Firefox trovato in: $root"
|
|
fi
|
|
}
|
|
|
|
if have_cmd certutil && [[ -n "${REAL_USER:-}" && -n "${REAL_HOME:-}" ]]; then
|
|
close_running_browsers
|
|
log "Aggiornamento NSS utente per: $REAL_USER"
|
|
|
|
ensure_nssdb "$REAL_HOME/.pki/nssdb"
|
|
import_nss_db "$REAL_HOME/.pki/nssdb"
|
|
|
|
# Firefox
|
|
import_firefox_root "$REAL_HOME/.mozilla/firefox"
|
|
import_firefox_root "$REAL_HOME/snap/firefox/common/.mozilla/firefox"
|
|
import_firefox_root "$REAL_HOME/.var/app/org.mozilla.firefox/.mozilla/firefox"
|
|
|
|
# Fork Firefox-like (nativi)
|
|
import_firefox_root "$REAL_HOME/.floorp"
|
|
import_firefox_root "$REAL_HOME/.librewolf"
|
|
import_firefox_root "$REAL_HOME/.waterfox"
|
|
import_firefox_root "$REAL_HOME/.zen"
|
|
import_firefox_root "$REAL_HOME/.mullvad"
|
|
import_firefox_root "$REAL_HOME/.ghostery"
|
|
import_firefox_root "$REAL_HOME/.basilisk"
|
|
import_firefox_root "$REAL_HOME/.moonchild productions/pale moon"
|
|
import_firefox_root "$REAL_HOME/.config/floorp"
|
|
import_firefox_root "$REAL_HOME/.config/librewolf"
|
|
|
|
# Fork Firefox-like (flatpak)
|
|
import_firefox_root "$REAL_HOME/.var/app/io.gitlab.librewolf-community/.librewolf"
|
|
import_firefox_root "$REAL_HOME/.var/app/net.waterfox.waterfox/.waterfox"
|
|
import_firefox_root "$REAL_HOME/.var/app/one.ablaze.floorp/.floorp"
|
|
import_firefox_root "$REAL_HOME/.var/app/com.mullvad.MullvadBrowser/.mullvad"
|
|
|
|
# Browser snap Chromium-based / affini
|
|
for _snap_nssdb in \
|
|
"$REAL_HOME/snap/chromium/current/.pki/nssdb" \
|
|
"$REAL_HOME/snap/chromium/common/.pki/nssdb" \
|
|
"$REAL_HOME/snap/brave/current/.pki/nssdb" \
|
|
"$REAL_HOME/snap/microsoft-edge/current/.pki/nssdb" \
|
|
"$REAL_HOME/snap/opera/current/.pki/nssdb" \
|
|
"$REAL_HOME/snap/vivaldi/current/.pki/nssdb" \
|
|
; do
|
|
ensure_nssdb "$_snap_nssdb"
|
|
import_nss_db "$_snap_nssdb"
|
|
done
|
|
|
|
# Browser Chromium-based nativi — tutti usano ~/.pki/nssdb (già importato sopra)
|
|
# ma alcuni hanno il proprio nssdb separato
|
|
for _chrome_cfg in \
|
|
"$REAL_HOME/.config/google-chrome" \
|
|
"$REAL_HOME/.config/google-chrome-beta" \
|
|
"$REAL_HOME/.config/google-chrome-unstable" \
|
|
"$REAL_HOME/.config/chromium" \
|
|
"$REAL_HOME/.config/chromium-browser" \
|
|
"$REAL_HOME/.config/BraveSoftware/Brave-Browser" \
|
|
"$REAL_HOME/.config/BraveSoftware/Brave-Browser-Beta" \
|
|
"$REAL_HOME/.config/BraveSoftware/Brave-Browser-Nightly" \
|
|
"$REAL_HOME/.config/vivaldi" \
|
|
"$REAL_HOME/.config/vivaldi-snapshot" \
|
|
"$REAL_HOME/.config/opera" \
|
|
"$REAL_HOME/.config/opera-beta" \
|
|
"$REAL_HOME/.config/opera-developer" \
|
|
"$REAL_HOME/.config/microsoft-edge" \
|
|
"$REAL_HOME/.config/microsoft-edge-beta" \
|
|
"$REAL_HOME/.config/microsoft-edge-dev" \
|
|
"$REAL_HOME/.config/thorium" \
|
|
"$REAL_HOME/.config/Thorium" \
|
|
"$REAL_HOME/.config/ungoogled-chromium" \
|
|
"$REAL_HOME/.config/yandex-browser" \
|
|
"$REAL_HOME/.config/epiphany" \
|
|
; do
|
|
[[ -d "$_chrome_cfg" ]] || continue
|
|
while IFS= read -r -d '' p; do
|
|
ensure_nssdb "$p"
|
|
import_nss_db "$p"
|
|
done < <(find "$_chrome_cfg" -type d -name nssdb -print0 2>/dev/null || true)
|
|
done
|
|
|
|
# Chromium / Chrome / Brave / Vivaldi / Edge / Opera flatpak
|
|
for _flatpak_id in \
|
|
org.chromium.Chromium \
|
|
com.google.Chrome \
|
|
com.google.ChromeDev \
|
|
com.brave.Browser \
|
|
com.vivaldi.Vivaldi \
|
|
com.opera.Opera \
|
|
com.microsoft.Edge \
|
|
io.github.ungoogled_software.ungoogled_chromium \
|
|
com.github.Eloston.UngoogledChromium \
|
|
org.gnome.Epiphany \
|
|
; do
|
|
if [[ -d "$REAL_HOME/.var/app/${_flatpak_id}" ]]; then
|
|
ensure_nssdb "$REAL_HOME/.var/app/${_flatpak_id}/.pki/nssdb"
|
|
import_nss_db "$REAL_HOME/.var/app/${_flatpak_id}/.pki/nssdb"
|
|
fi
|
|
while IFS= read -r -d '' p; do
|
|
ensure_nssdb "$p"
|
|
import_nss_db "$p"
|
|
done < <(find "$REAL_HOME/.var/app/${_flatpak_id}" -type d -name nssdb -print0 2>/dev/null || true)
|
|
done
|
|
|
|
fix_selinux_user_browser_contexts
|
|
else
|
|
warn "certutil non disponibile o utente reale non determinato — NSS utente saltato"
|
|
fi
|
|
|
|
# ── 4. Step finale di sicurezza ───────────────────────────────────────────────
|
|
if have_cmd certutil && [[ -n "${REAL_USER:-}" && -n "${REAL_HOME:-}" ]]; then
|
|
FINAL_NSS_DB="$REAL_HOME/.pki/nssdb"
|
|
|
|
log "Step finale di sicurezza su NSS utente: $FINAL_NSS_DB"
|
|
ensure_nssdb "$FINAL_NSS_DB"
|
|
|
|
nss_delete_matching_nicknames_user "sql:$FINAL_NSS_DB"
|
|
|
|
if run_as_real_user certutil -A \
|
|
-d "sql:$FINAL_NSS_DB" \
|
|
-n "$CA_NICKNAME" \
|
|
-t "CT,," \
|
|
-i "$CA_SRC" >/dev/null 2>&1; then
|
|
log "✅ Step finale NSS utente completato"
|
|
else
|
|
warn "Step finale NSS utente fallito in sql:, provo dbm:"
|
|
nss_delete_matching_nicknames_user "dbm:$FINAL_NSS_DB"
|
|
if run_as_real_user certutil -A \
|
|
-d "dbm:$FINAL_NSS_DB" \
|
|
-n "$CA_NICKNAME" \
|
|
-t "CT,," \
|
|
-i "$CA_SRC" >/dev/null 2>&1; then
|
|
log "✅ Step finale NSS utente completato (dbm)"
|
|
else
|
|
warn "Step finale NSS utente fallito anche in dbm:"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
fix_selinux_user_browser_contexts
|
|
|
|
# ── 5. Verifica finale ────────────────────────────────────────────────────────
|
|
if have_cmd update-ca-certificates && [[ -d /etc/ssl/certs ]]; then
|
|
hash_val="$(openssl x509 -in "$CA_SRC" -noout -hash 2>/dev/null || true)"
|
|
if [[ -n "$hash_val" ]] && ls "/etc/ssl/certs/${hash_val}".* >/dev/null 2>&1; then
|
|
log "✅ Verifica bundle OK: /etc/ssl/certs/${hash_val}.0 presente"
|
|
else
|
|
warn "Hash non trovato in /etc/ssl/certs — verifica: ls /etc/ssl/certs/${hash_val}.*"
|
|
fi
|
|
fi
|
|
|
|
if have_cmd certutil && [[ -n "${REAL_USER:-}" && -n "${REAL_HOME:-}" ]]; then
|
|
if verify_nss_user_ca "sql:$REAL_HOME/.pki/nssdb"; then
|
|
log "✅ Verifica NSS utente/fingerprint OK: $REAL_HOME/.pki/nssdb"
|
|
else
|
|
warn "Verifica NSS utente non riuscita o fingerprint errata su sql:$REAL_HOME/.pki/nssdb"
|
|
fi
|
|
fi
|
|
|
|
install_bastionguard_cef_selinux_policy
|
|
fix_user_ca_permissions
|
|
fix_selinux_system_trust_contexts
|
|
fix_selinux_user_browser_contexts
|
|
restart_bastionguard_cef_user_service_if_active
|
|
log "CA attiva (SHA-256): $(current_ca_fingerprint_sha256)"
|
|
log "✅ Installazione completata"
|
|
exit 0
|