- Published on
IPsec 완전 정복 — VPN 터널의 동작 원리부터 구축까지
- Authors
- Name
- 들어가며
- IPsec이란?
- 핵심 개념
- ESP (Encapsulating Security Payload) — 핵심
- IKE (Internet Key Exchange) — 키 협상
- 실무: AWS Site-to-Site VPN
- IPsec vs WireGuard vs OpenVPN

들어가며
VPN을 켜면 인터넷이 느려지는 건 알지만, 왜 느려지는지 아시나요? 그 안에서 IPsec이라는 프로토콜이 모든 패킷을 암호화하고, 인증하고, 터널링하고 있기 때문입니다.
IPsec은 네트워크 엔지니어뿐 아니라, 클라우드 아키텍트(VPC 피어링, Site-to-Site VPN), DevOps 엔지니어(WireGuard vs IPsec), 보안 엔지니어 모두에게 필수 지식입니다.
IPsec이란?
IPsec (IP Security) = IP 계층(L3)에서 동작하는 보안 프로토콜 스위트
구성요소:
├── AH (Authentication Header): 인증만 (무결성 + 출처 확인)
├── ESP (Encapsulating Security Payload): 암호화 + 인증 (실무에서 99%)
├── IKE (Internet Key Exchange): 키 교환 + SA 협상
└── SA (Security Association): 보안 연결 정보 저장소
핵심 개념
SA (Security Association) — 보안 연결의 계약서
SA = 두 장비 간의 "보안 합의서"
포함 정보:
├── SPI (Security Parameter Index): SA 식별자 (32bit)
├── 암호화 알고리즘: AES-256-GCM
├── 인증 알고리즘: SHA-256 HMAC
├── 키 값: 공유 비밀 키
├── 수명: 3600초 또는 100GB
└── 모드: 터널 / 전송
SA는 단방향 → 양방향 통신에는 SA 2개 필요!
터널 모드 vs 전송 모드
[전송 모드 (Transport Mode)]
호스트 ↔ 호스트 직접 통신
원본 IP 헤더 유지, 페이로드만 암호화
┌──────────┬──────────┬────────────────┐
│ IP Header│ESP Header│ 암호화된 Payload│
│ (원본) │ │ │
└──────────┴──────────┴────────────────┘
[터널 모드 (Tunnel Mode)] ← Site-to-Site VPN
게이트웨이 ↔ 게이트웨이 (원본 패킷 전체를 감싸기)
새 IP 헤더 추가, 원본 IP도 암호화!
┌──────────┬──────────┬─────────────────────────┐
│ 새 IP │ESP Header│ 암호화된 [원본IP + Data] │
│ Header │ │ │
└──────────┴──────────┴─────────────────────────┘
실무에서 터널 모드가 중요한 이유: 내부 네트워크 IP 주소가 외부에 노출되지 않음!
ESP (Encapsulating Security Payload) — 핵심
ESP 패킷 구조:
┌─────────────────────────────────────────────┐
│ IP Header (새로운, 터널모드) │
├─────────────────────────────────────────────┤
│ ESP Header │
│ ├── SPI (32bit): 어떤 SA를 쓸지 │
│ └── Sequence Number (32bit): 재전송 방지 │
├─────────────────────────────────────────────┤
│ ★ 암호화 영역 (Encrypted) │
│ ├── 원본 IP Header (터널모드) │
│ ├── 원본 Payload (TCP/UDP + Data) │
│ └── ESP Trailer (Padding + Next Header) │
├─────────────────────────────────────────────┤
│ ESP Auth (ICV): HMAC-SHA256 (무결성 검증) │
└─────────────────────────────────────────────┘
# ESP 암호화 과정 (의사코드)
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
def esp_encrypt(original_packet, sa):
"""ESP 암호화 (AES-256-GCM)"""
# 1. SA에서 키와 알고리즘 가져오기
key = sa.encryption_key # 256-bit
spi = sa.spi
seq = sa.next_sequence_number()
# 2. ESP Header 생성
esp_header = spi.to_bytes(4, 'big') + seq.to_bytes(4, 'big')
# 3. Padding 추가 (블록 크기 맞추기)
pad_len = (16 - (len(original_packet) + 2) % 16) % 16
padding = bytes(range(1, pad_len + 1))
esp_trailer = padding + bytes([pad_len, 4]) # 4 = IP-in-IP
# 4. 암호화 (AES-256-GCM)
nonce = os.urandom(12)
aesgcm = AESGCM(key)
plaintext = original_packet + esp_trailer
ciphertext = aesgcm.encrypt(nonce, plaintext, esp_header) # AAD = ESP Header
# 5. 새 IP 헤더 + ESP Header + 암호화된 데이터
new_ip = create_ip_header(sa.tunnel_src, sa.tunnel_dst)
return new_ip + esp_header + nonce + ciphertext
IKE (Internet Key Exchange) — 키 협상
IPsec에서 가장 복잡한 부분! 두 단계로 나뉩니다.
Phase 1 (IKE SA) — "서로 신뢰할 수 있나?"
목적: 안전한 채널 확립 (이후 Phase 2를 보호)
[Initiator] [Responder]
│ │
│──── SA Proposal ────────────▶│ 암호화/해시/DH 그룹 제안
│◀─── SA Accepted ────────────│ 합의
│ │
│──── DH Public Value ────────▶│ Diffie-Hellman 키 교환
│◀─── DH Public Value ────────│ 양쪽이 같은 비밀 키 도출!
│ │
│──── Auth (PSK/Cert) ────────▶│ 사전 공유 키 또는 인증서
│◀─── Auth ────────────────────│ 상호 인증
│ │
╔══════════════════════════════╗
║ IKE SA 수립 (암호화 채널) ║
╚══════════════════════════════╝
Phase 2 (IPsec SA) — "어떤 트래픽을 어떻게 보호할지"
IKE SA로 보호된 채널 안에서:
[Initiator] [Responder]
│ │
│──── IPsec SA Proposal ──────▶│ ESP/AH, 암호화 알고리즘
│◀─── IPsec SA Accepted ──────│ 합의
│ │
│──── Traffic Selector ────────▶│ 어떤 IP/Port 범위를 보호?
│◀─── Traffic Selector ────────│ 합의
│ │
╔══════════════════════════════╗
║ IPsec SA 수립 (x2, 양방향) ║
║ → 실제 데이터 암호화 시작! ║
╚══════════════════════════════╝
Diffie-Hellman 키 교환 (마법의 핵심)
# DH 키 교환 — 도청자가 봐도 비밀 키를 알 수 없음!
p = 23 # 큰 소수 (실제: 2048/4096 bit)
g = 5 # 생성자
# Alice
a = 6 # Alice의 비밀 값
A = pow(g, a, p) # 5^6 mod 23 = 8 → 공개 전송
# Bob
b = 15 # Bob의 비밀 값
B = pow(g, b, p) # 5^15 mod 23 = 19 → 공개 전송
# 공유 비밀 키 도출 (같은 값!)
alice_secret = pow(B, a, p) # 19^6 mod 23 = 2
bob_secret = pow(A, b, p) # 8^15 mod 23 = 2
assert alice_secret == bob_secret # ✅ 같은 비밀 키!
# 도청자: p=23, g=5, A=8, B=19를 알아도
# a나 b를 모르면 비밀 키 2를 구할 수 없음 (이산 로그 문제)
실무: AWS Site-to-Site VPN
온프레미스 (192.168.1.0/24)
│
├── Customer Gateway (IPsec)
│ │
│ │ ══ IPsec 터널 1 (Active) ══
│ │ ══ IPsec 터널 2 (Standby) ══
│ │
├── Virtual Private Gateway
│
AWS VPC (10.0.0.0/16)
# Linux에서 IPsec VPN 설정 (strongSwan)
sudo apt install strongswan
# /etc/ipsec.conf
cat << 'EOF'
conn aws-vpn
type=tunnel
auto=start
left=203.0.113.1 # 로컬 공인 IP
leftsubnet=192.168.1.0/24 # 로컬 네트워크
right=52.10.20.30 # AWS VGW IP
rightsubnet=10.0.0.0/16 # AWS VPC CIDR
ike=aes256-sha256-modp2048
esp=aes256-sha256
keyexchange=ikev2
authby=secret
EOF
# /etc/ipsec.secrets
echo '203.0.113.1 52.10.20.30 : PSK "MyPreSharedKey123"' | sudo tee /etc/ipsec.secrets
# 시작
sudo ipsec restart
sudo ipsec status
IPsec vs WireGuard vs OpenVPN
| 항목 | IPsec (IKEv2) | WireGuard | OpenVPN |
|---|---|---|---|
| 레이어 | L3 | L3 | L3/L4 |
| 코드량 | ~400K lines | ~4K lines | ~100K lines |
| 속도 | 빠름 | 가장 빠름 | 보통 |
| 설정 | 복잡 | 간단 | 보통 |
| 모바일 | 우수 (IKEv2) | 우수 | 보통 |
| 엔터프라이즈 | 표준 | 신흥 | 널리 사용 |
📝 퀴즈 — IPsec (클릭해서 확인!)
Q1. ESP와 AH의 차이는? ||ESP: 암호화 + 인증(무결성). AH: 인증만 (암호화 없음). 실무에서는 ESP가 99% 사용됨||
Q2. 터널 모드와 전송 모드의 차이는? ||터널 모드: 원본 IP 패킷 전체를 새 IP로 감싸서 암호화 (게이트웨이 간 VPN). 전송 모드: 원본 IP 헤더 유지, 페이로드만 암호화 (호스트 간 직접)||
Q3. IKE Phase 1과 Phase 2의 역할은? ||Phase 1: IKE SA 수립 — DH 키 교환 + 상호 인증으로 안전한 채널 확보. Phase 2: IPsec SA 수립 — Phase 1 채널 안에서 실제 트래픽 보호 정책 합의||
Q4. SPI(Security Parameter Index)란? ||32비트 식별자로, 수신 측이 어떤 SA(보안 합의)를 적용할지 결정하는 데 사용. ESP 헤더에 포함됨||
Q5. DH 키 교환이 안전한 이유는? ||이산 로그 문제 — 공개 값(g, p, A, B)을 알아도 비밀 지수(a, b)를 구하는 것이 계산적으로 불가능. 도청자가 모든 공개 통신을 봐도 공유 비밀 키를 도출할 수 없음||
Q6. IPsec에서 SA가 단방향인 이유와 그 결과는? ||각 SA는 한 방향의 트래픽만 보호 (보안 파라미터가 방향별로 다를 수 있음). 따라서 양방향 통신에는 SA 2개(인바운드 + 아웃바운드)가 필요||