Chống ransomware và bảo mật MongoDB
Bảo mật MongoDB không đơn thuần là việc đặt một mật khẩu mạnh. Đây là một bài toán phòng thủ nhiều lớp, bao gồm: thu hẹp bề mặt lộ ra ở tầng mạng, giám sát và kiểm soát hành vi ở tầng runtime/kernel (thông qua eBPF), và đảm bảo khả năng khôi phục dữ liệu bằng backup, lý tưởng là loại backup không thể bị chỉnh sửa hay xóa (immutable).
Bài viết này tập trung giải quyết một vấn đề rất thực tế: phần lớn các vụ tấn công tống tiền (ransom) nhắm vào MongoDB trên Internet không xuất phát từ các kỹ thuật khai thác lỗ hổng 0-day phức tạp, mà đến từ sự kết hợp giữa tự động hóa của kẻ tấn công và lỗi cấu hình của người quản trị.
1. MongoDB ransom dường như đã bị công nghiệp hóa
1.1. Một chuỗi tấn công điển hình trông như thế nào?
Theo phân tích của Flare công bố tháng 01/2026, nhiều chiến dịch tống tiền MongoDB được vận hành giống như một dây chuyền sản xuất, gồm 4 bước nối tiếp nhau:
Bước 1: Trinh sát (Recon/Discovery): Kẻ tấn công dùng các công cụ tìm kiếm thiết bị như Shodan, Censys, hoặc tự viết script quét Internet để dò tìm những instance MongoDB đang mở ra ngoài.
Bước 2: Xâm nhập đầu (Initial access): Khi tìm được mục tiêu, chúng kết nối thẳng vào các instance chưa bật xác thực (authentication) hoặc có cấu hình truy cập quá lỏng lẻo. Không cần khai thác phức tạp vì cửa gần như đã mở sẵn.
Bước 3: Gây thiệt hại (Impact): Sau khi vào được, chúng xóa sạch dữ liệu bằng cách drop database hoặc drop collection, hoặc wipe toàn bộ.
Bước 4: Tống tiền (Extortion): Cuối cùng, chúng tạo một collection hoặc document mới chứa thư đòi tiền chuộc, hướng dẫn nạn nhân cách thanh toán để (được cho là) có thể lấy lại dữ liệu.
Điểm mấu chốt cần nhớ: Kẻ tấn công hầu như không cần đến lỗ hổng 0-day nào cả. Chúng chỉ cần tìm được đúng một hệ thống bị phơi nhiễm do cấu hình sai, và phần còn lại là việc của script tự động.
1.2. Quy mô phơi nhiễm (theo ghi nhận của Flare, 01/2026)
Những con số dưới đây là snapshot tại thời điểm Flare tiến hành khảo sát:
Có hơn 200.000 MongoDB server có thể tìm thấy công khai trên Internet.
Trong đó, hơn 100.000 instance để lộ thông tin vận hành (operational information).
3.100 instance bị phơi nhiễm hoàn toàn, tức là mở ra Internet mà không có bất kỳ kiểm soát truy cập nào (fully exposed without access restrictions).
Trong nhóm 3.100 instance "fully exposed" này, đã có 1.416 instance (45,6%) bị xâm nhập: dữ liệu bị xóa sạch và thay bằng thư đòi tiền chuộc.
Nói cách khác, cứ khoảng 2 hệ thống mở toang ra Internet thì gần 1 hệ thống đã bị tấn công.
1.3. Mô hình kinh tế: đòi ít, nhưng đánh thật nhiều
Flare ghi nhận mức tiền chuộc thường rơi vào khoảng 0,005 BTC (tương đương khoảng 500 - 600 USD, và phần lớn các trường hợp quan sát được là khoảng 500 USD).
Mức tiền vừa phải này không phải là ngẫu nhiên. Đây là một chiến lược kinh tế có chủ đích: đòi một số tiền đủ nhỏ để nạn nhân cảm thấy trả cho xong thay vì bỏ công khôi phục, qua đó tối đa hóa tỷ lệ trả tiền trong mô hình tấn công hàng loạt.
1.4. Dấu hiệu cho thấy có một tác nhân chi phối
Khi phân tích các thư đòi tiền chuộc, Flare nhận thấy một điểm rất đáng chú ý:
Chỉ có 5 địa chỉ ví Bitcoin khác nhau được sử dụng trong toàn bộ các vụ quan sát được.
Đặc biệt, ví
bc1qe2l4ffmsqfdu43d7n76hp2ksmhclt5g9krx3duxuất hiện trong hơn 98% trường hợp.
Đây là tín hiệu mạnh cho thấy phần lớn hoạt động có thể đến từ một tác nhân duy nhất chi phối (hoặc một nhóm phối hợp rất chặt chẽ), chứ không phải do nhiều nhóm nhỏ lẻ hoạt động độc lập.
1.5. Nguyên nhân gốc vẫn là cấu hình sai
Flare nhấn mạnh rằng tại thời điểm công bố nghiên cứu:
Chưa có ghi nhận về bất kỳ lỗ hổng pre-authentication RCE (thực thi mã từ xa không cần xác thực) đã biết nào trong MongoDB.
Mặc dù nhiều server có dính CVE, nhưng phần lớn CVE quan sát được chỉ dẫn tới tấn công DoS (làm nghẽn dịch vụ), chứ không cho phép chiếm quyền.
Yếu tố tạo điều kiện chính vẫn là misconfiguration, tức là để database lộ ra Internet mà không bật authentication hoặc không đặt ACL đúng cách.
Nói ngắn gọn: kẻ tấn công không cần kỹ thuật cao siêu, chúng chỉ cần chờ người quản trị sơ suất.
Dựa trên các giả định thận trọng về nhóm 1.684 instance còn lại chưa thấy thư đòi tiền chuộc, Flare ước tính mức thu nhập tiềm năng của tác nhân tấn công có thể dao động từ 0 USD đến 842.000 USD, tùy thuộc vào số nạn nhân thực sự đã chịu trả tiền.
Tài liệu tham chiếu:
MongoDB Ransom Isn’t Back - It Never Left (26/01/2026): https://flare.io/learn/resources/blog/mongodb-ransom
Hackers exploit unsecured MongoDB instances to wipe data and demand ransom (02/02/2026): https://securityaffairs.com/187548/security/hackers-exploit-unsecured-mongodb-instances-to-wipe-data-and-demand-ransom.html
Exposed MongoDB Servers Remain Prime Targets…: https://www.rescana.com/post/exposed-mongodb-servers-remain-prime-targets-for-automated-data-extortion-attacks-and-vulnerability
2. (Docker) Giải phẫu cái bẫy giữa iptables và ufw
Một trong những lỗi cấu hình nguy hiểm nhất khi chạy MongoDB (cũng như các database khác) trong Docker là tin rằng đã ufw deny thì port sẽ được chặn, trong khi vẫn publish port ra ngoài bằng flag -p.
Vấn đề ở đây không phải là UFW bị hỏng, mà nằm ở đường đi của gói tin và thứ tự các chain iptables mà Docker can thiệp vào.
2.1. Vì sao ufw deny không chặn được port đã publish bằng Docker?
Khi chạy lệnh:
docker run -p 27017:27017 mongo:...
Docker sẽ tự động thêm các rule iptables để publish port này. Luồng xử lý gói tin sau đó diễn ra như sau:
Bước 1: DNAT ở bảng nat, chain PREROUTING: Gói tin đi tới <HOST_IP>:27017 bị đổi đích (DNAT) sang <CONTAINER_IP>:<container_port> rất sớm, trước cả khi kernel quyết định routing cho gói tin đó.
Bước 2: Gói tin đi theo đường forwarding: Sau khi đã bị DNAT, traffic không còn đi vào máy host nữa, mà đi theo forwarding path để vào bridge của Docker (ví dụ docker0). Điều này có nghĩa gói tin sẽ được xử lý ở bảng filter, chain FORWARD , chứ không phải chain INPUT.
Bước 3: Docker chèn rule riêng vào FORWARD: Docker thêm các rule trong chain FORWARD để nhảy (jump) qua những chain do chính nó quản lý, thường gồm DOCKER-USER và các chain DOCKER/* khác (tùy vào phiên Docker Engine và backend firewall đang dùng).
Bước 4: UFW chủ yếu quản lý rule ở chain INPUT và OUTPUT. Trên Ubuntu/Debian, policy mặc định cho FORWARD trong cấu hình UFW là DROP (xem tại /etc/default/ufw), nhưng Docker đã override điều này bằng các rule riêng của nó. → Kết quả: một rule dạng ufw deny 27017 thường không match hoặc không có hiệu lực như người quản trị kỳ vọng.
Chính tài liệu của Docker cũng mô tả chính xác vấn đề này: traffic đi tới container bị chuyển hướng (divert) ngay tại bảng nat , trước khi chạm tới các chain mà UFW kiểm soát. Vì vậy, các cấu hình UFW về cơ bị bỏ qua.
Ghi chú: UFW thực ra có khả năng quản lý forwarding thông qua lệnh ufw route và thay đổi DEFAULT_FORWARD_POLICY. Tuy nhiên, khi kết hợp với Docker (vốn tự sinh DNAT và rule riêng), việc viết rule UFW rất dễ sai và khó bảo trì lâu dài. Đây là lý do nhiều team chọn dùng tool ufw-docker - công cụ này sẽ map các policy của UFW vào đúng điểm cần thiết (cụ thể là chain DOCKER-USER).
2.2. Kiểm chứng nhanh
Để thực sự hiểu cái bẫy này, cách tốt nhất là tự tay dựng lại từ đầu: chạy MongoDB trong Docker, bật UFW để chặn port, rồi kiểm tra xem port có thực sự bị chặn hay không. Sau khi kiểm tra thử, sẽ thấy rõ lý do tại sao ufw deny lại vô hiệu.
Chuẩn bị
Một máy Linux (Ubuntu/Debian) có public IP, gọi là Host A.
Một máy thứ hai ở bên ngoài Host A (laptop, VPS khác, hoặc bất kỳ máy nào có thể ping tới Host A qua Internet), gọi là Host B.
Đã cài sẵn
docker,ufw,nmaptrên Host A.
Bước 1: Trên Host A: Chạy MongoDB trong Docker và publish port
# Chạy MongoDB container, publish port 27017 ra ngoài
docker run -d \
--name mongo-test \
-p 27017:27017 \
-e MONGO_INITDB_ROOT_USERNAME=admin \
-e MONGO_INITDB_ROOT_PASSWORD=changeme \
mongo:7
# Kiểm tra container đã chạy
docker ps | grep mongo-test
Bước 2: Trên Host A: Bật UFW và thêm rule deny 27017
# Bật UFW với policy mặc định
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Cho phép SSH để không tự khóa mình ra
sudo ufw allow 22/tcp
# Deny port 27017, đây là rule mà chúng ta KỲ VỌNG sẽ chặn MongoDB
sudo ufw deny 27017/tcp
# Bật UFW
sudo ufw enable
# Xem lại rule UFW đang hoạt động
sudo ufw status verbose
Kết quả mong đợi (về mặt logic): port 27017 sẽ bị chặn từ ngoài Internet. Nhưng thực tế thì không phải vậy...
root@ip-172-31-44-106:/home/ssm-user# sudo ufw default deny incoming
Default incoming policy changed to 'deny'
(be sure to update your rules accordingly)
root@ip-172-31-44-106:/home/ssm-user# sudo ufw default allow outgoing
Default outgoing policy changed to 'allow'
(be sure to update your rules accordingly)
root@ip-172-31-44-106:/home/ssm-user# sudo ufw allow 22/tcp
Rules updated
Rules updated (v6)
root@ip-172-31-44-106:/home/ssm-user# sudo ufw deny 27017/tcp
Rules updated
Rules updated (v6)
root@ip-172-31-44-106:/home/ssm-user# sudo ufw enable
Firewall is active and enabled on system startup
root@ip-172-31-44-106:/home/ssm-user# sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), deny (routed)
New profiles: skip
To Action From
-- ------ ----
22/tcp ALLOW IN Anywhere
27017/tcp DENY IN Anywhere
22/tcp (v6) ALLOW IN Anywhere (v6)
27017/tcp (v6) DENY IN Anywhere (v6)
root@ip-172-31-44-106:/home/ssm-user#
Bước 3: Trên Host A: Kiểm tra xem container đang bind ở đâu
# 1) Xem container đang expose port nào
docker port mongo-test
# Kết quả điển hình:
# 27017/tcp -> 0.0.0.0:27017
# 27017/tcp -> [::]:27017
# 2) Xem socket đang lắng nghe ở host
sudo ss -tlnp | grep 27017
# Nếu thấy 0.0.0.0:27017 hoặc *:27017 → đang lắng nghe trên mọi interface
# 3) Xem rule-set iptables thực tế (Docker đã tự inject rule vào đây)
sudo iptables-save | grep -E "27017|DOCKER"
# sẽ thấy Docker đã thêm rule DNAT và FORWARD cho phép 27017
Kết quả command:
root@ip-172-31-44-106:/home/ssm-user# docker port mongo-test
27017/tcp -> 0.0.0.0:27017
27017/tcp -> [::]:27017
root@ip-172-31-44-106:/home/ssm-user# sudo ss -tlnp | grep 27017
LISTEN 0 4096 0.0.0.0:27017 0.0.0.0:* users:(("docker-proxy",pid=3012,fd=8))
LISTEN 0 4096 [::]:27017 [::]:* users:(("docker-proxy",pid=3017,fd=8))
root@ip-172-31-44-106:/home/ssm-user# sudo iptables-save | grep -E "27017|DOCKER"
:DOCKER - [0:0]
:DOCKER-BRIDGE - [0:0]
:DOCKER-CT - [0:0]
:DOCKER-FORWARD - [0:0]
:DOCKER-INTERNAL - [0:0]
:DOCKER-USER - [0:0]
-A FORWARD -j DOCKER-USER
-A FORWARD -j DOCKER-FORWARD
-A DOCKER -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 27017 -j ACCEPT
-A DOCKER ! -i docker0 -o docker0 -j DROP
-A DOCKER-BRIDGE -o docker0 -j DOCKER
-A DOCKER-CT -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A DOCKER-FORWARD -j DOCKER-CT
-A DOCKER-FORWARD -j DOCKER-INTERNAL
-A DOCKER-FORWARD -j DOCKER-BRIDGE
-A DOCKER-FORWARD -i docker0 -j ACCEPT
-A ufw-user-input -p tcp -m tcp --dport 27017 -j DROP
:DOCKER - [0:0]
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 27017 -j DNAT --to-destination 172.17.0.2:27017
root@ip-172-31-44-106:/home/ssm-user#
Đến đây, sẽ nhận ra: mặc dù UFW đã deny 27017, nhưng Docker vẫn có rule riêng trong iptables cho phép traffic đi vào container.
Bước 4 : Trên Host B: Test từ bên ngoài
Lấy public IP của Host A (giả sử là 54.255.190.152), rồi từ Host B chạy:
# Scan port 27017 từ bên ngoài
nmap -Pn -p 27017 54.255.190.152
# Kết quả NẾU UFW có hiệu lực (mong đợi lý thuyết):
# PORT STATE SERVICE
# 27017/tcp filtered unknown
# Kết quả THỰC TẾ (cái bẫy):
# PORT STATE SERVICE
# 27017/tcp open mongod
Kết quả command:
root@ip-172-31-38-124:~# nmap -Pn -p 27017 54.255.190.152
Starting Nmap 7.94SVN ( https://nmap.org ) at 2026-04-21 03:33 UTC
Nmap scan report for ec2-54-255-190-152.ap-southeast-1.compute.amazonaws.com (54.255.190.152)
Host is up (0.00046s latency).
PORT STATE SERVICE
27017/tcp open mongod
Nmap done: 1 IP address (1 host up) scanned in 0.06 seconds
root@ip-172-31-38-124:~#
Để chắc chắn hơn, thử kết nối thật bằng mongosh hoặc nc:
# Trên Host B
nc -zv 54.255.190.152 27017
# Kết quả: Connection to 203.0.113.10 27017 port [tcp/*] succeeded!
# Hoặc nếu có mongosh:
mongosh "mongodb://admin:changeme@203.0.113.10:27017/"
# Sẽ kết nối thành công → MongoDB đang thực sự bị phơi nhiễm ra Internet
Kết quả command:
root@ip-172-31-38-124:~# nc -zv 54.255.190.152 27017
Connection to 54.255.190.152 27017 port [tcp/*] succeeded!
root@ip-172-31-38-124:~#
Bước 5: Kết luận từ kịch bản
Nếu nmap báo open và nc/mongosh kết nối được từ Host B → port đang bị phơi nhiễm bất chấp UFW đã deny. đã tự tay tái tạo lại "cái bẫy" kinh điển này.
Điều gì đã xảy ra bên dưới?
Gói tin từ Host B tới Host A trên port 27017 đi vào chain
PREROUTINGcủa bảngnat.Docker có rule DNAT ở đây, đổi đích thành IP container → gói tin rẽ sang forwarding path, không đi vào chain
INPUT.UFW rule
deny 27017nằm ởINPUT→ không bao giờ được kích hoạt.Traffic đến container an toàn, MongoDB phục vụ request như không có gì xảy ra.
Dọn dẹp sau khi test
# Dừng và xóa container
docker rm -f mongo-test
# Xóa rule UFW test
sudo ufw delete deny 27017/tcp
# (Tùy chọn) tắt UFW nếu chỉ bật để test
# sudo ufw disable
Sau khi đã tận mắt thấy vấn đề, các mục 2.3 và 2.4 sẽ trình bày cách xử lý đúng: bind vào loopback hoặc whitelist IP qua chain DOCKER-USER.
2.3. Giải pháp #1: (An toàn nhất) Chỉ bind vào loopback
Nếu database chỉ cần được truy cập từ chính máy host (hoặc qua reverse proxy, SSH tunnel, Docker network nội bộ trên cùng host), thì **đừng publish ra 0.0.0.0**hãy bind vào 127.0.0.1:
# docker-compose.yml
services:
mongo:
image: mongo:7
ports:
- "127.0.0.1:27017:27017" # Chỉ host mới truy cập được
Lệnh CLI tương đương:
docker run -p 127.0.0.1:27017:27017 mongo:7
Note: Với các phiên Docker Engine cũ hơn 28.0.0, port dù đã bind vào 127.0.0.1 vẫn có thể bị truy cập bởi các host khác nằm cùng phân đoạn L2 (tức là cùng switch vật lý). Vấn đề này đã được fix từ Docker 28.0.0 trở đi (chi tiết tại moby/moby#45610).
Nếu đang chạy phiên Docker cũ hơn: hãy upgrade lên, hoặc kết hợp thêm rule DOCKER-USER được trình bày ở mục 2.4.
Tốt hơn nữa là tránh publish hoàn toàn, đặt app và DB trong cùng một Docker network internal, app gọi DB qua DNS service name (mongo:27017), không cần ports: của MongoDB.
2.4. Giải pháp #2: Whitelist IP bằng chain DOCKER-USER
Khi ứng dụng chạy trên một máy khác (App server, bastion host, qua VPN…), cần cho phép truy cập từ xa nhưng có kiểm soát. Nơi đúng để đặt policy này là chain DOCKER-USER , vì Docker đảm bảo rằng chain này luôn được xử lý trước các rule do chính Docker tự sinh trong FORWARD.
Tips: Bind vào một IP cụ thể của host
Trước khi lao vào viết rule phức tạp, hãy nhớ đến một giải pháp đơn giản hơn: nếu host có nhiều IP (ví dụ vừa có IP public, vừa có IP private) và chỉ muốn DB lắng nghe trên một IP cụ thể, thì hãy bind port publish trực tiếp vào IP đó:
# Chỉ bind vào IP private, không phơi ra IP public
docker run -p 10.0.1.5:27017:27017 mongo:7
# docker-compose.yml
ports:
- "10.0.1.5:27017:27017"
Cách này thường dễ maintain hơn viết rule conntrack --ctorigdst, và nên được ưu tiên khi topology cho phép.
Pitfall #1: -dport trong DOCKER-USER match port của container, KHÔNG phải của host
Lý do: khi gói tin đến chain DOCKER-USER, nó đã đi qua DNAT ở PREROUTING rồi, nên trường --dport lúc này mang giá trị sau DNAT, tức là port bên trong container.
Ví dụ:
p 27017:27017(host port trùng container port) → trongDOCKER-USERdùng-dport 27017.p 27018:27017(khác nhau) → trongDOCKER-USERvẫn phải dùng-dport 27017(container port), không phải 27018.
Nếu muốn match dựa trên thông tin gốc trước khi DNAT xảy ra, hãy dùng extension conntrack:
# Match theo port gốc mà client gửi đến (trước khi DNAT)
sudo iptables -I DOCKER-USER -p tcp \
-m conntrack --ctorigdstport 27018 -j DROP
# Match theo IP gốc mà client gửi đến (dùng khi host có nhiều IP, chỉ muốn lock 1 IP)
sudo iptables -I DOCKER-USER -p tcp \
-m conntrack --ctorigdst 1.2.3.4 --ctorigdstport 27018 -j DROP
Lưu ý: conntrack đòi hỏi module kernel tương ứng (hầu hết distro hiện đại đều có sẵn). Nếu đang ở môi trường stripped-down không có module này, hãy ưu tiên match theo container port hoặc bind vào IP cụ thể như mẹo phía trên.
Pitfall #2: Đừng quên RELATED,ESTABLISHED khi áp policy rộng
Nếu chỉ DROP inbound theo một --dport 27017 cụ thể, thì traffic outbound của container (như DNS query, gọi API bên ngoài…) vẫn hoạt động bình thường, vì rule chỉ match theo destination port cụ thể.
Nhưng nếu áp một policy chặn rộng hơn (ví dụ: DROP tất cả traffic không thuộc whitelist subnet, hoặc đặt default policy của DOCKER-USER/FORWARD thành DROP), thì bắt buộc phải có rule cho phép RELATED,ESTABLISHED ở đầu chain. Nếu thiếu, traffic return và outbound của container sẽ bị đứt gãy.
Bộ rule tham khảo:
Pattern này cover các trường hợp bridge network thông thường với IPv4. Các case nâng cao (IPv6, Swarm ingress, macvlan/ipvlan, multi-network…) cần điều chỉnh thêm.
Giả định:
eth0= interface public của host (thay đúng tên:ens3,enp1s0…)10.0.1.20= IP của app server được whitelistPublish
p 27017:27017(host port trùng container port)
# 1) Cho phép các response thuộc kết nối đã được accept trước đó
sudo iptables -I DOCKER-USER 1 \
-m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# 2) Whitelist app server truy cập vào port DB
sudo iptables -I DOCKER-USER 2 \
-i eth0 -s 10.0.1.20 -p tcp --dport 27017 -j ACCEPT
# 3) DROP mọi nguồn khác cố kết nối tới port DB
sudo iptables -I DOCKER-USER 3 \
-i eth0 -p tcp --dport 27017 -j DROP
Về thứ tự: lệnh iptables -I CHAIN N chèn rule vào vị trí N (đẩy các rule cũ xuống dưới). Sau 3 lệnh trên, thứ tự match trong DOCKER-USER sẽ là:
1. RELATED,ESTABLISHED → ACCEPT
2. From 10.0.1.20 tới :27017 → ACCEPT
3. Anything tới :27017 → DROP
… RETURN (mặc định do Docker tạo)
Lưu ý về interface: Pattern phía trên chỉ áp dụng cho traffic vào qua
eth0. Nếu DB cần được truy cập qua nhiều đường (ví dụ: VPN interfacewg0/tun0, private NICeth1, mạng overlay…), cần phải thêm rule ACCEPT tương ứng cho từng interface/CIDR hợp lệ. Nếu không, các kết nối hợp lệ khác sẽ bị fall-through xuống rule DROP.
Pattern thay thế
Dùng negated source (dấu !) trong một rule duy nhất:
# Chặn mọi nguồn TRỪ 10.0.1.20
sudo iptables -I DOCKER-USER \
-i eth0 -p tcp --dport 27017 ! -s 10.0.1.20 -j DROP
# Hoặc áp dụng cho cả subnet
sudo iptables -I DOCKER-USER \
-i eth0 -p tcp --dport 27017 ! -s 10.0.1.0/24 -j DROP
# Hoặc cho một dải IP cụ thể (cần module -m iprange)
sudo iptables -I DOCKER-USER -i eth0 -m iprange -p tcp --dport 27017 \
! --src-range 10.0.1.20-10.0.1.30 -j DROP
2.5. Làm sao để rule không mất sau khi reboot?
Theo mặc định, iptables không tự persist qua các lần khởi động lại. Có hai cách phổ biến để xử lý:
Cách 1: iptables-persistent (Debian/Ubuntu):
sudo apt install iptables-persistent
sudo netfilter-persistent save
Cách 2: Dùng tool chaifeng/ufw-docker (được khuyến nghị nếu team đã quen với UFW): công cụ này cho phép viết rule theo cú pháp UFW quen thuộc, nhưng sẽ inject vào đúng chain DOCKER-USER và persist thông qua UFW.
# Ví dụ cú pháp sau khi cài ufw-docker
sudo ufw-docker allow mongo 27017
2.6. Ghi chú về backend iptables vs nftables
Phần này dễ gây nhầm lẫn, nên cần phân biệt rõ hai lớp:
Lớp 1: Kernel subsystem: hầu hết các distro hiện đại (Ubuntu 22.04+, Debian 12+, RHEL 9+) đã chuyển sang dùng nftables làm kernel firewall subsystem.
Lớp 2: Command-line tool: trên những distro này, lệnh iptables mà gõ thực ra chỉ là iptables-nft wrapper, rule thật sự vẫn nằm trong nftables phía dưới, chỉ là cú pháp viết theo kiểu iptables truyền thống. Vì vậy:
iptables-savevẫn hoạt động và hữu ích (đọc qua wrapper).Khi cần debug sâu hoặc có nghi ngờ, lệnh
sudo nft list rulesetsẽ cho bức tranh đầy đủ và chính xác hơn.Có thể kiểm tra wrapper nào đang được dùng bằng
iptables --version(kết quả sẽ báonf_tableshoặclegacy).
Về phía Docker Engine: một số phiên bản/triển khai Docker mới đã có option liên quan đến firewall backend (cho phép chọn nftables native thay vì đi qua iptables wrapper). Tuy nhiên trong thực tế, trên nhiều distro, iptables thường vẫn là iptables-nft wrapper và nft list ruleset vẫn là cách debug đầy đủ nhất. Chain DOCKER-USER vẫn tồn tại với ngữ nghĩa hook tương đương ở cả hai backend.
Kiểm tra backend đang dùng hiện tại:
docker info | grep -i firewall
cat /etc/docker/daemon.json 2>/dev/null
Với bridge network, các backend đều hướng tới chức năng tương đương; khi debug hãy chọn đúng công cụ hiển thị rule (iptables-save qua wrapper, hoặc nft list ruleset).
2.7. Checklist bảo mật DB trong Docker
Dù chọn giải pháp nào, đây là mức tối thiểu không thể bỏ qua:
[ ] Luôn bật authentication (
MONGO_INITDB_ROOT_USERNAME/PASSWORDcho MongoDB, hoặc thông số tương đương cho DB khác). Không bao giờ chạy DB mà không có auth, kể cả khi chỉ trên internal network.[ ] Bật TLS cho mọi kết nối đi qua mạng không tin cậy.
[ ] Không publish ra
0.0.0.0trừ khi thực sự cần thiết.[ ] Nếu bắt buộc phải public, hãy đặt sau reverse proxy/VPN/bastion, hoặc whitelist IP bằng
DOCKER-USER.[ ] Dùng Docker Engine ≥ 28.0.0 nếu đang dựa vào bind
127.0.0.1để cách ly khỏi mạng L2.[ ] Verify bằng nmap từ máy bên ngoài sau mỗi lần thay đổi cấu hình, không tin vào giả định.
[ ] Backup và audit log DB đầy đủ; rotate credentials định kỳ.
Tài liệu tham chiếu
Packet filtering and firewalls: https://docs.docker.com/engine/network/packet-filtering-firewalls/
Docker with iptables (chain
DOCKER-USER, conntrack, DNAT caveat): https://docs.docker.com/engine/network/firewall-iptables/Port publishing and mapping (bind
127.0.0.1, cảnh báo L2): https://docs.docker.com/engine/network/port-publishing/moby/moby issue #45610, L2 access to
127.0.0.1published ports: https://github.com/moby/moby/issues/45610chaifeng/ufw-docker: https://github.com/chaifeng/ufw-docker
3. (Kubernetes & Cilium) Phòng thủ dựa trên identity
Khi chuyển sang môi trường Kubernetes, bề mặt tấn công của MongoDB không còn đơn giản chỉ là host port bị phơi nhiễm như đã ở mục 2. Thay vào đó, nó mở rộng ra ba vấn đề mới: loại Service được chọn, lateral movement (di chuyển ngang) giữa các pod, và khả năng giám sát flow mạng. Cilium - một CNI dựa trên eBPF - cung cấp bộ công cụ mạnh để giải quyết cả ba vấn đề này. Tuy nhiên, cần hiểu đúng năng lực của nó để không overclaim (phóng đại).
3.1. Rủi ro đầu tiên: chọn sai Service Type
type: LoadBalancer mà thiếu loadBalancerSourceRanges: Cloud provider sẽ cấp cho một IP public và route traffic vào Service → DB bị phơi ra Internet công khai. Đây là một trong những nguyên nhân phổ biến nhất dẫn đến việc MongoDB bị scanner quét và bị ransomware mã hóa.
type: NodePort: Kubernetes sẽ mở một port NodePort (mặc định trong dải 30000 - 32767) trên mọi Worker Node trong cluster. Nếu bất kỳ worker node nào có đường ingress từ bên ngoài (có public IP hoặc reachable từ mạng không tin cậy, tùy thuộc vào security group, NACL, route table, firewall trên node, và policy externalTrafficPolicy đang dùng), thì NodePort có thể khiến DB bị truy cập ngoài ý muốn. NodePort luôn luôn mở rộng bề mặt tấn công, ngay cả khi tất cả node đều là private, vì bất cứ ai có quyền vào được VPC đều có thể thử port đó.
Khuyến nghị:
DB nên dùng
type: ClusterIP(hoặcHeadless Servicekhi cần DNS cho StatefulSet), chỉ có thể truy cập từ trong cluster.Nếu bắt buộc phải expose ra ngoài, hãy dùng
LoadBalancerkèm theoloadBalancerSourceRanges:spec: type: LoadBalancer loadBalancerSourceRanges: - 10.0.1.0/24 # App VPC - 203.0.113.5/32 # BastionLưu ý quan trọng:
loadBalancerSourceRangessẽ được cloud provider dịch thành firewall rules/LB ACL/Security Group rules, mỗi provider implement khác nhau (AWS NLB, GCP LB, Azure LB có hành vi không đồng nhất; thậm chí một số provider hoàn toàn bỏ qua field này với một số class LB nhất định). Luôn luôn verify bằng nmap từ bên ngoài sau khi apply cấu hình.Truy cập từ ngoài cluster nên đi qua VPN/service mesh gateway, không bao giờ expose DB trực tiếp.
3.2. Cilium và eBPF: giảm phụ thuộc vào iptables, chứ không phải xoá sổ iptables
Thực tế những gì Cilium làm được:
Thay thế kube-proxy bằng eBPF (tùy chọn
kubeProxyReplacement=true): kube-proxy chạy ở iptables mode có rule-set rất lớn và churn cao khi endpoints thay đổi: mỗi lần Service/Endpoint thay đổi là phải reload rule, tạo áp lực lớn lên iptables và conntrack trong cluster quy mô lớn. Cilium dùng eBPF hash map để lookup hiệu quả hơn rất nhiều và tránh phải reload. Khi bật tính năng này, có thể xoá hoàn toàn DaemonSetkube-proxyvà cleanup các ruleKUBE-*.eBPF host-routing (có từ Cilium 1.9): Cilium có thể bypass phần lớn datapath iptables cho traffic pod-to-pod, rút ngắn đường đi của gói tin.
Thay thế cơ chế enforce NetworkPolicy: Cilium enforce policy ở tầng eBPF thay vì tạo các iptables chain truyền thống.
Tuy nhiên, Cilium KHÔNG xoá sổ iptables hoàn toàn:
Cilium vẫn có thể tự sinh iptables rule cho một số trường hợp nhất định (masquerade, interop với host firewall, cleanup rule).
Có thể chạy Cilium song song với kube-proxy (
kubeProxyReplacement=false): đây là cấu hình phổ biến khi đang migrate dần.Các rule iptables do app hoặc host tự viết (SSH firewall, Docker, v.v.) vẫn tồn tại độc lập, Cilium không động tới.
⇒ Cilium thay thế các cơ chế iptables nặng nề của kube-proxy và (tùy cấu hình) bypass phần lớn datapath iptables cho traffic pod-to-pod bằng eBPF, nhưng không loại trừ iptables ra khỏi hệ thống.
3.3. Identity-based policy với CiliumNetworkPolicy
Điểm mạnh cốt lõi của Cilium trong việc bảo mật DB chính là security identity dựa trên label của Pod (về chất là label-based ở layer 3/4, còn identity là abstraction mà Cilium tổng hợp từ tập các Kubernetes label của endpoint), chứ không dựa vào IP:
Khi gán label
app=mongodbcho Pod MongoDB vàrole=backendcho Pod của app, Cilium sẽ tính ra một Identity riêng cho mỗi tập label đó.Policy match dựa trên Identity → khi pod mới được spawn (với IP mới), nó tự động được áp đúng policy, không cần sync lại rule theo IP.
Điều này giúp chặn lateral movement: nếu một Pod ứng dụng bị compromise, kẻ tấn công cũng không tự nhiên truy cập được MongoDB, trừ khi Pod đó mang đúng label.
Lưu ý về phạm vi áp dụng: Identity-based chỉ áp cho các endpoint mà Cilium quản lý. Với traffic ra ngoài cluster, Cilium vẫn phải dựa vào IP/CIDR (
toCIDR,toCIDRSet), FQDN (toFQDNs), hoặc entity (toEntities: [world, cluster, host, ...]). Các Pod chạyhostNetwork: truecũng không phải là Cilium endpoint.
Policy cơ bản: chỉ cho phép backend truy cập MongoDB
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: mongodb-allow-backend
namespace: database
spec:
endpointSelector:
matchLabels:
app: mongodb
ingress:
- fromEndpoints:
- matchLabels:
role: backend
toPorts:
- ports:
- port: "27017"
protocol: TCP
Policy này sẽ biến Pod MongoDB thành default-deny cho ingress (vì có khai báo section ingress), rồi chỉ cho phép các Pod có label role=backend truy cập. ếu fromEndpoints không chỉ định namespace label, rule sẽ được hiểu mặc định là match các endpoint trong cùng namespace với policy, muốn cross-namespace thì phải chỉ rõ.
Policy cho phép truy cập từ namespace khác
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: mongodb-allow-backend-from-apps
namespace: database
spec:
endpointSelector:
matchLabels:
app: mongodb
ingress:
- fromEndpoints:
- matchLabels:
role: backend
k8s:io.kubernetes.pod.namespace: apps
toPorts:
- ports:
- port: "27017"
protocol: TCP
Cilium expose namespace của endpoint như một label, thường có dạng k8s:io.kubernetes.pod.namespace. Tuy nhiên, prefix và cách normalize label có thể khác nhau giữa các phiên bản/cấu hình → luôn verify label thực tế trước khi viết policy:
kubectl -n kube-system exec ds/cilium -- cilium endpoint list
kubectl -n kube-system exec ds/cilium -- cilium endpoint get <endpoint-id>
Hoặc xem trong Hubble UI ở cột Labels của endpoint. Nếu môi trường không có prefix k8s:, hãy dùng io.kubernetes.pod.namespace: apps.
3.4. Những lỗi thường gặp với CiliumNetworkPolicy
Pitfall #1: Phân biệt rõ endpointSelector (target) và from/toEndpoints (peer).
CiliumNetworkPolicy là một resource namespaced, và endpointSelector , tức target mà policy áp vào chỉ có thể chọn các Pod trong cùng namespace với policy (Kubernetes cấm cross-namespace ở đây vì sẽ phá vỡ nguyên tắc isolation). Nhưng các rule fromEndpoints/toEndpoints - tức peer - thì có thể match endpoint ở namespace khác nếu match kèm namespace label như ví dụ 3.3, hoặc nếu dùng CiliumClusterwideNetworkPolicy (CCNP) thì toàn bộ policy sẽ hoạt động ở phạm vi toàn cluster.
Pitfall #2: Default-deny tự động được bật khi Pod bị match bởi bất kỳ rule nào. Ngay khoảnh khắc Pod MongoDB bị match bởi một policy có section ingress, nó sẽ tự động chuyển sang chế độ default-deny cho ingress. Mọi traffic không match với một rule allow nào đó sẽ bị drop. Điều này tốt cho security, nhưng rất dễ gây outage nếu quên thêm rule cho health check, Prometheus scraping, backup job, v.v.
Pitfall #3: Client Pod cũng cần egress policy nếu đã có default-deny. Nếu namespace apps đã có policy nào đó match client Pod với section egress, thì client sẽ mất khả năng kết nối ra ngoài (kể cả tới MongoDB), trừ khi thêm rule egress tương ứng:
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: backend-allow-mongo
namespace: apps
spec:
endpointSelector:
matchLabels:
role: backend
egress:
- toEndpoints:
- matchLabels:
app: mongodb
k8s:io.kubernetes.pod.namespace: database
toPorts:
- ports:
- port: "27017"
protocol: TCP
- toEndpoints:
- matchLabels:
k8s:io.kubernetes.pod.namespace: kube-system
k8s-app: kube-dns
toPorts:
- ports:
- port: "53"
protocol: UDP
rules:
dns:
- matchPattern: "*"
Pitfall #4: DNS là thủ phạm phổ biến nhất của việc policy bị sai. Client gọi mongodb.database.svc.cluster.local cần phải resolve DNS trước. Nếu quên allow egress 53/UDP tới CoreDNS, app sẽ bị timeout, và log của Cilium chỉ cho thấy DNS đã bị DROP, rất dễ nhầm là lỗi khác.
Pitfall #5 : Policy chỉ áp dụng cho các Pod mà Cilium quản lý, KHÔNG áp cho traffic host-networking. Các Pod chạy với hostNetwork: true không phải là Cilium endpoin, CNP không điều khiển được chúng. Để xử lý nhóm Pod này, cần dùng CCNP với nodeSelector hoặc dùng Cilium Host Firewall riêng.
3.5. Hubble: quan sát flow mạng bằng eBPF
Hubble tap (gắn) trực tiếp vào các eBPF program của Cilium để xuất ra flow events kèm theo context Kubernetes (pod name, namespace, label, policy verdict), không cần tcpdump và không có overhead của packet capture truyền thống.
Các lệnh hữu ích
# Xem flow bị DROPPED trong namespace database (để troubleshoot policy bị sai)
hubble observe --namespace database --verdict DROPPED
# Xem flow tới MongoDB theo thời gian thực
hubble observe --to-pod database/mongodb-0 --follow
# Chỉ xem các flow bị drop do policy (không phải lỗi khác)
hubble observe --verdict DROPPED --type policy-verdict
# Xem flow từ namespace lạ tới DB (phát hiện lateral movement)
hubble observe --to-namespace database --follow -o compact
Các giá trị hợp lệ của --verdict gồm: FORWARDED, DROPPED, AUDIT, REDIRECTED, ERROR, TRACED, TRANSLATED.
Giới hạn của L7 parser
Theo tài liệu chính thức của Cilium, L7 visibility và L7 policy out-of-the-box phổ biến nhất chỉ hỗ trợ HTTP, DNS, Kafka. KHÔNG có parser dành cho MongoDB (hoặc Redis, MySQL, PostgreSQL) theo mặc định trong Cilium.
Điều này có nghĩa là:
Hubble có thể thấy flow MongoDB ở mức L3/L4: source/dest pod, port 27017, verdict, số bytes, TCP flags.
Hubble KHÔNG thể parse các lệnh MongoDB (insert/find/update) ở mức L7 out-of-the-box.
Nếu muốn quan sát ở mức command/query, có thể dùng một trong các cách sau (sắp xếp theo thứ tự khuyến nghị thực chiến):
MongoDB audit log (native): bật tính năng
auditLogcó sẵn trong MongoDB (phiên Enterprise hoặc Percona Server for MongoDB) để ghi lại các lệnh truy vấn. Đây là cách chuẩn nhất để có L7 visibility cho MongoDB, và hoàn toàn không phụ thuộc vào CNI.Tetragon: project runtime observability của Isovalent, cũng dựa trên eBPF. Tetragon quan sát được syscall/process event và (với TracingPolicy phù hợp) có thể observe connection, process execve, file access ở mức kernel. Đây là bổ sung tốt cho Hubble khi cần context về process/syscall.
Proxy/sidecar custom: đặt Envoy hoặc mongos phía trước MongoDB để log query.
Viết custom Cilium L7 parser: viết extension qua Envoy proxylib để parse MongoDB wire protocol. Về kỹ thuật thì khả thi, nhưng ít team làm được trong production vì phải maintain code Go và rebuild Cilium agent.
Phát hiện port scanning/bursty connection
Hubble CLI không có chức năng tự động detect bursty connection sẵn. Cách làm là:
Bật Hubble metrics xuất ra Prometheus (dùng flag
hubble.metrics.enabled=[flow, drop, tcp, dns:query]).Viết Prometheus alert dựa trên các metric
hubble_flows_processed_totalhoặchubble_drop_total, cảnh báo khi rate drop tăng bất thường tới một identity/namespace cụ thể.Dùng Hubble UI (
hubble-ui) để xem service dependency map và phát hiện các connection bất thường một cách trực quan.
Ví dụ một alert rule viết bằng PromQL:
# Cảnh báo khi tỷ lệ drop tới database namespace vượt ngưỡng
sum(rate(hubble_drop_total{destination_namespace="database"}[5m])) > 10
3.6. Checklist bảo mật MongoDB trong Kubernetes
[ ] Chọn Service type đúng: ClusterIP cho nội bộ; không dùng NodePort cho DB; nếu bắt buộc phải dùng LoadBalancer thì phải kèm
loadBalancerSourceRangesvà verify từ bên ngoài.[ ] Authentication bắt buộc ở mức MongoDB (không chỉ dựa vào NetworkPolicy).
[ ] Bật TLS cho connection MongoDB khi traffic đi qua mạng không tin cậy.
[ ] CiliumNetworkPolicy default-deny cho namespace chứa DB, và explicit allow theo label.
[ ] Có egress policy cho client + allow DNS 53/UDP tới kube-dns.
[ ] Verify label thực tế trên endpoint trước khi viết policy cross-namespace (dùng
cilium endpoint get).[ ] Test bằng Hubble: chạy
hubble observe --to-pod <mongo> --verdict DROPPEDsau khi apply policy để chắc chắn không drop nhầm traffic hợp lệ.[ ] Thiết lập Prometheus alert trên
hubble_drop_totalvà flow rate bất thường.[ ] Bật MongoDB audit log hoặc dùng Tetragon nếu cần quan sát ở mức command/L7.
[ ] Backup, rotate credentials, audit RBAC định kỳ.
Tài liệu tham chiếu:
Layer 3 Policies,
endpointSelector&fromEndpoints: https://docs.cilium.io/en/stable/security/policy/layer3/Using Kubernetes Constructs In Policy (namespace isolation,
io.kubernetes.pod.namespace): https://docs.cilium.io/en/stable/security/policy/kubernetes/Policy Enforcement Modes (default-deny): https://docs.cilium.io/en/stable/security/policy/intro/
Layer 7 Protocol Visibility (L7 parser: HTTP/DNS/Kafka): https://docs.cilium.io/en/stable/observability/visibility/
Kube-proxy replacement (kubeProxyReplacement): https://docs.cilium.io/en/stable/network/kubernetes/kubeproxy-free/
Tuning Guide (eBPF host-routing, Cilium 1.9+): https://docs.cilium.io/en/stable/operations/performance/tuning/
Hubble CLI Cheat Sheet (filter verdict, protocol, namespace): https://cilium.isovalent.com/hubfs/marketing%20briefs/Isovalent%20-%20Cilium%20Hubble%20Cheat%20Sheet.pdf
GitHub issue cilium/cilium#14072: L7 Visibility limited to DNS and HTTP : https://github.com/cilium/cilium/issues/14072
Service types, loadBalancerSourceRanges: https://kubernetes.io/docs/concepts/services-networking/service/
Tetragon (Isovalent): L7/process-level observability qua eBPF, bổ sung cho Hubble: https://tetragon.io/
System Event Auditing (audit log để có L7 visibility ở application layer): https://www.mongodb.com/docs/manual/core/auditing/
4. Mổ xer lỗ hổng MongoBleed (CVE-2025-14847)
MongoBleed là một lời nhắc khó chịu nhưng quan trọng: ngay cả khi đã khóa chặt lớp mạng (không publish port public, đã có NetworkPolicy, đã có firewall), thì thân phần mềm vẫn có thể là điểm yếu.
CVE-2025-14847 là một lỗ hổng information disclosure (rò rỉ thông tin) nằm trong cơ chế zlib network compression của MongoDB Server. Lỗ hổng này cho phép một client chưa xác thực (pre-auth) kích hoạt hành vi rò rỉ uninitialized heap memory (vùng nhớ heap chưa được khởi tạo) từ tiến trình mongod/mongos.
Đây không phải là lỗ hổng chỉ tồn tại trên lý thuyết: đã có PoC (proof-of-concept) công khai, và CVE này đã được đưa vào danh sách CISA KEV (Known Exploited Vulnerabilities - các lỗ hổng đã được xác nhận bị khai thác trong thực tế). Nhiều báo cáo threat intel cũng ghi nhận hoạt động quét và khai thác tự động nhắm vào các MongoDB instance phơi nhiễm trên Internet trong giai đoạn sau khi lỗ hổng được công bố.
4.1. Tóm tắt nhanh (TL;DR)
| Thuộc tính | Giá trị |
|---|---|
| CVE ID | CVE-2025-14847 |
| Tên gọi | MongoBleed |
| Người phát hiện | Joe Desimone (Elastic Security) |
| Ngày công bố | 19/12/2025 |
| CVSS v4.0 | 8.7 (High) |
| CVSS v3.1 | 7.5 (High) |
| CISA KEV | Có, thêm vào ngày 29/12/2025 |
| Loại lỗ hổng | Information disclosure (đọc uninitialized heap memory) |
| chất | Không phải RCE (không cho phép thực thi mã từ xa), nhưng nếu credentials/secrets bị leak ra thì có thể bị chain thành follow-on compromise |
| Cần xác thực không? | Không (pre-auth) |
| Điều kiện trigger | Server/client thương lượng thành công được zlib network compression |
| Public PoC | Có, tại github.com/joe-desimone/mongobleed |
| Phạm vi rủi ro | Chủ yếu là self-hosted và các instance phơi nhiễm; MongoDB Atlas đã được vendor vá; các managed service khác cần xác nhận patch level thực tế với provider |
Ghi chú về quy mô: các con số kiểu 87k instance phơi nhiễm (theo Censys) hoặc 213k+ (theo Shodan) sẽ thay đổi tùy thuộc vào thời điểm đo, query sử dụng, và tiêu chí lọc (ví dụ có thể bao gồm cả các instance đã patch hoặc không bật zlib).
4.2. Cơ chế khai thác (heap disclosure qua OP_COMPRESSED)
MongoDB giao tiếp qua wire protocol sử dụng opcode OP_MSG (với payload dạng BSON). Khi bật tính năng network compression, message sẽ được "bọc" lại bên trong opcode OP_COMPRESSED:
struct OP_COMPRESSED {
struct MsgHeader {
int32 messageLength;
int32 requestID;
int32 responseTo;
int32 opCode;
};
int32_t originalOpcode;
int32_t uncompressedSize; // ← trường này không được validate
uint8_t compressorId;
char *compressedMessage;
};
Luồng khai thác ở mức khái niệm diễn ra như sau:
Attacker gửi tới server một message
OP_COMPRESSEDđược craft đặc biệt, với trườnguncompressedSizelớn hơn rất nhiều so với payload nén thực tế.Do việc xử lý và validate các length fields không chặt chẽ, server có thể cấp phát một buffer lớn và/hoặc xử lý lỗi theo cách khiến uninitialized heap memory bị lọt vào response trả về.
Một leak path (đường rò rỉ) hay được mô tả trong các phân tích kỹ thuật là đi qua error handling path: khi một BSON object bị malformed (không có null terminator), server sẽ parse memory cho tới khi gặp byte null, và khi việc parse thất bại, nó trả về error response có thể kèm theo nội dung của heap.
Attacker lặp lại request này nhiều lần để thu thập dần dần các mảnh heap khác nhau → tăng xác suất lấy được dữ liệu nhạy cảm.
Root cause ở cấp độ code: trong file message_compressor_zlib.cpp có dòng return {output.length()}; , dòng này trả về kích thước buffer đã được cấp phát thay vì actual length (kích thước thực tế) của dữ liệu đã giải nén. patch chỉ là one-line, đổi thành return length;. Commit công khai có thể verify trực tiếp trên repo MongoDB (xem References #5).
Những dữ liệu có thể bị leak (tùy thuộc vào trạng thái heap tại thời điểm trigger):
Các fragment của kết quả truy vấn trước đó/fragment của BSON document.
Credentials/secrets đang tồn tại trong memory (API keys, session tokens, fragment config).
Internal state/PII (tùy thuộc vào workload cụ thể).
Không có giới hạn leak cố định X MB nào cả: cả lượng dữ liệu và nội dung leak đều phụ thuộc vào heap layout, code path được kích hoạt, và số lần attacker lặp request.
4.3. Các phiên bị ảnh hưởng & patch chính thức
Các phiên đã có patch (cần upgrade lên phiên này hoặc cao hơn):
| Major version | Patched version |
|---|---|
| 8.2.x | 8.2.3+ |
| 8.0.x | 8.0.17+ |
| 7.0.x | 7.0.28+ |
| 6.0.x | 6.0.27+ |
| 5.0.x | 5.0.32+ |
| 4.4.x | 4.4.30+ |
Pitfall: Các version EOL vẫn bị ảnh hưởng nhưng KHÔNG có patch
Theo thông tin từ CVE/NVD và các phân tích công khai, các dòng 3.6 / 4.0 / 4.2 (và các version cũ hơn) vẫn bị ảnh hưởng bởi lỗ hổng này, nhưng đã EOL → không nên trông chờ vendor sẽ phát hành patch.
Nếu đang chạy version 4.2 hoặc cũ hơn, tuyệt đối không được hiểu nhầm rằng: không thấy tên version của mình trong danh sách patched = mình an toàn. Trên thực tế, bắt buộc phải:
Upgrade major version lên một dòng còn được hỗ trợ (tối thiểu là 4.4.30+), hoặc
Migrate workload sang nền tảng khác.
Lưu ý với các managed service
MongoDB-compatible không phải lúc nào cũng đồng nghĩa với MongoDB Server: AWS DocumentDB và Azure Cosmos DB API for MongoDB có codebase riêng và vòng đời patching riêng của chúng.
Phải xác nhận patch level thực tế với provider, không được đoán mò dựa trên major version label.
MongoDB Atlas: đã được MongoDB patch trực tiếp; theo advisory từ vendor thì không có bằng chứng cho thấy customer data bị compromise.
Self-hosted/Percona Server for MongoDB: phải tự chịu trách nhiệm upgrade và verify.
4.4. Mitigation tạm thời (khi chưa kịp upgrade)
Biện pháp tạm thời an toàn nhất là: disable zlib network compression để loại bỏ đường trigger của CVE.
Cách 1: Cấu hình trong mongod.conf:
net:
compression:
compressors: snappy,zstd # loại bỏ zlib khỏi danh sách
Cách 2: Qua setParameter :
mongod --setParameter networkMessageCompressors=snappy,zstd
# Hoặc tương tự với mongos:
mongos --setParameter networkMessageCompressors=snappy,zstd
# Tắt compression hoàn toàn (tùy thuộc môi trường và version):
mongod --setParameter networkMessageCompressors=disabled
Sau đó restart mongod/mongos để apply.
Một số gợi ý:
Nên giữ lại
snappy,zstdđể vẫn có compression (tiết kiệm bandwidth), chỉ loại zlib ra khỏi danh sách compression là đủ.Kiểm tra cả phía client/driver: nếu driver đang dùng zlib thông qua connection string, hãy chỉnh config để loại zlib ra khỏi danh sách compressors.
Đây chỉ là mitigation tạm thờ, upgrade vẫn là giải pháp duy nhất để patch triệt để.
Giảm phơi nhiễm (luôn nên làm, kể cả khi đã patch):
Chặn inbound Internet tới port 27017/TCP.
Chỉ allow các nguồn được explicit trust (app subnet, VPN).
Áp dụng network segmentation (xem mục 2 về iptables/DOCKER-USER và mục 3 về CiliumNetworkPolicy).
4.5. Phát hiện dấu hiệu đã bị khai thác
Các indicator (dấu hiệu) được liệt kê dưới đây đều mang tính heuristic, tức là mang tính suy đoán, không có gì đảm bảo chắc chắn 100% vì attacker hoàn toàn có thể điều chỉnh rate (tốc độ) và pattern (mẫu hành vi) để né tránh bị phát hiện. Hãy dùng các dấu hiệu này để như tài liệu tham khảo để phân loại và điều tra sâu hơn, chứ không nên coi chúng là nguồn thông tin duy nhất để kết luận.
Indicator #1: Log tăng đột biến bất thường từ một IP/AS lạ:
Log messages kiểu "Slow query" tăng vọt một cách bất thường (một số variant PoC mặc định có thể sinh ra hàng nghìn message kiểu này).
Tần suất cao hơn hẳn so với baseline bình thường của workload.
Indicator #2: BSON parse errors xuất hiện dày đặc:
- Log pattern
InvalidBSON: incorrect BSON length in element with field namexuất hiện với tần suất cao là dấu hiệu rất đặc trưng của việc BSON bị tampering có chủ ý. Driver MongoDB hợp pháp rất hiếm khi tạo ra pattern này.
Indicator #3: Connection burst bất thường:
Số connection/giây từ cùng một source IP tăng vọt, đặc biệt đáng lo nếu nguồn đó không nằm trong allowlist của bạn.
Một số variant PoC có thể sinh ra connection rate rất cao trong khoảng thời gian ngắn; ngưỡng cụ thể còn tùy thuộc vào exploit và attacker.
Indicator #4: Connection thiếu client metadata:
Driver MongoDB hợp pháp luôn gửi kèm client metadata trong quá trình handshake.
Pattern đáng ngờ: event connection accepted + disconnection mà không có event metadata nằm giữa, lặp đi lặp lại với tần suất cao → cần điều tra ngay.
Ví dụ các lệnh để rà log (tùy vào log format đang dùng):
# Đếm tổng số message "Slow query"
grep -i "slow query" /var/log/mongodb/mongod.log | wc -l
# Đếm BSON parse error pattern (đặc trưng)
grep -i "incorrect bson length" /var/log/mongodb/mongod.log | wc -l
# Group theo source IP trong khoảng 1 giờ gần đây
grep -i "slow query" /var/log/mongodb/mongod.log | \
awk '{print $NF}' | sort | uniq -c | sort -rn | head -20
Nếu đã centralize log về SIEM, nên thiết lập alert cho các trường hợp sau:
Rate tăng đột biến theo source IP so với baseline.
Rate BSON parse error tăng đột biến.
Các connection attempt tới DB đến từ nguồn không nằm trong allowlist.
Bất kỳ connection attempt nào tới port 27017 đến từ Internet (trong trường hợp không public DB ra ngoài).
4.6. Nếu nghi ngờ đã bị khai thác: Quy trình incident response tham khảo
Vì lỗ hổng này leak dữ liệu trực tiếp từ memory, nên không thể biết chính xác thứ gì đã bị lấy đi. Nguyên tắc ở đây là: giả định kịch xấu nhất và tiến hành rotate toàn bộ:
[ ] Rotate credentials MongoDB: tất cả user, đặc biệt chú ý các account
root,admin, và các service account.[ ] Rotate API keys/cloud keys nếu ứng dụng có load chúng vào process memory nằm gần MongoDB.
[ ] Rotate JWT signing keys/session tokens, mọi token đã cấp phát trước đó đều nên được coi là có khả năng đã bị compromise.
[ ] Rotate TLS certificates nếu private key có lúc nằm trong cùng process space.
[ ] Audit hoạt động database gần đây: xem ai đã query những gì, có account mới nào xuất hiện bất thường không, có dấu hiệu role escalation không.
[ ] Preserve log và network telemetry để phục vụ forensic: source IP, timestamp, burst pattern, số lượng BSON error.
[ ] Báo cáo tuân thủ (compliance reporting) nếu thuộc phạm vi GDPR/HIPAA/PCI-DSS, memory disclosure vẫn được tính là một data breach.
4.7. Checklist defense in depth
Ngay cả sau khi đã vá lỗ hổng, đây là các lớp phòng thủ nên có để tránh tình huống chỉ cần một lỗ hổng là game over:
[ ] Patch ngay lập tức: upgrade lên các version đã vá được liệt kê ở mục 4.3.
[ ] (Tùy nhu cầu) disable zlib kể cả sau khi đã patch, nhằm giảm attack surface.
[ ] Network segmentation: chỉ cho phép app/VPN đã trusted có thể truy cập tới port MongoDB.
[ ] Không public DB ra Internet, kể cả lý do là chỉ tạm thời để test.
[ ] Bật TLS cho mọi connection đi qua mạng không tin cậy.
[ ] Authentication bắt buộc ở mức MongoDB, không nên dựa hoàn toàn vào network control.
[ ] Bật verbose logging + command audit log (có sẵn ở MongoDB Enterprise/Percona), và centralize về SIEM.
[ ] Thiết lập alert dựa trên các indicator đã liệt kê ở mục 4.5.
[ ] Secrets hygiene: rotate credentials theo định kỳ.
[ ] Kiểm tra patch level định kỳ, không đợi đến lúc có CVE mới kiểm tra.
Tài liệu tham chiếu:
CVE-2025-14847: https://nvd.nist.gov/vuln/detail/CVE-2025-14847
Known Exploited Vulnerabilities: https://www.cisa.gov/known-exploited-vulnerabilities-catalog
SERVER-115508 (advisory & mitigation): https://jira.mongodb.org/browse/SERVER-115508
MongoBleed technical breakdown + fixed versions: https://www.akamai.com/blog/security-research/cve-2025-14847-all-you-need-to-know-about-mongobleed
MongoDB commit patch (one-line fix trong
message_compressor_zlib.cpp): https://github.com/mongodb/mongo/commit/505b660a14698bd2b5233bd94da3917b585c5728Exploit details & impact analysis: https://unit42.paloaltonetworks.com/mongobleed-cve-2025-14847/
Exploited in the wild: https://arcticwolf.com/resources/blog/cve-2025-14847/
Memory leak vulnerability analysis: https://www.tenable.com/blog/cve-2025-14847-mongobleed-mongodb-memory-leak-vulnerability-exploited-in-the-wild
Risk, detection & IOC: https://www.varonis.com/blog/mongobleed-cve-2025-14847-memory-leak-vulnerability
Detection indicators & slow query patterns: https://www.abstract.security/blog/critical-mongodb-vulnerability-cve-2025-14847-mongobleed
Chain to follow-on compromise, not RCE: https://www.bitsight.com/blog/critical-vulnerability-alert-cve-2025-14847-mongodb-mongobleed
joe-desimone/mongobleed: https://github.com/joe-desimone/mongobleed
5. Runtime Security với Tetragon
Các mục 2, 3, 4 trước đó đã cover phần bảo mật ở tầng mạng (iptables, CiliumNetworkPolicy) và việc patch lỗ hổng. Nhưng thử đặt một tình huống: nếu attacker đã vào được bên trong Pod MongoDB rồi thì sao, dù là qua RCE chain, supply chain attack, hay credential bị leak? Lúc đó, network policy không còn ngăn cản được các hành vi diễn ra bên trong Pod nữa.
Đây chính là lúc cần đến runtime security: quan sát và can thiệp vào hành vi của process/syscall ngay bên trong container, ở tầng kernel.
Tetragon là một lựa chọn phổ biến cho mục đích này, dựa trên eBPF, là một sub-project nằm trong hệ sinh thái Cilium/Isovalent (Cilium đã CNCF graduated từ tháng 10/2023; còn Tetragon là sub-project nằm dưới umbrella Cilium).
Một lựa chọn thay thế đáng nhắc đến là Falco (của Sysdig),cũng là eBPF-based, có rule language và ecosystem riêng; nếu team đã quen với Falco thì không nhất thiết phải migrate sang Tetragon.
Tetragon không yêu cầu phải dùng Cilium CNI, có thể chạy Tetragon trên cluster dùng CNI khác, hoặc thậm chí trên Linux server/Docker host nằm ngoài Kubernetes. Tuy nhiên, nếu đã dùng Cilium sẵn thì phần metadata và khả năng interop sẽ mượt mà hơn khi phân loại sự cố.
5.1. Tetragon làm được gì và không làm được gì?
Những gì Tetragon LÀM ĐƯỢC:
Hook vào các kernel event: syscalls (thông qua
sys_*kprobe), kernel functions (kprobe), tracepoints, LSM hooks, uprobes.Quan sát toàn bộ process lifecycle (exec, exit), file access, network connection, kèm theo metadata Kubernetes (pod, namespace, container, labels).
Thực thi enforcement ngay trong kernel theo hai cách chính:
Override return value: buộc function/syscall trả về error code mà không thực thi thật, tức là chặn từ trước khi operation kịp diễn ra.
Signal (thường là SIGKILL): gửi signal kill process ngay khi event match với rule.
Những gì Tetragon KHÔNG LÀM ĐƯỢC (quan trọng):
SIGKILL chỉ là biện pháp stop the bleeding, không thể rollback. Nếu hành vi đã kịp thực thi (ví dụ kernel đã trả bytes về user space, packet đã kịp gửi đi), thì SIGKILL không thể thu hồi lại dữ liệu hoặc hành động đó.
Override/denial kiểu trả lỗi ngay trong kernel phụ thuộc vào các feature của kernel. Với kiểu kprobe override, kernel cần bật
CONFIG_BPF_KPROBE_OVERRIDE=y; các kiểu attach khác (fentry/LSM) có cơ chế và yêu cầu khác nhau. Trên nhiều managed Kubernetes, kernel có thể không bật đủ các feature này.Không thay thế được việc patching, authentication, hay các application-layer control. Tetragon chỉ là một lớp trong mô hình defense in depth.
Detection dựa vào binary path rất dễ bị bypass: attacker có thể rename binary, dùng built-in shell (ví dụ
bashvới/dev/tcp), dùng inline script (Python/Perl), hoặc inject code vào một process hợp pháp đang chạy (ví dụ RCE ngay trongmongod) - khi đó mọi syscall vẫn mang binary path/usr/bin/mongod, vàmatchBinariessẽ không giúp được gì.Pattern chặn curl/wget/nc chỉ bắt được một vector cụ thể, chứ không phải chặn reverse shell theo nghĩa tổng quát.
5.2. Yêu cầu về kernel và các caveat khi deploy
Trước khi bắt tay vào viết policy, cần verify môi trường:
| Yêu cầu | Cách kiểm tra |
|---|---|
| Kernel Linux có hỗ trợ eBPF hiện đại (≥5.x cho phần lớn các feature) | uname -r và `cat /boot/config-$(uname -r) |
Có bật CONFIG_BPF_KPROBE_OVERRIDE=y (cần cho kprobe Override action) |
grep CONFIG_BPF_KPROBE_OVERRIDE /boot/config-$(uname -r) |
| BPF filesystem đã được mount | `mount |
| Có quyền cluster admin trên Kubernetes | Cần để deploy DaemonSet Tetragon |
Một số tối ưu hóa của Tetragon (ví dụ multi-attach kprobe trong kernel mới) phụ thuộc vào version kernel. Nếu kernel của bạn thiếu các tối ưu này, Tetragon vẫn hoạt động bình thường, chỉ là sẽ dùng cơ chế attach truyền thống; overhead và startup time có thể khác một chút, nhưng chức năng cốt lõi không bị ảnh hưởng.
Trên các managed Kubernetes (EKS/GKE/AKS), kernel của host có thể thiếu một số config. Hãy verify trước khi dựa vào Override action; nếu không có, phương án fallback là dùng Signal (Sigkill), với trade-off đã đề cập ở mục 5.1.
5.3. So sánh Network Policy (Cilium CNI) và Runtime Security (Tetragon)
| Khía cạnh | CiliumNetworkPolicy (CNI) | Tetragon (Runtime) |
|---|---|---|
| Đối tượng kiểm soát | Network flow (L3/L4 theo IP/port, L7 cho HTTP/DNS/Kafka) | Kernel events: syscalls, kprobes, tracepoints, LSM hooks |
| Đơn vị enforcement | Packet/connection | Syscall/kernel function call |
| Hành động có thể thực hiện | Allow, Drop, Redirect (qua proxy L7) | Override return value, Signal (SIGKILL), Post (chỉ log) |
| Thời điểm enforce | Trước khi packet đến được ứng dụng | Trước (Override) hoặc ngay sau (SIGKILL) khi event xảy ra |
| Phạm vi bao phủ | Chỉ các endpoint do Cilium quản lý (Pod và một số entity) | Toàn bộ process trong Pod và cả host (có thể filter theo namespace/pod/container) |
| Khi Pod đã bị compromise? | Không ngăn được các hành vi bên trong Pod (đọc file, exec, escalate), nhưng vẫn giới hạn được egress/exfil | Có tác dụng vì hook ở tầng kernel, trừ khi attacker chạy code bên trong một process hợp pháp |
| Khi node bị compromise (root)? | Cả Cilium và Tetragon đều không được thiết kế để chống root trên node, khi đã có node-level root, rất nhiều lớp phòng thủ có thể bị phá |
Hai công cụ này bổ sung cho nhau, không thay thế cho nhau.
5.4. Ví dụ TracingPolicy cho Pod MongoDB
Disclaimer: các YAML dưới đây chỉ mang tính minh họa. Các field của Tetragon CRD (tên action, type của args, signature của LSM hook, v.v.) có thể thay đổi theo version. Khi triển khai thực tế, hãy đối chiếu với docs và CRD của version Tetragon đang dùng (
kubectl explain tracingpolicy.spec), và luôn deploy ở modePost(chỉ log) trước khi bậtSigkill.
Các policy dưới đây được áp cho Pod có label app: mongodb trong namespace database, hãy điều chỉnh theo setup thực tế của bạn.
Policy 1: Chặn exec các binary không được phép xuất hiện trong container DB
Một container mongod chính thức về cơ bản chỉ nên chạy mongod, mongos, và một vài probe tool. Nếu có process mới được exec, như shell hoặc network tool, thì gần như chắc chắn đó là hành vi bất thường.
apiVersion: cilium.io/v1alpha1
kind: TracingPolicyNamespaced
metadata:
name: mongodb-block-unexpected-exec
namespace: database
spec:
podSelector:
matchLabels:
app: mongodb
kprobes:
- call: "sys_execve"
syscall: true
args:
- index: 0
type: "string"
- index: 1
type: "char_buf"
sizeArgIndex: 2
- index: 2
type: "char_buf"
sizeArgIndex: 3
selectors:
- matchArgs:
- index: 0
operator: "In"
values:
- "/bin/bash"
- "/bin/sh"
- "/bin/dash"
- "/usr/bin/curl"
- "/usr/bin/wget"
- "/usr/bin/nc"
- "/usr/bin/ncat"
- "/usr/bin/socat"
- "/usr/bin/python"
- "/usr/bin/python3"
- "/usr/bin/perl"
matchActions:
- action: Sigkill
Các giới hạn:
Policy này bắt dựa trên binary path cụ thể. Attacker hoàn toàn có thể copy binary sang path khác, rename nó, hoặc dùng built-in shell (ví dụ
bashvới/dev/tcpđể làm reverse shell mà không cầnnc).Nhiều image minimal có layout khác nhau (ví dụ BusyBox dùng
/bin/busybox, Alpine có shell là symlink/bin/sh). Verify các binary thực tế trong image MongoDB bạn đang dùng.Pattern an toàn hơn là allowlist: chỉ cho phép
/usr/bin/mongod,/usr/bin/mongos, và vài probe tool; mọi exec khác đều → Sigkill. Allowlist khó hơn vì phải audit toàn bộ các binary cần chạy (kể cả init, healthcheck), nhưng bảo mật chặt hơn hẳn.
Policy 2: Monitor truy cập vào file cấu hình/credential của MongoDB
Các file quan trọng cần bảo vệ: /etc/mongod.conf, keyFile (thường ở /data/configdb/keyfile hoặc path custom), TLS cert/key, các secrets được mount vào.
apiVersion: cilium.io/v1alpha1
kind: TracingPolicyNamespaced
metadata:
name: mongodb-monitor-sensitive-files
namespace: database
spec:
podSelector:
matchLabels:
app: mongodb
kprobes:
- call: "security_file_permission"
syscall: false
args:
- index: 0
type: "file"
- index: 1
type: "int"
selectors:
- matchBinaries:
- operator: "NotIn"
values:
- "/usr/bin/mongod"
- "/usr/bin/mongos"
matchArgs:
- index: 0
operator: "Prefix"
values:
- "/etc/mongod.conf"
- "/data/configdb/"
- "/etc/ssl/mongodb/"
- "/var/run/secrets/"
matchActions:
- action: Post # chỉ log trước; đổi sang Sigkill sau khi đã rõ baseline
Lưu ý:
Hook LSM
security_file_permissioncó thể rất noisy (nó được gọi trên mọi file access). Luôn chạy ở modePosttrước và theo dõi event rate.Cách Tetragon resolve path cho args type
filephụ thuộc vào version; nếu Prefix match không hoạt động đúng như mong đợi, hãy test lại syntax theo docs hiện hành.
matchBinaries: NotIn [mongod, mongos]được dùng để loại trừ các truy cập hợp pháp từ chính process MongoDB.
Policy 3: Phát hiện outbound connection bất thường từ Pod DB
Trong kiến trúc bình thường, Pod MongoDB không tự mình khởi tạo outbound connection ra Internet. Nếu có, thì đây là tín hiệu rất đáng nghi (có thể là exfiltration, kết nối C2, hoặc đang quét tiếp).
apiVersion: cilium.io/v1alpha1
kind: TracingPolicyNamespaced
metadata:
name: mongodb-detect-unexpected-egress
namespace: database
spec:
podSelector:
matchLabels:
app: mongodb
kprobes:
- call: "tcp_connect"
syscall: false
args:
- index: 0
type: "sock"
selectors:
- matchBinaries:
- operator: "NotIn"
values:
- "/usr/bin/mongod"
- "/usr/bin/mongos"
matchActions:
- action: Post # log trước; đổi sang Sigkill khi đã rõ baseline
Pitfall: RCE diễn ra bên trong process: Nếu attacker chạy code ngay bên trong chính process
mongod(ví dụ qua một exploit MongoDB hoặc deserialization attack), thì mọi syscall (kể cảtcp_connectoutbound) đều vẫn mang binary path/usr/bin/mongod. RulematchBinaries: NotIn [mongod]sẽ không bắt được trường hợp này. Vì vậy cần kết hợp thêm:
CiliumNetworkPolicy egress ở mục 3 (chặn dựa trên destination, không dựa trên source process).
Anomaly detection trên pattern connection (destination lạ, burst bất thường).
Audit log ở tầng application (MongoDB audit log).
Tips: policy này nên được coi là lớp phòng thủ thứ hai bên cạnh CNP egress, nó cung cấp context về process (ai đã gọi), giúp việc incident response dễ hơn rất nhiều khi có sự cố.
Policy 4: Phát hiện hành vi cố escalate privilege (setuid root)
apiVersion: cilium.io/v1alpha1
kind: TracingPolicyNamespaced
metadata:
name: mongodb-block-setuid-root
namespace: database
spec:
podSelector:
matchLabels:
app: mongodb
kprobes:
- call: "sys_setuid"
syscall: true
args:
- index: 0
type: "int"
selectors:
- matchArgs:
- index: 0
operator: "Equal"
values:
- "0"
matchActions:
- action: Sigkill
5.5. Deploy và vận hành
Cài Tetragon qua Helm:
helm repo add cilium https://helm.cilium.io
helm install tetragon cilium/tetragon -n kube-system
Apply policy:
kubectl apply -f mongodb-block-unexpected-exec.yaml
Xem events (cần CLI tetra; tên pod/DaemonSet có thể khác nhau tùy chart/version, check bằng kubectl -n kube-system get ds | grep tetragon):
# Stream events realtime, format compact
kubectl -n kube-system exec -ti ds/tetragon -c tetragon -- \
tetra getevents -o compact --namespace database
# Xuất JSON để ingest vào SIEM
kubectl -n kube-system exec -ti ds/tetragon -c tetragon -- \
tetra getevents -o json --namespace database
Xem danh sách policy đang load:
kubectl -n kube-system exec -ti ds/tetragon -c tetragon -- \
tetra tracingpolicy list
Quy trình test trước khi enforce:
Deploy policy trước với action
Post(chỉ log), không enforce.Monitor events trong ít nhất 1 tuần để xem có false positive nào từ backup job, health check, operator, hay sidecar logging không.
Khi đã tự tin, đổi sang
Sigkillcho các case đã rõ ràng; tiếp tục giữPostcho các trường hợp vẫn còn mơ hồ.
Tích hợp với SIEM:
Tetragon events có thể xuất ra JSON → ship về Elastic / Splunk / Loki.
Thiết lập alert cho các pattern như: process_exec event với binary lạ, file access vào sensitive path, outbound connection từ Pod DB.
5.6. Các giới hạn và pitfall cần nắm rõ
Pitfall #1: SIGKILL chỉ là stop the bleeding, không rollback được. Nếu kernel đã kịp trả dữ liệu về user space trước khi SIGKILL tới nơi, thì dữ liệu đã coi như bị leak rồi. Với các operation có tính phá hủy (như rm, chmod), SIGKILL bắn sau khi syscall đã chạy cũng không thể undo được. Cơ chế Override return value tốt hơn trong một số trường hợp vì nó chặn trước khi syscall được thực thi, tuy nhiên bị giới hạn bởi các syscall nào được support và cần kernel config đúng.
Pitfall #2: Bypass bằng cách không dùng đúng binary path. Attacker hoàn toàn có thể copy/rename binary, dùng built-in shell (ví dụ bash với /dev/tcp/host/port), hoặc chạy code qua một interpreter (như python -c, perl -e). Dùng matchBinaries kèm theo hash checksum thì chặt hơn, nhưng phức tạp về mặt vận hành.
Pitfall #3: RCE in-process không bị bắt bởi matchBinaries. Nếu attacker khai thác vulnerability ngay trong chính mongod và chạy code bên trong một process hợp pháp, thì mọi syscall đều vẫn mang binary path mongod. Lúc này, rule matchBinaries: NotIn sẽ không giúp được gì. Cần kết hợp thêm các layer khác: CVE patching, network egress policy, và application audit log.
Pitfall #4: Tetragon cần được deploy ở chế độ privileged. Tetragon DaemonSet cần có CAP_SYS_ADMIN và hostPID để load được eBPF program và quan sát tất cả process. Nếu attacker đã giành được root trên node, họ hoàn toàn có thể unload các eBPF program của Tetragon. Runtime security bảo vệ rất tốt cho trường hợp Pod-level compromise, nhưng không chống được node-level root.
Pitfall #5: False positive có thể làm tê liệt ứng dụng. Backup job, operator (ví dụ mongodb-kubernetes-operator), probe script, liveness check, tất cả đều có thể trigger policy nếu rule viết quá rộng. Vì vậy, luôn chạy ở Post mode trước khi bật Sigkill. Một policy viết sai có thể giết mongod trong một loop và làm cho Pod liên tục crash.
Pitfall #6: Overhead không phải bằng không. Overhead phụ thuộc vào: số lượng policy, số kprobe đang hook, và tần suất event (những syscall hot-path như read/write sẽ tốn hơn nhiều so với execve). Hãy benchmark trong môi trường thực của bạn trước khi chạy production, đừng tin vào các con số generic.
Pitfall #7: Tetragon không phải là compliance tool out-of-the-box. Tetragon cung cấp capability để bạn tự build detection, chứ không phải là ruleset sẵn cho PCI-DSS/HIPAA. Bạn phải tự viết policy dựa trên threat model riêng của tổ chức mình.
5.7. Checklist triển khai Tetragon cho MongoDB
[ ] Verify kernel requirements (eBPF, và
CONFIG_BPF_KPROBE_OVERRIDEnếu cần dùng Override action).[ ] Deploy Tetragon DaemonSet qua Helm, verify pod đã chạy đầy đủ trên mọi node.
[ ] Đối chiếu CRD schema của version Tetragon đang dùng trước khi apply các YAML minh họa.
[ ] Viết policy ở mode
Posttrước, monitor log trong ít nhất 1 tuần để bắt false positive.[ ] Các policy quan trọng cho MongoDB: block exec binary lạ, monitor sensitive file, detect unexpected egress, detect setuid root.
[ ] Luôn dùng
matchBinaries: NotIn [mongod, mongos, ...]để tránh false positive từ chínhmongodhợp pháp, nhưng phải hiểu rõ giới hạn: các trường hợp RCE in-process sẽ không bị bắt bởi filter này.[ ] Tích hợp events vào SIEM (export JSON → Elastic/Splunk/Loki).
[ ] Chỉ khi thực sự confident mới chuyển sang
Sigkill, và phải có sẵn playbook rollback trong trường hợp Pod bị kill nhầm.[ ] Review định kỳ: policy có còn khớp với threat model hiện tại không, có binary mới nào cần whitelist hoặc blacklist không.
[ ] Hiểu rõ Tetragon chỉ là một lớp trong mô hình phòng thủ, không thể thay thế cho patching, network policy, authentication, hay MongoDB audit log.
Tài liệu tham chiếu
Tetragon: https://tetragon.io/docs/
Enforcement (Override vs Signal): https://tetragon.io/docs/concepts/enforcement/
Tracing Policy Example: https://tetragon.io/docs/concepts/tracing-policy/example/
Kubernetes Identity Aware Policies (TracingPolicyNamespaced, podSelector, containerSelector): https://tetragon.io/docs/concepts/tracing-policy/k8s-filtering/
Selectors (matchBinaries, matchArgs, matchActions): https://tetragon.io/docs/concepts/tracing-policy/selectors/
cilium/tetragon (repo chính): https://github.com/cilium/tetragon
Cilium graduation announcement (10/2023): https://www.cncf.io/announcements/2023/10/11/cloud-native-computing-foundation-announces-cilium-graduation/
Falco (CNCF): Alternative runtime security eBPF-based: https://falco.org/
BPF & kprobe, context cho kernel requirements: https://docs.kernel.org/bpf/
Security Checklist (áp dụng song song với runtime security): https://www.mongodb.com/docs/manual/administration/security-checklist/
6. Chiến lược backup
Khi ransomware đã mã hóa hoặc xóa sạch dữ liệu production, backup chính là hy vọng cuối cùng. Nhưng ransomware hiện đại đã đủ thông minh để tìm và xóa luôn các tệp backup, và nhiều team đã phải học bài học này bằng một cái giá rất đắt: credential admin bị compromise → attacker list ra backup bucket → xóa sạch → để lại ransom note trên DB production.
Mục tiêu của phần này: thiết kế một backup strategy mà ngay cả khi attacker có quyền admin đầy đủ, họ vẫn không thể xóa được backup trong suốt retention window.
6.1. Quy tắc 3-2-1 (và biến thể hiện đại)
Đây là nền tảng của mọi backup strategy:
3 bản sao dữ liệu (bản production + 2 bản backup).
2 loại media/storage khác nhau.
1 bản offsite (nằm ngoài data center của production, lý tưởng nhất là ở một khu vực địa lý khác).
Biến thể hiện đại của quy tắc này thường được gọi là 3-2-1-1-0:
Thêm 1 bản offline/immutable (dùng Object Lock, tape, hoặc air-gap).
Và 0 lỗi khi test restore (tức backup đã được verify).
6.2. Immutable backup với S3 Object Lock
Compliance mode vs Governance mode
S3 Object Lock cung cấp hai chế độ hoạt động:
| Chế độ | Ai có thể xóa/override trước khi hết hạn? |
|---|---|
| Compliance | KHÔNG AI CẢ, kể cả root user của AWS account. Không thể rút ngắn retention period, cũng không thể đổi mode. |
| Governance | Những người có permission s3:BypassGovernanceRetention (và phải explicit thêm header x-amz-bypass-governance-retention:true). |
Theo tài liệu chính thức của AWS: ở Compliance mode, các object version đã được protect không thể bị overwrite hoặc delete bởi bất kỳ user nào, kể cả root; không thể đổi retention mode, và cũng không thể rút ngắn retention period.
Cách duy nhất để xóa object dưới Compliance mode trước khi hết retention là xóa hẳn AWS account đang chứa nó. Đây là một caveat quan trọng: nếu attacker compromise được billing credentials và close account, thì backup vẫn có thể mất. Trường hợp này hiếm gặp, nhưng vẫn là một attack vector có thật (xem mục 6.4 về cross-account replication).
Pitfall: Delete marker trong Compliance mode
Vì S3 Versioning bắt buộc phải bật khi Object Lock được on, nên khi user chạy simple DELETE (không kèm --version-id), S3 sẽ tạo ra một delete marker thay vì xóa thực sự. Object version gốc vẫn còn nguyên, chỉ là không hiện ra mặc định khi list bucket.
Điều này có nghĩa là:
Những người không hiểu cơ chế có thể lầm tưởng rằng đã xóa được rồi → dẫn đến panic.
Attacker có thể spam delete marker để tạo noise, hoặc làm ẩn backup khỏi các monitoring tool chỉ list theo current version.
Để thực sự retrieve lại: phải list versions (kèm cả delete marker) và restore version cụ thể.
Verify protection bằng CLI:
# Xem retention của object
aws s3api get-object-retention \
--bucket my-mongodb-backups \
--key 2026/04/15/full-backup.tar.gz
# Output nên trả về:
# "Mode": "COMPLIANCE",
# "RetainUntilDate": "2026-05-15T..."
Bổ sung Legal holds
Ngoài retention period có thời hạn, Object Lock còn cung cấp Legal holds, giữ object ở trạng thái bất biến không có thời hạn cố định, chỉ được gỡ khi người có permission s3:PutObjectLegalHold explicit remove nó.
Legal holds hữu ích trong các tình huống sau:
Điều tra pháp lý (e-discovery).
Incident response, khi muốn freeze backup tại một thời điểm mà chưa rõ sẽ giữ trong bao lâu.
Khi retention period đã gần hết nhưng chưa muốn cho phép xóa.
Cấu hình bucket (ví dụ với Terraform)
resource "aws_s3_bucket" "mongodb_backups" {
bucket = "acme-mongodb-backups-prod"
object_lock_enabled = true # PHẢI bật ngay khi tạo bucket
force_destroy = false # An toàn: không cho destroy nếu còn object
tags = {
Environment = "production"
Purpose = "mongodb-backup"
Compliance = "object-lock"
}
}
resource "aws_s3_bucket_versioning" "mongodb_backups" {
bucket = aws_s3_bucket.mongodb_backups.id
versioning_configuration {
status = "Enabled" # Bắt buộc khi dùng Object Lock
}
}
resource "aws_s3_bucket_object_lock_configuration" "mongodb_backups" {
bucket = aws_s3_bucket.mongodb_backups.id
rule {
default_retention {
mode = "COMPLIANCE" # Không ai xóa được, kể cả root
days = 30 # Chỉ dùng 1 trong 2: days HOẶC years
}
}
depends_on = [aws_s3_bucket_versioning.mongodb_backups]
}
resource "aws_s3_bucket_public_access_block" "mongodb_backups" {
bucket = aws_s3_bucket.mongodb_backups.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_s3_bucket_server_side_encryption_configuration" "mongodb_backups" {
bucket = aws_s3_bucket.mongodb_backups.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
bucket_key_enabled = true
}
}
resource "aws_s3_bucket_lifecycle_configuration" "mongodb_backups" {
bucket = aws_s3_bucket.mongodb_backups.id
rule {
id = "cleanup-noncurrent-versions"
status = "Enabled"
# Dọn các version không phải current sau khi qua retention window
noncurrent_version_expiration {
noncurrent_days = 90
}
# Clean up multipart uploads bị fail
abort_incomplete_multipart_upload {
days_after_initiation = 7
}
}
depends_on = [aws_s3_bucket_versioning.mongodb_backups]
}
Object Lock nên được bật ngay khi tạo bucket thì mới là cách sạch nhất. Với các bucket đã tồn tại sẵn, bạn phải dùng S3 Batch Operations để lock từng object , rất phức tạp và tốn chi phí cho các bucket lớn.
Object Lock KHÔNG bảo vệ khỏi việc mất KMS key
Đây là điểm rất quan trọng: AWS docs cảnh báo rõ ràng rằng Object Lock chỉ ngăn hành vi delete/overwrite, chứ không bảo vệ khỏi việc mất access vào KMS key dùng để encrypt object. Nếu bạn đang dùng SSE-KMS với customer-managed key, và attacker xóa hoặc disable KMS key đó, thì backup của bạn sẽ trở thành một đống byte không thể đọc được.
Các biện pháp mitigation:
Dùng KMS key policy thật chặt để không ai có thể schedule deletion key (window tối thiểu là 7 ngày, nên set 30 ngày).
Bật KMS key rotation (tự động rotate hàng năm).
SSE-S3 (AWS-managed key) là một lựa chọn đánh đổi: ít flexibility hơn vì bạn không kiểm soát được key (không thể phân tách quyền, không thể audit việc dùng key), nhưng đổi lại không có rủi ro key bị xóa hay disable nhầm.
Cân nhắc dùng AWS Backup Vault Lock cho EBS snapshot và RDS backup. Đây là cơ chế immutable riêng của AWS Backup (không liên quan đến S3 Object Lock), cho phép khóa backup vault ở chế độ tương tự Compliance mode, kể cả root user cũng không thể xóa backup trong retention window.
6.3. Point-in-Time Recovery (PITR) cho MongoDB
Nguyên lý cơ bản
Công thức PITR trong MongoDB là: Full backup snapshot + Oplog replay tới thời điểm mục tiêu.
Luồng diễn ra như sau:
T0: snapshot full backup, đồng thời capture oplog timestamp tại thời điểm đó.
T0 → T2: oplog liên tục ghi lại mọi write operation trong khoảng thời gian này.
T2: sự cố xảy ra (attacker chạy
dropDatabase, accidental bulk delete, bug trong migration...).T3 = T2 - 1 giây (hoặc
ordinalngay trước operation xấu): đây là recovery target của bạn.Restore: apply full backup T0 → replay oplog từ T0 tới T3 → DB trở về đúng state ngay trước thời điểm incident.
Cú pháp mongorestore :
# Bước 1: restore full dump
mongorestore \
--uri "mongodb://admin:***@localhost:27017/?authSource=admin" \
--drop \
/backup/mongo-20260415-020000
# Bước 2: replay oplog tới thời điểm ngay trước incident
mongorestore \
--uri "mongodb://admin:***@localhost:27017/?authSource=admin" \
--oplogReplay \
--oplogLimit "1743388800:5" \
/backup/oplog-for-replay
Trong đó --oplogLimit <unix_timestamp>:<ordinal> có ý nghĩa: mọi operation tại hoặc sau timestamp này không được apply. Nói cách khác, recovery sẽ dừng ở đúng giây ngay trước lệnh dropDatabase.
Pitfall: Oplog Window
PITR chỉ hoạt động khi oplog chưa bị recycle. Lý do: oplog là một capped collection, khi nó đầy, các entries cũ sẽ bị overwrite.
Write volume cao → oplog rotate nhanh → window ngắn.
Attacker chỉ cần chờ oplog rotate → bạn mất luôn khả năng replay tới đúng thời điểm cần.
Best practice:
Maintain oplog window tối thiểu 24–48 giờ, với production nghiêm túc nên để 72h+.
Check oplog window bằng
rs.printReplicationInfo()trên primary:configured oplog size: 50000MB log length start to end: 172800 secs (48 hrs) oplog first event time: Mon Apr 14 2026 02:00:00 GMT oplog last event time: Wed Apr 16 2026 02:00:00 GMT now: Wed Apr 16 2026 02:00:00 GMTTăng oplog size nếu window quá ngắn:
db.adminCommand({replSetResizeOplog: 1, size: 50000})(đơn vị MB).Backup oplog thường xuyên (ví dụ mỗi 10 phút với PBM) để có các "slice" lưu bền — không phụ thuộc vào oplog đang sống trên DB.
Chọn tool PITR phù hợp
| Tool | Phù hợp với | Ghi chú |
|---|---|---|
mongodump --oplog + mongorestore --oplogReplay |
Self-hosted quy mô nhỏ, test/dev | Thủ công, phải tự script và schedule; không hỗ trợ native cho sharded cluster |
| Percona Backup for MongoDB (PBM) | Self-hosted production | Open-source, tự động slice oplog (mặc định 10 phút), support cả replica set và sharded cluster, có lệnh pbm restore --time |
| MongoDB Atlas Continuous Cloud Backup | Atlas managed | UI-based, restore theo Date & Time hoặc Oplog Timestamp; window tùy vào backup policy; bật tính năng này sẽ làm tăng chi phí cluster |
| MongoDB Ops Manager | Enterprise self-hosted | Dùng MongoDB Backup Restore Utility (MBRU) để apply oplog vào snapshot |
Khuyến nghị:
Self-hosted production: PBM là lựa chọn mặc định, open-source, được maintain tốt, hỗ trợ sharded cluster rất ổn.
Atlas: bật Continuous Cloud Backup ngay từ đầu, chi phí cao hơn nhưng restore dễ dàng hơn nhiều.
Tránh chỉ dựa vào
mongodump --oplogthuần cho production quy mô lớn, vì nó thiếu tính năng slice tự động và khó quản lý retention.
6.4. Defense in depth cho backup
Object Lock là một lớp bảo vệ rất mạnh, nhưng không phải duy nhất. Một kiến trúc backup thực sự bền vững cần có nhiều lớp phòng thủ:
Lớp 1: Cross-account replication
Nếu attacker compromise được AWS account production (bao gồm cả billing/root), thì ngay cả bucket có Object Lock cũng có thể mất (qua con đường delete account). Biện pháp mitigation:
Đặt backup bucket ở một AWS account riêng (dedicated backup account trong AWS Organization).
Production account chỉ có permission write vào backup bucket, không có quyền delete hay disable.
Ngược lại, backup account cũng không cho phép production account đụng vào retention hay lifecycle config.
Cấu trúc IAM tham khảo:
Production Account (111111111111)
├── MongoDB EC2/EKS
└── IAM role: backup-writer
└── s3:PutObject TO: arn:aws:s3:::backup-bucket-in-dr-account/*
DR/Backup Account (222222222222)
├── S3 bucket với Object Lock Compliance mode
└── Bucket policy: chỉ chấp nhận PutObject từ 111111111111
Lớp 2: Cross-region replication
Dùng S3 Cross-Region Replication (CRR) để replicate backup sang một region khác. Kết hợp cùng Object Lock ở destination bucket:
Region A (primary): bucket backup với Object Lock 30 ngày.
Region B (DR): replica bucket với Object Lock 90 ngày.
Nếu region A gặp sự cố (rất hiếm) hoặc attacker can thiệp được vào region A, thì region B vẫn còn nguyên.
Lớp 3: Air-gapped/offline
Với các data cực kỳ critical (tài chính, y tế, compliance cao), có thể cân nhắc:
AWS S3 Glacier Deep Archive kết hợp Vault Lock: immutable, chi phí thấp, nhưng restore chậm (tính bằng giờ đến ngày).
Tape backup offline: đắt về mặt vận hành, nhưng ít bị ảnh hưởng bởi ransomware trên network.
Lớp 4: Monitoring và alerting
Alert khi backup không chạy trong X giờ.
Alert khi kích thước backup giảm bất thường (vì attacker có thể đang corrupt source data trước khi backup kịp chạy).
Alert khi có attempt delete/modify retention trên backup bucket (qua CloudTrail event).
Alert khi KMS key liên quan đến backup có pending deletion hoặc bị disable.
6.5. Backup chưa test là backup vô giá trị
Backup chỉ thực sự có giá trị khi đã được verify restore thành công. Số liệu trong ngành cho thấy có đến khoảng 1/3 team phát hiện backup của họ bị corrupt hoặc không restore được, và thường là vào đúng lúc cần, tức là đã quá muộn.
Cadence đa tầng
| Tần suất | Scope | Mục tiêu |
|---|---|---|
| Hàng tuần (tự động) | Smoke test: restore 1 collection random vào sandbox, check row count và chạy vài query sanity | Phát hiện sớm nếu backup file bị corrupt |
| Hàng tháng (tự động + người xác nhận) | Full restore một backup ngẫu nhiên vào môi trường sandbox, chạy application smoke tests | Verify end-to-end restore flow, đo RTO thực tế |
| Hàng quý (drill có kế hoạch) | Disaster recovery drill: giả lập kịch bản ransomware, đo đầy đủ RTO + RPO, document chi tiết từng bước | Giúp team quen với quy trình, cập nhật runbook |
| Hàng năm (full DR) | Failover toàn bộ production sang DR environment, chạy thật | Verify xem kiến trúc DR có còn hoạt động được không sau rất nhiều thay đổi trong năm |
Đo lường RTO/RPO
RTO (Recovery Time Objective): tính từ lúc quyết định restore đến lúc DB hoạt động trở lại, đơn vị là giờ.
RPO (Recovery Point Objective): lượng dữ liệu tối đa chấp nhận được mất, đơn vị là phút/giờ (với PITR có oplog slice 10 phút, RPO có thể đạt ≤10 phút).
Mỗi lần test, hãy log lại RTO/RPO thực tế và đối chiếu với SLA. Nếu RTO thực > RTO mục tiêu → bạn cần đầu tư thêm (backup storage nhanh hơn, có pre-warmed standby instance, có automated restore script).
6.6. Checklist triển khai
[ ] Tuân thủ 3-2-1-1-0: 3 bản sao, 2 media, 1 offsite, 1 immutable, 0 lỗi restore.
[ ] Bật S3 Object Lock Compliance mode với retention ≥ 30 ngày (hoặc theo yêu cầu compliance riêng).
[ ] Cross-account replication: đặt backup bucket ở một AWS account riêng biệt.
[ ] Cross-region replication cho mục đích DR.
[ ] Hiểu rõ nuance của delete marker trong Compliance mode, và đào tạo ops team về điểm này.
[ ] Bảo vệ KMS key: thiết lập key policy chặt, bật rotation, đảm bảo không ai có thể schedule deletion.
[ ] Oplog window ≥ 48h; monitor định kỳ bằng
rs.printReplicationInfo().[ ] PITR tool đã được setup (PBM cho self-hosted, Continuous Cloud Backup cho Atlas).
[ ] Thực hiện weekly automated smoke test + monthly full restore test + quarterly DR drill.
[ ] Đo RTO/RPO ở mỗi lần test, đối chiếu với SLA, document lại drift.
[ ] CloudTrail alert cho mọi attempt delete/modify retention trên backup bucket.
[ ] Runbook incident viết rõ ràng: ai làm gì, contact ai, step-by-step các lệnh restore, và phải được test trong các drill.
[ ] Backup account billing/root được bảo vệ bằng MFA hardware token, tách biệt hoàn toàn với dev/prod admin.
Tài liệu tham chiếu:
S3 Object Lock overview (compliance mode, root cannot delete): https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lock.html
Object Lock considerations (delete marker nuance, KMS caveat): https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lock-managing.html
S3 Object Lock feature page: https://aws.amazon.com/s3/features/object-lock/
Cohasset Associates Compliance Assessment (SEC 17a-4, FINRA, CFTC): https://d1.awsstatic.com/r2018/b/S3-Object-Lock/Amazon-S3-Compliance-Assessment.pdf
*mongorestore --oplogReplayvà-oplogLimit*: https://www.mongodb.com/docs/database-tools/mongorestore/Replica Set Oplog (oplog window, sizing): https://www.mongodb.com/docs/manual/core/replica-set-oplog/
Continuous Cloud Backup & Point-in-Time Recovery: https://www.mongodb.com/docs/atlas/recover-pit-continuous-cloud-backup/
Point-in-Time Restore: https://www.mongodb.com/docs/ops-manager/current/tutorial/restore-pit-snapshot-http/
Point-in-time Recovery: https://docs.percona.com/percona-backup-mongodb/features/point-in-time-recovery.html
PITR tutorial: https://docs.percona.com/percona-backup-mongodb/usage/pitr-tutorial.html
Vault Lock (cho EBS/RDS backup): https://docs.aws.amazon.com/aws-backup/latest/devguide/vault-lock.html
Lời kết
Làn sóng wipe/extortion nhắm vào MongoDB cho ta thấy một thực tế rất rõ ràng: rủi ro lớn nhất thường không đến từ kỹ thuật tấn công tinh vi, mà đến từ việc phơi nhiễm và cấu hình sai. Và khi sự cố thực sự xảy ra, một lớp phòng thủ đơn lẻ gần như luôn luôn thất bại.
Vì vậy, bảo mật MongoDB cần được triển khai theo mô hình Defense in Depth: nhiều lớp phòng thủ độc lập nhau, mỗi lớp hoặc là giảm xác suất bị compromise, hoặc là giảm blast radius (phạm vi thiệt hại) khi có lớp khác bị xuyên thủng.
1. Lớp network
Docker: bind vào localhost, hoặc chỉ bind vào interface private; đặt firewall rule ở đúng điểm (ví dụ chain
DOCKER-USER) để tránh tình huống kinh điển: ufw deny nhưng port vẫn mở.Kubernetes: dùng
ClusterIPhoặc Headless service cho DB, hạn chế tối đaNodePort/LoadBalancer; luôn áp dụng allowlist và network segmentation.Network policy (Cilium hoặc Kubernetes NetworkPolicy) giúp giảm lateral movement và hạn chế egress/exfiltration, nhưng không thay thế được authentication.
2. Lớp identity
Luôn bật SCRAM hoặc x.509; áp dụng nguyên tắc least privilege cho users/roles.
Secrets phải có RBAC chặt, bật encryption-at-rest; ưu tiên dùng Vault hoặc secret manager để rotate định kỳ, tránh để credential tồn tại vĩnh viễn, token nên là short-lived trong mọi tình huống có thể.
3. Lớp runtime (eBPF)
Runtime security (ví dụ Tetragon hoặc Falco) cho phép quan sát và phản ứng ngay ở tầng kernel (process, file, network events) để phát hiện hành vi bất thường.
Đây là lớp giúp giảm thiệt hại và tăng khả năng điều tra, không phải silver bullet: SIGKILL không rollback được, và các trường hợp RCE in-process vẫn có thể bypass những rule dựa trên binary path.
4. Lớp patching
Theo dõi CVE (như MongoBleed) và vá theo lifecycle; các version đã EOL phải có kế hoạch upgrade hoặc migrate rõ ràng.
Operator và automation giúp giảm lỗi thao tác và chuẩn hóa rollout, nhưng vẫn cần canary deployment, backup, và kế hoạch rollback.
5. Lớp dữ liệu (Resilience)
Immutable backup (ví dụ S3 Object Lock) + tách quyền (cross-account) để chống delete.
PITR (snapshot + oplog) để khôi phục về đúng thời điểm ngay trước khi bị phá hoại.
Backup chỉ có giá trị thực sự khi được test restore định kỳ và đo RTO/RPO thực tế.
Mục tiêu ở đây không phải là không bao giờ bị tấn công, điều đó là bất khả thi. Mục tiêu thực tế hơn là:
Không bị phơi nhiễm ngoài ý muốn,
Khó bị xâm nhập,
Và nếu bị xâm nhập thì phát hiện nhanh, khoanh vùng tốt, và khôi phục chắc chắn.