Add Ubuntu configuration backup and restore scripts

This commit is contained in:
Carlos Tello
2026-09-21 10:17:31 -03:00
parent 9bd3d1052d
commit c9a2e73c61
3 changed files with 427 additions and 0 deletions
+54
View File
@@ -97,6 +97,60 @@ openclaw doctor
openclaw gateway status openclaw gateway status
``` ```
## Respaldo y recuperación
Para guardar la configuración del servidor antes de realizar cambios, ejecuta
el script como `root`:
```bash
chmod +x backup_ubuntu_config.sh restore_ubuntu_config.sh
sudo ./backup_ubuntu_config.sh
```
El respaldo se guarda por defecto en `/var/backups` e incluye inventario de
hardware, paquetes, servicios, red, UFW, systemd, NVIDIA, Ollama, LiteLLM,
OpenClaw y configuraciones de usuario. No incluye modelos Ollama, el entorno
virtual de LiteLLM, bases de datos ni logs completos.
Las claves SSH, certificados privados y credenciales de OpenClaw quedan fuera
por defecto. Para incluirlos explícitamente, protege el archivo resultante:
```bash
sudo ./backup_ubuntu_config.sh --include-secrets
sudo chmod 600 /var/backups/ubuntu-config-*.tar.gz
```
En una instalación Ubuntu nueva, instala primero el sistema base y crea los
usuarios necesarios. Después copia el archivo de respaldo y usa el restaurador
en modo vista previa:
```bash
sudo ./restore_ubuntu_config.sh /ruta/al/respaldo.tar.gz
```
Para aplicar la configuración:
```bash
sudo ./restore_ubuntu_config.sh /ruta/al/respaldo.tar.gz --yes
```
Puedes reinstalar los paquetes APT y arrancar los servicios habilitados de la
máquina original de forma explícita:
```bash
sudo ./restore_ubuntu_config.sh /ruta/al/respaldo.tar.gz \
--yes --install-packages --start-services
```
Revisa siempre los servicios y el firewall después de restaurar:
```bash
systemctl --failed
systemctl status ollama litellm
sudo ufw status verbose
nvidia-smi
```
## Tool calling y rutas de Windows ## Tool calling y rutas de Windows
Cuando un modelo envía rutas de Windows dentro de argumentos JSON, las barras Cuando un modelo envía rutas de Windows dentro de argumentos JSON, las barras
+209
View File
@@ -0,0 +1,209 @@
#!/usr/bin/env bash
set -Eeuo pipefail
readonly SCRIPT_NAME="$(basename "$0")"
readonly DEFAULT_OUTPUT_DIR="/var/backups"
include_secrets=false
output_dir="$DEFAULT_OUTPUT_DIR"
show_help() {
cat <<EOF
Uso: sudo $SCRIPT_NAME [opciones]
Crea un respaldo comprimido de la configuración de Ubuntu para replicarla en
otra instalación. No incluye modelos Ollama, entornos virtuales ni bases de datos.
Opciones:
--output-dir DIR Directorio donde guardar el archivo .tar.gz.
--include-secrets Incluye claves SSH, certificados privados y secretos
de OpenClaw. El archivo resultante debe protegerse.
-h, --help Muestra esta ayuda.
EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
--output-dir)
[[ $# -ge 2 ]] || { echo "Falta DIR para --output-dir" >&2; exit 2; }
output_dir="$2"
shift 2
;;
--include-secrets)
include_secrets=true
shift
;;
-h|--help)
show_help
exit 0
;;
*)
echo "Opción desconocida: $1" >&2
show_help >&2
exit 2
;;
esac
done
if [[ "${EUID}" -ne 0 ]]; then
echo "Ejecuta este script con sudo o como root." >&2
exit 1
fi
command -v tar >/dev/null || { echo "tar es obligatorio." >&2; exit 1; }
command -v gzip >/dev/null || { echo "gzip es obligatorio." >&2; exit 1; }
hostname_value="$(hostname -s 2>/dev/null || echo unknown)"
timestamp="$(date +%Y%m%d-%H%M%S)"
backup_name="ubuntu-config-${hostname_value}-${timestamp}"
work_dir="$(mktemp -d)"
archive_path="${output_dir}/${backup_name}.tar.gz"
cleanup() {
rm -rf "$work_dir"
}
trap cleanup EXIT
mkdir -p "$work_dir/metadata" "$work_dir/files" "$work_dir/packages" "$work_dir/services"
mkdir -p "$output_dir"
umask 077
copy_path() {
local source="$1"
local destination="$work_dir/files$source"
if [[ -e "$source" || -L "$source" ]]; then
mkdir -p "$(dirname "$destination")"
cp -a "$source" "$destination"
fi
}
capture() {
local name="$1"
shift
"$@" > "$work_dir/metadata/$name.txt" 2>&1 || true
}
capture_shell() {
local name="$1"
local command_text="$2"
bash -c "$command_text" > "$work_dir/metadata/$name.txt" 2>&1 || true
}
printf 'Creando respaldo en %s\n' "$archive_path"
printf '%s\n' "hostname=$hostname_value" "created_at=$(date --iso-8601=seconds)" \
"include_secrets=$include_secrets" > "$work_dir/metadata/backup-info.txt"
capture_shell os-release 'cat /etc/os-release'
capture_shell kernel 'uname -a'
capture_shell hardware 'lscpu; echo; free -h; echo; lsblk -f'
capture_shell disks 'df -hT; echo; findmnt'
capture_shell network 'ip -brief address; echo; ip route; echo; resolvectl status 2>/dev/null || true'
capture_shell users 'getent passwd | awk -F: '\''$3 >= 1000 || $1 == "root" {print $1 ":" $3 ":" $4 ":" $6}'\'''
capture_shell mounts 'mount'
capture_shell environment 'printenv | sort'
capture_shell nvidia 'command -v nvidia-smi && nvidia-smi -q || true'
capture_shell ollama 'command -v ollama && ollama list || true'
capture_shell versions 'command -v node && node --version || true; command -v npm && npm --version || true; command -v python3 && python3 --version || true; command -v ollama && ollama --version || true; command -v openclaw && openclaw --version || true'
capture_shell ufw 'command -v ufw && ufw status verbose || true'
capture_shell iptables 'command -v iptables-save && iptables-save || true'
capture_shell sysctl 'sysctl -a 2>/dev/null'
capture_shell timers 'systemctl list-timers --all --no-pager'
capture_shell enabled-services 'systemctl list-unit-files --state=enabled --no-legend --no-pager'
capture_shell failed-services 'systemctl --failed --no-pager'
if command -v dpkg-query >/dev/null; then
dpkg-query -W -f='${binary:Package}\t${Version}\n' > "$work_dir/packages/dpkg-status.tsv" || true
fi
if command -v apt-mark >/dev/null; then
apt-mark showmanual | sort > "$work_dir/packages/apt-manual.txt" || true
fi
if command -v snap >/dev/null; then
snap list > "$work_dir/packages/snap-list.txt" 2>&1 || true
fi
if command -v pip3 >/dev/null; then
pip3 freeze > "$work_dir/packages/pip3-freeze.txt" 2>&1 || true
fi
systemctl list-unit-files --type=service --no-legend --no-pager \
> "$work_dir/services/all-service-units.txt" 2>&1 || true
systemctl list-unit-files --state=enabled --type=service --no-legend --no-pager \
| awk '{print $1}' | sort -u > "$work_dir/services/enabled-service-names.txt" || true
# Configuración del sistema y de los servicios usados por este proyecto.
for path in \
/etc/apt \
/etc/default \
/etc/environment \
/etc/fstab \
/etc/hostname \
/etc/hosts \
/etc/issue \
/etc/netplan \
/etc/NetworkManager \
/etc/systemd/system \
/etc/sysctl.d \
/etc/modprobe.d \
/etc/ufw \
/etc/ollama \
/etc/nvidia \
/etc/profile.d
do
copy_path "$path"
done
# Configuraciones de usuario que no suelen contener credenciales.
while IFS=: read -r username _ uid _ _ home _; do
[[ -d "$home" ]] || continue
[[ "$uid" -ge 1000 || "$username" == root ]] || continue
for relative_path in .bashrc .profile .config/systemd/user litellm_config.yaml; do
copy_path "$home/$relative_path"
done
printf '%s\t%s\t%s\t%s\n' "$username" "$uid" "$(id -g "$username" 2>/dev/null || echo 0)" "$home" \
>> "$work_dir/metadata/users.tsv"
done < <(getent passwd)
if [[ "$include_secrets" == true ]]; then
printf 'Incluyendo archivos sensibles: SSH, certificados y configuración privada de OpenClaw.\n'
for path in /etc/ssh /etc/ssl/private /etc/letsencrypt; do
copy_path "$path"
done
while IFS=: read -r username _ uid _ _ home _; do
[[ -d "$home" ]] || continue
[[ "$uid" -ge 1000 || "$username" == root ]] || continue
for relative_path in .ssh .openclaw .npmrc; do
copy_path "$home/$relative_path"
done
done < <(getent passwd)
else
cat > "$work_dir/metadata/excluded-secrets.txt" <<EOF
No se incluyeron secretos. Para incluirlos explícitamente, usa:
sudo $SCRIPT_NAME --include-secrets
Excluidos por defecto: /etc/ssh, /etc/ssl/private, /etc/letsencrypt, ~/.ssh,
~/.openclaw y ~/.npmrc.
EOF
fi
cat > "$work_dir/RESTORE.txt" <<EOF
Este archivo fue generado por $SCRIPT_NAME.
1. Instala Ubuntu con la misma arquitectura y crea el usuario de servicio.
2. Copia este archivo a la nueva máquina.
3. Ejecuta restore_ubuntu_config.sh con este archivo y revisa los cambios.
4. Reinstala los modelos Ollama y valida los servicios antes de exponerlos.
El respaldo no contiene modelos Ollama, /opt/litellm-env, bases de datos ni
logs completos. Si no usaste --include-secrets, debes restaurar las credenciales
manualmente.
EOF
printf 'Archivos incluidos:\n'
find "$work_dir" -type f -printf '%P\n' | sort > "$work_dir/metadata/file-list.txt"
cat "$work_dir/metadata/file-list.txt"
tar -C "$work_dir" -czf "$archive_path" .
chmod 600 "$archive_path"
printf '\nRespaldo creado: %s\n' "$archive_path"
printf 'Protección: permisos 600\n'
+164
View File
@@ -0,0 +1,164 @@
#!/usr/bin/env bash
set -Eeuo pipefail
readonly SCRIPT_NAME="$(basename "$0")"
archive_path=""
confirmed=false
install_packages=false
start_services=false
show_help() {
cat <<EOF
Uso: sudo $SCRIPT_NAME ARCHIVO.tar.gz [opciones]
Restaura configuraciones de un respaldo creado por backup_ubuntu_config.sh.
Por seguridad, sin --yes solo muestra lo que se restauraría.
Opciones:
--yes Ejecuta la restauración.
--install-packages Instala los paquetes marcados como manuales en el respaldo.
--start-services Habilita y arranca los servicios que estaban habilitados.
-h, --help Muestra esta ayuda.
EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
--yes)
confirmed=true
shift
;;
--install-packages)
install_packages=true
shift
;;
--start-services)
start_services=true
shift
;;
-h|--help)
show_help
exit 0
;;
-*)
echo "Opción desconocida: $1" >&2
show_help >&2
exit 2
;;
*)
if [[ -n "$archive_path" ]]; then
echo "Solo se admite un archivo de respaldo." >&2
exit 2
fi
archive_path="$1"
shift
;;
esac
done
if [[ -z "$archive_path" ]]; then
echo "Debes indicar un archivo .tar.gz." >&2
show_help >&2
exit 2
fi
if [[ "${EUID}" -ne 0 ]]; then
echo "Ejecuta este script con sudo o como root." >&2
exit 1
fi
[[ -f "$archive_path" ]] || { echo "No existe: $archive_path" >&2; exit 1; }
command -v tar >/dev/null || { echo "tar es obligatorio." >&2; exit 1; }
work_dir="$(mktemp -d)"
cleanup() {
rm -rf "$work_dir"
}
trap cleanup EXIT
tar -xzf "$archive_path" -C "$work_dir" --no-same-owner
if [[ ! -d "$work_dir/files" || ! -f "$work_dir/metadata/backup-info.txt" ]]; then
echo "El archivo no parece un respaldo válido de backup_ubuntu_config.sh." >&2
exit 1
fi
printf 'Respaldo: %s\n' "$archive_path"
cat "$work_dir/metadata/backup-info.txt"
printf '\nArchivos de configuración que se restaurarían:\n'
find "$work_dir/files" -mindepth 1 -maxdepth 8 -printf '%P\n' | sort | sed -n '1,200p'
if [[ "$confirmed" != true ]]; then
cat <<EOF
Vista previa solamente. Para aplicar estos cambios:
sudo $SCRIPT_NAME "$archive_path" --yes
Opciones adicionales:
--install-packages Reinstala paquetes APT manuales.
--start-services Habilita y arranca los servicios respaldados.
EOF
exit 0
fi
if [[ "$install_packages" == true && -s "$work_dir/packages/apt-manual.txt" ]]; then
export DEBIAN_FRONTEND=noninteractive
apt-get update
xargs -r apt-get install -y --no-install-recommends < "$work_dir/packages/apt-manual.txt"
fi
if [[ -d "$work_dir/files/etc" ]]; then
cp -a "$work_dir/files/etc/." /etc/
fi
if [[ -d "$work_dir/files/home" ]]; then
cp -a "$work_dir/files/home/." /home/
fi
if [[ -d "$work_dir/files/root" ]]; then
cp -a "$work_dir/files/root/." /root/
fi
if [[ -s "$work_dir/metadata/users.tsv" ]]; then
while IFS=$'\t' read -r username _ _ home; do
[[ -n "$username" && -d "$home" ]] || continue
if id "$username" >/dev/null 2>&1; then
chown -R "$username:$(id -gn "$username")" "$home" 2>/dev/null || true
else
echo "Aviso: no existe el usuario $username; revisa $home manualmente." >&2
fi
done < "$work_dir/metadata/users.tsv"
fi
systemctl daemon-reload
if [[ "$start_services" == true && -s "$work_dir/services/enabled-service-names.txt" ]]; then
while IFS= read -r service; do
[[ -n "$service" ]] || continue
systemctl enable "$service" 2>/dev/null || echo "Aviso: no se pudo habilitar $service" >&2
done < "$work_dir/services/enabled-service-names.txt"
systemctl restart ollama 2>/dev/null || true
systemctl restart litellm 2>/dev/null || true
systemctl restart openclaw-gateway 2>/dev/null || true
fi
if command -v ufw >/dev/null 2>&1; then
ufw --force reload 2>/dev/null || true
fi
cat <<EOF
Restauración completada.
Revisa antes de exponer el servidor:
systemctl --failed
systemctl status ollama litellm
ufw status verbose
nvidia-smi
Los modelos Ollama y el entorno virtual de LiteLLM no forman parte del respaldo.
Debes reinstalarlos o descargarlos por separado.
EOF