Linux Bridge Firewall trong suốt với nftables: Kiểm soát mạng mà không đổi IP hay topology

Network tutorial - IT technology blog
Network tutorial - IT technology blog

Tình huống quen thuộc: bạn được yêu cầu bổ sung firewall vào giữa router và switch của một hệ thống đang chạy ổn định. Khách hàng không cho phép thay đổi bất kỳ địa chỉ IP nào, không được đổi gateway trên các máy chủ. Đây là bài toán mà nhiều sysadmin gặp phải — và transparent firewall chính là câu trả lời.

Vấn đề thực tế: Cắm firewall vào mà không “phá” topology

Mình từng gặp case như này: một hệ thống production với ~50 máy chủ đang dùng dải 192.168.10.0/24, gateway là 192.168.10.1 (router của khách hàng). Yêu cầu là phải kiểm soát traffic giữa các nhóm máy chủ, lọc một số protocol nguy hiểm, nhưng tuyệt đối không được đổi IP hay cấu hình lại bất kỳ thiết bị nào đang chạy.

Firewall truyền thống hoạt động ở Layer 3 — nó nhận packet, xử lý routing, rồi forward. Điều này có nghĩa là bạn phải đổi gateway của tất cả máy trong mạng sang IP của firewall. Với 50 máy chủ production? Không ai dám làm vào giờ hành chính, chưa kể rủi ro downtime và thời gian rollback nếu có sự cố.

Phân tích nguyên nhân: Tại sao firewall thông thường không phù hợp

Vấn đề nằm ở chỗ firewall Layer 3 (routing-based) bắt buộc bạn phải thay đổi topology:

  • Phải có 2 IP khác nhau trên 2 interface (một phía LAN, một phía uplink)
  • Tất cả thiết bị trong mạng phải đổi default gateway sang IP của firewall
  • DNS, monitoring, backup — tất cả đều phụ thuộc vào gateway cũ sẽ bị ảnh hưởng dây chuyền

Bridge firewall (transparent firewall) hoạt động ở Layer 2 — nó “trong suốt” như một switch thông thường. Từ góc nhìn của các máy trong mạng, firewall này không tồn tại. Packet đi qua nó mà không cần biết nó ở đó.

Về mặt kỹ thuật, khi bạn tạo một Linux bridge, kernel chuyển tiếp Ethernet frame giữa các interface thành viên mà không thay đổi địa chỉ MAC hay IP. nftables có khả năng hook vào bridge netfilter để kiểm tra và lọc các frame này trước khi chúng được forward.

Các cách giải quyết

Cách 1: ebtables (cũ, không khuyến nghị)

ebtables là công cụ lọc packet ở bridge layer từ thời iptables. Cú pháp phức tạp, không unified với iptables/ip6tables, và đang dần bị deprecated. Mình đã dùng nó vài năm trước và không có gì tệ hơn việc phải maintain 3 bộ rule riêng biệt cho IPv4, IPv6 và bridge.

Cách 2: iptables + physdev module

Dùng iptables với module physdev để match packet đi qua bridge. Cách này hoạt động được nhưng cần bật nhiều sysctl phức tạp và cú pháp khó nhớ, dễ sai:

# Cách cũ với iptables physdev — không khuyến nghị
iptables -I FORWARD -m physdev --physdev-in eth1 --physdev-out eth2 \
  -p tcp --dport 22 -j ACCEPT

Cách 3: nftables bridge (hiện đại, khuyến nghị)

nftables hỗ trợ native bridge filtering từ kernel 4.17+. Một ruleset duy nhất có thể xử lý cả IPv4, IPv6 và Ethernet frame. Đây là cách mình đang dùng trên tất cả hệ thống mới triển khai.

Cách tốt nhất: Triển khai Transparent Firewall với nftables

Bước 1: Chuẩn bị hệ thống

Máy chủ Linux cần ít nhất 2 interface mạng (ví dụ: eth0 kết nối vào router/uplink, eth1 kết nối vào switch LAN nội bộ). Kiểm tra kernel version và load module cần thiết:

# Kiểm tra kernel version (cần >= 4.17 cho nftables bridge support)
uname -r

# Load module bridge netfilter
modprobe br_netfilter
modprobe nft_bridge_meta

# Kiểm tra module đã load chưa
lsmod | grep -E "br_netfilter|nft_bridge"

Bước 2: Tạo Linux Bridge

Tạo bridge và thêm 2 interface vào. Bridge không nhất thiết cần IP — nhưng nếu muốn SSH vào firewall để quản lý thì nên gán IP management:

# Tạo bridge br0
ip link add name br0 type bridge

# Thêm các interface thành viên vào bridge
ip link set eth0 master br0
ip link set eth1 master br0

# Bật tất cả interface
ip link set eth0 up
ip link set eth1 up
ip link set br0 up

# (Tùy chọn) Gán IP management nếu cần SSH vào firewall
ip addr add 192.168.10.254/24 dev br0

# Tắt STP nếu topology không có loop (tránh delay 30s khi khởi động)
ip link set br0 type bridge stp_state 0

Để persistent sau reboot, cấu hình qua Netplan (Ubuntu 22.04+):

# /etc/netplan/01-bridge.yaml
network:
  version: 2
  ethernets:
    eth0:
      dhcp4: false
    eth1:
      dhcp4: false
  bridges:
    br0:
      interfaces: [eth0, eth1]
      addresses: [192.168.10.254/24]
      dhcp4: false
      parameters:
        stp: false
netplan apply

Bước 3: Bật sysctl cho bridge netfilter

Đây là bước hay bị bỏ sót nhất. Mặc định, kernel không pass bridge traffic qua netfilter. Phải bật explicitly, không thì viết rule nào cũng vô nghĩa:

cat > /etc/sysctl.d/99-bridge-firewall.conf << 'EOF'
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-arptables = 1
net.ipv4.ip_forward = 1
EOF

sysctl --system

Bước 4: Viết ruleset nftables

Đây là phần cốt lõi. nftables bridge family lọc ở Layer 2 (Ethernet frame), còn ip/ip6 family lọc ở Layer 3 (IP packet đi qua bridge):

cat > /etc/nftables.conf << 'EOF'
#!/usr/sbin/nft -f

flush ruleset

# Bridge table — lọc ở Layer 2 (Ethernet frame)
table bridge filter {
    chain forward {
        type filter hook forward priority filter; policy accept;

        # Chặn VLAN tag không mong muốn (802.1Q)
        ether type 0x8100 counter drop comment "Block unexpected VLAN tags"

        # Log ARP để phát hiện ARP spoofing
        ether type arp counter log prefix "ARP: "
    }
}

# IP table — lọc ở Layer 3
table ip filter {
    chain forward {
        type filter hook forward priority filter; policy drop;

        # Cho phép established/related connections (quan trọng nhất)
        ct state established,related accept
        ct state invalid drop

        # Cho phép ICMP với rate limit
        ip protocol icmp limit rate 10/second accept

        # Cho phép SSH từ management network
        ip saddr 192.168.10.0/24 tcp dport 22 accept

        # Cho phép HTTP/HTTPS
        tcp dport { 80, 443 } accept

        # Cho phép DNS và NTP
        udp dport { 53, 123 } accept
        tcp dport 53 accept

        # Log và drop tất cả còn lại
        counter log prefix "FW-DROP: " drop
    }
}

# IPv6 table
table ip6 filter {
    chain forward {
        type filter hook forward priority filter; policy drop;
        ct state established,related accept
        ct state invalid drop
        ip6 nexthdr icmpv6 accept
        counter log prefix "FW6-DROP: " drop
    }
}
EOF

# Kiểm tra syntax trước khi apply
nft -c -f /etc/nftables.conf

# Apply ruleset
nft -f /etc/nftables.conf

# Bật service để tự động load khi reboot
systemctl enable --now nftables

# Xem ruleset đang active
nft list ruleset

Bước 5: Cô lập traffic giữa các nhóm máy

Đây mới là giá trị thật của transparent firewall — kiểm soát traffic giữa các máy trong cùng subnet mà không cần VLAN hay thay đổi gì. Ví dụ: chỉ cho phép app server kết nối vào DB server:

# Tạo named set chứa IP của app servers
nft add set ip filter app_servers { \
  type ipv4_addr\; \
  elements = { 192.168.10.10, 192.168.10.11, 192.168.10.12 }\; \
}

# Chỉ app_servers mới được kết nối vào DB (192.168.10.50) port 3306
nft add rule ip filter forward \
  ip daddr 192.168.10.50 tcp dport 3306 \
  ip saddr @app_servers accept

nft add rule ip filter forward \
  ip daddr 192.168.10.50 tcp dport 3306 drop

Bước 6: Giám sát và debug

Khi mới triển khai, theo dõi log để đảm bảo không có traffic hợp lệ bị block nhầm. Tuần đầu mình thường để policy accept với log để xem traffic pattern trước khi chuyển sang drop:

# Xem log firewall real-time
journalctl -f | grep "FW-DROP:"

# Xem counter của từng rule (để biết rule nào đang match nhiều)
nft list ruleset | grep counter

# Reset counter để đo traffic trong khoảng thời gian cụ thể
nft reset counters table ip filter

# Test connectivity từ máy trong mạng
ping -c 3 8.8.8.8
curl -I https://google.com
telnet 192.168.10.50 3306

Khi cần tính nhanh subnet để viết rule cho các dải IP khác nhau, mình hay dùng toolcraft.app/vi/tools/developer/ip-subnet-calculator — nhập CIDR là ra ngay network range, broadcast, số host. Rất tiện khi cần phân chia rule cho nhiều segment cùng lúc mà không muốn ngồi tính tay.

Những điểm cần chú ý khi vận hành

  • Performance: Bridge firewall xử lý thêm ở software layer. Với đường truyền 1Gbps thông thường thì không đáng kể, nhưng trên 10Gbps+ nên cân nhắc dùng hardware offload hoặc XDP.
  • Single point of failure: Transparent firewall là SPOF cho toàn bộ mạng. Trên production nghiêm túc, nên pair với keepalived/VRRP để HA — nếu firewall chết thì mạng không bị đứt hoàn toàn.
  • VLAN trunk: Nếu mạng dùng VLAN trunk (802.1Q), cần bật VLAN filtering trên bridge và xử lý từng VLAN riêng. Phức tạp hơn đáng kể so với flat network.
  • Logging volume: Rule log cuối chain có thể tạo rất nhiều log nếu có scan/attack. Nên thêm rate limit cho log: limit rate 5/minute log prefix "FW-DROP: ".

Transparent firewall với nftables là giải pháp mình thấy clean nhất khi cần thêm lớp bảo mật mà không làm phiền đến topology hiện có. Không cần giải thích với khách hàng tại sao phải đổi gateway, không cần maintenance window dài — cắm vào, cấu hình, xong. Và nếu sau này muốn bỏ đi, chỉ cần tháo bridge ra, mạng trở về trạng thái cũ ngay lập tức.

Share: