Backup Criptografado Automatizado via Pendrive Bootável

!!!Há uma falha critica de segurança no projeto mais detalhes em!!!
6.9.2.1 Falha de segurança!

1. Resumo

Este projeto surgiu após problemas recorrentes causados por backups desorganizados, ausência de padronização entre mídias e dificuldade em rastrear versões consistentes dos arquivos. Em alguns casos, isso resultou em perda de dados e incerteza sobre a integridade das cópias disponíveis.

Para resolver esse cenário, foi desenvolvida uma infraestrutura portátil de backup baseada em um ambiente Arch Linux executado a partir de um pendrive bootável. O sistema opera de forma independente da instalação principal, automatizando rotinas de backup por meio do rsync, mecanismos de interrupção segura em caso de falha e registro detalhado de logs para auditoria e análise.

O projeto também incorpora versionamento utilizando snapshots Btrfs para reduzir consumo de armazenamento e preservar histórico das alterações. Seu foco não é ser uma solução genérica, mas fornecer um processo previsível, reproduzível e confiável para um ambiente específico. Atualmente o desenvolvimento principal foi concluído e a solução encontra-se em fase de validação operacional.

2. Desafio

2.1. Partições e volumes criptografados do SSD

O principal desafio do projeto não era apenas copiar arquivos, mas lidar com múltiplas camadas de armazenamento, criptografia e sistemas de arquivos distintos sem depender do sistema operacional instalado. Antes que qualquer backup pudesse ser executado, era necessário reconstruir manualmente diversas etapas de preparação do ambiente.

3. Arquitetura de Armazenamento

3.1. SSD interno.

O SSD principal contém a partição EFI BOOT, o volume criptografado LUKS2 e, internamente, um volume EXT4 utilizado como volume único para o sistema operacional.

3.2. HD externo

O HD externo contém um volume criptografado LUKS2 e, internamente, um volume Btrfs contendo snapshots versionados do sistema principal.

4. Sistema operacional

A escolha do Arch Linux foi motivada pela necessidade de um ambiente previsível, mínimo e com baixo acoplamento. Como o objetivo do projeto é executar uma rotina crítica de backup a partir de um pendrive bootável, reduzir a quantidade de componentes e serviços em execução ajuda a minimizar interferências externas durante o processo.

A filosofia do Arch Linux favorece esse modelo ao permitir a construção de um sistema composto apenas pelos componentes estritamente necessários para a execução da tarefa. Em vez de depender de uma distribuição com diversos serviços e abstrações habilitados por padrão, a proposta foi manter o ambiente o mais simples e controlável possível.

Um ponto de atenção é o fato de o Arch Linux ser uma distribuição rolling release, característica que pode introduzir alterações frequentes ao longo do tempo. Para reduzir esse risco, o sistema utiliza o kernel Linux LTS (Long Term Support), priorizando estabilidade e previsibilidade em vez da adoção imediata de recursos mais recentes.

Como resultado, o ambiente executado pelo pendrive opera com o mínimo necessário, reduzindo a superfície de falhas e mantendo o processo de backup mais controlado.

5. Fluxo Geral da Solução

5.1. Execução em diagrama

O diagrama abaixo representa o fluxo operacional completo executado automaticamente pelo sistema.

5.2. Vídeo demonstrativo

O vídeo abaixo mostra o boot no pendrive e a execução completa do processo de backup em uma VM.

5.3. Acesse o repositório no Github

https://github.com/Andarilho95/backup-system

6. Análise do Workflow

6.1. Iniciar Arch Linux

Durante a inicialização, o ambiente executa login automático e inicia a rotina principal de backup.

if [[ "$(tty)" == "/dev/tty1" ]]; then
  ./run-bak.sh
fi
6.2. Executar script

O script run-bak.sh é responsável por execução, logging e desligamento da máquina após todo o processo ter sido executado com sucesso.

#!/bin/bash

START_DATE=$(date '+%Y-%m-%d-%H-%M-%S')

LOG="/bak-logs/${START_DATE}-bak_script.log"

#faz o logging do script
./bak.sh 2>&1 | tee "$LOG"

if grep -q "BACKUP_FINALIZADO_POWEROFF" "$LOG"; then
  echo "Backup OK, Good Bye!!!"
  systemctl poweroff
fi
6.3. Script principal bak.sh

O script não foi projetado para recuperação automática de falhas. A solução adota uma abordagem fail-fast, onde eventos inesperados interrompem imediatamente a execução para evitar continuidade em estados potencialmente inconsistentes.

# gerador de logs
log() {
  local level="$1"
  shift
  printf '[%s] [%s] %s\n' \
    "$(date '+%Y-%m-%d-%H-%M-%S')" \
    "$level" \
    "$*"
}
# interrompe a execução aguardando intervenção manual
panic() {
  echo
  read -p "ENTER para continuar..."
  exit 1
}
6.4. Detecção de volumes.

Após o script inicia há uma verificação rápida se o volume criptográfico do root e o HD externo estão disponíveis.

# verifica se os volumes ROOT e HD_EXTERNO estão presentes no sistema
if ! blkid | grep -q "$CRYPT_UUID_ROOT"; then
  log ERROR "Volume ROOT criptografado não detectado"
  panic
fi

if ! blkid | grep -q "$CRYPT_UUID_HD_EXTERNO"; then
  log ERROR "Volume HD externo criptografado não detectado"
  panic
fi
6.5. Desbloqueio de volumes

Os volumes LUKS2 são desbloqueados com autenticação manual. Senhas incorretas geram nova tentativa; falhas inesperadas interrompem o processo para evitar estados inconsistentes.

while true; do
  log INFO "Inserir password criptográfica HD_EXTERNO"
# captura a senha
  read -s -p "Senha: " passwd_HD_EXTERNO
  echo
# envia a senha para o cryptsetup abrir o volume cryptografado do HD externo  
  printf '%s' "$passwd_HD_EXTERNO" | cryptsetup open UUID=${CRYPT_UUID_HD_EXTERNO} HD_EXTERNO --key-file=-
#captura o codigo de saida do cryptsetup
  HD_EXTERNO_status=$?
# limpa a variavel com a senha para mais segurança
  unset passwd_HD_EXTERNO
# verifica se houve sucesso no cryptsetup
  if ((HD_EXTERNO_status == 0)); then
    log INFO "HD_EXTERNO desbloqueado"
    break
# caso a senha seja digitada errada o loop retorna
  elif ((HD_EXTERNO_status == 2)); then
    log INFO "Senha errada digite novamente!!!"
    # caso o erro seja outro aborta
  else
    log ERROR "Erro ao desbloquear volume!!!"
    panic
    fi
done
6.6. Montagem do volume de backup BTRFS

A montagem do volume de backup com sistema de arquivos BTRFS, recebe o nível de compressão seis com o algorítimo de compressão ZSTD. Esse nível de compressão garante economia de espaço de armazenamento, níveis de compressão maiores podem ser muito lentos e exigir muito da CPU levando a um processo de backup lento. A aplicação de compressão pode tornar o backup mais rápido devido a HDs terem taxas de transferências muito baixas, arquivos comprimidos frequentemente podem chegar a ter a metade do tamanho, utilizando menos espaço em disco e sendo transferidos mais rapidamente.

if ! mount -t btrfs -o compress=zstd:6 UUID=${FS_UUID_HD_EXTERNO} '/mnt/HD_EXTERNO'; then
  log ERROR "Erro ao montar o volume de backup BTRFS!!!"
  panic
fi
6.7. Verificação de armazenamento disponível

Há uma verificação se o tamanho total do volume root pode ser armazenado no HD externo e exibe ao usuário, quanto está sendo usado e quanto resta de espaço disponível.

FREE_DISK_HD=$(df  /dev/mapper/HD_EXTERNO --output=avail | grep -v Avail)
USED_DISK_ROOT=$(df  /dev/mapper/ROOT --output=used | grep -v Used)

SHOW_FREE_DISK_HD=$(df -h /dev/mapper/HD_EXTERNO --output=avail | grep -v Avail)
SHOW_USED_DISK_ROOT=$(df -h /dev/mapper/ROOT --output=used | grep -v Used)

# verifica se há espaço em disco suficiente para backup
if [ "$FREE_DISK_HD" -gt "$USED_DISK_ROOT" ]; then
  log INFO "Há espaço em disco para backup atual"
  echo
  echo "Usado pelo sistema     ${SHOW_USED_DISK_ROOT}"
  echo
  echo "Disponivel para backup ${SHOW_FREE_DISK_HD}"
  echo
else
  log ERROR "Espaço em disco insuficifiente para o backup"
  echo
  echo "Usado pelo sistema     ${SHOW_USED_DISK_ROOT}"
  echo
  echo "Disponivel para backup ${SHOW_FREE_DISK_HD}"
  echo
  # saido script em caso de falta de espaço em disco para o backup
  panic 
fi
6.8. Gerenciamento de snapshots
6.8.1 Verificação de snapshots anteriores

Os snapshots seguem o padrão AAAA-MM-DD-HH-MM-SS_versão. O sistema identifica automaticamente a versão mais recente disponível e a utiliza como base para a próxima execução.

for s in "$SNAPSHOTS_DIRECTORY"/*; do
  n=${s##*_}

  (( n > counter )) && {
    counter=$n
    last="$s"
  }
done
6.8.1. Snapshot atual

Após a identificação do snapshot anterior o script cria um novo snapshot com a data e hora atuais, e com a versão atual.

# cria o nome do snapshot baseado na data e hora e adiciona o numero dele na cadeia de snapshots
new="$(date '+%Y-%m-%d-%H-%M-%S')_$((counter+1))"

# cria o snapshot
if btrfs subvolume snapshot "$last" "$SNAPSHOTS_DIRECTORY/$new"; then
  log INFO "Subvolume criado"
  echo "Nome do Subvolume ---> $new"
else
  log ERROR "Erro ao criar snapshot ${new}"
  panic
fi

6.9. Transferência de dados

6.9.1 Rsync

O Rsync preserva permissões e sincroniza alterações em relação ao snapshot utilizado como base. Arquivos removidos também são excluídos, mantendo uma cópia consistente do sistema.

Diretórios considerados não essenciais, como arquivos da Steam, foram excluídos para evitar crescimento desnecessário do volume de dados.

log INFO "Copiando arquivos verificar tty2 ( ALT + -> )"

# copia os dados preservando a estrutura original do sistema
rsync -aAXHv --delete --info=progress2 \
  --exclude='/dev/*' \
  --exclude='/proc/*' \
  --exclude='/sys/*' \
  --exclude='/tmp/*' \
  --exclude='/run/*' \
  --exclude='/mnt/*' \
  --exclude='/media/*' \
  --exclude='/lost+found/' \
  --exclude='/home/nathan/.local/share/Steam/steamapps/common' \
  --exclude='/swapfile' \
  /mnt/ROOT/ \
  "$SNAPSHOTS_DIRECTORY/$new" \
  > /bak-logs/"$START_DATE"-rsync.log 2>&1 &
6.9.2 Rsync logs e status

Toda a execução do Rsync pode ser acompanhada no tty2 usando Alt + → ou Ctrl + Alt + 2. O Rsync gera logs de todo o processo, que podem ser analisados posteriormente. Após iniciar o Rsync, capturamos seu PID e executamos o tail no tty2 para acompanhar detalhadamente a execução enquanto o script aguarda a finalização do processo. Quando o Rsync terminar, verificamos o status da execução e, em caso de sucesso, o script prossegue.

#captura pid do rsync
rsync_pid=$!

# joga o log do rsync no tty2
setsid sh -c "exec tail -f /bak-logs/${START_DATE}-rsync.log <> /dev/tty2 >&0 2>&1" &

# espera o rsync finalizar
wait "$rsync_pid"

# captura o codigo de saida do rsync
rsync_status=$?

# verifica se o rsync finalizou com sucesso
if ((rsync_status != 0 )); then
  log ERROR "Erro no rsync, os dados podem ter sido copiados incorretamente!!!"
  panic
fi

log INFO "Dados copiados com sucesso"
6.9.2.1 Falha de segurança!

Após a primeira ultilização da ferramenta identifiquei uma falha critica na segurança no sistema de logging mais especificamente nesta linha.

setsid sh -c "exec tail -f /bak-logs/${START_DATE}-rsync.log <> /dev/tty2 >&0 2>&1" &

A execução do rsync gera logs que são escritos em /bak-logs/ esse diretório está no pendrive sem criptografia alguma. A falha expõe toda a estrutura interna do sistema de arquivos criptografado do SSD, com diretórios e arquivos todos listatados porém sem exposição do conteúdo. Acredito que o fix mais robusto seja a criptografia do sistema no pendrive ou um pipeline que o tail apenas capture a saida do rsync e descarte no void do Linux /dev/null.

6.10. Finalização

Após todo esse processo o script desmonta todos os volumes e fecha os volumes criptografados retornando ao script run-bak.sh que finaliza o processo de registro de logs e verifica se o script bak.sh foi finalizado com sucesso e desliga a máquina.

# desmonta a raiz do sistema
if ! umount /mnt/ROOT; then
  log ERROR "Erro ao desmontar ROOT!!!"
  panic
fi

log INFO "Desmontando HD_EXTERNO"
# desmonta o HD externo
if ! umount /mnt/HD_EXTERNO; then
  log ERROR "Erro ao desmontar HD_EXTERNO!!!"
  panic
fi

log INFO "Fechando volume luks ROOT"
# fecha o volume criptografado do sistema
if ! cryptsetup close /dev/mapper/ROOT; then
  log ERROR "Erro ao fechar volume luks ROOT!!!"
  panic
fi

log INFO "Fechando volume luks HD_EXTERNO"
# fecha o volume criptografado do HD externo
if ! cryptsetup close /dev/mapper/HD_EXTERNO; then
  log ERROR "Erro ao fechar volume luks HD_EXTERNO!!!"
  panic
fi

# nunca modificar ele é usado para marcar se o script terminou com exito e pode desligar o sistema
log INFO "BACKUP_FINALIZADO_POWEROFF"

7. Considerações

A estratégia adotada prioriza interrupção controlada em vez de recuperação automática, reduzindo a possibilidade de propagação silenciosa de falhas. Em situações inesperadas, a execução é interrompida para impedir continuidade em estados potencialmente inconsistentes. O objetivo é usá-la com bastante frequência e caso ocorram erros na execução a chance de corrupção de dados seja mínima pois há diversos pontos de verificação.

8. Acesse o repositório no Github

https://github.com/Andarilho95/backup-system