Khi sử dụng Docker, mặc định nó đã thêm rất nhiều các quy tắc firewall. Các quy tắc này cho phép bạn định tuyến một cách thông minh các cổng của server đến đúng container, nhưng cũng cho phép trao đổi giữa các network ví dụ như trong Swarm. Như vậy sẽ rất phức tạp nếu chúng ta muốn thiết lập các quy tắc riêng của mình khi mà Docker đưa ra các quy tắc của riêng mình.
UFW là một ứng dụng rất đơn giản giúp bạn không phải can thiệp sâu vào các quy tắc phức tạp này. Với một vài lệnh, bạn có thể cho phép hoặc chặn một cổng từ IP này sang IP khác.
Bất kỳ quy tắc nào bạn đặt ra sẽ được thông qua sau khi các quy tắc do Docker đặt ra. Vì vậy, nếu bạn chặn cổng 80 bằng UFW thì các container sẽ vẫn có thể truy cập được. Mục tiêu của chúng ta ở đây là thực thi các quy tắc UFW trước Docker. Có một chuỗi trong IPTables được gọi là DOCKER-USER, cho phép các quy tắc được thực thi trước các quy tắc chung của container. Tuy nhiên, trong trường hợp của mình UFW không thể giao tiếp với chuỗi này mà chỉ với ufw-user-input. Vì vậy, hãy bắt đầu bằng cách đặt lại các quy tắc này mỗi khi khởi động lại UFW.
Hãy sửa lại nội dung file /etc/ufw/before.init như sau.
Trước khi thay đổi nội dung file /etc/ufw/before.init, bạn hãy thực hiện backup file này.
cp /etc/ufw/before.init /etc/ufw/before.init.bak
Nội dung sau khi sửa đổi sẽ như sau.
set -e
box "$1" in
start)
# typically required
;;
stop)
iptables -F DOCKER-USER || true
iptables -A DOCKER-USER -j RETURN || true
iptables -X ufw-user-input || true
# typically required
;;
status)
# optional
;;
flush-all)
# optional
;;
*)
echo "'$1' not supported"
echo "Usage: before.init {start|stop|flush-all|status}"
;;
Bây giờ bạn phải nói với tường lửa rằng các quy tắc do UFW xác định phải được thực thi trước các quy tắc của Docker. Hãy thêm những dòng này vào /etc/ufw/after.rules nội dung sau. Tương tự bạn hãy backup file /etc/ufw/after.rules trước.
cp /etc/ufw/after.rules /etc/ufw/after.rules.bak
Hãy thêm nội dung này vào
*filter
:DOCKER-USER - [0:0]
:ufw-user-input - [0:0]
:ufw-after-logging-forward - [0:0]
-A DOCKER-USER -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A DOCKER-USER -m conntrack --ctstate INVALID -j DROP
-A DOCKER-USER -i $INTERFACE -j ufw-user-input
-A DOCKER-USER -i $INTERFACE -j ufw-after-logging-forward
-A DOCKER-USER -i $INTERFACE -j DROP
COMMIT
Lưu ý: $INTERFACE phải được thay thế bằng tên của interface vật lý mà Docker đang sử dụng (ens3, eno1,…). Trước khi khởi động lại UFW, bạn cũng phải cho phép các kết nối chính đến server để tránh mất kết nối tới server khi áp dụng lại các quy tắc mới, ví dụ như:
# Web
ufw allow web
# Docker Swarm cluster
# The ports used by Docker Swarm to communicate between the different nodes
ufw allow proto tcp from $SERVER1_IP to any port 2377,7946
ufw allow proto udp from $SERVER1_IP to any port 4789,7946
ufw allow proto tcp from $SERVER2_IP to any port 2377,7946
ufw allow proto udp from $SERVER2_IP to any port 4789,7946
# ...
Tuỳ vào môi trường server của bạn mà bạn có thể sẽ gặp 1 số lỗi, trường hợp của mình thì mình sẽ chạy 3 command dưới để fix một số lỗi sau khi config lại ufw.
sed -i 's/IPV6=yes/IPV6=no/' /etc/default/ufw
sed -ie 's/LOGLEVEL=.*/LOGLEVEL=off/' /etc/ufw/ufw.conf
sed -ie 's/ENABLED=no/ENABLED=yes/' /etc/ufw/ufw.conf
Sau khi xem xét cẩn thận các rule mới được cập nhật, hãy tiến hành reload lại ufw để áp dụng quy tắc mới.
ufw reload
Bạn có thể dễ dàng thực hiện chạy thử, khởi động container đang listen trên server (ví dụ: cổng 8000) và bạn sẽ không có quyền truy cập vào dịch vụ cho đến khi bạn mở cổng bằng UFW. Bây giờ chúng ta có toàn quyền kiểm soát server của chúng tôi.