CyStack logo
  • Sản phẩm & Dịch vụ
  • Giải pháp
  • Bảng giá
  • Công ty
  • Tài liệu
Vi

vi

Trang chủHướng dẫnChuyển tiếp cổng bằng Iptables trên Linux [Hướng dẫn chi tiết]
Chuyên gia

Chuyển tiếp cổng bằng Iptables trên Linux [Hướng dẫn chi tiết]

CyStack blog 11 phút để đọc
CyStack blog31/07/2025
Locker Avatar

Chris Pham

Technical Writer

Locker logo social
Reading Time: 11 minutes

NAT (Network Address Translation) là thuật ngữ chung để chỉ quá trình mangling gói tin nhằm chuyển hướng chúng đến một địa chỉ IP khác. Thông thường, kỹ thuật này được sử dụng để cho phép lưu lượng vượt qua ranh giới giữa các mạng. Một máy chủ triển khai NAT thường có quyền truy cập vào hai hoặc nhiều mạng và được cấu hình để định tuyến lưu lượng giữa các mạng đó.

Chuyển tiếp cổng bằng Iptables

Port forwarding (chuyển tiếp cổng) là quá trình chuyển tiếp các yêu cầu đến một cổng cụ thể sang một máy chủ, mạng hoặc cổng khác. Vì quá trình này thay đổi đích đến của gói tin trong quá trình truyền nên nó cũng được xem là một dạng của NAT.

Trong hướng dẫn này, chúng ta sẽ tìm hiểu cách chuyển tiếp cổng bằng Iptables trên Linux thông qua kỹ thuật NAT. Cách làm này rất hữu ích khi bạn đã thiết lập một mạng riêng nhưng vẫn muốn cho phép một số lưu lượng cụ thể đi vào thông qua một máy chủ gateway được chỉ định.

Yêu cầu

Để thực hiện theo hướng dẫn này, bạn cần:

  • Hai máy chủ Ubuntu 20.04 được cấu hình trong cùng một trung tâm dữ liệu và đã bật kết nối mạng riêng (private networking). Trên mỗi máy chủ, bạn cần tạo một tài khoản người dùng không phải root với quyền sudo. Bạn có thể tham khảo cách thiết lập máy chủ với Ubuntu 20.04.
  • Trên một trong hai máy chủ, bạn cần cấu hình mẫu tường lửa sử dụng iptables để máy chủ đó có thể đóng vai trò làm tường lửa. Sau khi hoàn tất, máy chủ tường lửa của bạn cần đảm bảo những yếu tố sau:
    • Đã cài đặt gói iptables-persistent
    • Đã lưu bộ quy tắc mặc định vào tệp /etc/iptables/rules.v4
    • Hiểu cách thêm hoặc chỉnh sửa quy tắc bằng cách chỉnh sửa tệp luật hoặc sử dụng lệnh iptables

Máy chủ mà bạn đã thiết lập mẫu tường lửa sẽ đóng vai trò làm tường lửa và bộ định tuyến (router) cho mạng riêng. Để minh họa, máy chủ thứ hai sẽ được cấu hình chạy web server và chỉ có thể truy cập thông qua giao diện mạng riêng của nó. Bạn sẽ cấu hình máy tường lửa để chuyển tiếp các yêu cầu nhận được từ giao diện công khai đến web server, thông qua giao diện riêng.

Thông tin máy chủ

Trước khi bắt đầu, bạn cần xác định rõ các giao diện mạng và địa chỉ đang được sử dụng trên cả hai máy chủ.

Xác định thông tin mạng

Để lấy thông tin mạng của hệ thống, trước tiên bạn cần xác định các giao diện mạng hiện có. Bạn có thể liệt kê các giao diện mạng trên máy và địa chỉ tương ứng bằng cách chạy lệnh sau:

ip -4 addr show scope global
Sample Output
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    inet 203.0.113.1/18 brd 45.55.191.255 scope global eth0
       valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    inet 10.0.0.1/16 brd 10.132.255.255 scope global eth1
       valid_lft forever preferred_lft forever

Kết quả hiển thị cho thấy hai giao diện mạng (eth0eth1) cùng với các địa chỉ được gán tương ứng (203.0.113.1 và 10.0.0.1). Để xác định giao diện nào là giao diện công khai, hãy chạy lệnh sau:

ip route show | grep default
Output
default via 111.111.111.111 dev eth0

Thông tin giao diện trong kết quả (ví dụ là eth0) sẽ cho biết giao diện đang kết nối với cổng mặc định của hệ thống. Gần như chắc chắn, đây chính là giao diện công khai.

Các giá trị này cần được xác định trên từng máy chủ và sử dụng trong các bước tiếp theo của hướng dẫn.

Dữ liệu mẫu được sử dụng trong hướng dẫn

Để minh họa rõ ràng hơn, trong hướng dẫn này chúng ta sẽ sử dụng các địa chỉ và giao diện mẫu dưới đây. Các giá trị này cần được thay thế bằng thông tin thực tế phù hợp với hệ thống của bạn.

Thông tin mạng máy chủ web:

  • Địa chỉ IP công khai: 203.0.113.1
  • Địa chỉ IP riêng: 10.0.0.1
  • Giao diện công khai: eth0
  • Giao diện riêng: eth1

Thông tin mạng máy chủ tường lửa:

  • Địa chỉ IP công khai: 203.0.113.2
  • Địa chỉ IP riêng: 10.0.0.2
  • Giao diện công khai: eth0
  • Giao diện riêng: eth1

Cài đặt máy chủ web

Bắt đầu bằng việc kết nối đến máy chủ web, sử dụng tài khoản có quyền sudo để đăng nhập.

Cài đặt Nginx

Bước đầu tiên là cài đặt Nginx trên máy chủ web và cấu hình để nó chỉ lắng nghe trên giao diện mạng riêng. Điều này đảm bảo rằng web server chỉ có thể truy cập được nếu port forwarding được thiết lập đúng cách.

Bắt đầu bằng cách cập nhật bộ nhớ đệm gói cục bộ:

sudo apt update

Tiếp theo, sử dụng apt để tải và cài đặt phần mềm:

sudo apt install nginx

Giới hạn Nginx chỉ hoạt động trên mạng riêng

Khi cài đặt xong Nginx, tiến hành kiểm tra tệp cấu hình server block mặc định để giới hạn Nginx chỉ lắng nghe trên giao diện mạng riêng. Mở tệp bằng trình soạn thảo văn bản ưa thích của bạn, ở đây ta sẽ dùng nano:

sudo nano /etc/nginx/sites-available/default

Bên trong tệp, tìm dòng listen. Thông thường sẽ có hai dòng listen nằm liền nhau ở phần đầu cấu hình.

/etc/nginx/sites-enabled/default
server {
    listen 80 default_server;
    listen [::]:80 default_server ipv6only=on;

    . . .
}

Ở dòng listen đầu tiên, thêm địa chỉ IP riêng của máy chủ web cùng với dấu hai chấm phía trước số cổng 80 để chỉ định Nginx chỉ lắng nghe trên giao diện riêng. Trong hướng dẫn này chỉ triển khai chuyển tiếp IPv4, do đó bạn có thể xóa dòng listen thứ hai dành cho IPv6.

Tiếp theo, chỉnh sửa các chỉ thị listennhư sau:

/etc/nginx/sites-enabled/default
server {
    listen 10.0.0.1:80 default_server;

    . . .
}

Sau khi chỉnh sửa, lưu và đóng tệp. Nếu dùng nano, nhấn CTRL + X, sau đó YENTER.

Bây giờ kiểm tra tệp cấu hình để đảm bảo không có lỗi cú pháp:

sudo nginx -t

Output
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Nếu không có lỗi, khởi động lại Nginx để áp dụng cấu hình mới:

sudo systemctl restart nginx

Xác minh giới hạn truy cập mạng

Tại thời điểm này, bạn nên kiểm tra mức độ truy cập tới máy chủ web.

Từ máy chủ tường lửa, thử truy cập web server qua giao diện mạng riêng bằng lệnh:

curl --connect-timeout 5 10.0.0.1

Nếu thành công, kết quả mà bạn sẽ nhận được là:

Output
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
. . .

Nếu thử sử dụng giao diện công khai, bạn sẽ nhận được thông báo cho biết không thể kết nối:

curl --connect-timeout 5 203.0.113.1

Output
curl: (7) Failed to connect to 203.0.113.1 port 80: Connection refused

Kết quả này không ngoài mong đợi.

Cấu hình tường lửa để chuyển tiếp cổng 80

Bây giờ bạn sẽ tiến hành cấu hình port forwarding trên máy chủ tường lửa.

Bật chức năng chuyển tiếp trong kernel

Việc đầu tiên là bật tính năng chuyển tiếp gói tin ở cấp độ kernel. Theo mặc định, phần lớn hệ thống đều tắt chức năng này.

Để bật chuyển tiếp cho phiên làm việc hiện tại:

echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
Output
1

Để bật vĩnh viễn, chỉnh sửa tệp /etc/sysctl.conf. Có thể thực hiện việc này bằng cách mở tệp với quyền sudo:

sudo nano /etc/sysctl.conf

Bên trong tệp, tìm và bỏ dấu # ở đầu dòng để kích hoạt, đây là kết quả:

net.ipv4.ip_forward=1

Lưu và đóng tệp, sau đó áp dụng thay đổi. Trước tiên, chạy lệnh sau:

sudo sysctl -p

Output
net.ipv4.ip_forward = 1

Kế đến, chạy thêm lệnh nhưng thay tùy chọn -p bằng --system:

sudo sysctl --system

Output
. . .
* Applying /usr/lib/sysctl.d/50-pid-max.conf ...
kernel.pid_max = 4194304
* Applying /etc/sysctl.d/99-cloudimg-ipv6.conf ...
net.ipv6.conf.all.use_tempaddr = 0
net.ipv6.conf.default.use_tempaddr = 0
* Applying /etc/sysctl.d/99-sysctl.conf ...
net.ipv4.ip_forward = 1
* Applying /usr/lib/sysctl.d/protect-links.conf ...
fs.protected_fifos = 1
fs.protected_hardlinks = 1
fs.protected_regular = 2
fs.protected_symlinks = 1
* Applying /etc/sysctl.conf ...
net.ipv4.ip_forward = 1

Thêm quy tắc chuyển tiếp vào tường lửa cơ bản

Tiếp theo, cấu hình tường lửa sao cho lưu lượng vào giao diện công khai (eth0) trên cổng 80 sẽ được chuyển tiếp đến giao diện riêng (eth1).

Tường lửa mà bạn đã cấu hình trong hướng dẫn trước có chuỗi FORWARD được đặt mặc định là DROP toàn bộ lưu lượng. Cần bổ sung các quy tắc cho phép chuyển tiếp kết nối đến máy chủ web. Để đảm bảo an toàn, cấu hình sẽ được siết chặt, chỉ cho phép các kết nối cần thiết được chuyển tiếp.

Trong chuỗi FORWARD, chấp nhận các kết nối mới có đích là cổng 80, bắt nguồn từ giao diện mạng công khai và đi tới giao diện mạng riêng. Các kết nối mới được nhận diện bằng tiện ích mở rộng conntrack, cụ thể sẽ là các gói tin TCP có cờ SYN như trong ví dụ sau.

sudo iptables -A FORWARD -i eth0 -o eth1 -p tcp --dport 80 -m conntrack --ctstate NEW -j ACCEPT

Cấu hình này sẽ cho phép gói tin đầu tiên dùng để thiết lập kết nối được đi qua tường lửa. Cũng cần cho phép toàn bộ lưu lượng kế tiếp ở cả hai chiều phát sinh từ kết nối đó. Để làm được điều này, cần cho phép lưu lượng thuộc các trạng thái ESTABLISHEDRELATED giữa hai giao diện mạng công khai và mạng riêng. Trước tiên áp dụng quy tắc cho giao diện mạng công khai:

sudo iptables -A FORWARD -i eth0 -o eth1 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

Tiếp theo là mạng riêng:

sudo iptables -A FORWARD -i eth1 -o eth0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

Kiểm tra lại chính sách mặc định của chain FORWARD có phải là DROP:

sudo iptables -L FORWARD

Tới thời điểm này, bạn đã cho phép một số loại lưu lượng giữa hai giao diện công khai và riêng đi qua tường lửa. Tuy nhiên, bạn vẫn chưa cấu hình các quy tắc để iptables biết cách dịch địa chỉ và định tuyến lưu lượng.

Thêm các quy tắc NAT để định tuyến gói tin chính xác

Tiếp theo, bạn sẽ thêm các quy tắc giúp iptables biết cách định tuyến lưu lượng. Bạn cần thực hiện hai thao tác riêng biệt để iptables có thể sửa đổi gói tin đúng cách, cho phép client giao tiếp với máy chủ web.

Thao tác đầu tiên, gọi là DNAT, sẽ diễn ra trong chuỗi PREROUTING của bảng nat. DNAT là thao tác thay đổi địa chỉ đích của gói tin để cho phép nó được định tuyến chính xác khi đi qua các mạng. Các client trên mạng công khai sẽ kết nối tới máy chủ tường lửa mà không biết gì về cấu trúc mạng riêng. Do đó, bạn cần thay đổi địa chỉ đích của từng gói tin để khi được gửi vào mạng riêng, gói tin có thể đến đúng máy chủ web.

Vì bạn chỉ định cấu hình chuyển tiếp cổng thay vì NAT toàn bộ gói tin đi qua tường lửa, bạn nên giới hạn quy tắc này ở cổng 80. Quy tắc này sẽ khớp với các gói tin đến cổng 80 và thay đổi địa chỉ đích sang địa chỉ IP riêng của máy chủ web (ví dụ sau đây dùng địa chỉ 10.0.0.1):

sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j DNAT --to-destination 10.0.0.1:80

Quá trình này mới chỉ giải quyết được một nửa vấn đề. Gói tin có thể được định tuyến đến đúng máy chủ web. Tuy nhiên, nó vẫn mang địa chỉ gốc của client làm địa chỉ nguồn. Máy chủ sẽ cố gắng gửi phản hồi trực tiếp tới địa chỉ đó, khiến bạn không thể thiết lập kết nối TCP hợp lệ.

Để định tuyến đúng cách, bạn cũng cần sửa đổi địa chỉ nguồn của gói tin khi nó rời khỏi tường lửa và đến máy chủ web. Địa chỉ nguồn cần được thay đổi thành địa chỉ IP riêng của máy chủ tường lửa (ví dụ: 10.0.0.2). Phản hồi sẽ được gửi về tường lửa, sau đó chuyển tiếp trở lại cho client như mong đợi.

Để bật tính năng này, thêm một quy tắc vào chuỗi POSTROUTING của bảng nat, được đánh giá ngay trước khi gói tin rời khỏi hệ thống. Quy tắc này sẽ khớp với các gói tin đến máy chủ web dựa trên địa chỉ IP và cổng:

sudo iptables -t nat -A POSTROUTING -o eth1 -p tcp --dport 80 -d 10.0.0.1 -j SNAT --to-source 10.0.0.2

Sau khi thêm quy tắc này, máy chủ web sẽ có thể truy cập được bằng cách mở trình duyệt và truy cập vào địa chỉ công khai của máy chủ tường lửa:

curl 203.0.113.2
Output
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
. . .

Việc cấu hình chuyển tiếp cổng đã hoàn tất.

Điều chỉnh bộ quy tắc vĩnh viễn

Sau khi cấu hình xong chuyển tiếp cổng, bạn có thể lưu lại vào bộ quy tắc vĩnh viễn.

Nếu không quan tâm đến việc mất các chú thích hiện có trong bộ quy tắc hiện tại, bạn có thể dùng lệnh netfilter-persistent để sử dụng dịch vụ iptables và lưu lại các quy tắc:

sudo netfilter-persistent save

Output
 * Saving netfilter rules...                                                    run-parts: executing /usr/share/netfilter-persistent/plugins.d/15-ip4tables save
run-parts: executing /usr/share/netfilter-persistent/plugins.d/25-ip6tables save
                                                                         [ OK ]

Nếu bạn muốn giữ nguyên các chú thích trong tập tin, hãy mở và chỉnh sửa thủ công:

sudo nano /etc/iptables/rules.v4

Bạn sẽ cần điều chỉnh cấu hình trong bảng filter để bổ sung các quy tắc chuỗi FORWARD đã thêm trước đó. Đồng thời, chỉnh sửa phần cấu hình bảng nat để thêm các quy tắc PREROUTINGPOSTROUTING. Nội dung tương tự như sau:

/etc/iptables/rules.v4

*filter
# Allow all outgoing, but drop incoming and forwarding packets by default
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]

# Custom per-protocol chains
:UDP - [0:0]
:TCP - [0:0]
:ICMP - [0:0]

# Acceptable UDP traffic

# Acceptable TCP traffic
-A TCP -p tcp --dport 22 -j ACCEPT

# Acceptable ICMP traffic

# Boilerplate acceptance policy
-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A INPUT -i lo -j ACCEPT

# Drop invalid packets
-A INPUT -m conntrack --ctstate INVALID -j DROP

# Pass traffic to protocol-specific chains
## Only allow new connections (established and related should already be handled)
## For TCP, additionally only allow new SYN packets since that is the only valid
## method for establishing a new TCP connection
-A INPUT -p udp -m conntrack --ctstate NEW -j UDP
-A INPUT -p tcp --syn -m conntrack --ctstate NEW -j TCP
-A INPUT -p icmp -m conntrack --ctstate NEW -j ICMP

# Reject anything that's fallen through to this point
## Try to be protocol-specific w/ rejection message
-A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable
-A INPUT -p tcp -j REJECT --reject-with tcp-reset
-A INPUT -j REJECT --reject-with icmp-proto-unreachable

# Rules to forward port 80 to our web server

# Web server network details:

# * Public IP Address: 203.0.113.1
# * Private IP Address: 10.0.0.1
# * Public Interface: eth0
# * Private Interface: eth1
# 
# Firewall network details:
# 
# * Public IP Address: 203.0.113.2
# * Private IP Address: 10.0.0.2
# * Public Interface: eth0
# * Private Interface: eth1
-A FORWARD -i eth0 -o eth1 -p tcp --syn --dport 80 -m conntrack --ctstate NEW -j ACCEPT
-A FORWARD -i eth0 -o eth1 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A FORWARD -i eth1 -o eth0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# End of Forward filtering rules

# Commit the changes

COMMIT

*raw
:PREROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT

*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]

# Rules to translate requests for port 80 of the public interface
# so that we can forward correctly to the web server using the
# private interface.

# Web server network details:

# * Public IP Address: 203.0.113.1
# * Private IP Address: 10.0.0.1
# * Public Interface: eth0
# * Private Interface: eth1
# 
# Firewall network details:
# 
# * Public IP Address: 203.0.113.2
# * Private IP Address: 10.0.0.2
# * Public Interface: eth0
# * Private Interface: eth1
-A PREROUTING -i eth0 -p tcp --dport 80 -j DNAT --to-destination 10.0.0.1
-A POSTROUTING -d 10.0.0.1 -o eth1 -p tcp --dport 80 -j SNAT --to-source 10.0.0.2
# End of NAT translations for web server traffic
COMMIT

*security
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT

*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
COMMIT

Lưu và đóng tệp sau khi đã thêm nội dung và điều chỉnh các giá trị để phản ánh đúng môi trường mạng của hệ thống.

Tiếp theo, kiểm tra cú pháp của tập tin quy tắc:

sudo iptables-restore -t < /etc/iptables/rules.v4

Nếu không có lỗi, bạn có thể nạp lại tập quy tắc:

sudo service netfilter-persistent reload
Output
 * Loading netfilter rules...                                                   run-parts: executing /usr/share/netfilter-persistent/plugins.d/15-ip4tables start
run-parts: executing /usr/share/netfilter-persistent/plugins.d/25-ip6tables start
                                                                         [ OK ]

Bây giờ hãy kiểm tra xem máy chủ web của bạn có còn truy cập được thông qua địa chỉ IP công khai của máy chủ tường lửa hay không.

curl 203.0.113.2

Kết quả vẫn nên giống như trước đó.

Kết luận

Đến thời điểm này, bạn đã có thể lbạn đã có thể tự tin thực hiện chuyển tiếp cổng bằng Iptables trong Linux.

Quy trình này bao gồm việc cho phép chuyển tiếp ở cấp độ kernel, thiết lập quyền truy cập để cho phép lưu lượng của cổng cụ thể được chuyển tiếp giữa hai giao diện trên hệ thống tường lửa, và cấu hình các quy tắc NAT để các gói tin được định tuyến chính xác.

Quá trình này có thể có vẻ phức tạp, nhưng nó cũng thể hiện sự linh hoạt của hệ thống lọc gói netfilter và tường lửa iptables. Cách làm này có thể được sử dụng để che giấu cấu trúc mạng nội bộ của bạn đồng thời, vẫn cho phép lưu lượng dịch vụ tự do đi qua máy chủ tường lửa (gateway) một cách mượt mà.

0 Bình luận

Đăng nhập để thảo luận

Chuyên mục Hướng dẫn

Tổng hợp các bài viết hướng dẫn, nghiên cứu và phân tích chi tiết về kỹ thuật, các xu hướng công nghệ mới nhất dành cho lập trình viên.

Đăng ký nhận bản tin của chúng tôi

Hãy trở thành người nhận được các nội dung hữu ích của CyStack sớm nhất

Xem chính sách của chúng tôi Chính sách bảo mật.

Đăng ký nhận Newsletter

Nhận các nội dung hữu ích mới nhất