Files
fn_registry/bash/functions/browser/apply_chromium_extension_policy.sh
T
Egutierrez b75bd7e154 feat(browser): apply_chromium_extension_policy soporta --keep id=update_url
Permite force-instalar extensiones self-hosted bajo managed policy indicando
un update_url propio (p.ej. file:// a un update.xml local que apunta a un .crx).
Necesario para cargar extensiones propias (como la de captura de web_proxy)
cuando hay una managed policy activa y --load-extension queda desactivado en
Chromium 137+. Forma simple '<id>' sigue usando el update_url por defecto.
2026-06-05 17:22:20 +02:00

258 lines
10 KiB
Bash

#!/usr/bin/env bash
# apply_chromium_extension_policy — Escribe de forma idempotente la política managed de Chromium
# que fuerza la instalación de una whitelist de extensiones y bloquea (desinstala) una blocklist
# concreta, sin tocar el resto. Usa ExtensionInstallForcelist + ExtensionInstallBlocklist.
apply_chromium_extension_policy() {
local policy_path="/etc/chromium/policies/managed/extensions.json"
local update_url="https://clients2.google.com/service/update2/crx"
local dry_run=0
local -a keep_ids=()
local -a block_ids=()
# --- Parseo de argumentos ---
while [[ $# -gt 0 ]]; do
case "$1" in
--keep)
if [[ -z "${2:-}" ]]; then
echo "apply_chromium_extension_policy: --keep requiere un ID de extensión" >&2
return 1
fi
keep_ids+=("$2")
shift 2
;;
--block)
if [[ -z "${2:-}" ]]; then
echo "apply_chromium_extension_policy: --block requiere un ID de extensión" >&2
return 1
fi
block_ids+=("$2")
shift 2
;;
--policy-path)
if [[ -z "${2:-}" ]]; then
echo "apply_chromium_extension_policy: --policy-path requiere un valor" >&2
return 1
fi
policy_path="$2"
shift 2
;;
--update-url)
if [[ -z "${2:-}" ]]; then
echo "apply_chromium_extension_policy: --update-url requiere un valor" >&2
return 1
fi
update_url="$2"
shift 2
;;
--dry-run)
dry_run=1
shift
;;
*)
echo "apply_chromium_extension_policy: argumento desconocido: $1" >&2
echo "Uso: apply_chromium_extension_policy [--keep <ext_id>]... [--block <ext_id>]... [--policy-path <path>] [--update-url <url>] [--dry-run]" >&2
return 1
;;
esac
done
# --- Validar que hay al menos una extensión a forzar o bloquear ---
if [[ ${#keep_ids[@]} -eq 0 && ${#block_ids[@]} -eq 0 ]]; then
echo "apply_chromium_extension_policy: se requiere al menos un --keep o un --block <ext_id>" >&2
return 1
fi
# --- Construir el JSON ---
# Dos claves complementarias, ninguna bloquea las extensiones unpacked (--load-extension),
# de modo que extensiones locales como la de captura de web_proxy siguen cargando:
# 1. ExtensionInstallForcelist: fuerza la instalación de la whitelist (--keep), que además
# no se puede desinstalar desde la UI.
# 2. ExtensionInstallBlocklist: bloquea Y desinstala las extensiones de la blocklist
# (--block) en cualquier perfil. Solo afecta a los IDs listados; el resto no se toca.
local forcelist_json="[]" blocklist_json="[]"
if [[ ${#keep_ids[@]} -gt 0 ]]; then
local entries="" first=1
for kid in "${keep_ids[@]}"; do
# Cada --keep puede ser "<id>" (usa el update_url por defecto, Web Store) o
# "<id>=<update_url>" para una extensión self-hosted (p.ej. file:// a un update.xml local).
local id="${kid%%=*}" url="$update_url"
[[ "$kid" == *=* ]] && url="${kid#*=}"
[[ $first -eq 0 ]] && entries+=","$'\n'
entries+=" \"${id};${url}\""
first=0
done
forcelist_json=$(printf '[\n%s\n ]' "$entries")
fi
if [[ ${#block_ids[@]} -gt 0 ]]; then
local entries="" first=1
for id in "${block_ids[@]}"; do
[[ $first -eq 0 ]] && entries+=","$'\n'
entries+=" \"${id}\""
first=0
done
blocklist_json=$(printf '[\n%s\n ]' "$entries")
fi
local new_json
new_json=$(cat <<JSONEOF
{
"ExtensionInstallForcelist": ${forcelist_json},
"ExtensionInstallBlocklist": ${blocklist_json}
}
JSONEOF
)
# --- Modo dry-run ---
if [[ $dry_run -eq 1 ]]; then
echo "[dry-run] JSON que se escribiría en: ${policy_path}"
echo "---"
echo "$new_json"
echo "---"
echo "[dry-run] No se ha modificado el sistema."
return 0
fi
# --- Idempotencia: comparar con contenido actual ---
if [[ -f "$policy_path" ]]; then
local current_content
current_content=$(cat "$policy_path" 2>/dev/null || true)
if [[ "$current_content" == "$new_json" ]]; then
echo "apply_chromium_extension_policy: política ya aplicada (sin cambios). Nada que hacer."
return 0
fi
fi
# --- Backup del archivo existente ---
# CRÍTICO: el backup NUNCA puede vivir dentro del directorio de la policy. Chromium lee TODOS
# los archivos del directorio managed/ (sin filtrar por extensión de nombre), así que un
# "extensions.json.bak.YYYYMMDD" dentro de managed/ se mergea con la policy efectiva y reinyecta
# las extensiones del backup. Por eso el backup se guarda en un directorio hermano (policy-backups)
# que chromium no lee.
local backup_path=""
if [[ -f "$policy_path" ]]; then
local date_suffix policy_dir backup_dir
date_suffix=$(date +%Y%m%d)
policy_dir="$(dirname "$policy_path")"
case "$(basename "$policy_dir")" in
managed|recommended) backup_dir="$(dirname "$policy_dir")/policy-backups" ;;
*) backup_dir="$policy_dir" ;;
esac
backup_path="${backup_dir}/$(basename "$policy_path").bak.${date_suffix}"
if [[ ! -d "$backup_dir" ]]; then
if [[ $EUID -ne 0 ]]; then sudo mkdir -p "$backup_dir" 2>/dev/null; else mkdir -p "$backup_dir"; fi
fi
if [[ ! -f "$backup_path" ]]; then
echo "apply_chromium_extension_policy: creando backup → ${backup_path}"
if [[ $EUID -ne 0 ]]; then
sudo cp "$policy_path" "$backup_path" || {
echo "apply_chromium_extension_policy: no se pudo crear el backup en ${backup_path}" >&2
return 1
}
else
cp "$policy_path" "$backup_path" || {
echo "apply_chromium_extension_policy: no se pudo crear el backup en ${backup_path}" >&2
return 1
}
fi
else
echo "apply_chromium_extension_policy: backup del día ya existe (${backup_path}), se omite."
fi
fi
# --- Crear directorio padre si no existe ---
local policy_dir
policy_dir=$(dirname "$policy_path")
if [[ ! -d "$policy_dir" ]]; then
echo "apply_chromium_extension_policy: creando directorio ${policy_dir}"
if [[ $EUID -ne 0 ]]; then
sudo mkdir -p "$policy_dir" || {
echo "apply_chromium_extension_policy: no se pudo crear el directorio ${policy_dir}" >&2
return 1
}
else
mkdir -p "$policy_dir" || {
echo "apply_chromium_extension_policy: no se pudo crear el directorio ${policy_dir}" >&2
return 1
}
fi
fi
# --- Escribir el JSON vía tmpfile + sudo cp ---
local tmpfile
tmpfile=$(mktemp /tmp/chromium_policy_XXXXXX.json)
echo "$new_json" > "$tmpfile"
if [[ $EUID -ne 0 ]]; then
sudo cp "$tmpfile" "$policy_path" || {
echo "apply_chromium_extension_policy: no se pudo escribir ${policy_path}" >&2
rm -f "$tmpfile"
if [[ -n "$backup_path" && -f "$backup_path" ]]; then
echo "apply_chromium_extension_policy: restaurando backup tras error..."
sudo cp "$backup_path" "$policy_path" 2>/dev/null || true
fi
return 1
}
else
cp "$tmpfile" "$policy_path" || {
echo "apply_chromium_extension_policy: no se pudo escribir ${policy_path}" >&2
rm -f "$tmpfile"
if [[ -n "$backup_path" && -f "$backup_path" ]]; then
echo "apply_chromium_extension_policy: restaurando backup tras error..."
cp "$backup_path" "$policy_path" 2>/dev/null || true
fi
return 1
}
fi
rm -f "$tmpfile"
# --- Validar el JSON escrito ---
local validation_ok=0
if command -v python3 &>/dev/null; then
python3 -c "import json,sys; json.load(open(sys.argv[1]))" "$policy_path" 2>/dev/null && validation_ok=1
elif command -v jq &>/dev/null; then
jq . "$policy_path" &>/dev/null && validation_ok=1
else
validation_ok=1
fi
if [[ $validation_ok -eq 0 ]]; then
echo "apply_chromium_extension_policy: el JSON escrito no es válido — restaurando backup" >&2
if [[ -n "$backup_path" && -f "$backup_path" ]]; then
if [[ $EUID -ne 0 ]]; then
sudo cp "$backup_path" "$policy_path" 2>/dev/null || true
else
cp "$backup_path" "$policy_path" 2>/dev/null || true
fi
fi
return 1
fi
# --- Resumen final ---
echo "apply_chromium_extension_policy: política aplicada correctamente."
echo " Ruta : ${policy_path}"
if [[ ${#keep_ids[@]} -gt 0 ]]; then
echo " Forzadas (${#keep_ids[@]}):"
for id in "${keep_ids[@]}"; do echo " - ${id}"; done
fi
if [[ ${#block_ids[@]} -gt 0 ]]; then
echo " Bloqueadas/desinstaladas (${#block_ids[@]}):"
for id in "${block_ids[@]}"; do echo " - ${id}"; done
fi
echo " Extensiones unpacked (--load-extension, p.ej. web_proxy): NO afectadas."
if [[ -n "$backup_path" && -f "$backup_path" ]]; then
echo " Backup : ${backup_path}"
fi
echo ""
echo " AVISO: Chromium cachea la politica en memoria. Para que surta efecto:"
echo " pkill -9 chromium (cierra TODOS los procesos)"
echo " # Luego relanza Chromium. O desde un Chromium abierto:"
echo " # chrome://policy → 'Reload policies'"
}
# Auto-ejecución al correr el archivo directo (bash file.sh / fn run). Si se hace `source`,
# solo se define la función y no se ejecuta nada.
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
apply_chromium_extension_policy "$@"
fi