개발자로 후회없는 삶 살기

도커 PART.iptables 본문

[Infra]/[도커]

도커 PART.iptables

몽이장쥰 2022. 5. 28. 14:54

서론

iptables를 도커, CNI, 쿠버네티스가 어떻게 쓰는지 조사하기 위해 iptables의 개요를 익힙니다.

 

본론

- iptables란

필자가 생각하기는 별도의 네트워크를 하게 해주는 것인데, 방화벽입니다. ip랑 포트에 쉽게 접근하지 못하게 하는 접근 제어 방화벽입니다. IP 패킷이 시스템에 도착했을 때 그들이 어떤 행동을 취해야 하는지 결정하는 유틸리티입니다.

 

예를들어, 특정 IP 주소에서 오는 모든 트래픽을 차단하거나, 특정 포트로 들어오는 모든 트래픽을 허용하는 등의 규칙을 설정할 수 있습니다.

 

- iptables 명령어

1. -nL

실행 결과 리눅스를 처음 설치하면 아무것도 없습니다. 이러면 외부에서 전부 다 접근 가능합니다. (= 필자의 호스트 OS에서 vm 리눅스에 접근 가능!)

 

위 사진을 보면 도커가 차단 되어있습니다. 외부에서 호스트에 떠있는 도커 환경에 접근이 불가능합니다. 컨테이너는 하나의 isolation 개념인데 그러면 저의 로컬 OS에서 이 안의(도커) 80번 포트에 접속이 불가능합니다. isolation의 80 포트를 밖에서 접근하려면 또 다른 옵션이 필요합니다.

 

2. -A : add 의미 = 방화벽 추가

3. -A INPUT : 안으로 들어오는 패킷에 대해 차단을 하겠다!
4. -A INPUT -P tcp --dport 80 -j DROP :  tcp 포트에 대해서 dst포트인 80 포트 접근을 제어하겠다! > -nL하면 뜨는 목록이 차단을 한 것입니다. = 소스 ip에 대해서 dst ip로 오는데 80 포트를 차단하겠다! = 접근을 제어한 것입니다.


5. -D INPUT (번호) : 방화벽 제거
6. -L --line-numbers : (번호)를 아는 법

 

- iptables에서 도커가 어떻게 쓰이나

iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination <컨테이너의 IP 주소>:80

iptables를 사용하여 외부에서 도커 컨테이너에 대한 엑세스를 제한하는 방법에 관한 것으로 iptables에 눌을 추가 하여 도커 환경의 컨테이너를 외부에 노출시킬 수 있습니다. 위 명령어는 호스트 머신의 8080 포트로 들어오는 모든 트래픽을 컨테이너의 80번포트로 전달하는 것을 의미합니다.

 

=> 컨테이너 외부로 노출하기

가상 머신과 마찬가지로 도커 컨테이너 또한 가상 IP를 할당받습니다. 기본적으로 도커는 172.17.0.xxx의 IP 주소를 순차적으로 컨테이너에게 할당합니다. (+ 가상 브릿지를 추가하면 172.18.0.xxx로 시작) 브릿지의 첫 번째 컨테이너는 보통 172.17.0.2의 IP 주소를 받게 됩니다.

 

※ docker0가 defualt 가상의 브릿지/ enp2s0는 도커 컨테이너 환경의 메인 랜카드(호스트 OS 랜카드랑 맵핑)

 

=> docker0 기본 브릿지

도커 호스트는 리눅스 커널 내에서 컨테이너가 다른 컨테이너 또는 외부와 통신하기 위해 사용되는 ethernet 브릿지로서 호스트 시스템의 docker0 인터페이스를 가지고 있습니다.  기본으로 --net 지정 안 하면 컨테이너가 이 기본 브릿지로 연결됩니다.

docker0 브릿지는 모든 도커 컨테이너가 기본적으로 연결되며, docker0에 연결된 컨테이너는 외부와 통신하기 위해 도커가 생성한 iptables NAT 규칙을 사용합니다. 이는 컨테이너와 호스트 사이에 네트워크 통신을 가능하게 하는 역할입니다.

 

docker0 브릿지에 연결된 각 컨테이너는 이 브릿지를 통해 다른 컨테이너나 호스트와 통신할 수 있고 각 컨테이너는 docker0 브릿지에 연결된 가상 이더넷 인터페이스를 통해 고유한 IP 주소를 할당받습니다. 그러나 docker0 브릿지는 컨테이너 간에만 통신을 가능하게 하며, 외부 네트워크와의 통신은 호스트의 네트워크 설정에 따라 결정됩니다. 외부와의 통신을 원할 경우, 포트 포워딩을 해야합니다.

※ 컨테이너를 만들고 해당 컨테이너의 네트워크 인터페이스 확인 : ip a

=> 포트 바인딩

아무런 설정을 하지 않았다면, 우리가 생성한 컨테이너는 외부에서 접근할 수 없으며, 이 컨테이너를 외부에 노출시키기 위해서는 먼저 호스트와 연결이 되어야 합니다. 우리는 eth0의 IP와 포트 번호를 호스트의 IP와 포트 번호에 바인딩해야 합니다.


docker run -it --name network_test -p 9999:80 ubuntu:14.04

 

★ 컨테이너가 가상 브릿지랑 연결되어 있어도 도커 호스트와 컨테이너의 포트를 연결해야 도커 호스트로 요청이 들어왔을 때 포트가 연결된 컨테이너의 SW로 요청이 들어갑니다. 포트 바인딩과 브릿지에 등록은 둘 다 만족해야 하는 조건입니다.

 

1. 포트 바인딩은 호스트 컴퓨터로 요청이 들어왔을 때 서버가 컨테이너로 요청을 전달하기 위한 설정

2. 브릿지에 등록은 공유기인 브릿지에 컨테이너를 가상의 물리선으로 등록하는 것입니다.

3. iptables는 도커가 아닌 리눅스 환경에 있는 녀석으로  네트워크가 가능한 ip를 등록하는 것입니다. ( 신기하게 도커 호스트 환경의 브릿지가 리눅스 환경의 iptables에 등록된다는 것 )

 

- 컨테이너를 외부로부터 숨기기

1. 가상 네트워크

네트워크란 두 대 이상의 컴퓨터가 서로 연결되어 있는 것을 의미합니다. 도커 또한 컨테이너 관점에서는 독립적인 환경을 가진 가상 머신이므로 컨테이너가 다른 컨테이너 또는 컨테이너를 외부와 연결시키기 위해 각각의 컨테이너를 식별할 수 있는 IP주소가 필요하고 도커에서는 이를 미리 가상 네트워크로 설정해 놓습니다.

1) eth0 : 도커 호스트가 호스트 OS(리눅스)와 연결된 물리 랜카드

2) Docker0 : 도커 호스트 안에 만든 가상의 브릿지(가상의 브릿지 추가 가능)

3) 컨테이너 안 eth0 : 이미지가 가상 컴퓨터인 컨테이너로 실행되어 별도의 랜카드 생성됩니다.

 

호스트 NIC와 컨테이너의 연결 통로, docker0

도커 호스트가 호스트 OS(리눅스)와 이미 인터넷에 연결되어 있다고 가정합니다. 이 물리 랜카드가 NAPT 역할을 하는 리눅스의 iptables를 통해 docker0와 통신합니다. 그래서 컨테이너를 실행하게 되면 호스트는 NIC에서 직접 컨테이너와 연결하는 것이 아니라, docker0 브릿지를 통해 호스트 서버가 컨테이너와 연결합니다. docker0 브릿지를 통해 호스트 OS가 컨테이너와 연결합니다.

 

 

★ 결론 : 리눅스에서 ip a하면 라우터들이 쫙 나옵니다.

1. ip a는 리눅스 명령어로 도커 명령어가 아니므로 사실 그때 나오는 라우터들은 실제 호스트 os의 랜카드와 도커 기본 브릿지입니다.
2. 호스트 os가 있으면 그 안에 도커 호스트가 있고 그 안에 브릿지가 있고 컨테이너가 생긴다고 이해하면 됩니다.

 

-> iptables -nL 명령어 출력에 대하여 

mybridge로 기본 브릿지에 추가로 임의 하나만 만들었을 경우 호스트 OS에서 2개를 참조합니다.

 

 

hisbridge로 기본 + my + his 3개를 만들었을 경우 3개를 참조합니다. iptables에 연결되는 것은 호스트 OS가 STAGE1이고 도커 호스트가 STAGE2인 것 같습니다.

 

2. NAPT(Network Address Port Translation)

docker0와 물리 NIC 사이의 NAPT는 하나의 IP 주소를 여러 컴퓨터가 공유하는 기술로, 공유기입니다. (삼촌께서는 도커 환경이 컨테이너에 공유기 역할을 한다고 하셨습니다.) -> 도커에서는 NAPT로 리눅스의 iptables를 사용합니다. -> 위 사진을 보면 iptables에 브릿지(공유기)가 등록된 걸 확인 가능합니다.

 

-> 도커에서 NAPT를 사용하면

컨테이너 시작 시에 컨테이너 안에서 사용하고 있는 포트를 docker0에 개방합니다. > 외부 네트워크에서 호스트 ip에 접근하면 docker0로 접근하고 docker0에서 컨테이너의 포트로 연결이 매핑됩니다. > 따라서 컨테이너를 --net으로 브릿지에 연결시키면 브릿지가 공유기가 되고 브릿지에 연결된 컨테이너들은 사설 ip를 가집니다.

 

 

 

- 실습

 

※ 용어 :

1) eth : 메인 인터페이스 카드(랜카드)

2) docker0 : 기본 브릿지

3) overlay : 다른 호스트 OS의 컨테이너끼리의 통신 드라이버

 

=> 개념 강화

도커 호스트 안에 랜카드가 있습니다. eth가 랜카드로 라우터가 하나 있다고 보면 됩니다. 브릿지도 가상의 라우터가 하나 생긴다고 보면 됩니다. 브릿지가 있으면 가상의 호스트가 연결되는데 브릿지가 라우터이니 거기에 연결되는 호스트들은 컨테이너입니다.

 

파란색 브릿지는 default 브릿지로 도커를 처음 설치하면 기본 브릿지가 하나 있고 사용자가 생성할 수도 있습니다.

 

-> 통신

빨간 브릿지는 하나의 서브넷이 형성되므로 빨간 브릿지 하부에 있는 컨테이너는 통신이 가능합니다. 하지만 파란 브릿지의 컨테이너와 통신은 못합니다.

 

 

=> network ls

도커를 처음 설치하면 3개가 존재합니다. none은 사용하지 않겠다는 의미이므로 2개가 무엇인지 실습을 통해 알아보겠습니다.

현재 호스트 OS의 네트워크 인터페이스를 확인해 보면 이러합니다.

 

 

 

 

1) host

docker run -d --net host --name host 이미지 명

이름을 host로 호스트 방식으로 컨테이너를 실행했습니다. --net을 지정하지 않으면 기본 브릿지로 컨테이너를 실행합니다.

 

컨테이너 내부에 exec로 들어간 후 네트워크 인터페이스를 확인해 보면 랜카드의 ip도 똑같습니다. host 방식으로 도커 컨테이너를 실행하면 라우트를 만들지 않고 리눅스 호스트 네트워크를 사용합니다. 하지만 여러 개의 컨테이너를 연결할 경우 문제가 발생하기에 일반적으로는 잘 사용하지 않습니다.

 

2) bridge

docker network create mybridge(만들 브릿지 이름)

 

새롭게 브릿지를 만들고 인터페이스를 확인해 보면 도커 호스트에 새로운 브릿지가 생겼습니다. bridge가 docker0입니다. 

 

docker exec -it --net mybridge 이미지 명

처음 개념 부분에 올린 이미지와 같이 두 개의 컨테이너는 기본 브릿지에 연결하고 남은 두 개의 컨테이너는 만든 mybridge에 연결하였습니다. 

 

 

같은 브릿지에 연결된 컨테이너에 들어가서 ip를 확인해 보면 컨테이너 내부에는 브릿지가 없으므로 docker0는 없어졌고 랜카드 번호는 172.0.0.2와 172.0.0.3으로 같은 서브넷에 존재합니다. 도커 내부에 DNS 서버가 있으므로 컨테이너에 같은 서브넷의 IP를 할당해 줍니다.

 

같은 브릿지의 핑은 핑이 됩니다. 하지만 다른 브릿지의 컨테이너끼리는 핑이 안됩니다. 앞에서 말했듯이 다른 브릿지는 통신이 되지 않습니다.

 

호스트 OS는 모든 브릿지와 통신이 됩니다. 따라서 host 방식으로 컨테이너를 만든다면 그 컨테이너는 호스트 OS의 네트워크 그 자체이므로 모든 브릿지와 통신이 가능합니다.

 

결론

오늘은 도커가 iptables로 별도의 네트워크를 구성하고 외부로부터 독립된 네트워크를 만드는 방법을 알아보았습니다. 비로소 별도의 디스크와 별도의 네트워크 그리고 namespace, cgroup, chroot로 가상 컴퓨터를 만드는 방법의 완전체가 모였습니다!

 

'[Infra] > [도커]' 카테고리의 다른 글

도커 PART.도커 기반 서비스 개발  (0) 2023.01.29
도커 PART.docker-compose  (0) 2023.01.29
도커 PART.도커 life cycle  (0) 2023.01.26
루트 파일 시스템 RART.환경변수  (0) 2022.05.10
루트 파일 시스템 PART.chroot  (0) 2022.04.07
Comments