---
id: networking-firewall
title: "Firewall và port"
shortTitle: "Firewall & port"
track: networking
trackTitle: "Networking"
order: 6
level: important
status: published
estimatedMinutes: 30
prerequisites:
  - networking-tls
next: networking-nginx-reverse-proxy
reviewedAgainst: 2026-10-15
sources:
  - title: "KodeKloud Engineer — 100 Days of DevOps"
    url: https://engineer.kodekloud.com/practice
  - title: "iptables(8)"
    url: https://man7.org/linux/man-pages/man8/iptables.8.html
  - title: "nftables wiki"
    url: https://wiki.nftables.org/wiki-nftables/index.php/Quick_reference-nftables_in_10_minutes
  - title: "Azure network security groups"
    url: https://learn.microsoft.com/en-us/azure/virtual-network/network-security-groups-overview
---

# Firewall và port

App đã mở port chưa chắc đã truy cập được từ Internet, vì phía trước còn có thể có host firewall, security group và network ACL. Bài này giúp bạn chỉ ra đúng lớp đang chặn.

## Để làm gì

Sau bài này, bạn liệt kê được process nào đang nghe port nào, và nói được packet bị chặn ngay trên máy hay ở tầng mạng cloud.

## Gồm những gì

- Listener và port: `ss -lnt`.
- `iptables -t filter`: chain `INPUT`, `FORWARD`, `OUTPUT`.
- `iptables -L -n -v`, `-A`, `-I`, `-D`, `-P`. `-p tcp --dport`, `-j ACCEPT` / `DROP`.
- nftables hoặc ufw trên Linux hiện đại.
- Security group và NSG, và chúng khác host firewall ở đâu.

Bài này không hướng dẫn viết bộ rule cho production, và lab không đổi firewall trên máy bạn.

## Dùng khi nào

Tình huống điển hình: `curl` từ ngoài bị timeout, trong khi log của app vẫn nhận health check nội bộ. Hoặc bạn vừa `docker run -p` mà từ máy khác vẫn không vào được. Publish port và cho phép traffic vào port đó là hai việc khác nhau.

Đừng mở `0.0.0.0/0` cho port 22 hoặc 1433 để “thử cho nhanh”. Chỉ mở cho đúng IP của bastion hoặc của mạng admin.

## Kiến thức cốt lõi

Process gọi `bind` để nghe trên một port, và `ss -lnt` liệt kê các socket TCP đang nghe. Địa chỉ `127.0.0.1:3000` nghĩa là chỉ nhận connection từ chính máy đó, còn `0.0.0.0:3000` nhận từ mọi interface IPv4.

Host firewall lọc packet ngay trên máy. `iptables` sửa bảng lọc trong kernel. Bảng mặc định là `filter` (`-t filter` có thể bỏ). Ba chain có sẵn: `INPUT` tới process trên máy, `FORWARD` đi xuyên máy, `OUTPUT` máy tự gửi ra. Policy của chain builtin chỉ nhận `ACCEPT` hoặc `DROP` (`iptables -P`).

`-A` nối rule vào cuối chain. `-I chain` chèn đầu, hoặc `-I chain N` vào vị trí N (đếm từ 1). `-D` xóa theo spec hoặc theo số. `-L` liệt kê. `-n` in số, khỏi reverse DNS. `-v` thêm counter. `-p tcp --dport 80 -j ACCEPT` khớp TCP tới port 80 rồi cho qua. `DROP` bỏ packet, không trả lời. `RETURN` thoát chain hiện tại về chain gọi nó.

Nhiều distro hiện dùng nftables làm nền tảng. `iptables` trên máy đó thường là `iptables-nft`. `ufw` trên Ubuntu là lớp lệnh dễ hơn, cuối cùng vẫn sinh rule trong kernel. Docs Docker: rule `nft` thuần không tương thích Docker; Docker đọc `iptables`. Lab này chỉ `iptables -L -n -v`. Đừng `-F` và đừng `-P DROP` trên máy đang dùng.

Security group trên AWS và NSG trên Azure lọc ở tầng mạng ảo, trước khi packet tới NIC. Vì vậy trong máy, `ss` vẫn thấy process đang nghe, còn client bên ngoài thì timeout. Sửa `ufw` không mở được NSG.

Khi debug, đi theo thứ tự: process có đang nghe không, nghe trên địa chỉ nào, host firewall có cho phép trên interface đó không, và security group có cho phép source IP đó không.

## Lệnh và code mẫu

```bash
ss -lnt
sudo iptables -L -n -v
sudo iptables -t nat -L -n
```

Đọc từng dòng output; ví dụ `127.0.0.1:3000` nghĩa là chỉ nhận local. Đừng chạy `iptables -F`, `nft flush ruleset`, cũng đừng tắt firewall để thử. Thêm rule chỉ trên VM lab:

```bash
sudo iptables -I INPUT 1 -p tcp --dport 8080 -j ACCEPT
sudo iptables -L INPUT -n -v --line-numbers
sudo iptables -D INPUT 1
```

`-I INPUT 1` chèn vị trí 1 để dễ `-D INPUT 1` gỡ đúng dòng vừa thêm.

Trên PowerShell, lệnh tương ứng là:

```powershell
Get-NetTCPConnection -State Listen |
  Select-Object LocalAddress, LocalPort, OwningProcess
```

## Thực hành

1. Liệt kê các port đang nghe trên máy lab.
2. Chọn một dòng và ghi lại process đó nghe local hay trên mọi interface.
3. Ghi ra ba lớp có thể chặn: process không nghe, host firewall, NSG hoặc security group. Không sửa rule nào.
4. `sudo iptables -L -n -v`. Chỉ đọc. Đừng `-F`.

## Kết quả mong đợi

Bạn có danh sách port đang nghe, chỉ ra được một port của app quiz hoặc của hệ điều hành, và nói được nó bind vào `127.0.0.1` hay `0.0.0.0`.

## Checklist

- [ ] Chạy `ss` hoặc `Get-NetTCPConnection` trước khi đụng vào firewall.
- [ ] Port database không mở ra Internet.
- [ ] Rule trên cloud và rule trên máy cùng mở một port; nếu chỉ đổi một phía thì phía kia vẫn chặn.
- [ ] Đọc `iptables -L -n` trước khi thêm rule. `-A` vào cuối, `-I` vào đầu.

## Thẻ ôn

### networking-firewall-layers
kind: distinguish
cue: `ufw` mở port mà `curl` từ ngoài vẫn timeout: host firewall và security group hoặc NSG là hai lớp. Chỉ sửa một phía thì phía kia vẫn chặn.

Q: Trên máy, `ufw` đã cho phép port của dịch vụ. Từ laptop bên ngoài, `curl` vẫn timeout, không phải connection refused. Còn lớp nào có thể chặn, và trước khi sửa firewall nên xem gì?

A: Host firewall và luật trên cloud là hai lớp độc lập. `ufw` lọc trên chính máy, sau khi packet đã tới. Security group hoặc NSG lọc trước khi packet tới NIC. Mở `ufw` mà quên luật cloud thì packet không bao giờ tới process, timeout là triệu chứng hợp với chuyện bị bỏ giữa đường. Chỉ đổi một phía thì phía kia vẫn chặn. Trước khi đụng firewall, xem `ss` để chắc đã có listener đúng port. Không có listener thì mở firewall cũng không tạo ra dịch vụ.

### networking-firewall-bind
kind: trap
cue: Security group mở port 80 nhưng `ss` thấy app nghe `127.0.0.1` thì không có listener trên interface phía ngoài. Đừng mở `0.0.0.0/0` cho port 22 hoặc 1433.

Q: Security group đã mở port 80 ra Internet, app vẫn không nhận request từ ngoài. `ss` in `127.0.0.1:3000`. Dòng đó nghĩa là gì, và sửa bằng cách nào mà không mở mọi port quản trị ra thế giới?

A: `127.0.0.1` nghĩa là app chỉ nhận connection từ chính máy đó. Packet từ ngoài có thể tới được NIC, nhưng không có socket nào nghe trên interface phía ngoài, nên không có process nhận. Hai việc đang lệch: luật mở port 80, process lại nghe 3000 trên loopback. Đổi bind sang địa chỉ nội bộ mà proxy hoặc client nội bộ cần dùng, hoặc đặt reverse proxy phía trước và chỉ proxy mới nói chuyện với app. Đừng chữa bằng cách mở `0.0.0.0/0` cho port 22 hoặc 1433. Đó là cửa quản trị và database, không phải cách làm app nghe đúng interface.

## Lỗi thường gặp

**App nghe `127.0.0.1` trong khi security group đã mở port 80.** Packet tới được máy nhưng bị từ chối, vì không có listener trên interface đó. Đổi bind sang địa chỉ nội bộ cần dùng, hoặc đặt proxy phía trước.

**Mở port trên `ufw` nhưng cloud vẫn chặn.** Đây là hai bộ rule độc lập. Hãy xem cả NSG hoặc security group gắn với NIC, đừng chỉ nhìn `ufw status`.

**`-A` khi nghĩ rule sẽ chạy trước.** `-A` nối cuối. Packet khớp rule cũ (`DROP`) trước thì không tới dòng mới. `-I` chèn đầu, hoặc `-I chain N` vào vị trí N.

## Nguồn

1. [KodeKloud Engineer — 100 Days of DevOps](https://engineer.kodekloud.com/practice) — đề cương ngày IPtables Installation And Configuration. Trang practice cần đăng nhập. Bài này viết lại lệnh, không chép lab.
2. [iptables(8)](https://man7.org/linux/man-pages/man8/iptables.8.html) — Linux man-pages: `-t`, `-A`, `-I`, `-D`, `-L`, `-n`, `-v`, `-P`, `-j`.
3. [nftables in 10 minutes](https://wiki.nftables.org/wiki-nftables/index.php/Quick_reference-nftables_in_10_minutes) — bảng và chain.
4. [Network security groups](https://learn.microsoft.com/en-us/azure/virtual-network/network-security-groups-overview) — lọc trước khi tới VM.

## Tiếp theo

[Nginx reverse proxy](./03-nginx-reverse-proxy.md)
