e0fad0e82f
Rediseño de apply_chromium_extension_policy y nueva función de purga in-place, tras resolver por qué las extensiones bloqueadas reaparecían en Chromium 148. - apply_chromium_extension_policy: añade --block (ExtensionInstallBlocklist). Reemplaza el modo ExtensionSettings "*": blocked (que rompía las extensiones unpacked vía --load-extension, p.ej. la de captura de web_proxy con el error 'Loading of unpacked extensions is disabled by the administrator') por una blocklist específica. FIX RAÍZ: los backups se guardan fuera de policies/managed/ (en policy-backups/), porque Chromium lee TODOS los archivos del directorio managed/ sin filtrar extensión de nombre — un extensions.json.bak ahí se mergea con la policy y reinyecta las extensiones del backup (location=7). - clean_chrome_profile_extensions (nueva): purga in-place de un perfil existente (borra carpetas de Extensions/ + refs en Preferences/Secure Preferences) dejando solo la whitelist. Complementa la policy: la policy evita reinstalación, esta desinstala lo ya presente. Requiere chromium cerrado. Ambas: dominio browser, grupo navegator, guard de auto-ejecución, dry-run.
254 lines
10 KiB
Bash
254 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 id in "${keep_ids[@]}"; do
|
|
[[ $first -eq 0 ]] && entries+=","$'\n'
|
|
entries+=" \"${id};${update_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
|