Backup và phục hồi

Sao lưu chống Ransomware với Restic và S3 Object Lock

Hướng dẫn thiết lập cơ chế khóa sao lưu chống xóa (WORM) với Restic và S3 Object Lock: mã hóa đầu cuối, ngăn chặn kẻ chiếm quyền root xóa hoặc ghi đè file backup.

Sao lưu chống Ransomware với Restic và S3 Object Lock
MỤC LỤC

Khi quản trị máy chủ sản xuất, hầu hết các đội ngũ kỹ thuật đều đã thiết lập kịch bản sao lưu định kỳ theo chiến lược sao lưu 3-2-1 và đẩy dữ liệu ra các dịch vụ lưu trữ đám mây bên ngoài. Tuy nhiên, trong bối cảnh các cuộc tấn công mã độc tống tiền (Ransomware) ngày càng tinh vi, việc chỉ đơn thuần đẩy file backup lên một S3 bucket thông thường không còn đủ để bảo vệ hệ thống.

Một kịch bản tấn công điển hình hiện nay diễn ra như sau: Sau khi khai thác được lỗ hổng ứng dụng web và leo thang lên quyền root trên máy chủ, mã độc hoặc kẻ tấn công không vội vàng mã hóa dữ liệu ngay. Chúng sẽ dành thời gian rà soát hệ điều hành, tìm kiếm các file script backup, đọc các biến môi trường chứa S3 Access Key và Secret Key. Kẻ tấn công sử dụng chính các thông tin xác thực này để gửi lệnh xóa các bản sao lưu trên Cloud, hoặc tải file rác lên ghi đè kho lưu trữ dự phòng. Khi các bản sao lưu ngoại vi đã bị vô hiệu hóa, kẻ tấn công mới tiến hành mã hóa máy chủ để đòi tiền chuộc.

Bài viết này hướng dẫn bạn xây dựng cơ chế khóa sao lưu chống xóa (Immutable Backup / WORM) bằng cách kết hợp công cụ mã hóa Restic với tính năng S3 Object Lock (chế độ Compliance Mode). Mô hình này giúp cô lập dữ liệu sao lưu: ngay cả khi máy chủ bị chiếm quyền root cùng Access Key lưu trên máy, cơ chế WORM tại kho lưu trữ vẫn từ chối các thao tác xóa, ghi đè hoặc can thiệp dữ liệu trong suốt thời hạn bảo lưu.

Kiến trúc khóa sao lưu với Restic và S3 Object Lock

Kịch bản ransomware tìm và xóa các bản sao lưu

Để xây dựng một lớp phòng vệ hiệu quả, bạn cần hiểu rõ chuỗi hành vi của các nhóm tấn công mã độc có chủ đích (Targeted Ransomware):

Tìm kiếm Access Key và thông tin đăng nhập trên máy chủ

Khi chiếm được quyền điều khiển máy chủ Linux, kẻ tấn công thường thực hiện các lệnh tự động để săn lùng thông tin đăng nhập dịch vụ đám mây:

  • Quét lịch sử dòng lệnh: history, ~/.bash_history.
  • Kiểm tra các file cấu hình lưu trữ: ~/.aws/credentials, ~/.config/rclone/rclone.conf.
  • Đọc biến môi trường của các tiến trình Docker đang chạy: kiểm tra file .env, docker-compose.yml, hoặc trích xuất biến môi trường trong /proc/<pid>/environ.
  • Đọc nội dung các file script trong cron: /etc/crontab, /etc/cron.daily/, các script backup bằng shell.

Xóa các bản sao lưu từ xa trước khi mã hóa máy chủ

Nếu bạn sử dụng một tài khoản IAM thông thường với quyền đọc-ghi đầy đủ (s3:* hoặc có quyền DeleteObject), kẻ tấn công chỉ cần chạy một lệnh AWS CLI đơn giản:

bash
# Lệnh xóa sạch toàn bộ bucket dự phòng chỉ trong vài chục giây
aws s3 rm s3://ten-bucket-backup-cua-ban --recursive

Lúc này, dù bạn có đặt máy chủ dự phòng ở một trung tâm dữ liệu độc lập theo kiến trúc Warm-Standby hay đẩy dữ liệu sang một nhà cung cấp đám mây thứ hai, dữ liệu sao lưu trên kho đám mây vẫn có nguy cơ bị xóa sạch.

Do đó, nguyên tắc bảo mật cốt lõi là: Không bao giờ tin tưởng máy chủ ứng dụng sở hữu quyền xóa dữ liệu sao lưu. Máy chủ chỉ được phép ghi dữ liệu mới vào (Append-Only), còn quyền xóa phải bị khóa cứng ở tầng hạ tầng lưu trữ của nhà cung cấp dịch vụ đám mây.

Cơ chế WORM trên S3 Object Lock

Nguyên tắc ghi một lần, cấm sửa xóa (WORM)

WORM là viết tắt của Write Once, Read Many (Ghi một lần, Đọc nhiều lần). Đây là cơ chế bảo vệ ở cấp độ hạ tầng lưu trữ:

  • Khi một đối tượng (Object) được ghi vào bucket, nó sẽ được gắn một nhãn thời gian khóa bảo lưu (Retain Until Date).
  • Trong suốt khoảng thời gian khóa này, API của dịch vụ lưu trữ sẽ từ chối mọi yêu cầu sửa đổi nội dung, ghi đè hoặc xóa bỏ tệp tin.

So sánh chế độ Governance và Compliance

Các dịch vụ lưu trữ tương thích S3 (như AWS S3, Wasabi, Cloudflare R2, MinIO) cung cấp hai chế độ khóa đối tượng:

Tiêu chíChế độ Governance (Quản trị)Chế độ Compliance (Tuân thủ)
Quyền xóa trong thời hạn khóaTài khoản có quyền đặc biệt (s3:BypassGovernanceRetention) vẫn có thể xóa object nếu kèm cờ bypass.Hệ thống từ chối mọi yêu cầu xóa (kể cả từ tài khoản root của dịch vụ đám mây) cho đến khi hết hạn bảo lưu.
Khả năng rút ngắn thời hạnNgười quản trị có thể rút ngắn ngày hết hạn bảo lưu.Không thể rút ngắn thời hạn; chỉ có thể gia hạn thêm thời gian khóa.
Khả năng tắt chế độ khóaCó thể tắt hoặc xóa chính sách khóa.Khi đã kích hoạt, không thể tắt chế độ khóa cho đến khi hết hạn.
Mức độ bảo vệ chống RansomwareTrung bình (vẫn có rủi ro nếu kẻ tấn công chiếm được tài khoản quản trị Cloud).Mức bảo vệ cao nhất (ngăn chặn thao tác xóa ngay cả khi lộ Access Key hoặc máy chủ bị chiếm quyền root).

Bắt buộc bật Versioning trên S3 bucket

Để sử dụng Object Lock, bucket bắt buộc phải bật tính năng quản lý phiên bản (Versioning). Khi có một yêu cầu xóa file từ một người dùng thông thường, S3 sẽ không xóa dữ liệu thật mà chỉ tạo ra một phiên bản đánh dấu đã xóa (Delete Marker). Bản snapshot gốc vẫn nằm nguyên vẹn ở phiên bản trước đó và được bảo vệ bởi Object Lock.

Cấu hình quyền IAM chỉ cho phép ghi mới (Append-Only)

Để ngăn chặn kẻ tấn công lợi dụng Access Key lưu trên máy chủ để can thiệp vào bucket, bạn cần tạo một người dùng IAM chuyên dụng cho tác vụ sao lưu với chính sách chỉ cho phép ghi mới (Append-Only):

json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowResticBackupOperations",
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket",
        "s3:GetBucketLocation",
        "s3:GetObject",
        "s3:PutObject",
        "s3:PutObjectRetention"
      ],
      "Resource": [
        "arn:aws:s3:::htsviet-immutable-backup",
        "arn:aws:s3:::htsviet-immutable-backup/*"
      ]
    },
    {
      "Sid": "DenyDestructiveActions",
      "Effect": "Deny",
      "Action": [
        "s3:DeleteObject",
        "s3:DeleteObjectVersion",
        "s3:PutBucketPolicy",
        "s3:DeleteBucket"
      ],
      "Resource": [
        "arn:aws:s3:::htsviet-immutable-backup",
        "arn:aws:s3:::htsviet-immutable-backup/*"
      ]
    }
  ]
}

Phân tích chính sách:

  • Nhóm quyền Allow: Cho phép Restic kiểm tra danh sách file (ListBucket), đọc dữ liệu để đối chiếu index (GetObject), đẩy các khối dữ liệu mới lên (PutObject) và gắn nhãn khóa thời gian (PutObjectRetention).
  • Nhóm quyền Deny: Chặn tường minh mọi hành vi xóa file (DeleteObject), xóa phiên bản (DeleteObjectVersion) hoặc thay đổi cấu hình bucket. Nếu lộ cặp khóa này trên máy chủ, kẻ tấn công cũng không thể thực thi lệnh xóa hoặc can thiệp dữ liệu trên bucket.

Cấu hình Restic sao lưu lên S3 Object Lock

Restic là công cụ sao lưu mã nguồn mở hiện đại hỗ trợ nén dữ liệu, khử trùng lặp (Deduplication) và mã hóa đầu cuối bằng thuật toán mã hóa tiêu chuẩn (AES-256 hoặc ChaCha20-Poly1305). Toàn bộ dữ liệu được mã hóa trong bộ nhớ RAM của máy chủ trước khi truyền qua Internet.

Khởi tạo repository Restic trên S3

Tạo một file chứa các biến môi trường bảo mật tại /etc/restic/backup.env (phân quyền nghiêm ngặt chmod 600):

bash
export AWS_ACCESS_KEY_ID="AKIAxxxxxxxxxxxxxxxx"
export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
export RESTIC_REPOSITORY="s3:https://s3.eu-central-1.wasabisys.com/htsviet-immutable-backup"
export RESTIC_PASSWORD="<MAT_KHAU_MA_HOA_KHO_LUU_TRU>"

Tải các biến môi trường và tiến hành khởi tạo kho lưu trữ (chỉ thực hiện một lần duy nhất):

bash
set -a
source /etc/restic/backup.env
set +a

# Khởi tạo repository Restic trên S3 bucket đã bật Object Lock
restic init

Sao lưu thư mục ứng dụng và volume Docker

Để sao lưu các thư mục chứa dữ liệu có trạng thái của container theo chuẩn quản trị volume Docker:

bash
# Sao lưu thư mục dữ liệu ứng dụng kèm gắn thẻ định danh
restic backup /opt/docker-apps \
  --exclude="*.log" \
  --exclude="node_modules" \
  --tag "docker,production,app"

Đẩy trực tiếp dump database vào Restic qua stdin

Để tránh nguy cơ rò rỉ các file .sql không mã hóa tạm thời trên ổ đĩa máy chủ (nơi mã độc có thể đọc cắp dữ liệu), bạn sử dụng tính năng nạp dữ liệu qua chuẩn đầu vào (--stdin) của Restic:

bash
# Stream trực tiếp MySQL dump vào Restic không tạo file tạm trên ổ đĩa
mysqldump --single-transaction --quick --routines --triggers -u root -p'<DB_PASSWORD>' app_production \
  | restic backup --stdin --stdin-filename "mysql-app_production.sql" --tag "db,mysql,daily"

Quá trình này nén và mã hóa các bảng dữ liệu ngay trong bộ nhớ rồi đẩy thẳng từng gói dữ liệu đã mã hóa lên S3.

Kiểm tra thực tế: Thử xóa dữ liệu khi có quyền root

Để xác nhận cấu hình hoạt động đúng kỳ vọng, bạn nên thực hiện các bước kiểm tra mô phỏng sau:

Thử xóa bucket bằng AWS CLI

Giả sử kẻ tấn công đã có được Access Key từ file /etc/restic/backup.env. Chúng sử dụng AWS CLI để gửi lệnh xóa sạch toàn bộ bucket:

bash
aws s3 rm s3://htsviet-immutable-backup --recursive \
  --endpoint-url https://s3.eu-central-1.wasabisys.com

Kết quả trả về trên màn hình console:

text
delete failed: s3://htsviet-immutable-backup/data/3a/3a8f... An error occurred (AccessDenied) when calling the DeleteObject operation: Object is protected by Object Lock (Compliance Mode).

Thử xóa snapshot bằng lệnh restic forget

Kẻ tấn công thử dùng chính công cụ Restic để xóa bỏ toàn bộ snapshot:

bash
restic forget --keep-last 0 --prune

Kết quả: Dù Restic có thể cập nhật danh sách index trong bộ nhớ, nhưng khi gửi lệnh xóa các pack file vật lý trong bucket, dịch vụ S3 lập tức trả về lỗi từ chối truy cập. Toàn bộ các khối dữ liệu cũ vẫn được bảo vệ nguyên vẹn trên Cloud.

Xử lý xung đột giữa restic prune và thời hạn khóa S3

Khi kết hợp Restic với S3 Object Lock, có một bài toán kỹ thuật vận hành thực tế mà nhiều kỹ sư thường bối rối: Xung đột giữa lệnh dọn dẹp (prune) và thời hạn khóa bảo lưu của S3.

Vòng đời snapshot và thu hồi dung lượng với S3 Object Lock

Vì sao lệnh restic prune phát sinh lỗi

  • Lệnh restic prune hoạt động bằng cách tìm các khối dữ liệu (pack files) không còn snapshot nào tham chiếu đến và gửi lệnh xóa chúng khỏi bucket để giải phóng dung lượng đĩa.
  • Tuy nhiên, các pack file này khi được đẩy lên S3 đã bị gắn thời hạn khóa Object Lock (ví dụ: 30 ngày).
  • Nếu bạn chạy restic prune vào ngày thứ 7 (khi file mới tồn tại được 7 ngày), S3 sẽ từ chối xóa vì file vẫn đang nằm trong thời hạn bảo lưu. Restic sẽ in ra các dòng cảnh báo lỗi và dung lượng lưu trữ trên Cloud sẽ không giảm.

Phối hợp chu kỳ lưu snapshot và lịch dọn dẹp

Để giải quyết hài hòa giữa yêu cầu bảo mật và tối ưu chi phí lưu trữ, bạn cần áp dụng chiến lược 2 giai đoạn:

  1. Giai đoạn 1: Khóa bảo lưu 30 ngày (WORM Window):
  • Cấu hình Default Retention trên S3 Bucket là 30 ngày.
  • Mọi snapshot tạo ra đều nằm trong thời hạn khóa WORM của S3 trong 30 ngày đầu tiên. Trong giai đoạn này, bạn chấp nhận chi trả chi phí lưu trữ cho toàn bộ các khối dữ liệu đã tạo.
  1. Giai đoạn 2: Thu hồi dung lượng an toàn (Safe Pruning):
  • Cấu hình chính sách lưu giữ của Restic trong script sao lưu hàng ngày:
bash
     # Đánh dấu các bản sao lưu cũ cần loại bỏ
     restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 3
  • Lệnh restic forget chỉ đánh dấu logic các snapshot cần bỏ mà không xóa file vật lý ngay.
  • Thiết lập một lịch chạy riêng cho lệnh restic prune vào cuối tuần (chỉ chạy 1 lần/tuần). Khi các pack file đã vượt qua mốc 30 ngày bảo lưu, Object Lock tự động hết hạn một cách tự nhiên. Lúc này, lệnh restic prune sẽ xóa thành công các file rác mà không gặp thông báo lỗi.

Nhờ cơ chế này, sau chu kỳ 30 đến 45 ngày đầu tiên, dung lượng lưu trữ của bucket trên S3 sẽ đi vào trạng thái cân bằng ổn định, không bị phình to vô hạn.

Tự động chạy sao lưu định kỳ bằng Systemd Timer

Thay vì sử dụng cron truyền thống (vốn khó giám sát nhật ký lỗi và dễ bị treo tiến trình khi mạng chậm), bạn nên sử dụng Systemd Service và Timer trên máy chủ Linux:

Tạo service chạy backup: restic-backup.service

ini
[Unit]
Description=Restic Immutable Backup Service
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
EnvironmentFile=/etc/restic/backup.env
ExecStart=/usr/local/bin/backup-runner.sh
TimeoutStartSec=1800
PrivateTmp=true
ProtectSystem=full

Tạo lịch định kỳ: restic-backup.timer

ini
[Unit]
Description=Chạy sao lưu Restic hàng ngày lúc 02:00 sáng

[Timer]
OnCalendar=*-*-* 02:00:00
RandomizedDelaySec=600
Persistent=true

[Install]
WantedBy=timers.target

Kích hoạt timer:

bash
sudo systemctl daemon-reload
sudo systemctl enable --now restic-backup.timer

Lệnh systemctl list-timers sẽ cho bạn biết chính xác thời điểm lần sao lưu tiếp theo diễn ra, và toàn bộ nhật ký sao lưu được lưu trữ tập trung qua journalctl -u restic-backup.service.

Các bước tiếp theo để hoàn thiện kế hoạch phục hồi thảm họa

Cơ chế khóa sao lưu với Restic và S3 Object Lock giúp giữ lại một bản sao lưu an toàn khi máy chủ ứng dụng bị xâm nhập. Để hoàn thiện quy trình bảo vệ và vận hành ứng dụng, bạn có thể tham khảo các bài viết tiếp theo trong cụm:

  1. Tự động chuyển đổi lưu lượng khi máy chủ chính gặp sự cố: Thiết lập cơ chế giám sát và chuyển hướng lưu lượng tại tầng mạng qua kịch bản chuyển vùng DNS tự động khi Data Center sự cố (HTS-110).
  2. Kiểm chứng khả năng phục hồi thực tế: Đưa các bản sao lưu đã khóa vào quy trình kiểm thử thông qua kịch bản diễn tập phục hồi thảm họa cho đội ngũ kỹ thuật (HTS-111) để đo lường chính xác thời gian khôi phục (RTO).
  3. Đánh giá và kiểm toán định kỳ: Sử dụng checklist kiểm toán khả năng phục hồi sau thảm họa định kỳ (HTS-112) để đảm bảo chính sách khóa Object Lock luôn hoạt động đúng như thiết kế ban đầu.
Nội dung hướng dẫn này có hữu ích với hệ thống của bạn không?

Chia sẻ bài viết hữu ích

Gửi bài viết này cho đồng nghiệp

Đọc tiếp

Bài nên đọc tiếp

Xem tất cả bài về Backup và phục hồi