Dựng Proxmox Backup Server chống ransomware ngay hôm nay: 8 bước từ một sự cố thật của bên mình

Chia sẻ bài viết

Mục lục

Cuối tuần vừa rồi một node ảo hoá của bên mình bị chiếm quyền root. Kẻ tấn công xoá log, mã hoá kho đĩa VM ở /var/lib/vz, và phá luôn toàn bộ file backup vzdump nằm trên cùng node đó. Hơn 400 file, trong đó có những bản backup mà nếu chỉ dựa vào chúng thì coi như mất trắng. Thứ còn lại nguyên vẹn là bản backup trên Proxmox Backup Server đặt ở máy khác, vì token mà node dùng để đẩy backup lên PBS không có quyền xoá. Node bị chiếm root hoàn toàn nhưng không xoá được bất kỳ backup nào trên PBS. Bài này mình viết lại đúng những gì đã cứu được dữ liệu, thành các bước để anh em đang chạy Proxmox làm ngay trong hôm nay. Mình không đi vào chi tiết vụ tấn công, chỉ nói phần backup.

Vì sao vzdump trên cùng node không phải backup

Backup vzdump mặc định lưu ở local (/var/lib/vz/dump) hoặc một NFS mount trên chính node. Kẻ tấn công đã có root trên node thì hai chỗ đó chỉ là hai thư mục nữa. Trong đợt tấn công đang diễn ra với Proxmox cũ (mình viết ở bài này), nhiều nạn nhân ghi nhận kẻ tấn công chủ động tìm các mount có chữ "backup" trong tên để xoá trước khi mã hoá. Cho nên câu hỏi không phải "có backup chưa" mà là "kẻ tấn công cầm root của node có xoá được backup đó không". Nếu câu trả lời là có, thì chưa có backup.

PBS chống ransomware bằng cách nào

Ba thứ trong thiết kế của Proxmox Backup Server khiến nó khác một thư mục backup:

Một, PBS lưu dữ liệu dạng chunk theo hash, không bao giờ ghi đè chunk đã có. Client (node PVE) chỉ gửi chunk mới lên, không có đường nào để sửa nội dung backup cũ, kể cả client đã bị chiếm.

Hai, hệ thống quyền tách rời quyền ghi và quyền xoá. Quyền Datastore.Backup chỉ cho phép tạo backup mới (và đọc lại backup của chính mình), không cho xoá hay sửa. Quyền xoá là Datastore.Prune, một quyền riêng, và node PVE không cần quyền đó vì việc dọn backup cũ nên chạy bằng prune job ngay trên PBS. Đây chính là chốt đã cứu dữ liệu của bên mình.

Ba, verify job đối chiếu checksum toàn bộ backup theo lịch, nên nếu có gì đó bắt đầu sửa file trên chính PBS thì verify sẽ fail và báo ngay.

Làm ngay: 8 bước

Bước 1: PBS phải nằm trên máy khác

Cài PBS trên một máy vật lý riêng, hoặc ít nhất là một host khác với các node cần backup, dùng mật khẩu root khác, không join cùng cluster, không share storage. Đặt PBS thành một VM trên chính cluster mình đang backup là vô nghĩa, node bị chiếm thì VM đó cũng đi theo. Máy PBS không cần mạnh, cần đĩa lớn và ổn định. ZFS mirror hai ổ là đủ cho hầu hết cụm nhỏ.

Bước 2: tạo datastore và namespace

Mỗi cluster (hoặc mỗi khách) một namespace riêng, để phân quyền hẹp ở bước sau. Tạo datastore bằng lệnh, namespace tạo trên giao diện (Datastore, Content, Add Namespace):

proxmox-backup-manager datastore create tank /tank/pbs

Bước 3: user riêng, token riêng, chỉ có quyền ghi

Không bao giờ dùng root@pam để cấu hình storage trên PVE. Tạo user thường, sinh API token, gán đúng một role DatastoreBackup trên đúng một namespace:

proxmox-backup-manager user create backup@pbs
proxmox-backup-manager user generate-token backup@pbs pve-vn
# lưu lại token secret in ra màn hình, chỉ hiện một lần

proxmox-backup-manager acl update /datastore/tank/pve-vn DatastoreBackup --auth-id 'backup@pbs!pve-vn'

# kiểm tra lại: chỉ được có DatastoreBackup, không có Admin, DatastoreAdmin, DatastorePowerUser
proxmox-backup-manager acl list

Role DatastoreBackup gồm Datastore.Backup và Datastore.Audit, không có Datastore.Prune, không có Datastore.Modify. Nghĩa là token này tạo được backup, đọc được backup của chính nó để restore, và hết. Kẻ chiếm được node lấy được token này cũng chỉ làm được đúng chừng đó. Nếu anh em đang dùng DatastorePowerUser hay DatastoreAdmin cho node thì đổi ngay, vì hai role đó có quyền xoá.

Bước 4: thêm PBS vào PVE bằng token đó

# lấy fingerprint trên PBS
proxmox-backup-manager cert info | grep Fingerprint

# trên node PVE
pvesm add pbs pbs-main \
  --server 10.10.10.5 --datastore tank --namespace pve-vn \
  --username 'backup@pbs!pve-vn' --password 'TOKEN_SECRET' \
  --fingerprint 'AA:BB:...'

Rồi tạo backup job trong Datacenter, Backup, chọn storage pbs-main, chạy thử một VM, và thử xoá bản backup vừa tạo từ giao diện PVE. Phải nhận lỗi permission denied. Nếu xoá được thì bước 3 sai.

Bước 5: dọn backup cũ bằng prune job trên PBS, giữ dài hạn

Vì node không có quyền xoá nên retention cấu hình trên PVE sẽ không có tác dụng, phải đặt prune job trên PBS:

proxmox-backup-manager prune-job create prune-pve-vn \
  --store tank --ns pve-vn --schedule daily \
  --keep-daily 7 --keep-weekly 4 --keep-monthly 6

Giữ ít nhất vài bản theo tháng. Ransomware hay nằm im vài ngày đến vài tuần trước khi mã hoá, nên bản backup mới nhất có thể đã chứa mã độc, bản tháng trước mới sạch. Với VM quan trọng, đánh dấu protected cho một bản đã kiểm tra là sạch (Datastore, Content, chọn snapshot, Change Protection), prune job sẽ bỏ qua bản đó.

Bước 6: verify job và thông báo

proxmox-backup-manager verify-job create verify-tank \
  --store tank --schedule 'sat 02:00' \
  --ignore-verified true --outdated-after 30

Cấu hình notification (Configuration, Notifications) gửi email hoặc webhook khi verify fail, backup job fail, sync fail. Trong sự cố vừa rồi, dấu hiệu đầu tiên bên mình thấy chính là job đẩy backup từ node đó lên PBS bắt đầu lỗi, trước khi ai kịp nhìn vào node.

Bước 7: bản thứ hai, ở nơi khác, kéo về chứ không đẩy đi

Một PBS vẫn là một điểm chết. Dựng PBS thứ hai ở địa điểm khác (nhà, văn phòng, datacenter khác, hoặc thuê chỗ) và cho nó tự kéo backup từ PBS chính về bằng sync job. Chiều "kéo" quan trọng: PBS chính không giữ credential nào của PBS phụ, nên PBS chính bị chiếm cũng không với tới PBS phụ được. Trên PBS phụ:

proxmox-backup-manager remote create pbs-main \
  --host 10.10.10.5 --auth-id 'sync@pbs!pull' \
  --password 'TOKEN_SECRET' --fingerprint 'AA:BB:...'

proxmox-backup-manager sync-job create pull-tank \
  --store tank2 --remote pbs-main --remote-store tank --remote-ns pve-vn \
  --schedule 'hourly' --remove-vanished false

Token sync@pbs!pull trên PBS chính chỉ cần role DatastoreReader trên namespace đó. Tham số remove-vanished false là bắt buộc: nếu ai đó xoá được backup trên PBS chính thì bản trên PBS phụ vẫn còn, không bị xoá theo. Ai muốn thêm một lớp nữa thì dùng tape hoặc export định kỳ ra ổ rời rút ra.

Bước 8: khoá chính PBS

Port 8007 của PBS chỉ mở cho IP các node PVE và IP quản trị, không mở ra internet, không mở cho cả dải LAN. Bật 2FA cho root@pam và mọi user có quyền admin. Không tạo token cho root@pam. Cập nhật PBS cùng lịch với PVE. Log của PBS đẩy ra một máy khác. Và mỗi tháng restore thử một VM ra VMID mới rồi boot lên xem có chạy không, backup chưa restore thử thì chưa biết có dùng được không.

Checklist 5 phút

Chạy trên PBS để soát lại toàn bộ:

# token nào đang có quyền xoá?
proxmox-backup-manager acl list | grep -E 'Admin|PowerUser'

# prune, verify, sync đã có lịch chưa?
proxmox-backup-manager prune-job list
proxmox-backup-manager verify-job list
proxmox-backup-manager sync-job list

# sync có bật remove-vanished không? (phải là false hoặc không có)
grep -A8 'sync:' /etc/proxmox-backup/sync.cfg | grep remove-vanished

# 8007 đang nghe cho ai?
ss -lntp | grep 8007
iptables -L INPUT -n | grep 8007

Và trên node PVE: cat /etc/pve/storage.cfg, storage pbs phải dùng username dạng user@pbs!token, không phải root@pam.

Về phía bên mình

Node bị tấn công đã được cách ly ngay khi phát hiện và đang được cài lại sạch trên Proxmox VE 9, dữ liệu khôi phục từ PBS. Bên mình đang dựng thêm PBS thứ hai kéo về theo đúng bước 7 ở trên, vì trước sự cố mới chỉ có một datastore, đó là lỗ hổng còn lại của chính mình, và là lý do mình viết bài này ngay thay vì đợi xong việc.

Khách tự dựng Proxmox mà muốn bên mình dựng PBS chuẩn theo 8 bước này, hoặc cần chỗ đặt PBS phụ ở datacenter khác (Việt Nam hoặc Mỹ) để kéo backup về, cứ liên hệ. Bài liên quan: các lỗ hổng Proxmox và KVM 2026đợt tấn công Proxmox VE 7/8 đang diễn ra.