티스토리 뷰

안녕하세요. Kubernetes Advanced Networking Study(=KANS) 3기 모임에서 스터디한 내용을 정리했습니다. 해당 글에서는 컨테이너 네트워크 보안에 대해 알아보겠습니다. 

 

컨테이너 네트워크 & IPTables

- 컨테이너네트워크 네임스페이스호스트네트워크 격리 환경을 구성됩니다.

- 리눅스에서 방화벽 기능을 제공하는 IPtables호스트컨테이너의 통신에 관여를 합니다.

Netfilter & iptables - Google Slides

 

Netfilter & iptables

Netfilter & iptables sam.0

docs.google.com

 

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 모드 기본 정보 확인

O'REILLY - Networking and Kubernetes 책
O'REILLY - Networking and Kubernetes 책

- 도커에서 기본적으로 쓸 수 있는 네트워크 확인, 컨테이너 기본 생성 시 자동으로 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 - 링크

https://youtu.be/SJFO2w5Q2HI

 

# 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)

 

 

컨테이너 보안

 

보안가이드

2019년, SK Infosec 클라우드 보안 가이드 (컨테이너 보안)

 

2019년, SK Infosec 클라우드 보안 가이드 (컨테이너 보안)

 

2024년, KISA 클라우드 취약점 점검 가이드 - Link

 

2024년, KISA 클라우드 취약점 점검 가이드 - Link

 

카타 컨테이너 (Kata Container)

- 컨테이너 처럼 느껴지게 작동하는 경량 VM으로 HW 를 통한 강력한 workload 격리로 안전한 컨테이너를 제공하는 기술

- 특징 : 보안(개별적인 전용 kernel), 분리된 (Network I/O Memory) 제공 → 하드웨어 강제격리 제공, VM처럼 성능의 소모 없이 분리된 환경을 제공

- 지원하는 하이퍼바이저 : qemu, cloud-hypervisor, firecracker, ACRN - 링크

 

카타 컨테이너 아키텍처 - 링크

https://katacontainers.io/learn/

 

https://github.com/kata-containers/kata-containers/blob/main/docs/design/architecture.md

 

카타 컨테이너 환경 vs 기존의 일반 컨테이너 환경

https://katacontainers.io/learn/

 

 

실습 환경 삭제

# 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

 

A Deep Dive into Iptables and Netfilter Architecture

netfilter와 iptables를 이해하기에 좋은 자료입니다.

netpple.github.io

https://docs.google.com/presentation/d/1tXS3N0196WmdaWYa0ZLVpIMt7uDQdBO6PGdq25z0gvs/edit#slide=id.p

 

Netfilter & iptables

Netfilter & iptables sam.0

docs.google.com

 

'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
«   2024/10   »
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
글 보관함