Tuesday, July 17, 2007

Dùng iptables NAT thay thế cho reverse proxy

Hôm rồi một khách hàng thông báo website của họ cứ chập chờn, lúc vào được, lúc lại không. Tôi vào kiểm tra thì thấy web-server của họ hoàn toàn ổn, duy một điều coi log lại không thấy kết nối nào đến được web-server cả. Loay hoay một hồi, khách hàng mới báo cho biết rằng họ có sử dụng một con squid làm reverse proxy đứng trước web-server, mục đích là để cache lại static content.

Tôi vào kiểm tra con squid này thì phát hiện trong log của nó phun ra khá nhiều lỗi. Google cho biết đây là những lỗi khá nghiêm trọng và cũng có đưa ra vài giải pháp, nhưng do tôi không có nhiều kinh nghiệm với squid thành ra loay hoay mãi mà vẫn không sao giải quyết được.

Tôi quyết định tạm thời stop con squid này lại, tìm một giải pháp thay thế. Ban đầu tôi định sử dụng Apache để làm reverse proxy do tôi có nhiều kinh nghiệm với thằng này. Tuy vậy, giải pháp này khá mất thời gian mà con reverse proxy này cũng sẽ bị "kết liễu" trong vài ngày tới, nên tôi quyết định sử dụng chức năng NAT của iptables để làm luôn cho gọn.

Thú thật là rất lâu rồi tôi không đụng đến iptables, nên tôi cũng mất gần 15' coi tài liệu mới nhớ lại cách làm. Tôi ghi lại ở đây để sau này có gặp sự cố tương tự thì có cách giải quyết nhanh:

iptables -t nat -A PREROUTING -p tcp --dport 80 -d 1.1.1.1 -i eth0 -j DNAT --to 2.2.2.2:80

iptables -t nat -A POSTROUTING -p tcp --dport 80 -o eth0 -j SNAT --to 1.1.1.1

Ghi chú:

- 1.1.1.1 là IP address của eth0 trên reverse proxy, 2.2.2.2 là IP address của eth0 trên web-server.

- 2 lệnh này được chạy trên reverse proxy và chúng chỉ "redirect" traffic đến cổng 80, nếu bạn cần redirect traffic đến các cổng khác như 443 chẳng hạn, bạn phải thêm vào các lệnh tương ứng.

- Lệnh số 1 có tác dụng chuyển destination IP address (hence DNAT) của tất cả TCP packet đến cổng 80 của IP 1.1.1.1 thành cổng 80 của IP 2.2.2.2. Lệnh này nằm ở chain PREROUTING, nghĩa là nó được apply trước giai đoạn routing.

- Lệnh số 2 có tác dụng chuyển source IP address (hence SNAT) của tất cả TCP packet đi ra bằng đường eth0 có destination port là 80 thành 1.1.1.1. Lệnh này nằm ở chain POSTROUTING, nghĩa là nó được apply sau giai đoạn routing.

Giải thích:

1. client 3.3.3.3 gửi một packet (src=3.3.3.3, dst=1.1.1.1) đến reverse proxy 1.1.1.1

2. Lệnh thứ nhất sẽ chuyển packet này thành (src=3.3.3.3, dst=2.2.2.2).

3. Lệnh thứ hai sẽ chuyển packet này thành (src=1.1.1.1, dst=2.2.2.2).

3. Sau khi web-server 2.2.2.2 nhận được packet này, nó sẽ tạo ra một packet (src=2.2.2.2, dst=1.1.1.1) và gửi lại cho reverse proxy 1.1.1.1.

4. reverse proxy 1.1.1.1 sẽ nhìn vào NAT table của lệnh thứ hai để chuyển packet này thành (src=2.2.2.2, dst=3.3.3.3)

5. reverse proxy 1.1.1.1 tiếp tục nhìn vào NAT table của lệnh thứ nhất để chuyển packet này thành (src=1.1.1.1, dst=3.3.3.3)

6. reverse proxy 1.1.1.1 gửi packet (src=1.1.1.1, dst=3.3.3.3) lại cho client 3.3.3.3

14 comments:

Vietwow said...

Chào mrro,
Bác có thể phác hoạ mô hình mạng (bằng hình vẻ đơn giản hoặc bằng text) trên của bác ko ? Vì theo mình mô hình trên Con Reverse Proxy phải có 2 NIC, 1 NIC là Public IP, NIC còn lại sẽ có Private IP & và đây chính là NIC connect trực tiếp đến Web Server thật. Như vậy eth0 trong bài viết trên của bácl là NIC nào ?

Thân

thaidn said...

Reverse Proxy 1 NIC hay 2 NIC không thành vấn đề cho lắm. 2 NIC thì tốt hơn 1 NIC, nhưng trong trường hợp tôi vừa làm, reverse proxy chỉ có duy nhất 1 NIC mà thôi.

Vietwow said...

Về câu lệnh :
iptables -t nat -A POSTROUTING -p tcp --dport 80 -o eth0 -j SNAT --to 1.1.1.1

Ý nghĩa của nó thì mình hiểu (để NAT các máy nằm bên trong có thể đi ra NET được) nhưng mình ko hiểu được ý nghĩa của nó trong trường hợp reverse proxy của bác, mình nghĩ reverse proxy thì chỉ cần dòng đầu tiền (iptables -t nat -A PREROUTING -p tcp --dport 80 -d 1.1.1.1 -i eth0 -j DNAT --to 2.2.2.2:80) là đủ rồi chứ, vì webserver thật bên trong chỉ có nhiệm vụ là nhận request từ bên ngoài và xử lý và respone lại chứ nó đâu có nhu cầu tự mở kết nối ra ngoài đâu mà phải cần source NAT ?

Thân

thaidn said...

Chào vietwow,

Nếu chỉ có câu lệnh "iptables -t nat -A PREROUTING -p tcp --dport 80 -d 1.1.1.1 -i eth0 -j DNAT --to 2.2.2.2:80", các packet từ 3.3.3.3 đến rp 1.1.1.1 có (src=3.3.3.3,dst=1.1.1.1) sẽ tự động chuyển thành (src=3.3.3.3,dst=2.2.2.2) rồi chuyển sang cho thằng webserver 2.2.2.2. Lúc này, thằng 2.2.2.2 nhận được packet đó, nó sẽ reply lại cho thằng 3.3.3.3 bằng một packet có (src=2.2.2.2,dst=3.3.3.3), nhưng thằng 3.3.3.3 sẽ từ chối nhận packet này, bởi lẽ lúc đầu nó gửi cho thằng 1.1.1.1, chứ không gửi cho thằng 2.2.2.2. Đó là lý do cần dòng lệnh thứ hai.

BTW, dòng lệnh thứ hai nên sửa lại thế này sẽ chính xác hơn:

iptables -t nat -A POSTROUTING -p tcp -d 2.2.2.2 --dport 80 -o eth0 -j SNAT --to 1.1.1.1

Vietwow said...
This comment has been removed by the author.
Vietwow said...

ok, Thanx bác, mình đã hiểu, thì ra mình lẫn lộn giữa 2 khái niệm Port forwarding, và reverse proxy, mình cứ nghĩ dòng đầu đã là port forwarding vậy là đủ rồi ^_^

Vietwow said...

Mà như vậy thì mô hình của bác cũng giống như mô hình port forwarding của modem ADSL nhỉ, ko biết mình hiểu vậy có đúng ko ? Con Reverse Proxy hoạt động giống y chang cách con ADSL router port forwarding :)

thaidn said...

vietwow: đúng rồi đó bồ, nó y chang ;). Tôi đã nói là dùng iptables thay thế cho "reverse proxy" mà, cái này là "dirty trick" dùng trong trường hợp con rp gặp sự cố thôi ;)

Vietwow said...
This comment has been removed by the author.
Anonymous said...

Dòng thứ 2 vẫn thừa.

các packet từ 3.3.3.3 đến rp 1.1.1.1 có (src=3.3.3.3,dst=1.1.1.1) sẽ tự động chuyển thành (src=3.3.3.3,dst=2.2.2.2) rồi chuyển sang cho thằng webserver 2.2.2.2. Lúc này, thằng 2.2.2.2 nhận được packet đó, nó sẽ reply lại cho thằng 3.3.3.3 bằng một packet có (src=2.2.2.2,dst=3.3.3.3), nhưng thằng 3.3.3.3 sẽ từ chối nhận packet này, bởi lẽ lúc đầu nó gửi cho thằng 1.1.1.1, chứ không gửi cho thằng 2.2.2.2. Đó là lý do cần dòng lệnh thứ hai.

Đơn giản vì nếu gói tin trả về đi qua máy đã forward cái port vào, iptables đủ thông minh để giải NAT (De-NAT) cho gói tin về địa chỉ trước khi NAT. Nghĩa là trong ví dụ của Thai, gói tin trả về cho 3.3.3.3 sẽ tự thay src=1.1.1.1, dù có hay không có dòng thứ 2. Iptables là statefull, và các CHAIN của nó được bố trí như thế mà.
Cậu cứ thử đi.
MyQuartz.

Mis Anh said...

Chào mọi người! Mình đang làm bài tập quản trị Unix với ND: "Dựng DNAT dùng IPtable". Vì không rành nó lắm, hy vọng mọi người có thể giúp mình.
Mail : ngocanh99999@gmai.com



Thanks and best regards!

LE VU TECH said...

Chào bạn Thái, bạn cho mình hỏi một chút về việc NAT của iptables. Không biết iptables dựa vào thông tin nào trong gói tin TCP để thực hiện việc De-NAT nhỉ ? Và thông tin này được lưu trữ chỗ nào, chúng ta có thể xem dưới dạng plain text không ? Cám ơn bạn.

Thai Duong said...

Vu: bạn có thể xem tài liệu ở http://www.netfilter.org/documentation/index.html.

Trong đó có tài liệu nói về NAT và cách triển khai của iptables/netfilter.

Sỹ Bảo said...

anh cho hỏi có cách nào keep source ip client 3.3.3.3 vào phía trong không??? tức là server 2.2.2.2 vẫn thấy được ip client là 3.3.3.3 chứ ko phải thấy ip proxy là 1.1.1.1