Đồ án này triển khai một pipeline phát hiện bất thường theo network flow (IDS) và có thể mở rộng thành IPS bằng cách tự động sinh lệnh chặn IP (iptables).
- Sensor/Collector: NFStream sniff traffic trên interface thật, dựng flow và map về feature-set kiểu CICFlowMeter.
- Realtime Scoring: Python service load MLP binary classifier (0 = benign, 1 = anomaly) để chấm điểm flow theo thời gian thực.
- Backend API + Storage: FastAPI + MySQL lưu flow + feature + kết quả ML, đồng thời cung cấp API để truy vấn.
- Firewall Controller (tuỳ chọn): đọc bảng
firewall_actionsvà apply iptables (BLOCK/UNBLOCK).
Linux host / gateway
|
| NFStream sniffer (bắt traffic ở host)
v
python-real-time-service
- sắp xếp + chuẩn hoá feature
- scaler -> MLP -> ra điểm cảnh báo
- (tuỳ chọn) Isolation Forest
|
v POST /api/events (có thể kèm HMAC)
backend (FastAPI)
|
v
MySQL 8
- flow_events
- firewall_actions
- request_nonces
(Tuỳ chọn chặn tự động)
firewall-controller (cần NET_ADMIN)
|
v đọc firewall_actions
iptables
Lưu ý:
docker-compose.sensor.ymlcố ý không publish port cho backend/rt ra ngoài Internet. Trên Linux, bạn vẫn có thể truy cập bằng IP nội bộ của Docker bridge (mặc định172.30.0.0/24) hoặc dùngdocker execđể test.
Toàn bộ hệ thống được khóa theo đúng 34 features (tên + thứ tự) kiểu CICFlowMeter. Đây là điểm quan trọng nhất để mô hình chạy ổn định từ train đến realtime.
Nguồn “ground truth” của danh sách feature:
python-real-time-service/feature_extractor.py(FEATURES)backend/main.py(FEATURE_NAMES) — backend sẽ drop key thừa và fill thiếu = 0.0nfstream/nfstream_sniffer.py(CIC_FEATURES) — map NFStream -> CIC-styletraining-model/README.md— mô tả train/evaluate
Nếu bạn thay đổi feature-set, bạn phải re-train model + scaler và đồng bộ lại toàn bộ các list trên.
MySQL khởi tạo bằng mysql-init/schema.sql, gồm 3 bảng chính:
flow_events: metadata +features_json(JSON đúng 34 features) + output từ MLfirewall_actions: lệnh BLOCK/UNBLOCK (để firewall-controller thực thi)request_nonces: chống replay cho cơ chế ingest ký HMAC
GET /healthPOST /flow— nhận 1 flow (dict) hoặc nhiều flow (list) theo format JSON.
GET /healthPOST /api/events— realtime-service gửi flow + feature + score vào đâyGET /api/events?limit=100&only_anomaly=false— truy vấn event (mới nhất trước)
FastAPI swagger:
- backend:
http://<BACKEND_IP>:8000/docs - realtime:
http://<RT_IP>:9000/docs
(Mặc định <BACKEND_IP>=172.30.0.20, <RT_IP>=172.30.0.30 theo .env.)
.
├─ backend/ # FastAPI + MySQL (lưu event, API query)
├─ python-real-time-service/ # Realtime scoring: scaler + MLP + gửi backend
│ └─ trained_models/ # mlp.h5, scaler.pkl (+ scaler_params.json)
├─ nfstream/ # Sniffer dựng flow từ packet (host network)
├─ firewall-controller/ # (tuỳ chọn) poll DB -> iptables BLOCK/UNBLOCK
├─ mysql-init/ # schema.sql
├─ scripts/ # tiện ích (vd: gen TLS cho MySQL)
├─ training-model/ # train/evaluate MLP (supervised)
└─ docker-compose.*.yml # chạy sensor stack / firewall stack
Khuyến nghị chạy trên Linux (Ubuntu/Debian) vì cần sniff traffic và iptables.
- Docker + Docker Compose (v2)
- Quyền để capture traffic:
- container
nfstreamchạynetwork_mode: hostvà cầnNET_ADMIN+NET_RAW(hoặc bậtprivileged: truenếu môi trường khắt khe)
- container
- Nếu dùng IPS:
firewall-controllercầnNET_ADMINvà chạy host network (iptables)
Trên macOS/Windows (Docker Desktop) việc sniff interface thật + iptables thường không hoạt động đúng như Linux.
Sửa file .env (ít nhất các phần sau):
- MySQL password:
MYSQL_ROOT_PASSWORDMYSQL_PASSWORD
- Interface bắt gói:
CAPTURE_INTERFACE(vd:eth0,ens33,wlan0)
- Threshold:
MLP_THRESHOLD(mặc định 0.5)
Nếu dải mạng
172.30.0.0/24bị trùng với network của bạn, đổiSENSOR_NET_SUBNETtrong.env.
cd fi_mlp_fixed
docker compose -f docker-compose.sensor.yml up -d --buildKiểm tra container:
docker compose -f docker-compose.sensor.yml psXem log realtime:
# sniffer
docker logs -f nfstream-sniffer
# realtime scoring
docker logs -f flow-rt
# backend
docker logs -f flow-backendMặc định các service không publish port ra ngoài, nhưng trên Linux bạn có thể gọi trực tiếp bằng IP bridge:
curl -s http://172.30.0.30:9000/health | jq
curl -s http://172.30.0.20:8000/health | jqNếu máy bạn không route được vào IP bridge, có thể test bằng docker exec:
docker exec -it flow-rt python -c "import requests; print(requests.get('http://localhost:9000/health').text)"
docker exec -it flow-backend python -c "import requests; print(requests.get('http://localhost:8000/health').text)"# 20 events gần nhất
curl -s "http://172.30.0.20:8000/api/events?limit=20" | jq '.[0:3]'
# chỉ lấy anomaly
curl -s "http://172.30.0.20:8000/api/events?limit=20&only_anomaly=true" | jqĐể có dữ liệu, bạn cần tạo traffic thật trên interface đang capture (mở web, ping, tải file,...).
docker compose -f docker-compose.sensor.yml downTham khảo chi tiết trong training-model/README.md. Tóm tắt nhanh:
training-model/dataset/supervised_train.csvtraining-model/dataset/supervised_test.csv
Yêu cầu tối thiểu:
- Có cột
y(0/1) - Có các cột feature đúng theo Feature contract (đúng tên và thứ tự)
cd training-model
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python scripts/mlp_training.pyOutput:
training-model/models/mlp.h5training-model/models/scaler.pkltraining-model/models/scaler_params.json(portable, tránh phụ thuộc pickle)
# có thể override threshold bằng env var
MLP_THRESHOLD=0.5 python scripts/evaluate.pyOutput:
training-model/results/metrics.jsontraining-model/results/plots/(ROC, confusion matrix, score distribution, ...)
Copy các file sau sang python-real-time-service/trained_models/:
mlp.h5scaler.pkl(hoặc giữ kèmscaler_params.jsonđể fallback)
Sau đó restart realtime container:
docker compose -f docker-compose.sensor.yml up -d --force-recreate python-realtimeRealtime-service có cơ chế kiểm tra feature contract (scaler feature_names_in_ / model input dim). Nếu mismatch sẽ fail sớm để tránh chạy sai.
Mục tiêu: khi backend nhận verdict anomaly, nó có thể tự ghi một action vào DB, rồi firewall-controller sẽ thực thi iptables.
Trong .env trên sensor host:
AUTO_BLOCK=trueKhi backend ingest event có is_anomaly=true, backend sẽ insert vào bảng firewall_actions một record BLOCK.
Trên firewall host (máy gateway/router hoặc máy cần apply iptables):
- Trỏ DB về sensor host (nơi đang publish MySQL):
FW_DB_HOST=<IP_sensor_host>
FW_DB_PASSWORD=<mysql_password>
FW_DB_USER=tls_user
FW_DB_NAME=tls_ids- Start container:
docker compose -f docker-compose.firewall.yml up -d --build- Xem log:
docker logs -f fw-controllerBiến môi trường quan trọng (trong docker-compose.firewall.yml hoặc env của container):
IPTABLES_CHAIN(mặc địnhFORWARD)- Nếu firewall host là gateway chuyển tiếp traffic: dùng
FORWARD. - Nếu muốn chặn traffic vào chính máy firewall host: cân nhắc dùng
INPUT.
- Nếu firewall host là gateway chuyển tiếp traffic: dùng
FIREWALL_TARGET(DROPhoặcREJECT, mặc địnhDROP)FW_DRY_RUN=trueđể chạy thử (không thật sự sửa iptables)
firewall-controllercũng có cơ chế kiểm tra định kỳ (~30s) để re-add rule nếu bị xoá.
Bạn có thể insert action trực tiếp vào MySQL để test:
INSERT INTO firewall_actions (src_ip, action_type, target, description)
VALUES ('1.2.3.4', 'BLOCK', 'iptables', 'manual test');
INSERT INTO firewall_actions (src_ip, action_type, target, description)
VALUES ('1.2.3.4', 'UNBLOCK', 'iptables', 'manual test');Hệ thống hỗ trợ ký request (chống tamper + chống replay) bằng 3 header:
X-Timestamp: unix epoch (seconds)X-Nonce: random nonceX-Signature: HMAC-SHA256 trên chuỗits.nonce.body
Bật/tắt bằng .env:
REQUIRE_INGEST_HMAC=true
INGEST_HMAC_SECRET=<secret>
INGEST_HMAC_MAX_AGE_SEC=120- Nếu
REQUIRE_INGEST_HMAC=true, backend sẽ từ chối ingest thiếu header/seed. - Backend lưu nonce vào bảng
request_nonces(TTL mặc định 300s) để chặn replay.
Khi firewall-controller chạy trên máy khác, khuyến nghị bật TLS cho MySQL. Repo có script:
chmod +x scripts/gen-mysql-tls.sh
./scripts/gen-mysql-tls.sh --sensor-ip <IP_sensor> --enable-sensor-composeScript sẽ tạo:
pki/mysql/ca.pem,pki/mysql/server.pem,pki/mysql/server.keymysql-conf/ssl.cnf- bundle cho firewall:
pki/mysql/fw-bundle/(copy sang firewall host)
Trên firewall host, bật biến:
DB_TLS_ENABLED=true
DB_SSL_CA=/pki/mysql/ca.pem
DB_SSL_CERT=/pki/mysql/fw-client.pem
DB_SSL_KEY=/pki/mysql/fw-client.key
DB_SSL_VERIFY_CERT=true
DB_SSL_VERIFY_IDENTITY=false # bật nếu muốn check SAN/hostname (cần mysql-connector-python đủ mới)Lưu ý: nếu chạy bằng Docker, bạn cần mount thư mục cert vào container firewall-controller (xem
docker-compose.firewall.yml).
Realtime-service có thể verify SHA256 trước khi load model:
MLP_MODEL_SHA256=<sha256 của mlp.h5>
SCALER_SHA256=<sha256 của scaler.pkl>-
Không có event nào được ghi:
- kiểm tra
CAPTURE_INTERFACEđúng chưa (vd:ip link) - kiểm tra quyền capture:
nfstream-sniffercầnNET_RAW+NET_ADMIN(hoặcprivileged: true) - xem log:
docker logs -f nfstream-sniffervàdocker logs -f flow-rt
- kiểm tra
-
Không gọi được
172.30.0.20:8000/172.30.0.30:9000từ host:- trên Linux thường route được; nếu không, dùng
docker execđể test - nếu dải
SENSOR_NET_SUBNETbị trùng, đổi sang subnet khác trong.env
- trên Linux thường route được; nếu không, dùng
-
Realtime-service fail vì mismatch feature/model:
- đảm bảo model + scaler được train trên đúng 34 feature và đúng thứ tự
- kiểm tra
python-real-time-service/feature_extractor.py(FEATURES)
-
Lỗi load scaler do khác phiên bản numpy/sklearn:
- repo đã có
scaler_params.jsonđể fallback portable; giữ file này cùngscaler.pkl
- repo đã có