Skip to content

Instantly share code, notes, and snippets.

@eduardomazolini
Created September 22, 2025 16:24
Show Gist options
  • Select an option

  • Save eduardomazolini/124d62de2b0c50b0a15de2d25ca766e2 to your computer and use it in GitHub Desktop.

Select an option

Save eduardomazolini/124d62de2b0c50b0a15de2d25ca766e2 to your computer and use it in GitHub Desktop.
Edita a imagem do Debian13 para a as minhas necessidades mas de forma bem generica.
#!/bin/bash
# Script para customizar cloud images Debian sem modificar a original
# Uso: ./customize-image.sh <imagem-original> [imagem-saida]
# Versão: 2.0
# Autor: Eduardo Mazolini
set -euo pipefail # Melhor tratamento de erros
# ===================================================================
# CONFIGURAÇÕES GLOBAIS
# ===================================================================
readonly SCRIPT_NAME="$(basename "$0")"
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Configurações do console serial
readonly SERIAL_DEVICE="ttyS0"
readonly BAUD_RATE="115200"
# Cores para output
readonly RED=$(printf '\033[0;31m')
readonly GREEN=$(printf '\033[0;32m')
readonly YELLOW=$(printf '\033[1;33m')
readonly BLUE=$(printf '\033[0;34m')
readonly NC=$(printf '\033[0m') # No Color
# ===================================================================
# FUNÇÕES AUXILIARES
# ===================================================================
# Função para logging colorido
log() {
local level="$1"
shift
local message="$*"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
case "$level" in
"INFO") echo -e "${BLUE}[INFO]${NC} ${timestamp} - $message" ;;
"WARN") echo -e "${YELLOW}[WARN]${NC} ${timestamp} - $message" ;;
"ERROR") echo -e "${RED}[ERROR]${NC} ${timestamp} - $message" ;;
"SUCCESS") echo -e "${GREEN}[SUCCESS]${NC} ${timestamp} - $message" ;;
*) echo "[$level] ${timestamp} - $message" ;;
esac
}
# Função para mostrar ajuda
show_help() {
cat << EOF
${SCRIPT_NAME} - Script para customização de cloud images Debian
USO:
$SCRIPT_NAME <imagem-original> [imagem-saida]
EXEMPLOS:
$SCRIPT_NAME debian-12-generic-amd64.qcow2
$SCRIPT_NAME debian-12-generic-amd64.qcow2 minha-imagem-custom.qcow2
OPÇÕES:
-h, --help Mostra esta ajuda
-v, --verbose Modo verboso (não implementado)
DESCRIÇÃO:
Este script customiza uma cloud image Debian adicionando:
• QEMU Guest Agent
• Console serial com autologin root
• SSH hardening
• Fail2ban
• Ferramentas básicas (curl, wget, htop, vim)
• Limpeza de logs e arquivos temporários
REQUISITOS:
• libguestfs-tools (apt install libguestfs-tools)
• qemu-utils (apt install qemu-utils)
EOF
}
# Função para verificar dependências
check_dependencies() {
log "INFO" "Verificando dependências..."
local missing_deps=()
if ! command -v virt-customize &> /dev/null; then
missing_deps+=("libguestfs-tools")
fi
if ! command -v qemu-img &> /dev/null; then
missing_deps+=("qemu-utils")
fi
if [ ${#missing_deps[@]} -gt 0 ]; then
log "ERROR" "Dependências não encontradas: ${missing_deps[*]}"
log "INFO" "Execute: apt install ${missing_deps[*]}"
return 1
fi
log "SUCCESS" "Todas as dependências encontradas"
return 0
}
# Função para validar imagem
validate_image() {
local image="$1"
log "INFO" "Validando imagem: $image"
if [ ! -f "$image" ]; then
log "ERROR" "Arquivo não encontrado: $image"
return 1
fi
if [ ! -r "$image" ]; then
log "ERROR" "Sem permissão de leitura: $image"
return 1
fi
# Verificar se é uma imagem válida usando qemu-img
if ! qemu-img info "$image" &> /dev/null; then
log "ERROR" "Arquivo não é uma imagem válida: $image"
return 1
fi
local size_mb=$(( $(stat -c%s "$image") / 1024 / 1024 ))
log "SUCCESS" "Imagem válida (${size_mb}MB): $image"
return 0
}
# Função para criar backup se não existir
create_backup() {
local original="$1"
local backup="${original}.backup"
if [ ! -f "$backup" ]; then
log "INFO" "Criando backup da imagem original..."
cp "$original" "$backup"
log "SUCCESS" "Backup criado: $backup"
else
log "INFO" "Backup já existe: $backup"
fi
}
# Função para executar virt-customize com tratamento de erro
safe_virt_customize() {
local image="$1"
shift
local description="$1"
shift
log "INFO" "$description"
if virt-customize -a "$image" "$@"; then
log "SUCCESS" "$description - Concluído"
return 0
else
log "ERROR" "$description - Falhou"
return 1
fi
}
# Função de limpeza em caso de erro
cleanup() {
local exit_code=$?
if [ $exit_code -ne 0 ] && [ -n "${OUTPUT_IMAGE:-}" ] && [ -f "${OUTPUT_IMAGE:-}" ]; then
log "WARN" "Script falhou. Removendo imagem parcialmente criada..."
rm -f "$OUTPUT_IMAGE"
fi
exit $exit_code
}
# ===================================================================
# FUNÇÃO PRINCIPAL
# ===================================================================
main() {
# Processar argumentos
case "${1:-}" in
-h|--help)
show_help
exit 0
;;
"")
log "ERROR" "Nenhuma imagem especificada"
show_help
exit 1
;;
esac
# Variáveis principais
local original_image="$1"
local output_image="${2:-${original_image%.qcow2}-custom.qcow2}"
# Tornar variáveis disponíveis globalmente para cleanup
readonly ORIGINAL_IMAGE="$original_image"
readonly OUTPUT_IMAGE="$output_image"
# Configurar trap para limpeza
trap cleanup EXIT INT TERM
# Verificações iniciais
check_dependencies || exit 1
validate_image "$original_image" || exit 1
# Verificar se imagem de saída já existe
if [ -f "$output_image" ]; then
log "WARN" "Imagem de saída já existe: $output_image"
read -p "Deseja sobrescrever? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
log "INFO" "Operação cancelada pelo usuário"
exit 0
fi
rm -f "$output_image"
fi
log "INFO" "=== INICIANDO CUSTOMIZAÇÃO ==="
log "INFO" "Imagem original: $original_image"
log "INFO" "Imagem de saída: $output_image"
log "INFO" "Console serial: ${SERIAL_DEVICE} @ ${BAUD_RATE} baud"
# Criar cópia da imagem
log "INFO" "Copiando imagem original..."
cp "$original_image" "$output_image"
log "SUCCESS" "Cópia criada com sucesso"
# ===================================================================
# CUSTOMIZAÇÕES
# ===================================================================
# 1. Atualização e instalação de pacotes
safe_virt_customize "$output_image" "Atualizando sistema e instalando pacotes" \
--update \
--install "qemu-guest-agent,openssh-server,fail2ban,curl,wget,htop,vim" || exit 1
# 2. Configuração de serviços
safe_virt_customize "$output_image" "Configurando serviços básicos" \
--run-command 'systemctl enable qemu-guest-agent' \
--run-command 'systemctl enable ssh' \
--run-command 'systemctl enable fail2ban' || exit 1
# 3. Configuração SSH hardening
safe_virt_customize "$output_image" "Aplicando SSH hardening" \
--edit '/etc/ssh/sshd_config:s/^#\?PermitRootLogin.*/PermitRootLogin no/' \
--edit '/etc/ssh/sshd_config:s/^#\?PasswordAuthentication.*/PasswordAuthentication no/' \
--edit '/etc/ssh/sshd_config:s/^#\?PubkeyAuthentication.*/PubkeyAuthentication yes/' \
--edit '/etc/ssh/sshd_config:s/^#\?X11Forwarding.*/X11Forwarding no/' \
--append-line '/etc/ssh/sshd_config:ClientAliveInterval 300' \
--append-line '/etc/ssh/sshd_config:ClientAliveCountMax 2' \
--append-line '/etc/ssh/sshd_config:MaxAuthTries 3' \
--append-line '/etc/ssh/sshd_config:Protocol 2' || exit 1
# 4. Configuração do console serial
log "INFO" "Configurando console serial com autologin..."
safe_virt_customize "$output_image" "Criando diretório de override" \
--run-command "mkdir -p /etc/systemd/system/serial-getty@${SERIAL_DEVICE}.service.d" || exit 1
safe_virt_customize "$output_image" "Configurando autologin root" \
--write "/etc/systemd/system/serial-getty@${SERIAL_DEVICE}.service.d/override.conf:[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin root --keep-baud ${BAUD_RATE},38400,9600 %I \$TERM
TTYVTDisallocate=no" || exit 1
safe_virt_customize "$output_image" "Habilitando serial getty" \
--run-command "systemctl enable serial-getty@${SERIAL_DEVICE}" || exit 1
# 5. Configuração do GRUB
log "INFO" "Configurando GRUB para console serial..."
safe_virt_customize "$output_image" "Configurando GRUB" \
--edit "/etc/default/grub:s/^GRUB_CMDLINE_LINUX=.*/GRUB_CMDLINE_LINUX=\"console=tty0 console=${SERIAL_DEVICE},${BAUD_RATE}\"/" \
--edit '/etc/default/grub:s/^#\?GRUB_TERMINAL=.*/GRUB_TERMINAL="console serial"/' \
--edit "/etc/default/grub:s/^#\?GRUB_SERIAL_COMMAND=.*/GRUB_SERIAL_COMMAND=\"serial --speed=${BAUD_RATE} --unit=0 --parity=no --stop=1\"/" \
--run-command "update-grub" || exit 1
# 6. Time Zone Brasil SP
safe_virt_customize "$output_image" "Time Zone Brasil SP" \
--timezone "America/Sao_Paulo" || exit 1
# 7. Limpeza e otimizações finais
safe_virt_customize "$output_image" "Aplicando limpeza final" \
--run-command 'apt autoremove -y && apt autoclean' \
--run-command 'find /var/log -name "*.log" -exec truncate -s 0 {} \;' \
--run-command 'rm -rf /tmp/* /var/tmp/*' || exit 1
# Aplicar SELinux relabel se necessário (comentado pois Debian não usa SELinux por padrão)
# --selinux-relabel
log "SUCCESS" "=== CUSTOMIZAÇÃO CONCLUÍDA ==="
# Mostrar informações da imagem final
local final_size_mb=$(( $(stat -c%s "$output_image") / 1024 / 1024 ))
log "INFO" "Imagem final: $output_image (${final_size_mb}MB)"
# Mostrar resumo das configurações
show_summary
}
# Função para mostrar resumo
show_summary() {
cat << EOF
${GREEN}=== CONFIGURAÇÕES APLICADAS ===${NC}
✓ Console serial: ${SERIAL_DEVICE} @ ${BAUD_RATE} baud
✓ Autologin root no console serial
✓ SSH hardening (sem senha, apenas chaves)
✓ QEMU Guest Agent habilitado
✓ Fail2ban configurado
✓ Ferramentas instaladas: curl, wget, htop, vim
✓ Sistema limpo (logs e arquivos temporários removidos)
${YELLOW}=== COMANDOS PARA PROXMOX ===${NC}
# Definir VMID
TEMPLATEID=9000
VMID=9001
#Imagem
IMAGE="$OUTPUT_IMAGE"
# Senha
SENHA="Mude a senha que esta aqui"
# Criar VM base
qm create \$TEMPLATEID --name debian13-custom --memory 2024 --cores 1 --cpu host --agent enabled=1,fstrim_cloned_disks=1
# BIOS e hardware básico (sem EFI ainda)
qm set \$TEMPLATEID --scsihw virtio-scsi-pci
qm set \$TEMPLATEID --bios ovmf
qm set \$TEMPLATEID --vga serial0
qm set \$TEMPLATEID --ostype l26
qm set \$TEMPLATEID --balloon 1024
# Console serial
qm set \$TEMPLATEID --serial0 socket
qm set \$TEMPLATEID --vga serial0
# Rede
qm set \$TEMPLATEID --net0 virtio,bridge=vmbr0
# Importar disco customizado
qm importdisk \$TEMPLATEID \$IMAGE local-lvm
qm set \$TEMPLATEID --virtio0 local-lvm:vm-\$TEMPLATEID-disk-0,discard=on,iothread=1
# AGORA criar EFI (criará disk-1)
qm set \$TEMPLATEID --efidisk0 local-lvm:0,efitype=4m,pre-enrolled-keys=1,format=raw
# Cria cloud-init
qm set \$TEMPLATEID --ide2 local-lvm:cloudinit
${BLUE}=== CONVERTE PARA TEMPLATE ===${NC}
# Converter para template
qm template \$TEMPLATEID
${BLUE}=== USO DO TEMPLATE ===${NC}
# Clonar nova VM
qm clone \$TEMPLATEID \$VMID --name minha-vm-debian
${BLUE}=== CONFIGURAÇÂO INDIVIDUAL ===${NC}
# Configurar cloud-init
qm set \$VMID --ciuser emazolini
qm set \$VMID --cipassword "\$SENHA"
qm set \$VMID --sshkeys sshkeys.pub
qm set \$VMID --ipconfig0 ip=dhcp
qm set \$VMID --nameserver 8.8.8.8,1.1.1.1
# Iniciar VM
qm start \$VMID
${GREEN}=== ACESSO ===${NC}
• Console serial: Proxmox Web UI > VM > Console (autologin como root)
• SSH: ssh admin@<ip-da-vm> (usando chave privada)
EOF
}
# ===================================================================
# EXECUÇÃO
# ===================================================================
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment