Các bước để giới hạn các kết nối bên ngoài vào container docker với iptables?


20

Mục tiêu của tôi là giới hạn quyền truy cập vào các container docker chỉ trong một vài địa chỉ IP công cộng. Có một quá trình đơn giản, lặp lại để thực hiện mục tiêu của tôi? Chỉ hiểu những điều cơ bản về iptables trong khi sử dụng các tùy chọn mặc định của Docker, tôi cảm thấy rất khó khăn.

Tôi muốn chạy một container, làm cho nó hiển thị với Internet công cộng, nhưng chỉ cho phép kết nối từ các máy chủ được chọn. Tôi dự kiến ​​sẽ đặt chính sách INPUT mặc định của RE DỰ ÁN và sau đó chỉ cho phép kết nối từ máy chủ của mình. Nhưng các quy tắc và chuỗi NAT của Docker cản trở và các quy tắc INPUT của tôi bị bỏ qua.

Ai đó có thể cung cấp một ví dụ về cách thực hiện mục tiêu của tôi với các giả định sau đây không?

  • Lưu trữ IP công cộng 80,80,80,80 trên eth0
  • Lưu trữ IP riêng 192.168.1.10 trên eth1
  • docker run -d -p 3306:3306 mysql
  • Chặn tất cả kết nối với máy chủ / container 3306 ngoại trừ từ máy chủ 4.4.4.4 và 8.8.8.8

Tôi rất vui khi liên kết vùng chứa chỉ với địa chỉ IP cục bộ nhưng sẽ cần hướng dẫn về cách thiết lập quy tắc chuyển tiếp iptables đúng cách để tồn tại quá trình docker và khởi động lại máy chủ.

Cảm ơn!

Câu trả lời:


15

Hai điều cần lưu ý khi làm việc với các quy tắc tường lửa của docker:

  1. Để tránh các quy tắc của bạn bị chặn bởi docker, hãy sử dụng DOCKER-USERchuỗi
  2. Docker thực hiện ánh xạ cổng trong PREROUTINGchuỗi của natbảng. Điều này xảy ra trước các filterquy tắc, vì vậy --dest--dportsẽ thấy IP và cổng bên trong của container. Để truy cập điểm đến ban đầu, bạn có thể sử dụng -m conntrack --ctorigdstport.

Ví dụ:

iptables -A DOCKER-USER -i eth0 -s 8.8.8.8 -p tcp -m conntrack --ctorigdstport 3306 --ctdir ORIGINAL -j ACCEPT
iptables -A DOCKER-USER -i eth0 -s 4.4.4.4 -p tcp -m conntrack --ctorigdstport 3306 --ctdir ORIGINAL -j ACCEPT
iptables -A DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 3306 --ctdir ORIGINAL -j DROP

LƯU Ý: Nếu không --ctdir ORIGINAL, điều này cũng sẽ khớp với các gói trả lời trở lại cho một kết nối từ container đến cổng 3306 trên một số máy chủ khác, gần như chắc chắn không phải là điều bạn muốn! Bạn không thực sự cần điều này nếu như tôi, quy tắc đầu tiên của bạn là -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT, vì điều đó sẽ xử lý tất cả các gói trả lời, nhưng --ctdir ORIGINALdù sao vẫn an toàn hơn khi sử dụng .


Điều này nên được chỉnh sửa để bao gồm --ctdir ? Tôi sử dụng-m conntrack --ctstate NEW --ctorigdstport 3306 --ctdir ORIGINAL
lonix

@Ionix, đúng vậy, mặc dù tôi chỉ tìm ra lý do tại sao nó làm tôi bối rối. Tôi đã thêm một chút giải thích.
SystemParadox

1
Lưu ý DOCKER-USERbảng mặc định chứa mục nhập: -A DOCKER-USER -j RETURNsẽ chạy trước ở trên nếu bạn sử dụng -A. Một giải pháp là chèn các quy tắc ở đầu theo thứ tự ngược lại với -I.
BMitch

@BMitch Hoặc thậm chí tốt hơn nữa , thêm tất cả các quy tắc trong một FILTERSchuỗi mới và -Ichèn các quy tắc mới (như bạn đã nói), để chuyển sang nó: -I INPUT -j FILTERS-I DOCKER-USER -i eth0 -j FILTERS
lonix

@BMitch Tuy nhiên, tôi vừa kiểm tra máy chủ của mình và quy tắc trả lại không có, có lẽ phiên bản docker mới nhất không còn chèn nữa không? Ý tưởng tốt để sử dụng -Imặc dù, chỉ để được an toàn.
lonix

8

Với Docker v.17.06, có một chuỗi iptables mới gọi là DOCKER-USER. Cái này dành cho các quy tắc tùy chỉnh của bạn: https://docs.docker.com/network/iptables/

Không giống như chuỗi DOCKER, nó không được thiết lập lại trên các thùng chứa xây dựng / bắt đầu. Vì vậy, bạn có thể thêm các dòng này vào cấu hình / tập lệnh iptables của mình để cung cấp máy chủ ngay cả trước khi cài đặt docker và khởi động các thùng chứa:

-N DOCKER
-N DOCKER-ISOLATION
-N DOCKER-USER
-A DOCKER-ISOLATION -j RETURN
-A DOCKER-USER -i eth0 -p tcp -m tcp --dport 3306 -j DROP
-A DOCKER-USER -j RETURN

Bây giờ cổng cho MySQL bị chặn khỏi truy cập bên ngoài (eth0) thậm chí nghĩ rằng docker mở cổng cho thế giới. (Các quy tắc này giả định, giao diện bên ngoài của bạn là eth0.)

Cuối cùng, bạn sẽ phải dọn sạch iptables khởi động lại dịch vụ docker trước, nếu bạn làm hỏng nó quá nhiều khi cố gắng khóa cổng như tôi đã làm.


Tôi nhớ tại sao bảng DOCKER-USER này khác với bất kỳ bảng nào do người dùng thêm vào khác .. Nó không có bộ lọc được áp dụng trước cho nó nên bạn vẫn phải tự chỉ định tên giao diện. Nếu bạn tạo một "MY-CHAIN" và chèn nó vào chuỗi FORWARD, nó sẽ có kết quả tương tự, phải không?
ColinM

Đúng, nó tạo ra sự khác biệt, bởi vì Docker chèn chuỗi DOCKER-USER vào chuỗi FORWARD: -A FORWARD -j DOCKER-USER -A FORWARD -j DOCKER-ISOLATION Đó là lý do tại sao, các hướng dẫn tùy chỉnh được thực thi trước chuỗi DOCKER.
ck1

Lưu ý rằng nếu bạn sử dụng --dportbên trong DOCKER-USER thì điều này phải khớp với IP bên trong của dịch vụ container, chứ không phải cổng bị lộ. Chúng thường khớp nhưng không phải lúc nào cũng được và điều này có thể dễ dàng xung đột với các dịch vụ khác, vì vậy tôi vẫn cho rằng giải pháp DOCKER-USER này là một nửa.
ColinM

4

CẬP NHẬT : Mặc dù có hiệu lực vào năm 2015, giải pháp này không còn là cách đúng đắn để làm điều đó.

Câu trả lời dường như có trong tài liệu của Docker tại https://docs.docker.com/articles/networking/#the-world

Các quy tắc chuyển tiếp của Docker cho phép tất cả các IP nguồn bên ngoài theo mặc định. Để chỉ cho phép một IP hoặc mạng cụ thể truy cập vào các bộ chứa, hãy chèn quy tắc phủ định ở đầu chuỗi bộ lọc DOCKER. Ví dụ: để hạn chế quyền truy cập bên ngoài sao cho chỉ IP nguồn.8.8.8 có thể truy cập vào các bộ chứa, có thể thêm quy tắc sau:iptables -I DOCKER -i ext_if ! -s 8.8.8.8 -j DROP

Điều cuối cùng tôi đã làm là:

iptables -I DOCKER -i eth0 -s 8.8.8.8 -p tcp --dport 3306 -j ACCEPT
iptables -I DOCKER -i eth0 -s 4.4.4.4 -p tcp --dport 3306 -j ACCEPT
iptables -I DOCKER 3 -i eth0 -p tcp --dport 3306 -j DROP

Tôi đã không chạm vào --iptableshoặc --icctùy chọn.


1
Nếu bạn làm như vậy iptables -vnL DOCKER, các cổng đích là tất cả các cổng trong container. Nếu tôi hiểu đúng, điều đó có nghĩa là các quy tắc trên sẽ chỉ ảnh hưởng đến cổng 3306trong vùng chứa - nghĩa là, nếu bạn ở -p 12345:3306trong container của mình, quy tắc của bạn vẫn sẽ là quy tắc bắt buộc để khóa truy cập (nghĩa là --dport 12345sẽ không hoạt động) , bởi vì các quy tắc ACCEPT của chuỗi DOCKER là hậu NAT.
trời

Đúng vậy, các quy tắc cần liên quan đến các cổng trong các container.
GGGforce

1
Hum, thật là xấu nếu bạn chạy nhiều container sử dụng NGINX nội bộ để thực hiện ủy quyền ngược (ví dụ: Zabbix, bộ cân bằng tải tùy chỉnh, v.v.) bởi vì nó sẽ yêu cầu bạn biết trước IP của container. Tôi vẫn đang tìm kiếm một giải pháp cho vấn đề không cần thiết --iptables=false, bởi vì đây dường như là sự lựa chọn tồi tệ nhất trong tất cả.
trời

Cảm ơn bạn! Bạn đã giải quyết vấn đề của tôi sau nhiều giờ tìm kiếm. Bây giờ tôi cuối cùng cũng có thể bỏ tù MySQL chỉ đến địa chỉ IP nhà của tôi mà không để lộ phần mềm dưới toàn thế giới.
Matt Cavanagh

1
Chuỗi DOCKER không được người dùng thao tác trực tiếp! Sử dụng chuỗi DOCKER-USER cho điều đó. Kiểm tra câu trả lời được chấp nhận.
Paul-Sebastian Manole

3

CẬP NHẬT: Mặc dù câu trả lời này vẫn còn hiệu lực, nhưng câu trả lời của @SystemParadox sử dụng DOCKER-USERkết hợp với --ctorigdstportlà tốt hơn.

Đây là một giải pháp duy trì tốt giữa khởi động lại và cho phép bạn ảnh hưởng đến cổng tiếp xúc hơn là cổng nội bộ .

iptables -t mangle -N DOCKER-mysql iptables -t mangle -A DOCKER-mysql -s 22.33.44.144/32 -j RETURN iptables -t mangle -A DOCKER-mysql -s 22.33.44.233/32 -j RETURN iptables -t mangle -A DOCKER-mysql -j DROP iptables -t mangle -A PREROUTING -i eth0 -p tcp -m tcp --dport 3306 -j DOCKER-mysql

Tôi đã tạo một hình ảnh Docker sử dụng phương pháp này để tự động quản lý iptables cho bạn, sử dụng biến môi trường hoặc động với etcd (hoặc cả hai):

https://hub.docker.com/r/colinmollenhour/confd-firewall/

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.