docker 映射端口穿透內置防火牆

一、問題現象

1、現象舉例:

# 自製的springboot項目的dockerfile
# springboot 其實就是一個簡單的hello-world程序,寫了一個HelloController 做測試
# dockerfile內容:
FROM java:8 
MAINTAINER shan <test@[email protected]> 
ADD ./demo.jar /demo.jar
EXPOSE 8848
CMD java -jar /demo.jar


# 構建鏡像
docker build -f ./dockerfile -t demo .

# 創建、映射端口、運行容器
docker run -d --name=demo -p 8838:8848 demo

在阿里雲服務器上外置的防火牆或安全組放行端口 8838 後,在瀏覽器發現可以直接訪問到數據

—-問題在於咱還沒有開啟內置的防火牆firewalld,卻出現了端口可以直接被訪問的情況

2、使用centos7 的防火牆firewalld查看放行端口情況

使用 docker 來部署項目,發現直接使用 -p 映射端口,會出現問題:

導致docker直接透過系統本機的防火牆,不用開端口可以直接外網訪問 8838 端口。

二、問題原因

1、docker無視防火牆firewalld 的原因:其實docker並不是繞過了防火牆,只是因為它往iptables里寫了規則,你在firewalld里看不到而已。(centos7系統既有iptables,也有firewalld)

在docker安裝完後,會接管iptables,只要你docker run的時候加入參數,他會自動向iptables裏面添加規則。所以使用 -p 容器端口:主機端口,最終會在iptables中添加上容器的端口。

☺ 防火牆 iptables 和 firewalld 的關係

Iptables:是centos6自帶的防火牆工具,對服務器自身、網絡通信流量進行控制,用於過濾數據包,屬於網絡層防火牆。

FirewallD:是centos7自帶的防火牆工具,但是也同樣支持iptables。能夠允許哪些服務可用,哪些端口可用,屬於更高一層的防火牆。

iptables 與 firewalld 都不是真正的防火牆, 它們都只是用來定義防火牆策略的防火牆管理工具。

● 對於 centos7 系統既有iptables,也有firewalld

2、使用 iptables的命令可以查看到docker 容器的端口

iptables -L DOCKER

三、問題的解決

1、依靠阿里雲服務器提供的外置防火牆(推薦

  • 有的服務器叫防火牆,有的服務器叫安全組,都是一個東西,都是系統的外置防火牆。

  • 防火牆就像一個門,想像一下你的錢藏到你的房間,這時候有一個小偷想要來偷你的錢,小偷需要先溜進你家大門【外置防火牆(安全組/防火牆)】,然後再溜進你的房間【內置防火牆(iptables/firewalld)】。

解決方式1:依靠阿里雲服務器提供的外置防火牆放行需要暴露給外界的端口

  • 相當於關閉了你家大門

雖然內置防火牆和外置防火牆一起使用更加安全,但是影響了數據訪問的效率了,為了速度,有時候確保安全即可,不用更加安全。

☺ 購買了騰訊雲服務的老鐵要注意一下:

配置安全組的時候,不要選擇放行所有端口,要選擇自定義,然後放行80、443、ping、ssh端口(ssh端口建議進行修改,不使用默認的22)

2、端口映射時指定監聽地址為本機

對於那些只需要在本地訪問,不需要向外暴露端口的服務,在進行端口映射的時候指定監聽地址為127.0.0.1。

# 創建、映射端口、運行容器
docker run -d --name=demo -p 127.0.0.1:8838:8848 demo
  • 這時候,外界(通過瀏覽器)就無法訪問了到8838 端口了,即使在外置防火牆放行了端口。

3、禁用 docker 的 iptables 規則

  • 原因就是docker 在iptables 加入規則,才導致內置防火牆放行了docker容器的端口,現在咱就根治它,在docker配置文件禁止修改iptables 規則。

① docker 配置修改,禁止 iptables 規則

# 編輯/lib/systemd/system/docker.service文件

# 在ExecStart後添加 --iptables=false 

② 重載 docker 配置 & 重啟 docker 服務

systemctl daemon-reload
systemctl restart docker

完成上述兩步以及可以採用系統 firewall 控制端口訪問,但會出現 docker 容器間無法訪問,而且容器內也無法訪問外部網絡

③ 使用類似 NAT 網絡方式使得 docker 可以訪問外部網絡

firewall-cmd --permanent --zone=public --add-masquerade
  • 使用該方法解決 docker 無視系統防火牆問題所帶來的缺點:容器內無法獲取得到客戶端的真實 IP,由於是類似 NAT 網絡,常常 nginx 日誌上記錄的是 docker0 網絡的子網 IP,對於一些業務無法獲取真實 IP 可能不能容忍,看個人的取捨吧。

4、使用 expose 方式暴露端口,然後採用 nginx 代理轉發(推薦)

(1) 使用dockerCompose 編排+nginx代理轉發

① dockerCompose 中編排內容:
version: "3"
services:
  app:
    image: app
    container_name: app
    build: ..
    expose:
     - "8888"
    depends_on:
      - mysql
      - redis
  nginx:
    image: nginx
    container_name: nginx
    ports:
      - 80:80
      - 443:443
    volumes:
      - /mnt/docker/nginx/:/etc/nginx/
    links:
      - app
    depends_on:
      - app
② nginx 中的配置:
upstream appstream{
        server app:8888; #dockerCompose編排之後,app服務名相當於域名,可以通過app找到對應的ip地址
}

server{
        listen 80;
        server_name blog.yilele.site; 
        location /api {
			proxy_pass //appstream;#nginx代理轉發
        }
}

(2) 上面例子的demo容器(使用dockerfile)+ nginx

server{
        listen 80;
        server_name blog.yilele.site; 
        location /hello {# 啟動docker的demo容器後,可以通過命令:docker inspect demo 找到demo對應的ip地址
			proxy_pass //demo的ip地址:8888;   # nginx代理轉發
        }
}

☺ ports 和 expose 區別:

  • ports: 暴露容器端口到主機的任意端口或指定端口。不管是否指定主機端口,使用ports都會將端口暴露給主機和其他容器

     - "9000:8080"     # 綁定容器的8080端口到主機的9000端口
      - "443"          # 綁定容器的443端口到主機的任意端口,容器啟動時隨機分配綁定的主機端口號
    
  • expose: 暴露容器給link到當前容器的容器。即暴露給處於同一個networks的容器。搭配link 進行使用。

    expose:
      - "8000"
    

參考文章:《docker無視防火牆問題總結》 //icharle.com/dockeriptables.html

如果本文對你有幫助的話記得給一樂點個贊哦,感謝!