Bỏ qua để đến nội dung

Runbook: khóa `age` cho backup

Áp dụng cho backup database, media và cấu hình (.env + credentials). Một recipient trong BACKUP_AGE_PUBLIC_KEY có thể giải mã mọi bản backup được mã hóa khi recipient đó có mặt. Không để private identity trên VPS.

Bản khóaNgười giữNơi cất
Vận hành hằng ngàyTên người vận hành chính: PO cần điền trước khi go-livePassword manager của người đó
Break-glassTên người vận hành chính: PO cần điền trước khi go-liveBản giấy hoặc USB ngoại tuyến, cất tách biệt với máy vận hành

S-D8 đã chốt cách custody nhưng chưa cung cấp tên cá nhân. Không đánh dấu AC5/Phase S hoàn tất cho tới khi PO điền tên thật, xác nhận người đó đã nhận cả hai bản, và cập nhật bảng trên.

Thực hiện trên máy người vận hành, không chạy trên VPS. Cất mỗi identity vào nơi bảo vệ phù hợp ngay sau khi tạo; không commit hoặc gửi qua chat/email.

Terminal window
umask 077
age-keygen -o age-portal-ops.txt
age-keygen -y age-portal-ops.txt

Lưu dòng age1... do lệnh age-keygen -y in ra. Chỉ public key này được đưa lên VPS, trong .env:

BACKUP_AGE_PUBLIC_KEY=age1...ops

Một khóa là đủ (S-D13, chốt 2026-10-10). Có thể thêm khóa dự phòng (age1...ops,age1...breakglass, tạo bằng age-keygen như trên) nếu muốn, nhưng script không bắt buộc. Với một khóa, cất private key ở hai nơi tách biệt (password manager + USB/giấy): mất nó là không mở được bất kỳ backup nào. Cả ba job backup_database.sh, backup_media.sh và backup_config.sh chỉ từ chối chạy khi biến này trống.

Manifest lấy hash trong cùng lượt đọc tạo tar; xác minh hash bằng nội dung file trong archive đã giải mã nếu nguồn trên VPS có thể đã thay đổi sau backup.

Không đặt nội dung private identity (AGE-SECRET-KEY-1...) trong .env, /opt/web_admin, hoặc /opt/backups. Khóa public sai định dạng khiến backup thất bại và phát cảnh báo; không xóa recipient đang hoạt động cho tới khi kiểm tra recipient mới giải mã được một bản backup.

Trên máy vận hành, dùng thư mục backup đã kéo về máy mã hóa (S-D7), hoặc tải một bản mã hóa về trước khi chạy. Xác minh bằng identity (và từng khóa dự phòng nếu có), để file giải mã chỉ tồn tại tạm trong thư mục riêng quyền 700:

Terminal window
umask 077
mkdir -m 700 -p ~/portal-key-check
age -d -i age-portal-ops.txt -o ~/portal-key-check/config.tar \
/path/to/config_YYYYMMDD_HHMMSS.tar.age
tar -tf ~/portal-key-check/config.tar
rm -f ~/portal-key-check/config.tar
# Nếu có cấu hình khóa dự phòng, lặp lại với identity đó.

Kết quả phải liệt kê .env, docker-compose.prod.yml, nginx/, crontab.txt, và các credential đang dùng. Lặp lại với bản DB hoặc media nếu cần kiểm tra đủ loại artifact. Xóa thư mục tạm sau khi xác minh. S.7 sẽ lặp bài kiểm tra này trong diễn tập khôi phục hằng quý.

  1. Tạo identity mới trên máy người vận hành và chuyển custody an toàn cho người giữ đã được PO chỉ định.
  2. Thêm public key mới vào BACKUP_AGE_PUBLIC_KEY, phân tách bằng dấu phẩy; giữ recipient cũ trong danh sách ít nhất 30 ngày (S-D3).
  3. Tạo backup DB, media, config mới và giải mã thử từng loại bằng identity mới.
  4. Sau khi đủ 30 ngày và kiểm tra thành công, gỡ recipient cũ khỏi .env. Chỉ hủy identity cũ khi mọi bản mã hóa bằng nó đã hết hạn, bao gồm bản đã kéo về máy người vận hành.

Nếu nghi lộ private key: coi toàn bộ backup 30 ngày gần nhất có thể bị đọc, thêm recipient mới và loại key bị lộ ngay, sau đó rotate secrets trong .env theo Task F.3. Đặc biệt, không đổi MFA_ENCRYPTION_KEY đơn lẻ: cần quy trình migrate và mã hóa lại MFA secret trong database như Task F.3 mô tả.

Nếu mất một identity, dùng identity còn lại để giải mã backup mới nhất, tạo identity thay thế, cập nhật recipient và xác minh như quy trình luân chuyển. Nếu mất cả hai, backup hiện có không thể giải mã; phải dựng lại secrets và credential bên thứ ba theo quy trình khôi phục. Báo cho PO trước khi tuyên bố khả năng khôi phục.

Áp dụng cho Portal v1.2.2