Docker 컨테이너 통신 및 웹 서버 pcap 분석
실습 1 구현 (도커 컨테이너 2개 통신)
파일 작성

디렉토리 구조

web1/Dockerfile

web2/Dockerfile

docker-compose.yml
docker compose up -d --build

- Docker Compose로 이미지를 새로 빌드하고 컨테이너를 백그라운드에서(-d) 실행한다.
- docker-compose.yml 안에 web1, web2 두 개의 서비스가 정의되어 있고 각각 bridge-lab-web1, bridge-lab-web2의 이미지 이름으로 빌드되었다.
- 또한 mynet이라는 네트워크와 myvol이라는 볼륨이 만들어졌고, web1, web2 두 컨테이너가 정상적으로 실행되었다.
docker exec -it web1 /bin/bash
ping web2

- 컨테이너 간 네트워크가 잘 연결되었는지 확인하기 위해 web1 컨테이너 내부로 들어가서 bash쉘을 실행하고 web2 컨테이너로 ping을 날렸다.
- “web2”는 Docker compose가 만든 가상 네트워크 mynet 안에서 DNS로 자동 인식되므로, web1은 web2의 이름만으로 IP를 찾아갈 수 있으며 ping이 성공한다.
- 상단 캡쳐 결과를 보면 web1에서 web2로 보낸 27개의 ping요청에 대해 loss 없이 모두 정상적으로 응답을 받았음을 확인할 수 있다.
추가 내용
docker network ls
빌드 전

**빌드 후 **(docker compose up -d --build)

- 빌드 후 mynet 네트워크가 새로 생겼음을 확인할 수 있다.
docker network inspect mynet

- mynet 네트워크의 세부 정보를 확인할 수 있다. 네트워크 기본 정보, 컨테이너 정보 등을 확인할 수 있다.
실습 2 구현 (프론트/백엔드)
파일 작성

디렉토리 구조

docker-compose.yml

backend/main.py

frontend/nginx.conf

docker-compose.yml
docker compose up --build

- 실습 1과 같은 방식으로 도커 이미지를 빌드했다. 다만 -d 옵션을 주지 않았으므로 foreground 모드로 실행되어 로그가 터미널에 출력됨을 확인할 수 있다.
- 빌드 과정을 거쳐 마지막에 새 이미지가 docker-network-backend라는 이름으로 만들어졌음을 확인할 수 있다.
- backend 이미지가 새로 빌드되며 mynet 네트워크가 생성되고, backend와 frontend 두 컨테이너가 생성되었다. 여기서 mynet은 frontend와 backend를 연결하는 역할을 한다.
- frontend와 backend의 컨테이너 로그도 보인다. 특히 backend 로그를 보면, FastAPI 서버가 Uvicorn으로 실행되어 http://0.0.0.0:8000에서 대기중임을 확인할 수 있다.
http://localhost:8080 접속

- http://localhost:8080 에 접속하면, “Hello from frontend” 문구가 나온다.
- docker-compose.yml파일에
ports: - "8080:80"으로 포트를 매핑해두었다. 즉 내 PC(host)의 8080포트를 컨테이너의 80번 포트로 매핑해준다는 뜻이다. - localhost:8080으로 접속하면 브라우저가 내 PC의 8080포트로 요청을 보낸다. Docker가 이를 frontend 컨테이너 80번 포트와 연결해주고, 컨테이너 안 nginx가 80번 포트에서 요청 대기중이었으므로 이 응답을 받아 frontend가 뜬다.
http://localhost:8080/backend/ping 접속

- nginx.conf 파일 내부를 보자.
- backend 컨테이너의 FastAPI 서버인 main.py에 /ping으로 GET 요청이 오면 JSON응답을 하도록 정의해뒀다. nginx는 backend 컨테이너에서 응답을 받아 브라우저에 전달한다.
로그 정리

- 브라우저 → Nginx: / 요청 → ”Hello from frontend”
- 브라우저 → Nginx: /favicon.ico 자동 요청 → ”Hello from frontend” (자동 요청, 별도 처리 규칙 없으므로 location / 블록에 매칭됨)
- 브라우저 → Nginx: /backend/ping 요청
기타

- backend에는 /backend/ping만 정의해두었다. /backend/asdf같이 정의되지 않은 것이 입력되면 frontend는 backend에 전달하긴 하지만, backend 컨테이너는 404 not found를 반환한다.
실습 3 구현 (web server pcap)
같은 네트워크 안에서 돌아가는 다른 컨테이너의 트래픽을 패킷 캡쳐 도구로 보기
파일 작성

디렉토리 구조

sniffer/Dockerfile
- 네트워크 트래픽을 캡쳐하고 분석하기 위한 컨테이너

web/Dockerfile
- FastAPI 서버를 실행하는 컨테이너(컨테이너 내부에서 80포트로 실행)

docker-compose.yml
- ports: 호스트의 8080 포트를 컨테이너의 80 포트에 연결
- cap_add로 sniffer 컨테이너에 트래픽 캡쳐 관련 권한을 허용해줌
- 사용자 정의 bridge network testnet 생성

web/main.py (후략)
- FastAPI로 만든 간단한 게시판 API 서버로, 트래픽을 발생시키는 여러 기능이 들어가있다.
실행
docker compose up --build -d


Packet sniffing
docker exec -it sniffer /bin/bash


- sniffer 컨테이너 쉘로 들어가서 tcpdump를 이용해 패킷을 캡쳐하고, Wireshark로 분석하기 위해 pcap파일로 저장했다.
1. 서버 상태 확인
docker exec -it sniffer curl http://web/



- 클라이언트(sniffer 컨테이너)=172.20.0.2, 서버(web 컨테이너)=172.20.0.3, 서버 포트 80
- TCP 3-way handshake
- 데이터 교환
- 연결 종료
2. 회원가입
docker exec -it sniffer curl -X POST "http://web/register" \
-H "Content-Type: application/json" \
-d '{"username": "202302627", "password": "test123"}'



- 클라이언트(sniffer 컨테이너)=172.20.0.2, 서버(web 컨테이너)=172.20.0.3, 서버 포트=80
- TCP 3-way handshake
- 데이터 교환
- 연결 종료
3. 로그인
docker exec -it sniffer curl -X POST "http://web/login" \
-H "Content-Type: application/json" \
-d '{"username": "202302627", "password": "test123"}'



- 클라이언트(sniffer 컨테이너)=172.20.0.2, 서버(web 컨테이너)=172.20.0.3, 서버 포트=80
- TCP 3-way handshake
- 데이터 교환
- 연결 종료
4. 게시글 작성
docker exec -it sniffer curl -X POST "http://web/posts" \
-H "Content-Type: application/json" \
-d '{"title": "테스트 게시글", "content": "curl 테스트입니다", "author": "202302627"}'


- 클라이언트(sniffer 컨테이너)=172.20.0.2, 서버(web 컨테이너)=172.20.0.3, 서버 포트=80
- TCP 3-way handshake
- 데이터 교환
- 연결 종료
5. 게시글 목록 확인
docker exec -it sniffer curl http://web/posts


- 클라이언트(sniffer 컨테이너)=172.20.0.2, 서버(web 컨테이너)=172.20.0.3, 서버 포트=80
- TCP 3-way handshake
- 데이터 교환
- 연결 종료
6. 로그아웃
docker exec -it sniffer curl -X POST "http://web/logout/202302627"


- 클라이언트(sniffer 컨테이너)=172.20.0.2, 서버(web 컨테이너)=172.20.0.3, 서버 포트=80
- TCP 3-way handshake
- 데이터 교환
- 연결 종료
결론
- sniffer 컨테이너 안에서 curl을 실행해서 컨테이너 내부에서 컨테이너 간 통신을 발생시켰다.
- Docker bridge 네트워크 환경에서 컨테이너 이름(web)이 DNS를 통해 내부 IP(172.20.0.3)로 해석됨을 확인하였다. 별도의 포트포워딩 없이, 같은 네트워크에 있는 sniffer 컨테이너가 web 컨테이너의 내부 포트 80으로 직접 접근할 수 있었다.
- 각 curl 명령은 별도의 프로세스이므로, Wireshark 분석 화면의 Info부분에서 매번 새 TCP 연결(포트 80과 연결되는 새 소스포트)이 만들어짐을 확인할 수 있었다.
- 또한 Wireshark 분석을 통해 요청 데이터 크기만큼 TCP Sequence 번호가 증가하고, 서버 응답의 Acknowledgment 번호가 그 증가된 만큼을 반영하는 것을 확인했다. 연결을 종료하며 FIN이 있는 부분은 약간 다르길래** **Next Sequence Number를 계산하는 규칙을 정리해봤다.
- 모든 요청과 응답은 HTTP/1.1 기반으로, 헤더와 바디가 평문(plaintext) 상태로 전송되었다. 로그인/회원가입 요청 시 아이디와 비밀번호가 그대로 노출되었으며, 이는 보안상 취약점이 될 수 있음을 확인하였다. 실제 서비스라면 HTTPS(SSL/TLS)를 사용해야 할 것이다.
- TCP에서 모든 요청마다 3-way handshake → 데이터 교환 → 연결 종료가 정상적으로 수행됨을 확인할 수 있었다. 여기서 연결 종료 과정을 TCP 4-way handshake라고 부른다고 한다.
기타

- 1~6 과정을 전부 수행하였으며, 중간에 로그인 과정을 건너뛰고 게시글 작성을 하려 했더니 401 Unauthorized가 뜸을 확인할 수 있다.

- 이미 가입한 정보로 다시 회원가입을 하려고 하면 400 Bad Request가 뜬다.
Leave a comment