Skip to content
Published on

IPsec 완전 정복 — VPN 터널의 동작 원리부터 구축까지

Authors
  • Name
    Twitter
IPsec Deep Dive

들어가며

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도 암호화!

  ┌──────────┬──────────┬─────────────────────────┐
  │ 새 IPESP 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)WireGuardOpenVPN
레이어L3L3L3/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개(인바운드 + 아웃바운드)가 필요||