Restaurar PostgreSQL a partir de backup sem o Databasus
Fazer backup não é só proteger os dados. É também poder recuperá-los. O Databasus garante que as suas cópias de segurança continuam recuperáveis mesmo que o VPS com o Databasus seja apagado, que você perca o acesso ou não consiga abrir a interface por alguma razão. Você não precisa do Databasus para recuperar os backups: eles são guardados em formato padrão e não há vendor lock-in.
O que você precisa
Para recuperar um backup manualmente, você precisa de:
- Arquivo de backup do seu armazenamento (armazenamento local, S3, Google Drive, etc.)
- Arquivo de metadados do mesmo armazenamento. Tem o mesmo nome do arquivo de backup, mas com a extensão
.metadata. - Chave secreta de
./databasus-data/secret.key(localizada no mesmo diretório dos arquivos de backup, normalmente/opt/databasus/)
Estrutura dos arquivos
Cada backup consiste em dois arquivos guardados no seu armazenamento (local ou na nuvem):
{database-name}-{timestamp}-{backup-id}- Dados do backup, criptografados e comprimidos{database-name}-{timestamp}-{backup-id}.metadata- Arquivo JSON com os detalhes da criptografia
O arquivo de metadados contém o salt de criptografia e o IV (nonce) em formato Base64:
{
"backupId": "550e8400-e29b-41d4-a716-446655440000",
"encryptionSalt": "base64-encoded-salt",
"encryptionIV": "base64-encoded-nonce",
"encryption": "encrypted"
}Descriptografia
O Databasus usa criptografia AES-256-GCM com derivação de chave PBKDF2. Cada backup tem uma chave de criptografia única derivada de:
- Chave mestra (do arquivo secret.key)
- ID do backup
- Salt aleatório (guardado nos metadados)
Use este script Python para descriptografar o seu backup:
import json
import base64
import struct
import os
from Crypto.Cipher import AES
from Crypto.Protocol.KDF import PBKDF2
from Crypto.Hash import SHA256
# Constants from Databasus encryption
MAGIC_BYTES = b"PGRSUS01"
HEADER_LENGTH = 64
CHUNK_SIZE = 1024 * 1024
PBKDF2_ITERATIONS = 100000
def decrypt_backup(backup_file, metadata_file, master_key):
"""
Decrypt a Databasus backup file using metadata and master key.
Args:
backup_file: Path to encrypted backup file
metadata_file: Path to metadata JSON file
master_key: Master key from ./databasus-data/secret.key
"""
# Validate files exist
if not os.path.exists(backup_file):
print(f"Error: Backup file not found: {backup_file}")
return
if not os.path.exists(metadata_file):
print(f"Error: Metadata file not found: {metadata_file}")
return
# Read metadata
with open(metadata_file, "r") as f:
metadata = json.load(f)
# Check if file is encrypted (case-insensitive check)
encryption_status = metadata.get("encryption", "").upper()
if encryption_status != "ENCRYPTED":
print(
f"Error: Backup is not encrypted (encryption status: {metadata.get('encryption')})"
)
print("No decryption needed. You can decompress/restore the file directly.")
return
backup_id = metadata["backupId"]
salt = base64.b64decode(metadata["encryptionSalt"])
iv = base64.b64decode(metadata["encryptionIV"])
# Generate output filename with decrypted_ prefix
backup_dir = os.path.dirname(backup_file) or "."
backup_name = os.path.basename(backup_file)
output_file = os.path.join(backup_dir, f"decrypted_{backup_name}")
# Derive encryption key using PBKDF2
key_material = (master_key + backup_id).encode("utf-8")
derived_key = PBKDF2(
key_material, salt, dkLen=32, count=PBKDF2_ITERATIONS, hmac_hash_module=SHA256
)
try:
with open(backup_file, "rb") as f_in, open(output_file, "wb") as f_out:
# Read and validate header
header = f_in.read(HEADER_LENGTH)
# Validate magic bytes
magic = header[:8]
if magic != MAGIC_BYTES:
raise ValueError(
f"Invalid magic bytes: expected {MAGIC_BYTES}, got {magic}"
)
# Decrypt chunks
chunk_index = 0
while True:
# Read chunk length (4 bytes)
length_bytes = f_in.read(4)
if not length_bytes:
break
chunk_length = struct.unpack(">I", length_bytes)[0]
# Read encrypted chunk
encrypted_chunk = f_in.read(chunk_length)
if not encrypted_chunk:
break
# Generate chunk nonce (base IV + chunk index)
chunk_nonce = bytearray(iv)
chunk_nonce[4:12] = struct.pack(">Q", chunk_index)
# Create cipher for this chunk
chunk_cipher = AES.new(derived_key, AES.MODE_GCM, nonce=bytes(chunk_nonce))
# Decrypt chunk
try:
decrypted_chunk = chunk_cipher.decrypt_and_verify(
encrypted_chunk[:-16], # ciphertext
encrypted_chunk[-16:], # auth tag
)
except ValueError as e:
if "MAC check failed" in str(e):
print("\nError: Failed to decrypt backup (MAC check failed)")
print("This usually means:")
print(" - The master key is incorrect")
print(" - The backup file is corrupted")
print(" - The metadata doesn't match this backup file")
print(f"\nFailed at chunk {chunk_index}")
raise
raise
# Write decrypted data
f_out.write(decrypted_chunk)
chunk_index += 1
print(f"Successfully decrypted {chunk_index} chunks to {output_file}")
except ValueError as e:
# Clean up partial output file after files are closed
if "MAC check failed" in str(e) and os.path.exists(output_file):
os.remove(output_file)
return
# Example usage:
if __name__ == "__main__":
decrypt_backup(
backup_file="./your-backup-file", # <--- change this to your backup file
metadata_file="./your-backup-file.metadata", # <--- change this to your metadata file
master_key="your-master-key-here", # <--- change this to your master key
)Instale as dependências necessárias:
pip install pycryptodomeComo usar o script:
- Salve o script acima num arquivo (por exemplo,
decrypt_backup.py) - Atualize os parâmetros na seção de exemplo de uso, no final do script
- Execute o script:
python decrypt_backup.pyO script cria automaticamente o arquivo de saída com o prefixo decrypted_. Por exemplo, se o seu arquivo de backup for backup-id.dump, o arquivo descriptografado será decrypted_backup-id.dump.
Restaurar na base de dados
Depois de descriptografar, restaure com as ferramentas próprias de cada base de dados:
PostgreSQL
Os backups de PostgreSQL usam compressão embutida e podem ser restaurados diretamente:
Base de dados local:
# Restore to local database
pg_restore -d your_database decrypted-backup.dumpBase de dados remota:
# Restore to remote database
pg_restore -h hostname -p 5432 -U username -d database_name decrypted-backup.dumpMySQL
Os backups de MySQL são comprimidos com zstd nível 5 e precisam ser descomprimidos antes de restaurar.
Passo 1: descomprimir o backup
Use a ferramenta de linha de comando zstd ou qualquer ferramenta de descompressão compatível (7-Zip, PeaZip, WinRAR, etc.):
# Decompress with zstd command-line tool
zstd -d decrypted-backup.sql.zst -o decrypted-backup.sql
# Or use graphical tools like 7-Zip, PeaZip, or WinRARPasso 2: restaurar na base de dados
Base de dados local:
# Restore to local database
mysql your_database < decrypted-backup.sqlBase de dados remota:
# Restore to remote database
mysql -h hostname -P 3306 -u username -p database_name < decrypted-backup.sqlMariaDB
Os backups de MariaDB são comprimidos com zstd nível 5 e precisam ser descomprimidos antes de restaurar.
Passo 1: descomprimir o backup
Use a ferramenta de linha de comando zstd ou qualquer ferramenta de descompressão compatível (7-Zip, PeaZip, WinRAR, etc.):
# Decompress with zstd command-line tool
zstd -d decrypted-backup.sql.zst -o decrypted-backup.sql
# Or use graphical tools like 7-Zip, PeaZip, or WinRARPasso 2: restaurar na base de dados
Base de dados local:
# Restore to local database
mariadb your_database < decrypted-backup.sqlBase de dados remota:
# Restore to remote database
mariadb -h hostname -P 3306 -u username -p database_name < decrypted-backup.sqlMongoDB
Os backups de MongoDB usam compressão gzip embutida e podem ser restaurados diretamente:
Base de dados local:
# Restore to local database
mongorestore --archive=decrypted-backup.archive --gzip --db your_databaseBase de dados remota:
# Restore to remote database
mongorestore --host hostname:27017 --username username --password password \
--archive=decrypted-backup.archive --gzip --db database_nameE se tiver problemas?
Se você encontrar algum problema durante o processo de recuperação:
- Peça ajuda a uma IA. Assistentes como ChatGPT, Claude ou Gemini são excelentes para ajudar com ferramentas de compressão e procedimentos de restauração de bases de dados. Basta descrever o problema, e eles guiam você pelo processo.
- Junte-se à nossa comunidade. Nossos desenvolvedores e membros da comunidade podem ajudar com o seu caso específico.