티스토리 뷰
안녕하세요. Kubernetes Advanced Networking Study(=KANS) 3기 모임에서 스터디한 내용을 정리했습니다. 해당 글에서는 컨테이너 네트워크 보안에 대해 알아보겠습니다.
컨테이너 네트워크 & IPTables
- 컨테이너는 네트워크 네임스페이스로 호스트와 네트워크 격리 환경을 구성됩니다.
- 리눅스에서 방화벽 기능을 제공하는 IPtables 는 호스트와 컨테이너의 통신에 관여를 합니다.
Netfilter & iptables - Google Slides
1. 도커 없이 네트워크 네임스페이스 환경에서 통신 구성
1.1 RED ↔ BLUE 네트워크 네임스페이스 간 통신
https://malwareanalysis.tistory.com/249
결론 : 네트워크 네임스페이스 생성 시 호스트 네트워크와 구별된다
# 터미널1~3 관리자
sudo su -
whoami
# veth (가상 이더넷 디바이스) 생성, man ip-link
ip link add veth0 type veth peer name veth1
# veth 생성 확인(상태 DOWN), ifconfig 에는 peer 정보 확인 안됨
# very pair 정보 확인 : ({iface}@if{pair#N})
ip -c link
ip -c addr # 축약 ip -c a
ifconfig -a
# 네트워크 네임스페이스 생성 , man ip-netns
ip netns add RED
ip netns add BLUE
# 네트워크 네임스페이스 확인
ip netns list
# veth0 을 RED 네트워크 네임스페이스로 옮김
ip link set veth0 netns RED
ip netns list
## 호스트의 ip a 목록에서 보이지 않음, veth1의 peer 정보가 변경됨
ip -c link
## RED 네임스페이스에서 ip a 확인됨(상태 DOWN), peer 정보 확인, link-netns RED, man ip-netns
ip netns exec RED ip -c a
# veth1 을 BLUE 네트워크 네임스페이스로 옮김
ip link set veth1 netns BLUE
ip -c link
ip netns exec BLUE ip -c a
# veth0, veth1 상태 활성화(state UP)
ip netns exec RED ip link set veth0 up
ip netns exec RED ip -c a
ip netns exec BLUE ip link set veth1 up
ip netns exec BLUE ip -c a
# veth0, veth1 에 IP 설정
ip netns exec RED ip addr add 11.11.11.2/24 dev veth0
ip netns exec RED ip -c a
ip netns exec BLUE ip addr add 11.11.11.3/24 dev veth1
ip netns exec BLUE ip -c a
# 터미널1 (RED 11.11.11.2)
## nsenter : 네임스페이스에 attach 하여 지정한 프로그램을 실행
tree /var/run/netns
nsenter --net=/var/run/netns/RED
ip -c a
## neighbour/arp tables management , man ip-neighbour
ip -c neigh
## 라우팅 정보, iptables 정보
ip -c route
iptables -t filter -S
iptables -t nat -S
# 터미널2 (호스트)
lsns -t net # nsenter 실행 후 TYPE(net) CMD(-bash) 생성 확인
ip -c a
ip -c neigh
ip -c route
iptables -t filter -S
iptables -t nat -S
# 터미널3 (BLUE 11.11.11.3)
nsenter --net=/var/run/netns/BLUE
ip -c a
ip -c neigh
ip -c route
iptables -t filter -S
iptables -t nat -S
# ping 통신 확인
# 터미널3 (BLUE 11.11.11.3)
tcpdump -i veth1
ip -c neigh
exit
# 터미널1 (RED 11.11.11.2)
ping 11.11.11.3 -c 1
ip -c neigh
exit
# 삭제
ip netns delete RED
ip netns delete BLUE
1.2 RED ← Bridge(br0) → BLUE 간 통신
결론 : arp table, route table, iptables 와 호스트의 bridge fdb 를 통하여 통신
기본 환경 설정 및 확인
# 네트워크 네임스페이스 및 veth 생성
ip netns add RED
ip link add reth0 type veth peer name reth1
ip link set reth0 netns RED
ip netns add BLUE
ip link add beth0 type veth peer name beth1
ip link set beth0 netns BLUE
# 확인
ip netns list
ip -c link
ip netns exec RED ip -c a
ip netns exec BLUE ip -c a
# 브리지 정보 확인
brctl show
# br0 브리지 생성
ip link add br0 type bridge
# br0 브리지 정보 확인
brctl show br0
brctl showmacs br0
brctl showstp br0
# reth1 beth1 을 br0 연결
ip link set reth1 master br0
ip link set beth1 master br0
brctl show br0
brctl showmacs br0
ip -br -c link
# reth0 beth0 에 IP 설정 및 활성화, br0 활성화
ip netns exec RED ip addr add 11.11.11.2/24 dev reth0
ip netns exec BLUE ip addr add 11.11.11.3/24 dev beth0
ip netns exec RED ip link set reth0 up; ip link set reth1 up
ip netns exec BLUE ip link set beth0 up; ip link set beth1 up
ip link set br0 up
ip -br -c addr
# 터미널1 (RED 11.11.11.2)
nsenter --net=/var/run/netns/RED
ip -c a;echo; ip -c route;echo; ip -c neigh
## 현재 네트워크 네임스페이스 정보 확인
ip netns identify $$
RED
# 터미널2 (호스트)
brctl showmacs br0
bridge fdb show
bridge fdb show dev br0
iptables -t filter -S
iptables -t filter -L -n -v
# 터미널3 (BLUE 11.11.11.3)
nsenter --net=/var/run/netns/BLUE
ip -c a;echo; ip -c route;echo; ip -c neigh
## 현재 네트워크 네임스페이스 정보 확인
ip netns identify $$
BLUE
==============================================================
# 터미널2 (호스트)
# ping 통신 전 사전 설정
## iptables 정보 확인
iptables -t filter -S | grep '\-P'
-P INPUT ACCEPT
-P FORWARD DROP
-P OUTPUT ACCEPT
iptables -nvL -t filter
## Ubuntu 호스트에서 패킷 라우팅 설정 확인 : 커널의 IP Forwarding (routing) 기능 확인 - 0(off), 1(on)
## echo 1 > /proc/sys/net/ipv4/ip_forward
cat /proc/sys/net/ipv4/ip_forward
1
==============================================================
# ping 통신 테스트
# 터미널1 (RED 11.11.11.2) >> ping 왜 실패했을까요?
ping -c 1 11.11.11.3
# 터미널2 (호스트)
tcpdump -l -i br0
watch -d 'iptables -v --numeric --table filter --list FORWARD'
watch -d 'iptables -v --numeric --table filter --list FORWARD;echo;iptables -v --numeric --table filter --list DOCKER-USER;echo;iptables -v --numeric --table filter --list DOCKER-ISOLATION-STAGE-1'
# 터미널3 (BLUE 11.11.11.3)
tcpdump -l -i beth0
RED → BLUE ping failed 분석
RED → BLUE ping 허용 설정 - 링크
1.3 RED/BLUE → 호스트 & 외부(인터넷) 통신
결론 : 호스트에 RED/BLUE와 통신 가능한 IP 설정 및 라우팅 추가, iptables NAT 를 통하여 통신
RED 와 BLUE 설정
ip netns add RED
ip link add reth0 netns RED type veth peer name reth1
ip netns add BLUE
ip link add beth0 netns BLUE type veth peer name beth1
ip link add br0 type bridge
ip link set reth1 master br0
ip link set beth1 master br0
ip netns exec RED ip addr add 11.11.11.2/24 dev reth0
ip netns exec BLUE ip addr add 11.11.11.3/24 dev beth0
ip netns exec RED ip link set reth0 up; ip link set reth1 up;
ip netns exec BLUE ip link set beth0 up; ip link set beth1 up;
ip link set br0 up
# iptables -t filter -A FORWARD -s 11.11.11.0/24 -j ACCEPT
iptables -t filter -I DOCKER-USER -j ACCEPT
호스트에서 RED 나 BLUE 로 ping 통신 → RED 에서 외부로 통신
# 터미널1 (RED 11.11.11.2)
nsenter --net=/var/run/netns/RED
tcpdump -i any
# 터미널3 (호스트)
exit
tcpdump -i br0 -n
# 터미널2 (호스트) >> 호스트에서 RED 로 통신이 안되는 이유가 무엇일까요?
ping -c 1 11.11.11.2
ip -c route
ip -c addr
==============================================================
# 터미널2 (호스트) >> br0 에 IP 추가(라우팅 정보)
ip addr add 11.11.11.1/24 dev br0
ping -c 1 11.11.11.2
ping -c 1 11.11.11.3
# 터미널1 (RED 11.11.11.2) >> 192.168.50.10 와 통신이 안되는 이유는 무엇일까요?
ping -c 1 11.11.11.1
ping -c 1 192.168.50.10
ip -c route
ip -c addr
==============================================================
# 터미널3 (호스트)
tcpdump -i any icmp -n
# 터미널1 (RED 11.11.11.2)
ip route add default via 11.11.11.1
ping -c 1 192.168.50.10
ip -c route
ping -c 1 8.8.8.8 >> 외부 대역(인터넷)과 통신이 안되는 이유가 무엇일까요?
# 터미널2 (호스트)
iptables -S -t nat
iptables -nvL -t nat
## POSTROUTING : 라우팅 Outbound or 포워딩 트래픽에 의해 트리거되는 netfilter hook
## POSTROUTING 에서는 SNAT(Source NAT) 설정
iptables -t nat -A POSTROUTING -s 11.11.11.0/24 -j MASQUERADE
watch -d 'iptables -v --numeric --table nat --list POSTROUTING'
iptables -nvL -t nat
conntrack -L --src-nat
# 터미널1 (RED 11.11.11.2)
ping -c 1 8.8.8.8
exit
# 터미널3 (BLUE 11.11.11.3)
nsenter --net=/var/run/netns/BLUE
ip route add default via 11.11.11.1
ping -c 1 8.8.8.8
exit
# 삭제
ip netns delete RED
ip netns delete BLUE
ip link delete br0
iptables -t filter -D DOCKER-USER -j ACCEPT
iptables -t nat -D POSTROUTING -s 11.11.11.0/24 -j MASQUERADE
2. 도커 네트워크 모델
2.1 도커 네트워크 모드
기본 네트워크 모드 : Bridge, Host, None + 추가 네트워크 플러그인 : macvlan, ipvlan, overlay
docker network ls
NETWORK ID NAME DRIVER SCOPE
5d4ec64ed746 bridge bridge local
71da3212e9dc host host local
90a304de3e67 none null local
docker info | grep Network
Network: bridge host ipvlan macvlan null overlay
- Host 는 호스트의 환경을 그대로 사용. 애플리케이션 별도 포트 포워딩 없이 바로 서비스 가능. 컨테이너의 호스트 이름도 호스트 머신의 이름과 동일.
- None 는 말 그래도 아무런 네트워크를 쓰지 않는 것. 외부와의 연결이 단절됨. 컨테이너 내부에 lo 인터페이스만 존재.
2.2 Bridge 모드
Bridge 모드 기본 정보 확인
- 도커에서 기본적으로 쓸 수 있는 네트워크 확인, 컨테이너 기본 생성 시 자동으로 docker0 브리지를 사용
- 기본 172.17.0.0/16 대역을 컨테이너가 사용, 대역 변경 설정 가능
- 도커는 IPtables 의 PREROUTING POSTROUTING 의 NAT Chains 를 변경한다
- 컨테이너 → 외부 : POSTROUTING 의 SNAT 처리
- 외부 → 컨테이너(Exposed services ports) : PREROUTING 에서 DNAT 처리
# 도커 네트워크 모드 확인
docker network ls
NETWORK ID NAME DRIVER SCOPE
a22960517f11 bridge bridge local
def31be9614b host host local
f0bf6fd89f14 none null local
# 도커 네트워크(플러그인) 정보 확인
docker info
docker info | grep Network
# 도커 bridge 상세 정보 확인 = docker inspect --type network bridge 동일
# 아래 "Gateway": "172.17.0.1" 정보가 출력되지 않을 경우에는 systemctl restart docker 입력 후 다시 확인
docker network inspect bridge | jq
[
{
"Name": "bridge",
"Id": "7c0902c8e4521b25b6a1233b018886256c8f877fed6a088a1a97e54804135613",
"Created": "2021-10-14T12:22:52.869292205Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}
]
# 브릿지 확인
brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.024229821c4f no
# 네트워크 인터페이스 확인
ip -c addr show docker0
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:29:82:1c:4f brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
# SNAT 정책 확인
iptables -t nat -S
iptables -t nat -S | grep MASQUERADE
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
# 라우팅 확인
ip -c route | grep docker0
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
컨테이너(busybox) 2대 생성
# 터미널1 (PINK) : PINK 이름의 busybox 컨테이너 생성
docker run -it --name=PINK --rm busybox
ip a
ip neigh
# 터미널3 (ORANGE) : ORANGE 이름의 busybox 컨테이너 생성
docker run -it --name=ORANGE --rm busybox
ip a
ip neigh
# 터미널2 (호스트)
## 컨테이너 생성 확인
docker ps
## veth 에 각각 서로 연결되는 veth peer 가 추가됬음을 확인, docker UP 확인
ip -br -c link
## 브릿지 정보 확인
brctl show docker0
컨테이너간 통신 확인
# 터미널2 (호스트)
tcpdump -i docker0 -n
iptables -t filter -S
-A FORWARD -i docker0 -o docker0 -j ACCEPT # 컨테이너 끼리는 FORWARD 가 ACCEPT(허용)
# 터미널1 (PINK)
## ORANGE 로 ping 테스트(IP는 다를 수 있습니다)
ping -c 1 172.17.0.3
ip neigh
route -n
# 터미널3 (ORNAGE)
## ORANGE 로 ping 테스트(IP는 다를 수 있습니다)
ping -c 1 172.17.0.2
ip neigh
route -n
컨테이너에서 외부로 통신
# 터미널2 (호스트)
tcpdump -i any icmp
iptables -t nat -S
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
conntrack -L --src-nat
icmp 1 29 src=172.17.0.5 dst=8.8.8.8 type=8 code=0 id=7 src=8.8.8.8 dst=10.0.2.15 type=0 code=0 id=7 mark=0 use=1
conntrack v1.4.5 (conntrack-tools): 1 flow entries have been shown.
# 터미널1 (PINK)
ping 8.8.8.8
exit # 실습 완료 후
# 터미널3 (ORNAGE)
ping 8.8.8.8
exit # 실습 완료 후
도커 컨테이너의 NET 네임스페이스 정보 확인 - 링크
2.3 Host 모드
호스트의 환경을 그대로 사용 가능. 호스트 드라이버의 네트워크는 별도로 생성할 필요 없이 기존의 host 라는 이름의 네트워크를 사용 - 링크 링크2
- 컨테이너의 호스트 이름도 호스트 머신의 이름과 동일. 네트워크도 동일. 애플리케이션 별도 포트 포워딩 없이 바로 서비스 할 수 있음.
# 컨테이너 실행
docker run --rm -d --network host --name my_nginx nginx
# HostConfig.NetworkMode "host" , Config.ExposedPorts "80/tcp" , NetworkSettings.Networks "host" 확인
docker inspect my_nginx
# curl 접속 확인
curl -s localhost | grep -o '<title>.*</title>'
# 호스트에서 tcp 80 listen 확인
ss -tnlp
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=3694,fd=7),("nginx",pid=3693,fd=7),("nginx",pid=3649,fd=7))
# 추가 실행 시도
docker run -d --network host --name my_nginx_2 nginx
# 확인
docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
369c40cf6dfd nginx "/docker-entrypoint.…" 7 seconds ago Exited (1) 3 seconds ago my_nginx_2
...
# 삭제
docker container stop my_nginx
2.4 컨테이너 외부에 노출
설정 및 확인 : bridge mode - 링크
# nginx:alpine 웹 컨테이너 3대 실행
# -p 옵션은 컨테이너 포트를 호스트 포트와 바인딩 연결 [호스트의 포트]:[컨테이너의 포트]
## -p 80 만 사용 시 호스트 포트 중 하나(랜덤)과 컨테이너의 80포트와 연결
## -p 여러개 사용하여 여러개 포트 개방
docker run -d --name=web1 -p 10001:80 --rm nginx:alpine
docker run -d --name=web2 -p 10002:80 --rm nginx:alpine
docker run -d --name=web3 -p 10003:80 --rm nginx:alpine
# 컨테이너 확인
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5747f508fe0b nginx:alpine "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:10003->80/tcp, :::10003->80/tcp web3
222390d43aa5 nginx:alpine "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:10002->80/tcp, :::10002->80/tcp web2
b10d8e163c77 nginx:alpine "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:10001->80/tcp, :::10001->80/tcp web1
# iptables 정보 확인
iptables -t nat -S | grep :80
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 10001 -j DNAT --to-destination 172.17.0.2:80
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 10002 -j DNAT --to-destination 172.17.0.3:80
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 10003 -j DNAT --to-destination 172.17.0.4:80
iptables -t filter -S
-A FORWARD -o docker0 -j DOCKER
-A DOCKER -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 80 -j ACCEPT
-A DOCKER -d 172.17.0.3/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 80 -j ACCEPT
-A DOCKER -d 172.17.0.4/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 80 -j ACCEPT
...
# 외부(자신의 PC)에서 접속(curl 혹은 웹브라우저)
curl -s 192.168.50.10:10001 | grep -o '<title>.*</title>'
curl -s 192.168.50.10:10002 | grep -o '<title>.*</title>'
curl -s 192.168.50.10:10003 | grep -o '<title>.*</title>'
# 연결 정보 확인
conntrack -L
iptables -t nat -L -n -v
# 삭제
docker stop $(docker ps -a -q)
컨테이너 보안
보안가이드
카타 컨테이너 (Kata Container)
- 컨테이너 처럼 느껴지게 작동하는 경량 VM으로 HW 를 통한 강력한 workload 격리로 안전한 컨테이너를 제공하는 기술
- 특징 : 보안(개별적인 전용 kernel), 분리된 (Network I/O Memory) 제공 → 하드웨어 강제격리 제공, VM처럼 성능의 소모 없이 분리된 환경을 제공
- 지원하는 하이퍼바이저 : qemu, cloud-hypervisor, firecracker, ACRN - 링크
카타 컨테이너 아키텍처 - 링크
카타 컨테이너 환경 vs 기존의 일반 컨테이너 환경
실습 환경 삭제
# CloudFormation 스택 삭제
aws cloudformation delete-stack --stack-name mylab
# [모니터링] CloudFormation 스택 상태 : 삭제 확인
while true; do
date
AWS_PAGER="" aws cloudformation list-stacks \
--stack-status-filter CREATE_IN_PROGRESS CREATE_COMPLETE CREATE_FAILED DELETE_IN_PROGRESS DELETE_FAILED \
--query "StackSummaries[*].{StackName:StackName, StackStatus:StackStatus}" \
--output table
sleep 1
done
[참고 링크]
[Sam.0] Netfilter and iptables - Link , A Deep Dive into Iptables and Netfilter Architecture - Link
https://docs.google.com/presentation/d/1tXS3N0196WmdaWYa0ZLVpIMt7uDQdBO6PGdq25z0gvs/edit#slide=id.p
'IT > Container&k8s' 카테고리의 다른 글
KIND(kubernetes in docker) (3) | 2024.09.01 |
---|---|
K8S Deep Dive (1) | 2024.09.01 |
컨테이너 격리 (0) | 2024.09.01 |
컨테이너 (1) | 2024.08.31 |
[k8s] Service Mesh (0) | 2023.10.29 |
- Total
- Today
- Yesterday
- AI
- k8s
- 파이썬
- IaC
- 혼공파
- VPN
- gcp serverless
- cni
- NW
- 혼공단
- k8s cni
- controltower
- GKE
- k8s calico
- operator
- AWS
- EKS
- S3
- OS
- 혼공챌린지
- SDWAN
- NFT
- 도서
- security
- cloud
- 국제 개발 협력
- GCP
- terraform
- handson
- PYTHON
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |