네트워크 공격 방어

리눅스 커널 관점에서 DoS/DDoS를 방어하는 핵심은 대역폭보다 먼저 상태(state) 예산CPU 예산을 보호하는 것입니다. 이 문서는 SYN Flood, UDP/ICMP 증폭, conntrack 고갈, Slowloris 계열, IP 스푸핑과 L2 위변조를 커널 수신 경로에 맞춰 정리하고, XDP, nftables/SYNPROXY, TCP backlog, conntrack, 관측 지표와 사고 대응 절차를 실무 관점에서 묶어 설명합니다. VPN 암호화는 IPSec & xfrm, WireGuard 문서를 따로 보세요.

전제 조건: 네트워크 스택, TCP 프로토콜 심화, Netfilter, BPF/XDP 문서를 먼저 읽으세요. 공격 방어 문서는 규칙만 외우면 오래 못 갑니다. 패킷이 어떤 큐를 지나고 어디서 상태를 만들며 어느 지점에서 드롭되는지 먼저 머리에 그려야 합니다.
일상 비유: 이 개념은 공항 보안 검색대와 비슷합니다. 출입문 앞에서 바로 막을 수 있는 사람은 문 앞에서 막고, 신원 확인이 꼭 필요한 사람만 안쪽 검사대로 보내야 전체 시설이 마비되지 않습니다. 네트워크에서도 먼저 드롭할수록 CPU와 메모리를 덜 씁니다.

핵심 요약

  • PPS — 초당 패킷 수입니다. 대역폭이 낮아도 PPS가 높으면 CPU가 먼저 무너질 수 있습니다.
  • backlog — 패킷 또는 연결이 잠시 대기하는 큐입니다. RX backlog, SYN backlog, accept queue를 구분해야 합니다.
  • conntrack — 상태 기반 방화벽/NAT를 위한 흐름 테이블입니다. 공격 시 가장 흔한 전역 병목입니다.
  • syncookie — SYN backlog overflow 시 서버가 반개방 연결 상태를 저장하지 않고 ACK를 검증하는 비상 모드입니다.
  • synproxy — 방화벽이 백엔드 대신 TCP 3-way handshake를 먼저 완료해 주는 보호막입니다.

단계별 이해

  1. 공격 분류
    대역폭형인지, 상태 고갈형인지, 애플리케이션 느린 요청형인지 먼저 구분합니다.
  2. 병목 지점 확인
    NIC RX 링, NAPI poll, softnet backlog, conntrack, listener queue, worker 중 어디가 먼저 차는지 계측합니다.
  3. 차단 위치 상향
    가능하면 XDP, 어렵다면 raw/prerouting, 그다음 input/socket으로 차단 위치를 앞당깁니다.
  4. 상태 예산 보호
    syncookies, SYNPROXY, notrack, timeout 단축으로 메모리 점유 시간을 줄입니다.
  5. 검증과 롤백
    카운터 증가 방향, 정상 트래픽 손실, 재현 테스트를 보고 단계적으로 고정합니다.
문서 범위: 이 페이지는 공격 완화와 자원 보호에 집중합니다.

위협 모델 먼저 잡기

같은 DDoS처럼 보여도 커널 입장에서 고갈되는 자원은 다릅니다. 대충 "패킷이 많다"로 묶으면 잘못된 계층에 방어를 걸게 됩니다. 커널 방어는 어떤 상태가 만들어지기 전에 끊을 수 있는가를 기준으로 설계해야 합니다.

공격 유형주요 고갈 자원대표 증상가장 먼저 볼 차단 위치
볼류메트릭 L3/L4 Flood NIC RX 예산, softirq CPU, cache miss pps 급증, rx_dropped, softnet_stat 증가 XDP, NIC 하드웨어 필터, ingress ACL
SYN Flood SYN backlog, request_sock, accept queue SyncookiesSent, TCPReqQFull*, ListenOverflows nftables SYNPROXY, syncookies, listener 튜닝
UDP/ICMP 반사·증폭 IRQ/NAPI 예산, conntrack, ICMP 응답 생성 비용 작은 대역폭 대비 높은 PPS, 닫힌 포트 ICMP 폭증 XDP, raw/prerouting, stateless rate limit
conntrack 고갈 전역 흐름 테이블 메모리와 조회 시간 nf_conntrack_countnf_conntrack_max 근접 notrack, timeout 단축, set/meter, flow 분리
Slowloris / Slow Read 소켓, 워커, 파일 디스크립터, 애플리케이션 큐 연결 수는 많지만 PPS는 낮음, 워커 고갈 애플리케이션 timeout, TCP_DEFER_ACCEPT, reverse proxy
스푸핑 / L2 위변조 정책 우회, 조사 난이도 상승 비정상 source, ARP cache 오염, 비대칭 경로 이슈 rp_filter, arp_filter, 스위치 DAI/port security
운영 원칙: 커널 방어의 첫 목표는 "공격을 완전히 없애기"가 아니라 정상 트래픽이 쓰는 상태와 CPU 시간을 지키는 것입니다. SYN flood에서는 request_sock, stateful firewall에서는 conntrack, 애플리케이션 계층에서는 worker와 fd가 먼저 자원 예산입니다.

수신 경로 병목을 알아야 방어가 맞습니다

패킷은 NIC RX 링에서 시작해 NAPI poll, softnet backlog, Netfilter/conntrack, TCP/UDP 스택, 소켓 큐, 애플리케이션 worker까지 올라갑니다. 공격이 어느 단계에서 CPU와 메모리를 태우는지 보지 않으면, 늦은 계층에서 비싼 검사를 하다가 이미 무너진 뒤에야 드롭하게 됩니다.

수신 경로와 대표 병목 NIC RX 링 RSS / IRQ affinity NAPI poll budget / usecs softnet backlog RPS / RFS Netfilter set / meter / ct TCP/UDP backlog / timeout Socket / App accept / worker 조기 차단 XDP_DROP 분산 처리 RPS / Flow Limit 상태 보호 notrack / ct timeout 연결 보호 syncookie / SYNPROXY 패킷을 더 앞단에서 드롭할수록 sk_buff 생성, conntrack 조회, TCP 상태 생성 비용을 줄일 수 있습니다.
NIC와 드라이버에 가까운 계층일수록 1패킷당 소비하는 CPU와 메모리가 작습니다.
병목 지점전형적 징후주요 지표실전 대응
NIC RX 링 / IRQ 드롭은 많지만 conntrack은 아직 여유 ethtool -S, IRQ 분포, RSS 큐 편중 RSS, IRQ affinity, XDP, L2 ACL
NAPI / softnet backlog 한 코어만 바쁘고 다른 코어는 놀음 /proc/net/softnet_stat, netdev_max_backlog RPS/RFS, netdev_budget, Flow Limit
Netfilter / conntrack 새 연결이 불규칙하게 실패 nf_conntrack_count, conntrack -S, nft counter notrack, set/meter, timeout 조정, ct 분리
SYN backlog / accept queue 접속 지연, ListenOverflows, syncookie 발동 nstat, ss -lnt, somaxconn SYNPROXY, syncookies, backlog/accept 튜닝
애플리케이션 worker PPS는 낮은데 응답이 멈춤 accept 대기, worker 점유율, 요청 timeout header/body timeout, TCP_DEFER_ACCEPT, reverse proxy
튜닝 방향: 커널 문서의 Receive Packet Steering(RPS) 설명은 소프트웨어로 프로토콜 처리를 다른 CPU backlog queue로 보낸다고 말합니다. 공격 시 한 큐만 과열되면 RSS만 볼 것이 아니라 RPS/RFS, IRQ affinity, 애플리케이션 worker 배치까지 함께 봐야 합니다.
호스트 방어의 한계: NIC에 패킷이 도착하기도 전에 회선이 이미 포화됐다면, 호스트 내부 drop은 CPU와 메모리 보호에는 유효해도 회선 자체를 회복시키지는 못합니다. 이런 상황은 upstream 필터링, scrubbing, 블랙홀 라우팅처럼 호스트 바깥 계층 대책이 함께 있어야 합니다.

RSS, RPS, RFS, Flow Limit으로 고 PPS를 분산합니다

고 PPS 공격은 평균 CPU 사용률보다 어느 큐와 어느 CPU가 과열되는지가 더 중요합니다. 리눅스 커널 문서는 RSS를 하드웨어 큐 분산, RPS를 소프트웨어 기반 RSS, RFS를 애플리케이션 CPU locality 강화, Flow Limit를 backlog가 절반을 넘었을 때 큰 flow를 먼저 억제하는 장치로 설명합니다. 이 네 가지는 서로 대체하는 기능이 아니라 계층이 다른 도구입니다.

수신 분산 계층: RSS → RPS → RFS → Flow Limit Ingress 패킷 공격/정상 혼합 NIC RX 큐 queue 0 / 1 / 2 IRQ affinity softirq CPU protocol 처리 backlog CPU socket locality 애플리케이션 CPU worker / accept RSS NIC 해시로 RX 큐/IRQ 분산 하드웨어 계층 RPS protocol 처리 CPU 재선택 소프트웨어 계층 RFS 앱이 돌고 있는 CPU로 유도 캐시 locality 개선 Flow Limit per-CPU input queue가 netdev_max_backlog의 절반을 넘으면 큰 flow를 먼저 약하게 드롭해 작은 flow를 보호
RSS는 NIC 큐를, RPS는 softirq CPU를, RFS는 애플리케이션 locality를 조정합니다. Flow Limit는 포화 시 큰 flow를 먼저 눌러 작은 flow의 생존성을 높입니다.
기법동작 계층주목적언제 쓰는가주의점
RSS NIC 하드웨어 RX 큐와 IRQ 분산 멀티큐 NIC, 하드웨어 queue 사용 가능 irqbalance나 잘못된 affinity가 분산 효과를 망칠 수 있음
RPS softirq / backlog 프로토콜 처리 CPU 재분배 NIC 큐 수가 CPU 수보다 적을 때 IPI 비용이 생기므로 RSS가 충분하면 오히려 불필요할 수 있음
RFS softirq → socket 애플리케이션 CPU locality 향상 worker pinning, SO_INCOMING_NAPI_ID 기반 최적화 rps_sock_flow_entries, 큐별 rps_flow_cnt 설정 필요
Flow Limit per-CPU input backlog 포화 시 큰 flow 억제 한 소스/flow가 queue를 독점하는 DoS 기본 비활성화. backlog가 절반을 넘을 때만 본격 동작
# 1. 하드웨어 RX 큐와 IRQ 분포 확인
ethtool -x eth0
grep eth0 /proc/interrupts

# 2. NIC 큐 수가 CPU 수보다 적을 때 RPS 활성화 예시
echo f > /sys/class/net/eth0/queues/rx-0/rps_cpus
echo f > /sys/class/net/eth0/queues/rx-1/rps_cpus

# 3. RFS 활성화: 전역 엔트리 + 큐별 엔트리
sysctl -w net.core.rps_sock_flow_entries=32768
echo 16384 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
echo 16384 > /sys/class/net/eth0/queues/rx-1/rps_flow_cnt

# 4. Flow Limit 활성화: backlog가 절반을 넘을 때 큰 flow 억제
sysctl -w net.core.flow_limit_cpu_bitmap=f
sysctl -w net.core.netdev_max_backlog=4096

# 5. 특정 포트를 별도 RSS context로 분리하는 ethtool 예시
ethtool -X eth0 hfunc toeplitz context new
ethtool -x eth0 context 1
ethtool -X eth0 equal 2 context 1
ethtool -N eth0 flow-type tcp6 dst-port 22 context 1
설정 순서: RSS로 하드웨어 큐가 이미 CPU마다 고르게 퍼져 있다면 RPS는 필수가 아닙니다. 커널 문서도 queue 수가 CPU 수 이상인 경우 RPS가 큰 이득이 없을 수 있다고 설명합니다. 수동 IRQ affinity를 잡아도 irqbalance가 다시 바꿀 수 있으니 같이 확인하세요.

SYN Flood: backlog가 어디서 차는지 구분해야 합니다

SYN Flood는 TCP가 3-way handshake를 완료하기 전까지 잠깐 저장하는 상태를 겨냥합니다. 여기서 자주 헷갈리는 것이 listen backlogSYN backlog의 차이입니다.

/* net/ipv4/tcp_ipv4.c + tcp_input.c 경로를 읽기 쉽게 줄인 의사 코드 */
int tcp_v4_rcv(struct sk_buff *skb)
{
    /* 1. LISTEN 소켓이면 새 연결 요청 처리 */
    if (sk_state(skb) == TCP_LISTEN)
        return tcp_conn_request(...);
}

int tcp_conn_request(..., struct sock *sk, struct sk_buff *skb)
{
    bool want_cookie = false;

    /* 2. SYN backlog가 꽉 찼는지 확인 */
    if (inet_csk_reqsk_queue_is_full(sk)) {
        if (!READ_ONCE(net->ipv4.sysctl_tcp_syncookies))
            goto drop;
        want_cookie = true;
    }

    /* 3. 정상 경로면 request_sock를 만들고 SYN+ACK 전송 */
    if (!want_cookie)
        inet_csk_reqsk_queue_hash_add(sk, req, timeout);

    /* 4. cookie 경로면 상태를 저장하지 않고 ISN에 쿠키 부호화 */
    if (want_cookie)
        isn = cookie_v4_init_sequence(sk, skb, ...);

    /* 5. 최종 ACK가 오면 cookie 검증 후 child socket 생성 */
    return tcp_v4_send_synack(sk, skb, ...);
drop:
    return 0;
}
코드 설명
  • 1-5행LISTEN 소켓으로 들어온 패킷은 일반 데이터 경로가 아니라 연결 생성 경로로 빠집니다. 방어 포인트가 일반 TCP 세션 처리와 다릅니다.
  • 9-13행inet_csk_reqsk_queue_is_full()가 반개방 연결 큐 포화를 판단합니다. 이때가 syncookie 발동 분기점입니다.
  • 16-17행정상 경로에서는 request_sock가 메모리에 생성됩니다. 공격자는 바로 이 상태 보유 비용을 노립니다.
  • 20-21행syncookie 경로는 상태를 저장하지 않고 초기 시퀀스 번호에 검증 정보를 부호화합니다. 메모리 사용량은 줄지만 항상 이상적인 경로는 아닙니다.
  • 23행최종 ACK가 돌아왔을 때만 실제 child socket을 만들기 때문에 backlog overflow 상황에서 생존성이 올라갑니다.
정상 리스너, syncookie, SYNPROXY 비교 정상 리스너 syncookie SYNPROXY 클라이언트 백엔드 SYN request_sock 저장 SYN backlog 사용 공격 시 메모리와 재전송 비용 증가 클라이언트 백엔드 SYN overflow 시 상태 미저장 ISN에 cookie 부호화 ACK 검증 뒤 child socket 생성 클라이언트 방화벽 백엔드 방화벽이 SYN/SYN+ACK/ACK 종료 백엔드는 ESTABLISHED만 수신 백엔드 SYN backlog와 request_sock 보호 장점 구성 단순 장점 상태 고갈 방지 장점 백엔드 리스너를 근본적으로 보호
syncookie는 백엔드 서버가 스스로 버티는 기술이고, SYNPROXY는 방화벽이 먼저 연결을 검증해 백엔드를 보호하는 기술입니다.
항목syncookieSYNPROXY
배치 위치 서버 커널 내부 방화벽/로드밸런서/프런트 호스트
보호 대상 해당 서버의 SYN backlog 백엔드 전체의 SYN backlog와 request_sock
상태 절감 overflow 시 반개방 상태 미저장 백엔드가 handshake 자체를 거의 안 보게 함
주의점 커널 문서가 TCP 확장과 서비스 저하 가능성을 경고 MSS, window scale, timestamp, SACK 옵션을 백엔드와 맞춰야 함
적합한 상황 개별 호스트 보호, 즉시 대응 공격이 프런트에서 집중되는 서비스, 다수 백엔드 보호
SYNPROXY 정확도: nftables 공식 예시는 nf_conntrack_tcp_loose=0으로 느슨한 TCP 추적을 끄고, 흐름에 맞지 않는 ACK를 INVALID로 분류한 뒤 SYNPROXY에 넘깁니다. 실제 운영에서 이 값을 빼먹으면 규칙은 맞아 보여도 기대한 대로 보호가 안 걸릴 수 있습니다.
# nftables SYNPROXY 예시
# 공식 nftables manpage 예시 구조를 Linux 서버 환경에 맞게 정리한 것

sysctl -w net.netfilter.nf_conntrack_tcp_loose=0

nft add table ip raw
nft 'add chain ip raw prerouting { type filter hook prerouting priority raw; policy accept; }'
nft add rule ip raw prerouting tcp flags syn notrack

nft add table ip filter
nft 'add chain ip filter input { type filter hook input priority filter; policy accept; }'
nft add rule ip filter input ct state invalid,untracked synproxy mss 1460 wscale 9 timestamp sack-perm
nft add rule ip filter input ct state invalid drop

# 서버 자체 보호용 최소 확인
sysctl -w net.ipv4.tcp_syncookies=1
sysctl -w net.ipv4.tcp_max_syn_backlog=8192
sysctl -w net.ipv4.tcp_synack_retries=3
sysctl -w net.core.somaxconn=8192
중요: syncookies=1만 켠다고 SYN Flood 방어가 끝나는 것이 아닙니다. accept queue가 꽉 차는 문제는 somaxconn, 애플리케이션의 accept 속도, reverse proxy 앞단 구조, ListenOverflows/ListenDrops 카운터를 같이 봐야 해결됩니다.

UDP/ICMP Flood와 반사·증폭 공격

UDP Flood는 TCP처럼 handshake 상태를 만들지 않기 때문에 "가볍다"고 오해하기 쉽지만, 실제 운영에서는 높은 PPS, 닫힌 포트 ICMP 응답 생성, conntrack 엔트리 남발, 애플리케이션 파서 비용 때문에 빠르게 CPU를 태웁니다. 특히 DNS, NTP, SSDP 같은 반사·증폭 계열은 상대적으로 작은 송신량으로 큰 응답을 유도할 수 있습니다.

sysctl기본값의미언제 만지는가
net.ipv4.icmp_ratelimit 1000 대상별 ICMP 응답 사이 최소 간격(ms) 닫힌 포트 UDP flood, 오류 응답 폭증
net.ipv4.icmp_msgs_per_sec 1000 호스트 전체 ICMP 초당 최대 메시지 수 전역 ICMP 송신 예산 보호
net.ipv4.icmp_msgs_burst 50 ICMP burst 허용량 짧은 스파이크 허용치 조정
net.ipv4.icmp_echo_ignore_broadcasts 1 broadcast/multicast echo 요청 무시 Smurf 류 반사 방지
# ICMP 응답 예산 보호
sysctl -w net.ipv4.icmp_ratelimit=1000
sysctl -w net.ipv4.icmp_msgs_per_sec=1000
sysctl -w net.ipv4.icmp_msgs_burst=50
sysctl -w net.ipv4.icmp_echo_ignore_broadcasts=1

# UDP / ICMP 속도 제한 예시
nft add table inet ddos
nft 'add chain inet ddos input { type filter hook input priority filter; policy accept; }'
nft add rule inet ddos input udp dport { 53, 123, 1900, 11211 } meter amp4 '{ ip saddr timeout 10s limit rate over 200/second }' drop
nft add rule inet ddos input ip protocol icmp icmp type echo-request meter ping4 '{ ip saddr timeout 10s limit rate over 20/second }' drop
nft add rule inet ddos input ct state invalid drop

# 서비스가 conntrack/NAT를 정말 필요로 하지 않는 앞단이라면 raw 훅에서 notrack 검토
# 단, notrack는 stateful firewall/NAT 관측을 우회하므로 적용 범위를 매우 좁게 잡아야 함
실무 포인트: 닫힌 UDP 포트로 들어오는 flood는 애플리케이션보다 먼저 커널이 ICMP 오류를 만들어 돌려보내는 비용이 병목이 됩니다. 서비스가 실제로 열려 있지 않은 포트라면 input 훅이나 그 앞단에서 조용히 drop하는 편이 보통 더 낫습니다.

conntrack 고갈은 전체 서비스에 전염됩니다

nf_conntrack는 특정 서비스 하나의 큐가 아니라 호스트 또는 네임스페이스 전체에서 공유되는 상태 자원입니다. 그래서 한 서비스로 들어오는 flood가 전혀 관계없는 다른 서비스의 새 연결까지 실패하게 만들 수 있습니다. 특히 UDP spray, spoofed SYN, fragment 폭탄은 짧은 시간에 많은 엔트리를 만들고, timeout이 길면 메모리 점유가 길어집니다.

항목기본값/성질보안 관점 의미
nf_conntrack_count 읽기 전용 현재 엔트리 수 실시간 수용량 확인의 기준점
nf_conntrack_max 기본적으로 nf_conntrack_buckets와 동일 전역 수용량 상한. 가득 차면 새 흐름이 실패
nf_conntrack_buckets 메모리 기반 자동 계산 해시 분산 품질과 lookup 충돌에 영향
nf_conntrack_tcp_timeout_syn_recv 60초 반개방 TCP 엔트리 잔류 시간
nf_conntrack_udp_timeout 30초 일반 UDP 엔트리 잔류 시간
nf_conntrack_udp_timeout_stream 120초 양방향 UDP stream 판정 후 잔류 시간
nf_conntrack_log_invalid 0 INVALID 패킷 로그를 프로토콜별로 선택 출력
nf_conntrack_acct 0 흐름별 패킷/바이트 카운터 부여. 관측에는 좋지만 약간의 추가 비용
nf_conntrack_checksum 1 잘못된 체크섬 패킷을 INVALID로 취급
nf_conntrack_frag6_high_thresh 262144 bytes IPv6 fragment reassembly 메모리 상한
# 현재 conntrack 상태 점검
cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_max
cat /proc/sys/net/netfilter/nf_conntrack_buckets
conntrack -S

# 공격 시 자주 보는 보수적 조정 예시
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_syn_recv=30
sysctl -w net.netfilter.nf_conntrack_udp_timeout=15
sysctl -w net.netfilter.nf_conntrack_udp_timeout_stream=60
sysctl -w net.netfilter.nf_conntrack_log_invalid=6
sysctl -w net.netfilter.nf_conntrack_acct=1

# state가 불필요한 아주 제한된 트래픽만 raw에서 notrack
# nft add table inet raw
# nft 'add chain inet raw prerouting { type filter hook prerouting priority raw; policy accept; }'
# nft add rule inet raw prerouting ip protocol icmp notrack
# 위 예시는 "가능한 문법" 예시일 뿐이며, 실제 운영에선 NAT/정책 우회 영향을 반드시 검토해야 합니다.
주의: conntrack timeout을 줄이는 것은 효과가 크지만, 정상 지연이 긴 서비스나 NAT 세션에는 부작용이 생길 수 있습니다. 특히 UDP는 서비스별 특성이 크게 다르므로 전역 sysctl만 믿지 말고 nftables ct timeout 객체 또는 서비스 분리로 가는 편이 안전합니다.

조각화와 재조립 큐도 별도 자원 예산입니다

fragment flood는 대역폭이 아주 크지 않아도 미완성 재조립 큐긴 timeout을 이용해 메모리를 오래 점유할 수 있습니다. 조각이 충분히 모이지 않으면 상위 계층까지 가지 못한 채 재조립 버퍼만 남습니다. 서비스가 실제로 큰 UDP, 터널, 특수 장비 트래픽을 얼마나 쓰는지 모른 채 threshold만 올리면 공격 표면도 같이 커집니다.

항목대상의미보안 관점 메모
net.ipv4.ipfrag_high_thresh IPv4 재조립 fragment queue가 사용할 수 있는 메모리 상한 상한이 너무 높으면 메모리 체류 비용이 커지고, 너무 낮으면 정상 fragment도 자주 탈락
net.ipv4.ipfrag_time IPv4 재조립 미완성 fragment queue 보관 시간 짧게 줄이면 공격 체류 시간은 감소하지만 느린 경로나 재전송 많은 환경에 영향
net.ipv4.ipfrag_max_dist IPv4 재조립 같은 source 기준으로 queue 사이를 얼마나 건너뛸 수 있는지 판단 문서가 기본값 64를 설명하며, 너무 작으면 재정렬된 정상 조각 드롭, 너무 크면 잘못된 조각 결합 위험
net.ipv6.ip6frag_high_thresh IPv6 재조립 IPv6 fragment 재조립 메모리 상한 IPv6 조각화는 경로 특성 영향이 커서 실제 트래픽 패턴 확인이 선행돼야 함
net.ipv6.ip6frag_time IPv6 재조립 IPv6 fragment 보관 시간 터널과 대형 UDP 응용이 있다면 지나치게 줄이기 어려움
net.netfilter.nf_conntrack_frag6_high_thresh IPv6 + conntrack conntrack의 IPv6 fragment 재조립 메모리 상한 문서 기본값은 262144 bytes이며, high를 넘으면 low 아래로 내려갈 때까지 큐 회수
net.netfilter.nf_conntrack_frag6_timeout IPv6 + conntrack conntrack 조각 보관 시간 stateful 방화벽 앞단이면 이 timeout이 공격 체류 시간에 직접 연결
# 현재 fragment / reassembly 관련 값 확인
sysctl net.ipv4.ipfrag_high_thresh
sysctl net.ipv4.ipfrag_time
sysctl net.ipv4.ipfrag_max_dist
sysctl net.ipv6.ip6frag_high_thresh
sysctl net.ipv6.ip6frag_time
sysctl net.netfilter.nf_conntrack_frag6_high_thresh
sysctl net.netfilter.nf_conntrack_frag6_low_thresh
sysctl net.netfilter.nf_conntrack_frag6_timeout

# 공격 시 자주 시도하는 보수적 조정 예시
sysctl -w net.ipv4.ipfrag_time=15
sysctl -w net.ipv4.ipfrag_max_dist=64
sysctl -w net.ipv6.ip6frag_time=15
sysctl -w net.netfilter.nf_conntrack_frag6_timeout=30

# 서비스가 조각화를 거의 쓰지 않는다면 fragment 비율 자체를 관측 경보로 올리는 편이 안전
nstat -az IpReasmReqds IpReasmOKs IpReasmFails
조정 원칙: ipfrag_max_dist는 "낮출수록 안전"한 값이 아닙니다. 커널 문서도 값이 너무 작으면 정상 재정렬을 드롭하고, 너무 크면 잘못된 fragment queue에 붙을 위험이 커질 수 있다고 설명합니다.

XDP, nftables, conntrack을 계층적으로 써야 합니다

방어를 잘하는 시스템은 "모든 검사를 한 군데서 많이" 하지 않습니다. 보통은 다음 순서가 좋습니다.

  1. XDP — 명백히 버릴 패킷을 가장 먼저 드롭합니다. spoofed source, 알려진 반사 포트, 비정상 헤더, 매우 단순한 rate limiting이 여기 적합합니다.
  2. nftables raw/prerouting — notrack 여부를 결정하고, 값싼 매치와 set/meter로 더 줄입니다.
  3. nftables filter/input + conntrack — 살아남은 패킷에만 stateful 정책을 적용합니다.
  4. TCP/애플리케이션 — syncookie, SYNPROXY, TCP_DEFER_ACCEPT, reverse proxy timeout으로 마지막 상태 예산을 보호합니다.

커널 XDP 문서는 redirect 경로를 세 단계로 설명합니다. bpf_redirect()/bpf_redirect_map()이 대상 정보를 per-CPU 구조체에 넣고, 드라이버가 xdp_do_redirect()로 enqueue한 뒤, NAPI poll 종료 전 xdp_do_flush()를 호출해 실제 전송을 완료합니다. 이 구조는 flood 방어에서 드롭만 아니라 격리에도 중요합니다. 예를 들어 공격 의심 트래픽을 AF_XDP나 cpumap으로 분리해 분석할 수 있습니다.

/* XDP에서 source별 간단한 토큰 버킷을 적용하는 의사 코드 */
struct rate_key {
    __u32 saddr;
    __u16 dport;
    __u8 proto;
};

struct rate_val {
    __u64 window_ns;
    __u32 packets;
};

SEC("xdp")
int ddos_guard(struct xdp_md *ctx)
{
    if (!parse_ipv4_udp(ctx, &iph, &uh))
        return XDP_PASS;

    if (uh->dest == bpf_htons(53) || uh->dest == bpf_htons(123)) {
        key.saddr = iph->saddr;
        key.dport = uh->dest;
        key.proto = iph->protocol;

        val = bpf_map_lookup_elem(&rate_map, &key);
        if (over_limit(val, bpf_ktime_get_ns()))
            return XDP_DROP;
    }

    return XDP_PASS;
}
언제 XDP가 필요한가: 수백만 pps 수준으로 올라가면 nftables도 충분히 빠르더라도 이미 sk_buff 생성과 Netfilter 훅 순회 비용이 아깝습니다. XDP는 패킷이 일반 네트워크 스택에 들어오기 전에 드롭할 수 있다는 점이 핵심입니다.

Slowloris 계열은 패킷보다 연결 점유 시간이 문제입니다

Slowloris, Slow POST, Slow Read 류 공격은 "패킷이 매우 많다"기보다 오래 붙잡고 놓지 않는 연결을 이용합니다. 그래서 XDP나 단순 PPS 제한은 거의 안 듣고, 소켓 수, accept loop, 워커 스레드, 애플리케이션 요청 timeout이 핵심이 됩니다. 커널이 할 수 있는 일과 애플리케이션이 해야 하는 일을 분리해서 봐야 합니다.

대책계층효과주의점
TCP_DEFER_ACCEPT 커널 소켓 데이터가 오기 전에는 listener를 덜 깨움 프로토콜 특성과 timeout 값에 민감
somaxconn / 애플리케이션 backlog 커널 + 앱 짧은 스파이크 흡수 worker가 느리면 문제를 뒤로 미루기만 함
tcp_abort_on_overflow 커널 accept queue overflow 시 즉시 reset 문서가 신중 사용을 권고
header/body/read timeout 애플리케이션 Slowloris 대응의 핵심 정상 느린 클라이언트에 영향
reverse proxy + buffer L7 백엔드 worker 보호 프런트단 메모리 정책 필요
# 커널 쪽에서 먼저 보는 값
sysctl net.core.somaxconn
sysctl net.ipv4.tcp_abort_on_overflow

# listener queue 관찰
ss -lnt
nstat -az TcpExtListenOverflows TcpExtListenDrops TcpExtTCPBacklogDrop

# 애플리케이션은 별도로 header/read timeout, keepalive timeout을 짧게 가져가야 함
# Slowloris는 커널 한 군데만 만져서 해결되지 않습니다.
핵심 구분: 높은 PPS 공격에는 XDP가 중요하지만, Slowloris는 보통 PPS가 낮습니다. 이 경우 CPU 그래프보다 연결 수, 워커 수, 요청 완료 시간 분포, reverse proxy timeout이 더 중요합니다.

스푸핑과 L2 위변조는 ingress filtering이 기본입니다

IP 스푸핑이 허용되면 UDP 증폭과 SYN Flood의 추적과 정책 적용이 모두 어려워집니다. 커널의 rp_filter는 RFC 3704가 설명하는 strict/loose reverse path filtering 개념과 대응됩니다. 커널 문서는 strict mode(1)가 DDoS 방어를 위해 권장되지만, 비대칭 라우팅이나 복잡한 멀티홈 환경이면 loose mode(2)가 더 적절할 수 있다고 설명합니다.

항목의미주의점
rp_filter 0 source 검증 안 함 기본값. 배포판이 부팅 스크립트에서 바꿀 수 있음
rp_filter 1 strict reverse path 비대칭 경로면 정상 트래픽 오탐 가능
rp_filter 2 loose reverse path strict보다 느슨하지만 멀티홈 환경에 현실적
arp_filter 0/1 라우팅 결과에 맞는 인터페이스만 ARP 응답 같은 서브넷에 여러 NIC가 있을 때 특히 중요
# 스푸핑 방어 시작점
sysctl -w net.ipv4.conf.all.rp_filter=1
sysctl -w net.ipv4.conf.default.rp_filter=1

# 비대칭 경로 / 멀티홈이면 loose mode 고려
sysctl -w net.ipv4.conf.eth0.rp_filter=2

# 같은 서브넷 다중 NIC 환경
sysctl -w net.ipv4.conf.all.arp_filter=1

# 매우 중요한 피어는 정적 neighbor 사용도 검토
ip neigh add 10.0.0.1 lladdr aa:bb:cc:dd:ee:ff nud permanent dev eth0
L2 보강: ARP spoofing은 호스트 커널만으로 끝내기 어렵습니다. 스위치의 DHCP snooping, Dynamic ARP Inspection, port security, 802.1X 같은 네트워크 장비 기능이 같이 있어야 실효성이 높습니다.

관측 지표: 어디가 먼저 무너지는지 바로 보이게 만들기

방어는 규칙보다 계측이 먼저입니다. 카운터가 없으면 "좋아진 것 같다" 수준에서 멈춥니다. 커널에서 바로 볼 수 있는 지표를 병목별로 묶으면 다음과 같습니다.

영역확인 명령의미
NIC / 드라이버 ethtool -S eth0 rx_packets, rx_dropped, ring overflow, queue 편중 여부 확인
softnet cat /proc/net/softnet_stat softirq backlog가 밀리는지 확인
TCP backlog nstat -az TcpExtSyncookiesSent TcpExtListenOverflows TcpExtTCPReqQFullDoCookies TcpExtTCPReqQFullDrop syncookie 발동, accept queue overflow, SYN request queue 포화 확인
listener 상태 ss -lnt Recv-Q/Send-Q로 accept queue 압력을 간접 확인
conntrack conntrack -S, cat /proc/sys/net/netfilter/nf_conntrack_count 전역 흐름 테이블 사용량과 drop 통계
Netfilter nft list ruleset -a rule counter와 set hit 수 확인
XDP bpftool prog show, perf record -a -e xdp:xdp_redirect_err -e xdp:xdp_exception XDP 예외, redirect 실패, 프로그램 부착 상태 확인
# 1. SYN Flood / listener 관측
nstat -az TcpExtSyncookiesSent TcpExtSyncookiesRecv TcpExtSyncookiesFailed \
          TcpExtListenOverflows TcpExtListenDrops \
          TcpExtTCPReqQFullDoCookies TcpExtTCPReqQFullDrop

# 2. conntrack 압력
cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_max
conntrack -S

# 3. 소프트웨어 수신 큐 병목
watch -n 1 'cat /proc/net/softnet_stat'

# 4. XDP redirect / 예외 추적
perf record -a -e xdp:xdp_redirect_err -e xdp:xdp_redirect_map_err -e xdp:xdp_exception -e xdp:xdp_devmap_xmit

# 5. 드라이버 카운터
watch -n 1 'ethtool -S eth0 | grep -E "rx_packets|rx_dropped|rx_errors|rx_missed"'
자주 쓰는 판단법:
  • SyncookiesSent만 오르고 ListenOverflows는 잠잠하면 우선 SYN request queue 압력부터 의심합니다.
  • ListenOverflowsListenDrops가 같이 오르면 애플리케이션의 accept 속도나 backlog가 더 큰 문제일 수 있습니다.
  • nf_conntrack_count가 빠르게 치솟는데 NIC 드롭은 없으면 stateful firewall/NAT가 공격의 실제 병목일 가능성이 큽니다.

사고 대응 절차

  1. 공격 형태를 분리합니다. bps보다 pps, TCP SYN 비율, UDP 대상 포트, ICMP 타입, 소스 분산 정도를 먼저 봅니다.
  2. 병목 카운터를 고정합니다. softnet_stat, nstat, nf_conntrack_count, ethtool -S 중 어디가 가장 먼저 증가하는지 찍습니다.
  3. 차단 계층을 앞당깁니다. input 훅에서 버티지 말고 XDP, raw, prerouting으로 옮길 수 있으면 옮깁니다.
  4. 상태를 줄입니다. SYNPROXY, syncookies, notrack, timeout 단축으로 메모리 체류 시간을 줄입니다.
  5. 정상 트래픽 손실을 검증합니다. health check, 실제 사용자 경로, TLS handshake, DNS 질의 등 핵심 정상 트래픽을 동시에 확인합니다.
  6. 임시 규칙을 영속 정책으로 정리합니다. 공격 중 급히 넣은 drop rule은 사후에 범위, 만료 시간, 모니터링과 함께 다시 설계합니다.

권장 하드닝 체크리스트

SYN Flood 커널 처리 경로와 syncookie 메커니즘

SYN Flood 공격이 커널 내부에서 어떤 함수 경로를 타는지 정확히 아는 것이 방어의 시작입니다. TCP 리스너로 SYN 패킷이 도착하면 tcp_v4_rcv()tcp_v4_do_rcv()tcp_rcv_state_process()tcp_conn_request() 경로를 거칩니다. 이 경로에서 tcp_syn_flood_action()이 syncookie 발동 여부를 결정하는 핵심 게이트입니다.

SYN Flood 커널 처리 경로와 syncookie 분기 tcp_v4_rcv() tcp_v4_do_rcv() tcp_conn_request() tcp_syn_flood_action() SYN backlog 포화 여부 판단 정상 경로 request_sock 할당 syncookie 경로 상태 미저장, ISN 부호화 여유 포화 SYN+ACK 재전송 대기 SYN+ACK cookie ISN 포함 syncookie ISN 부호화 구조 ISN = hash(saddr, daddr, sport, dport, count) | MSS_index | timestamp_bits ACK 수신 시 ISN으로부터 MSS, timestamp를 복원하여 상태 없이 연결 검증 cookie_v4_init_sequence() → cookie_v4_check() 경로
SYN backlog가 포화되면 tcp_syn_flood_action()이 syncookie 모드를 활성화하고, ISN에 연결 정보를 부호화하여 상태 없이 검증합니다.
/* net/ipv4/tcp_input.c — tcp_syn_flood_action() 핵심 로직 */
static bool tcp_syn_flood_action(const struct sock *sk,
                                  const char *proto)
{
    const char *msg = "Sending cookies";
    bool want_cookie = false;
    struct listen_sock *lopt;

    /* sysctl tcp_syncookies 값 확인 */
    if (READ_ONCE(net->ipv4.sysctl_tcp_syncookies) != 2) {
        /* 2가 아니면(=1) overflow 시에만 syncookie 발동 */
        lopt = inet_csk(sk)->icsk_accept_queue.listen_opt;
        if (!lopt->qlen_young)
            return false;
    }

    /* syncookies=2 이면 항상 cookie 모드 */
    want_cookie = true;
    NET_INC_STATS(net, LINUX_MIB_TCPREQQFULLDOCOOKIES);

    /* rate-limited 경고 로그 출력 */
    if (!net->ipv4.sysctl_tcp_syncookies)
        msg = "Dropping request";
    net_info_ratelimited("%s: Possible SYN flooding on port %d. %s.\n",
                         proto, sk->sk_num, msg);

    return want_cookie;
}
코드 설명
  • 9-13행sysctl_tcp_syncookies가 1이면 overflow 시에만, 2면 항상 syncookie를 발동합니다. 0이면 완전 비활성화되어 drop만 합니다.
  • 17-18행LINUX_MIB_TCPREQQFULLDOCOOKIES 카운터가 증가합니다. nstatTcpExtTCPReqQFullDoCookies로 관측됩니다.
  • 21-24행syncookies가 비활성화된 상태에서 SYN backlog가 차면 "Dropping request" 메시지가 dmesg에 rate-limited로 출력됩니다.
syncookies=2 주의: tcp_syncookies=2는 항상 cookie 모드를 강제하므로 TCP 옵션(window scale, SACK, timestamp) 협상이 제한됩니다. 커널 문서도 이 모드는 테스트 또는 극단적 상황에서만 사용하라고 경고합니다.
sysctl 값동작TCPReqQFullDoCookiesTCPReqQFullDrop
tcp_syncookies=0 syncookie 비활성화, SYN backlog 초과 시 drop 증가 안 함 증가
tcp_syncookies=1 SYN backlog overflow 시에만 cookie 발동 (권장) overflow 시 증가 증가 안 함 (cookie로 대체)
tcp_syncookies=2 항상 cookie 모드 강제 모든 SYN에서 증가 증가 안 함
/* net/ipv4/syncookies.c — cookie_v4_init_sequence() 핵심 */
__u32 cookie_v4_init_sequence(const struct sk_buff *skb,
                              __u16 *mssp)
{
    const struct iphdr *iph = ip_hdr(skb);
    const struct tcphdr *th = tcp_hdr(skb);
    int mssind;

    /* MSS를 4개 슬롯 중 하나로 양자화 */
    const __u16 msstab[] = { 536, 1300, 1440, 1460 };
    mssind = cookie_mss_index(*mssp, msstab);

    /* ISN = HMAC(saddr, daddr, sport, dport, count) | mssind */
    return secure_tcp_syn_cookie(iph->saddr, iph->daddr,
                                  th->source, th->dest,
                                  ntohl(th->seq),
                                  mssind);
}
MSS 양자화 한계: syncookie는 MSS를 4개 값(536, 1300, 1440, 1460) 중 하나로 양자화합니다. 이 때문에 정확한 MSS 협상이 안 되고, window scale과 SACK 옵션도 cookie에 부호화할 비트가 부족하여 제한됩니다. 이것이 syncookie를 "비상 모드"로 취급하는 근본 이유입니다.

SYN Flood 관측과 대응 순서

SYN Flood 공격이 의심될 때 커널 카운터를 읽는 순서가 중요합니다. 먼저 SyncookiesSent가 증가하는지 확인하고, 다음으로 ListenOverflowsListenDrops를 비교합니다. 두 값이 함께 오르면 accept queue 문제이고, SyncookiesSent만 오르면 SYN backlog 문제입니다.

# SYN Flood 단계별 진단 순서

# 1단계: syncookie 발동 여부 확인
nstat -az TcpExtSyncookiesSent TcpExtSyncookiesRecv TcpExtSyncookiesFailed
# SyncookiesSent > 0 → SYN backlog overflow 발생 중
# SyncookiesFailed > 0 → 위조된 ACK나 만료된 cookie

# 2단계: accept queue overflow 확인
nstat -az TcpExtListenOverflows TcpExtListenDrops
# ListenOverflows > 0 → accept queue가 가득 참
# 애플리케이션의 accept() 속도가 느린 것

# 3단계: 어느 리스너가 압력을 받는지 확인
ss -lnt | awk 'NR>1 && $2>0 {print}'
# Recv-Q > 0 인 리스너가 accept queue 압력을 받는 것

# 4단계: SYN 소스 분포 확인
conntrack -L -p tcp --state SYN_RECV 2>/dev/null | \
    awk '{for(i=1;i<=NF;i++) if($i~/^src=/) print $i}' | \
    sort | uniq -c | sort -rn | head -20

# 5단계: 대응 — backlog 확대 + syncookie 활성 확인
sysctl -w net.ipv4.tcp_max_syn_backlog=16384
sysctl -w net.core.somaxconn=16384
sysctl -w net.ipv4.tcp_syncookies=1
관측 패턴의미대응
SyncookiesSent 증가, ListenOverflows 0 SYN backlog만 포화, accept은 여유 tcp_max_syn_backlog 확대, tcp_synack_retries 축소
SyncookiesSent 증가, ListenOverflows 증가 SYN backlog와 accept queue 둘 다 포화 앱 accept 속도 개선, somaxconn 확대, SYNPROXY 검토
SyncookiesSent 0, ListenOverflows 증가 정상 연결이 많아 accept queue만 밀림 앱 worker 수 증가, reverse proxy 도입
SyncookiesFailed 높음 위조 ACK 또는 cookie 만료 공격 소스 차단, tcp_synack_retries 축소

request_sock 메모리 구조

SYN Flood가 노리는 request_sock는 per-listener 해시 테이블에 저장됩니다. 각 request_sock는 약 256바이트를 차지하며, tcp_max_syn_backlog 개수만큼 할당될 수 있습니다. 공격 시 이 메모리와 해시 조회 비용이 동시에 증가합니다.

/* include/net/request_sock.h — request_sock 핵심 필드 */
struct request_sock {
    struct sock_common     __req_common;
    struct request_sock   *dl_next;
    u16                    mss;
    u8                     num_retrans;
    u8                     syncookie:1;   /* cookie 경로 여부 */
    u8                     num_timeout:7;
    u32                    ts_recent;
    struct timer_list      rsk_timer;     /* SYN+ACK 재전송 타이머 */
    const struct request_sock_ops *rsk_ops;
    struct sock           *sk;            /* 부모 listen socket */
    struct saved_syn      *saved_syn;
};

/* 공격 시 request_sock 해시 체인이 길어지면:
 * 1. 조회 시간 O(n) 증가
 * 2. SYN+ACK 재전송 타이머가 CPU를 소모
 * 3. 메모리 사용량 = max_syn_backlog × sizeof(request_sock) */

SYNPROXY 아키텍처와 conntrack 연동

SYNPROXY는 방화벽이 백엔드 대신 TCP 3-way handshake를 먼저 완료하는 방어 기법입니다. 커널의 nf_synproxy_core 모듈이 핵심이며, Netfilter의 UNTRACKED 상태와 conntrack을 정교하게 연동합니다. 백엔드 서버는 검증된 연결만 받으므로 SYN backlog와 request_sock가 완전히 보호됩니다.

SYNPROXY 아키텍처: 방화벽이 handshake를 대행 공격자/클라 SYN 전송 SYNPROXY 방화벽 nf_synproxy_core raw: SYN → NOTRACK conntrack UNTRACKED → INVALID 백엔드 서버 ESTABLISHED만 수신 SYN SYN(new) SYNPROXY 세부 흐름 (시간순) 1. SYN 수신 (NOTRACK) 2. SYNPROXY → SYN+ACK 3. 클라 ACK 수신 4. 검증 완료 5. 백엔드에 새 SYN 6. 백엔드 SYN+ACK 7. 방화벽 ACK 전달 8. ESTABLISHED 핵심: 공격자의 SYN은 방화벽에서 끝나고, 백엔드는 검증된 연결만 받습니다 window scale, MSS, timestamp, SACK 옵션을 방화벽과 백엔드 사이에서 재협상
SYNPROXY는 공격 SYN을 방화벽에서 차단하고, 검증된 클라이언트만 백엔드에 새 연결로 전달합니다.
# SYNPROXY 전체 설정 예시 (nftables)
# 1단계: 전제 조건 — tcp_loose 비활성화
sysctl -w net.netfilter.nf_conntrack_tcp_loose=0

# 2단계: raw 테이블에서 SYN을 NOTRACK 처리
nft add table ip raw
nft 'add chain ip raw prerouting { type filter hook prerouting priority raw; policy accept; }'
nft add rule ip raw prerouting tcp dport 80 tcp flags syn notrack

# 3단계: filter 테이블에서 SYNPROXY 적용
nft add table ip filter
nft 'add chain ip filter input { type filter hook input priority filter; policy accept; }'
nft add rule ip filter input ct state invalid,untracked \
    synproxy mss 1460 wscale 9 timestamp sack-perm
nft add rule ip filter input ct state invalid drop

# 4단계: 검증 확인
conntrack -L -p tcp --state SYN_SENT  # 백엔드 방향 SYN만 보여야 정상
nft list ruleset -a                    # counter 확인
SYNPROXY 옵션의미설정 시 주의
mss 클라이언트에게 알려줄 MSS 값 백엔드 서버의 실제 MSS와 일치시켜야 합니다
wscale window scale 팩터 백엔드의 tcp_window_scaling 설정과 맞춰야 합니다
timestamp TCP timestamp 옵션 사용 백엔드가 timestamp를 사용하지 않으면 빼야 합니다
sack-perm SACK 허용 옵션 백엔드가 SACK을 지원해야 합니다
성능 영향: SYNPROXY는 방화벽이 두 개의 TCP 연결을 관리하므로 conntrack 엔트리가 2배로 늘어납니다. 대규모 서비스에서는 nf_conntrack_max를 충분히 높이고, SYNPROXY가 보호하는 포트 범위를 좁게 유지해야 합니다. 모든 포트에 SYNPROXY를 거는 것은 비효율적입니다.

rp_filter: Reverse Path Filtering으로 스푸핑 차단

rp_filter(Reverse Path Filter)는 수신 패킷의 소스 주소가 해당 인터페이스로 라우팅 가능한지 검증하여 IP 스푸핑을 차단합니다. RFC 3704의 BCP 38을 커널에서 구현한 것으로, strict 모드와 loose 모드의 동작 차이를 정확히 이해해야 비대칭 라우팅 환경에서 오탐을 피할 수 있습니다.

rp_filter: strict(1) vs loose(2) 비교 strict mode (rp_filter=1) loose mode (rp_filter=2) 수신 패킷 src: 10.0.1.5 iface: eth0 FIB lookup 10.0.1.5 → eth0? PASS (같은 iface) DROP (다른 iface) 일치 불일치 strict: 수신 인터페이스와 FIB best 경로 일치 필수 비대칭 라우팅 시 정상 패킷도 drop 가능 단일 업링크 서버에 권장 수신 패킷 src: 10.0.2.8 iface: eth1 FIB lookup 10.0.2.8 → 아무 iface? PASS (경로 존재) DROP (경로 없음) 존재 없음 loose: FIB에 해당 소스로의 경로가 존재하면 통과 인터페이스 일치는 검사하지 않음 멀티홈/비대칭 라우팅 환경에 적합
strict 모드는 수신 인터페이스와 FIB best 경로가 일치해야 통과시키고, loose 모드는 소스로의 경로가 존재하기만 하면 통과시킵니다.
/* net/ipv4/fib_frontend.c — __fib_validate_source() 핵심 로직 */
static int __fib_validate_source(struct sk_buff *skb,
                                  __be32 src, __be32 dst,
                                  u8 tos, int oif,
                                  struct net_device *dev,
                                  int rpf, struct in_device *idev)
{
    struct fib_result res;
    int ret;

    /* src 주소로 FIB lookup 수행 */
    ret = fib_lookup(net, &fl4, &res, 0);
    if (ret)
        goto last_resort;  /* 경로 없음 → drop */

    /* strict mode: 수신 dev와 FIB 결과 dev 비교 */
    if (rpf == 1) {
        if (res.fi->fib_dev != dev)
            ret = -EXDEV;  /* 인터페이스 불일치 → drop */
    }
    /* loose mode: 경로 존재 여부만 확인 (dev 비교 안 함) */

    return ret;
}
비대칭 라우팅 문제: BGP 멀티홈 환경에서 패킷이 eth0으로 들어오지만 응답은 eth1로 나가는 비대칭 경로가 있을 때, strict 모드는 정상 트래픽도 drop합니다. 이런 환경에서는 rp_filter=2를 사용하고, 스위치/라우터 레벨의 uRPF(Unicast RPF)와 조합하는 것이 현실적입니다.
rp_filter 값검증 기준적합한 환경주의점
0 (비활성화) 검증 안 함 스푸핑 방어 불필요한 내부망 외부 인터페이스에서는 권장하지 않음
1 (strict) 수신 인터페이스 = FIB best 경로 단일 업링크, 대칭 라우팅 비대칭 경로 시 오탐, VRF 사용 시 주의
2 (loose) FIB에 소스 경로 존재 여부만 멀티홈, BGP, 비대칭 라우팅 같은 AS 내 스푸핑은 못 잡음
conf.all과 conf.{iface} 우선순위: rp_filterconf.all과 인터페이스별 값 중 큰 값이 적용됩니다. 즉 conf.all.rp_filter=1이면 개별 인터페이스에서 0으로 설정해도 strict가 적용됩니다. 이 동작을 모르면 비대칭 라우팅 문제 해결이 안 됩니다.

rp_filter 실전 설정 예시

# 시나리오 1: 단일 업링크 서버 — strict 권장
sysctl -w net.ipv4.conf.all.rp_filter=1
sysctl -w net.ipv4.conf.default.rp_filter=1

# 시나리오 2: BGP 멀티홈 — loose 사용
sysctl -w net.ipv4.conf.all.rp_filter=0     # all을 0으로 해야 개별 인터페이스 설정 가능
sysctl -w net.ipv4.conf.eth0.rp_filter=2    # 외부 인터페이스는 loose
sysctl -w net.ipv4.conf.eth1.rp_filter=2    # 두 번째 업링크도 loose
sysctl -w net.ipv4.conf.eth2.rp_filter=1    # 내부 인터페이스는 strict

# 시나리오 3: VRF 환경 — VRF master에서 설정
sysctl -w net.ipv4.conf.vrf-mgmt.rp_filter=1
sysctl -w net.ipv4.conf.vrf-prod.rp_filter=2

# 드롭 카운터 확인 (rp_filter에 의한 드롭)
nstat -az IpInAddrErrors  # rp_filter 드롭 시 증가

# 진단: 특정 소스가 rp_filter에 걸리는지 확인
ip route get 10.0.1.5 from 0.0.0.0 iif eth0  # FIB 경로 확인
관련 sysctl기본값보안 효과
accept_source_route 0 IP 소스 라우팅 옵션 거부 — 스푸핑 경로 조작 방지
accept_redirects 1 ICMP redirect 수신 허용. 서버에서는 0 권장 — 라우팅 조작 방지
send_redirects 1 ICMP redirect 송신. 라우터가 아닌 호스트에서는 0 권장
log_martians 0 비정상 소스 주소 패킷 로그. 진단 시 1 활성화
secure_redirects 1 게이트웨이 목록에 있는 곳에서 온 redirect만 수용
IPv6 스푸핑 방지: IPv6에서는 rp_filter가 기본적으로 지원되지 않습니다. 대신 accept_ra=0(서버), accept_redirects=0, nftables에서 소스 주소 검증 규칙을 명시적으로 추가해야 합니다. IPv6 NDP spoofing은 RA Guard, SEND(Secure Neighbor Discovery)로 대응합니다.

TCP 보안 파라미터 하드닝

TCP 스택의 보안 관련 sysctl 파라미터는 개별적으로 보면 간단하지만, 조합에 따라 보안과 성능에 큰 차이가 납니다. 여기서는 tcp_syncookies, tcp_timestamps, tcp_rfc1337, tcp_tw_reuse 등의 상호작용을 정리합니다.

TCP 보안 파라미터 계층과 상호 영향 연결 수립 보호 tcp_syncookies (0/1/2) tcp_max_syn_backlog tcp_synack_retries tcp_abort_on_overflow somaxconn 프로토콜 보안 tcp_timestamps (0/1/2) tcp_rfc1337 tcp_window_scaling tcp_sack tcp_ecn TIME_WAIT / 재사용 tcp_tw_reuse (0/1/2) tcp_max_tw_buckets tcp_fin_timeout tcp_challenge_ack_limit 연동 연동
TCP 보안 파라미터는 연결 수립, 프로토콜 보안, TIME_WAIT 관리 세 그룹으로 나뉘며 서로 영향을 줍니다.
파라미터기본값보안 권장상세 설명
tcp_timestamps 1 1 (활성화 유지) PAWS(Protection Against Wrapped Sequences)에 필수입니다. 끄면 고속 연결에서 시퀀스 번호 충돌 위험이 커집니다. syncookie도 timestamp 비트를 활용합니다.
tcp_rfc1337 0 1 (활성화) RFC 1337의 TIME-WAIT 암살 문제를 방지합니다. TIME_WAIT 상태의 소켓이 RST를 받아도 즉시 닫지 않고 2MSL을 유지합니다.
tcp_tw_reuse 2 상황에 따라 0=비활성, 1=outgoing 연결에서 TIME_WAIT 재사용, 2=loopback만 재사용. timestamp가 활성화되어야 안전하게 동작합니다.
tcp_challenge_ack_limit 1000 충분히 높게 CVE-2016-5696 공격 대응. 초당 challenge ACK 수를 제한하여 off-path 공격자가 연결 상태를 추측하는 것을 어렵게 합니다.
tcp_synack_retries 5 2-3 SYN+ACK 재전송 횟수를 줄이면 SYN Flood 시 반개방 연결의 메모리 체류 시간이 단축됩니다.
tcp_fin_timeout 60 30 FIN_WAIT_2 상태의 최대 대기 시간. 짧게 줄이면 소켓 자원 회수가 빨라지지만 느린 클라이언트에 영향을 줄 수 있습니다.
# TCP 보안 하드닝 권장 설정
sysctl -w net.ipv4.tcp_syncookies=1
sysctl -w net.ipv4.tcp_timestamps=1
sysctl -w net.ipv4.tcp_rfc1337=1
sysctl -w net.ipv4.tcp_synack_retries=3
sysctl -w net.ipv4.tcp_max_syn_backlog=8192
sysctl -w net.core.somaxconn=8192
sysctl -w net.ipv4.tcp_fin_timeout=30
sysctl -w net.ipv4.tcp_max_tw_buckets=200000

# challenge ACK limit — CVE-2016-5696 대응
sysctl -w net.ipv4.tcp_challenge_ack_limit=999999999

# tcp_tw_reuse — timestamp와 함께 사용해야 안전
sysctl -w net.ipv4.tcp_tw_reuse=2  # loopback만 재사용 (기본값)

# 확인
sysctl -a | grep -E 'tcp_(syncookies|timestamps|rfc1337|synack_retries|max_syn|tw_reuse|challenge_ack|fin_timeout)'
tcp_timestamps를 끄지 마세요: timestamp를 끄면 PAWS 보호가 비활성화되고, syncookie의 window scale 복원도 제한됩니다. 보안 스캔 도구가 timestamp에서 uptime을 추측한다는 이유로 끄는 경우가 있지만, 이로 인한 보안 향상보다 프로토콜 보호 손실이 훨씬 큽니다.

Netfilter 보안 기능과 conntrack 상태 추적

Netfilter는 단순한 패킷 필터가 아니라 conntrack 상태 머신, ct helper(ALG), set/meter, flowtable 등 다양한 보안 기능을 제공합니다. 이 기능들을 보안 관점에서 올바르게 사용하려면 각 기능의 공격 표면과 성능 영향을 이해해야 합니다.

Netfilter 보안 기능 계층 conntrack 상태 추적 (nf_conntrack) NEW → ESTABLISHED → RELATED | INVALID | UNTRACKED stateful 정책 ct state 기반 필터링 INVALID → drop ESTABLISHED → accept ct helper (ALG) FTP, SIP, PPTP 등 데이터 채널 자동 허용 보안 위험: 공격 표면 확대 set / meter IP set 기반 매칭 per-source rate limiting 동적 차단 리스트 ct timeout 서비스별 timeout 분리 UDP: 15s vs 120s TCP SYN_RECV: 30s 보안 권장 정책 패턴 1. ESTABLISHED,RELATED → accept (빠른 경로) 2. INVALID → drop (비정상 상태 전이 차단) 3. NEW + 서비스 매칭 → accept (허용된 새 연결만)
conntrack 상태 추적을 기반으로 stateful 정책, ct helper, set/meter, ct timeout이 계층적으로 동작합니다.
conntrack 상태의미보안 정책
NEW 새 연결의 첫 패킷 서비스 포트별로 선택적 허용
ESTABLISHED 양방향 패킷이 관찰된 연결 기본 허용 (fast path)
RELATED 기존 연결과 관련된 새 연결 (ICMP 오류, ct helper) 허용하되 helper 범위 제한 필요
INVALID 어떤 알려진 연결에도 속하지 않는 패킷 반드시 drop — 스캔, 잘못된 상태 전이, 조작된 패킷
UNTRACKED raw 테이블에서 notrack 처리된 패킷 SYNPROXY 흐름 또는 stateless 서비스에 제한적 사용
# ct helper 보안: 자동 할당 비활성화 (명시적 할당만 허용)
sysctl -w net.netfilter.nf_conntrack_helper=0

# 명시적 ct helper 할당 (nftables)
nft add ct helper inet filter ftp-helper '{ type "ftp" protocol tcp; }'
nft add rule inet filter input tcp dport 21 ct helper set ftp-helper

# INVALID 패킷 반드시 drop
nft add rule inet filter input ct state invalid drop
nft add rule inet filter forward ct state invalid drop

# 서비스별 ct timeout 분리
nft add ct timeout inet filter aggressive-timeout '{ protocol udp; l3proto ip; policy = { unreplied : 10, replied : 30 }; }'
nft add rule inet filter prerouting udp dport 53 ct timeout set aggressive-timeout

# INVALID 로그 활성화 (디버깅용)
sysctl -w net.netfilter.nf_conntrack_log_invalid=6  # TCP만
ct helper 보안 위험: nf_conntrack_helper=1(자동 할당)이면 FTP, SIP, IRC 등의 helper가 패킷 내용을 파싱하여 동적으로 포트를 열어줍니다. 이는 공격자가 조작된 패킷으로 의도하지 않은 포트를 열 수 있는 공격 표면이 됩니다. 반드시 nf_conntrack_helper=0으로 설정하고 필요한 helper만 명시적으로 할당하세요.

DDoS 완화 커널 경로: rate limiting, early drop, XDP 방어

DDoS 공격을 커널에서 완화하는 전략은 가능한 한 앞단에서, 가능한 한 적은 비용으로 드롭하는 것입니다. XDP → TC ingress → raw/prerouting → filter/input 순서로 방어 계층을 구성하면, 각 계층에서 걸러지지 않은 트래픽만 다음 계층으로 넘어갑니다.

DDoS 완화 커널 계층별 방어 경로 XDP sk_buff 미생성 비용: 최소 10M+ pps 처리 TC ingress sk_buff 생성됨 비용: 낮음 BPF classifier raw / prerouting conntrack 이전 비용: 중간 notrack, stateless 필터 filter / input conntrack 이후 비용: 높음 stateful 정책 Socket App 방어 원칙: 앞단에서 드롭할수록 per-packet 비용이 작다 XDP (10ns) → TC (50ns) → nftables raw (200ns) → nftables filter+ct (500ns+) XDP 방어 패턴 1. 블랙리스트 IP → XDP_DROP 2. 반사 포트 (53,123,1900) rate limit 3. 비정상 헤더 (TTL=0, len 불일치) drop 4. SYN flood → XDP SYN cookie 5. cpumap redirect → 분석용 격리 nftables 방어 패턴 1. meter per-source rate limit 2. set 기반 동적 차단 리스트 3. ct timeout 서비스별 분리 4. SYNPROXY (검증된 연결만 전달) 5. conntrack early_drop 활성화
XDP에서 Socket까지 방어 계층을 쌓되, 각 계층의 per-packet 비용과 처리 능력이 다르므로 적절히 분배합니다.
/* XDP에서 SYN cookie를 직접 처리하는 패턴 (의사 코드) */
SEC("xdp")
int xdp_syn_cookie(struct xdp_md *ctx)
{
    struct ethhdr *eth;
    struct iphdr *iph;
    struct tcphdr *th;

    if (!parse_headers(ctx, &eth, &iph, &th))
        return XDP_PASS;

    /* SYN만 처리 (SYN+ACK, ACK는 통과) */
    if (!(th->syn && !th->ack))
        return XDP_PASS;

    /* per-source SYN rate 확인 */
    struct rate_info *ri = bpf_map_lookup_elem(&syn_rate, &iph->saddr);
    if (ri && ri->count > SYN_RATE_LIMIT)
        return XDP_DROP;

    /* XDP에서 SYN+ACK 생성 (bpf_xdp_adjust_head 사용) */
    /* cookie ISN 부호화: hash(saddr,daddr,sport,dport,secret) */
    generate_syn_ack_cookie(ctx, eth, iph, th);

    return XDP_TX;  /* SYN+ACK를 같은 인터페이스로 전송 */
}
conntrack early_drop: conntrack 테이블이 nf_conntrack_max에 근접하면 커널은 자동으로 비보증(unconfirmed) 엔트리부터 제거합니다. 이 메커니즘은 nf_conntrack_max를 올바르게 설정했을 때만 유효하며, 공격 시 정상 연결이 함께 제거될 수 있으므로 서비스별 ct timeout 분리가 중요합니다.

XDP SYN cookie vs 커널 syncookie 비교

최근 커널(5.19+)에서는 XDP 레벨에서 직접 SYN cookie를 처리하는 기능이 추가되었습니다. 이는 기존 TCP 스택의 syncookie와 비교하여 sk_buff 생성 자체를 회피하므로 훨씬 높은 PPS를 처리할 수 있습니다.

비교 항목커널 syncookieXDP SYN cookie
처리 위치 TCP 스택 (tcp_conn_request) XDP 드라이버 수준
sk_buff 생성 필요 (생성 후 처리) 불필요 (xdp_md에서 직접 처리)
처리 성능 수백만 pps 수준 수천만 pps 수준
TCP 옵션 지원 MSS (4단계), timestamp (제한적) 구현에 따라 다름 (보통 더 제한적)
conntrack 영향 conntrack 이후에 동작 conntrack 이전에 동작 (conntrack 부하 감소)
설정 복잡도 sysctl 하나 (tcp_syncookies=1) BPF 프로그램 작성/배포 필요
적합한 상황 일반 서버, 즉시 대응 대규모 DDoS, 전용 방어 호스트

DDoS 방어 계층별 비용 분석

각 방어 계층에서 패킷 하나를 처리하는 데 소요되는 CPU 사이클과 메모리 접근 횟수는 크게 다릅니다. 이 차이를 이해하면 방어 규칙의 최적 배치를 결정할 수 있습니다.

계층per-packet 시간(추정)메모리 접근가능한 작업제약
NIC 하드웨어 필터 0 (회선 속도) 없음 5-tuple 매칭, VLAN, MAC 필터 규칙 수 매우 제한적
XDP (native) ~10-50ns 최소 (L2/L3 헤더) 해시, map lookup, 헤더 파싱, TX sk_buff 없음, conntrack 없음
TC ingress ~50-100ns sk_buff 생성 BPF classifier, redirect, mark XDP보다 약간 늦지만 유연
nftables raw ~100-300ns nftables VM 실행 set, meter, notrack, stateless 매칭 conntrack 이전, stateful 불가
nftables filter + ct ~300-1000ns conntrack 해시 + ct 생성 ct state, SYNPROXY, ct helper conntrack 메모리/CPU 비용
Socket/App ~1-10us+ 소켓 할당, 앱 파싱 L7 검사, 앱 로직 가장 비쌈, 최후 방어선
# nftables 동적 차단 리스트 (meter + set)
nft add table inet ddos
nft 'add chain inet ddos input { type filter hook input priority filter; policy accept; }'

# per-source SYN rate limit: 초당 100개 초과 시 drop
nft add rule inet ddos input tcp flags syn \
    meter syn_flood '{ ip saddr timeout 30s limit rate over 100/second }' drop

# 동적 차단 set: 과도한 소스를 자동으로 차단 리스트에 추가
nft add set inet ddos blocklist '{ type ipv4_addr; timeout 5m; }'
nft add rule inet ddos input ip saddr @blocklist drop
nft add rule inet ddos input tcp flags syn \
    meter syn_detect '{ ip saddr timeout 10s limit rate over 500/second }' \
    add @blocklist '{ ip saddr timeout 5m }'

# 확인
nft list set inet ddos blocklist
nft list meter inet ddos syn_flood

네트워크 네임스페이스와 컨테이너 네트워크 격리

네트워크 네임스페이스는 Linux 커널의 핵심 격리 메커니즘으로, 각 네임스페이스가 독립적인 네트워크 스택(인터페이스, 라우팅 테이블, iptables/nftables 규칙, conntrack 테이블, 소켓)을 가집니다. 컨테이너 환경에서는 이 격리가 보안의 기본이지만, veth pair를 통한 통신과 bridge 구성에서 보안 허점이 생길 수 있습니다.

네트워크 네임스페이스 격리와 veth 보안 호스트 네임스페이스 eth0 (물리) br0 (bridge) veth-host-1 veth-host-2 컨테이너 NS 1 eth0 (veth-ns-1) 독립 라우팅 테이블 독립 nftables 규칙 독립 conntrack 컨테이너 NS 2 eth0 (veth-ns-2) 독립 라우팅 테이블 독립 nftables 규칙 독립 conntrack veth pair veth pair 보안 고려사항 1. bridge에서 컨테이너 간 L2 통신 제어 필요 2. 호스트 NS의 rp_filter/forwarding 설정 확인 3. NET_ADMIN capability 제한 (CAP_NET_ADMIN) 4. veth 트래픽에 BPF 정책 적용 5. 네임스페이스별 sysctl 독립 설정
각 네트워크 네임스페이스는 독립적인 네트워크 스택을 가지지만, veth pair와 bridge 구성에서 보안 허점이 생길 수 있습니다.
격리 요소네임스페이스 별 독립보안 주의점
네트워크 인터페이스 예 (각 NS에 자체 인터페이스) veth pair 반대쪽이 호스트 bridge에 연결되면 L2 통신 가능
라우팅 테이블 호스트의 ip_forward 설정이 NS 간 라우팅에 영향
nftables/iptables 호스트 FORWARD 체인이 NS 간 트래픽을 제어
conntrack 예 (커널 4.7+, net.netfilter.nf_conntrack_max per-NS) 한 NS의 conntrack 고갈이 다른 NS에 영향 안 줌
소켓 CAP_NET_RAW 없으면 raw 소켓 사용 불가
sysctl (net.*) 부분적 (네트워크 관련 sysctl은 NS별 독립) 일부 전역 sysctl은 init NS에서만 변경 가능
# 컨테이너 네임스페이스 보안 설정 예시

# 1. 호스트에서 컨테이너 간 bridge 트래픽 제어
sysctl -w net.bridge.bridge-nf-call-iptables=1
sysctl -w net.bridge.bridge-nf-call-ip6tables=1

# 2. 컨테이너 NS 내부 하드닝
ip netns exec container1 sysctl -w net.ipv4.conf.all.rp_filter=1
ip netns exec container1 sysctl -w net.ipv4.conf.all.accept_redirects=0
ip netns exec container1 sysctl -w net.ipv4.conf.all.send_redirects=0
ip netns exec container1 sysctl -w net.ipv4.icmp_echo_ignore_broadcasts=1

# 3. veth에 BPF 정책 적용 (TC ingress)
tc qdisc add dev veth-host-1 clsact
tc filter add dev veth-host-1 ingress bpf da obj container_policy.o sec classifier

# 4. 컨테이너별 conntrack 제한
ip netns exec container1 sysctl -w net.netfilter.nf_conntrack_max=65536

# 5. 네임스페이스 확인
ip netns list
ip netns exec container1 ip link show
ip netns exec container1 ss -lnt
bridge-nf-call-iptables: 이 sysctl이 1이면 bridge를 통과하는 L2 트래픽도 iptables/nftables FORWARD 체인을 거칩니다. Docker/Kubernetes 환경에서는 기본 활성화되어 있지만, 비활성화하면 컨테이너 간 격리 정책이 우회될 수 있습니다.

컨테이너 탈출 방지와 네트워크 격리 강화

컨테이너 네트워크 격리는 네임스페이스만으로 완전하지 않습니다. capability 제한, seccomp 필터, AppArmor/SELinux 정책이 함께 적용되어야 네트워크 관련 syscall 남용을 막을 수 있습니다.

위협공격 벡터방어 수단
raw 소켓 스니핑 CAP_NET_RAW로 같은 NS 내 트래픽 캡처 CAP_NET_RAW 제거 (Docker: --cap-drop NET_RAW)
네트워크 규칙 변경 CAP_NET_ADMIN으로 nftables/라우팅 조작 CAP_NET_ADMIN 제거 또는 seccomp 필터
NS 간 통신 우회 bridge 경유 L2 직접 통신 bridge-nf-call-iptables + FORWARD 정책
호스트 네트워크 접근 --network=host 사용 프로덕션에서 host 네트워크 모드 금지
conntrack 고갈 공격 한 컨테이너가 대량 연결 생성 NS별 nf_conntrack_max 제한
# Docker 컨테이너 네트워크 보안 실행 예시
docker run --cap-drop ALL --cap-add NET_BIND_SERVICE \
    --security-opt no-new-privileges \
    --sysctl net.ipv4.conf.all.rp_filter=1 \
    --sysctl net.ipv4.conf.all.accept_redirects=0 \
    --sysctl net.ipv4.icmp_echo_ignore_broadcasts=1 \
    --network bridge \
    my-app:latest

# Kubernetes NetworkPolicy 예시 (YAML)
# apiVersion: networking.k8s.io/v1
# kind: NetworkPolicy
# spec:
#   podSelector: {matchLabels: {app: web}}
#   policyTypes: [Ingress, Egress]
#   ingress:
#   - from: [{podSelector: {matchLabels: {app: frontend}}}]
#     ports: [{protocol: TCP, port: 8080}]

소켓 보안: SO_BINDTODEVICE, SO_MARK, cgroup BPF

소켓 수준의 보안 옵션은 네트워크 정책을 프로세스와 연결 단위로 적용할 수 있게 합니다. SO_BINDTODEVICE로 특정 인터페이스에만 바인딩하고, SO_MARK로 패킷에 fwmark를 설정하여 nftables/라우팅 정책과 연동하며, cgroup BPF로 프로세스 그룹별 네트워크 접근을 제어합니다.

소켓 보안: 인터페이스 바인딩, fwmark, cgroup BPF SO_BINDTODEVICE 소켓을 특정 인터페이스에 바인딩 CAP_NET_RAW 필요 관리 인터페이스 격리에 유용 SO_MARK (fwmark) 패킷에 마크 설정 → 정책 연동 nftables meta mark 매칭 ip rule fwmark 라우팅 분리 cgroup BPF 프로세스 그룹별 네트워크 제어 BPF_CGROUP_INET_INGRESS BPF_CGROUP_INET_EGRESS 소켓 보안 옵션 비교 인터페이스 격리 관리 네트워크 분리 서비스별 인터페이스 바인딩 VRF와 조합 가능 정책 라우팅 연동 fwmark → ip rule → 라우팅 테이블 nftables mark 기반 필터링 QoS 분류 프로세스 그룹 제어 컨테이너별 네트워크 정책 connect/bind 제한 대역폭 제한
SO_BINDTODEVICE는 인터페이스를, SO_MARK는 정책 라우팅/방화벽을, cgroup BPF는 프로세스 그룹별 네트워크를 제어합니다.
/* SO_BINDTODEVICE 사용 예시 */
int bind_to_device(int sockfd, const char *ifname)
{
    /* CAP_NET_RAW 권한 필요 */
    return setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
                      ifname, strlen(ifname) + 1);
}

/* SO_MARK 사용 예시 */
int set_fwmark(int sockfd, uint32_t mark)
{
    /* CAP_NET_ADMIN 권한 필요 */
    return setsockopt(sockfd, SOL_SOCKET, SO_MARK,
                      &mark, sizeof(mark));
}

/* cgroup BPF: 소켓 연결 제어 (의사 코드) */
SEC("cgroup/connect4")
int cg_connect4(struct bpf_sock_addr *ctx)
{
    /* 특정 IP 대역으로의 연결만 허용 */
    if ((ctx->user_ip4 & bpf_htonl(0xFFFF0000)) !=
        bpf_htonl(0x0A000000))  /* 10.0.0.0/16만 허용 */
        return 0;  /* 차단 */

    return 1;  /* 허용 */
}
소켓 옵션권한용도보안 효과
SO_BINDTODEVICE CAP_NET_RAW 특정 인터페이스에만 소켓 바인딩 관리 네트워크 격리, VRF 바인딩
SO_MARK CAP_NET_ADMIN 패킷에 fwmark 설정 정책 라우팅, nftables 매칭, QoS 분류
SO_INCOMING_CPU 없음 (읽기 전용) 패킷 수신 CPU 확인 RFS/NAPI 최적화 모니터링
SO_PRIORITY CAP_NET_ADMIN (6 이상) 패킷 우선순위 설정 TC qdisc 분류, VLAN PCP 매핑
TCP_DEFER_ACCEPT 없음 데이터 수신 전까지 accept 지연 Slowloris 류 공격 완화
fwmark + 정책 라우팅: SO_MARK로 설정한 fwmark는 ip rule의 fwmark 매칭과 연동됩니다. 예를 들어 관리 트래픽에 mark 100을 설정하고, ip rule add fwmark 100 table mgmt로 별도 라우팅 테이블을 사용하면 관리 네트워크를 서비스 네트워크로부터 완전히 분리할 수 있습니다.

cgroup BPF 네트워크 제어 상세

cgroup BPF는 프로세스 그룹 단위로 네트워크 접근을 제어하는 강력한 메커니즘입니다. BPF_CGROUP_INET_INGRESS, BPF_CGROUP_INET_EGRESS, BPF_CGROUP_INET4_CONNECT, BPF_CGROUP_INET4_BIND 등 다양한 attach point에서 BPF 프로그램을 실행할 수 있습니다.

attach type동작 시점제어 가능 항목사용 예시
BPF_CGROUP_INET_INGRESS 패킷 수신 시 수신 패킷 drop/accept 특정 cgroup의 인바운드 트래픽 제한
BPF_CGROUP_INET_EGRESS 패킷 송신 시 송신 패킷 drop/accept, mark 설정 아웃바운드 대역폭 제한, 정책 라우팅
BPF_CGROUP_INET4_CONNECT connect() 호출 시 대상 IP/포트 변경, 차단 서비스 메시 투명 프록시, 접근 제어
BPF_CGROUP_INET4_BIND bind() 호출 시 바인드 주소/포트 제어 컨테이너의 포트 바인딩 제한
BPF_CGROUP_UDP4_SENDMSG UDP sendmsg() 시 대상 주소 변경, 차단 DNS 쿼리 리다이렉션
BPF_CGROUP_SOCK_OPS TCP 이벤트 시 TCP 파라미터 튜닝 서비스별 TCP congestion 알고리즘 변경
# cgroup BPF 프로그램 attach 예시

# 1. cgroup 생성
mkdir -p /sys/fs/cgroup/net_restricted

# 2. BPF 프로그램 컴파일
clang -O2 -target bpf -c cgroup_net_policy.c -o cgroup_net_policy.o

# 3. BPF 프로그램 attach
bpftool cgroup attach /sys/fs/cgroup/net_restricted connect4 \
    pinned /sys/fs/bpf/cgroup_connect4

# 4. 프로세스를 해당 cgroup에 배치
echo $PID > /sys/fs/cgroup/net_restricted/cgroup.procs

# 5. 현재 attach된 BPF 프로그램 확인
bpftool cgroup show /sys/fs/cgroup/net_restricted

ftrace/bpftrace로 네트워크 보안 모니터링

네트워크 보안 이벤트를 실시간으로 추적하려면 커널의 tracing 인프라를 활용해야 합니다. ftrace의 kprobe/tracepoint와 bpftrace를 사용하면 conntrack 엔트리 생성/삭제, SYN backlog 상태, XDP 드롭, Netfilter 규칙 매칭 등을 프로덕션 환경에서도 안전하게 관찰할 수 있습니다.

네트워크 보안 모니터링: 도구와 관측 계층 사용자 공간 도구 nstat / ss conntrack -E bpftrace perf ethtool -S 커널 tracing 인프라 tracepoint (안정 ABI) kprobe (동적) perf_event BPF map XDP 계층 xdp:xdp_exception xdp:xdp_redirect_err Netfilter 계층 nf_conntrack_alloc nf_hook_slow TCP 계층 tcp:tcp_receive_reset tcp_syn_flood_action NIC 계층 ethtool counters softnet_stat 원칙: tracepoint(안정) 우선 → kprobe(동적)는 tracepoint 없을 때만 → map은 주기적 clear 프로덕션에서 kprobe는 커널 업데이트 시 함수명 변경에 주의
사용자 공간 도구가 커널 tracing 인프라를 통해 각 네트워크 계층의 보안 이벤트를 관찰합니다.
# 1. ftrace: tcp_syn_flood_action 호출 추적
echo 1 > /sys/kernel/debug/tracing/events/tcp/tcp_bad_csum/enable
echo 'p:syn_flood tcp_syn_flood_action' > /sys/kernel/debug/tracing/kprobe_events
echo 1 > /sys/kernel/debug/tracing/events/kprobes/syn_flood/enable
cat /sys/kernel/debug/tracing/trace_pipe

# 2. bpftrace: SYN 수신 rate 관측
bpftrace -e '
tracepoint:tcp:tcp_receive_reset {
    @rst[args->saddr] = count();
}
interval:s:5 { print(@rst); clear(@rst); }
'

# 3. bpftrace: conntrack 엔트리 생성 추적
bpftrace -e '
kprobe:__nf_conntrack_alloc {
    @ct_alloc = count();
}
kprobe:nf_conntrack_free {
    @ct_free = count();
}
interval:s:10 {
    printf("alloc: %d, free: %d\n", @ct_alloc, @ct_free);
    clear(@ct_alloc); clear(@ct_free);
}
'

# 4. bpftrace: XDP 드롭 카운터 추적
bpftrace -e '
tracepoint:xdp:xdp_bulk_tx {
    @xdp_act[args->action] = count();
}
interval:s:5 { print(@xdp_act); clear(@xdp_act); }
'

# 5. conntrack 이벤트 실시간 모니터링
conntrack -E -e NEW,DESTROY -p tcp --dport 80

# 6. bpftrace: nftables drop 원인 분석
bpftrace -e '
kprobe:nf_hook_slow {
    @hook[arg1] = count();
}
interval:s:10 { print(@hook); clear(@hook); }
'

# 7. perf: Netfilter/conntrack CPU 프로파일링
perf record -g -a -e cycles -- sleep 30
perf report --sort=dso,comm
추적 대상도구probe/tracepoint관찰 지표
SYN flood 발동 bpftrace kprobe:tcp_syn_flood_action 발동 빈도, 소스 분포
syncookie 발급 nstat TcpExtSyncookiesSent 초당 cookie 발급 수
conntrack 생성/삭제 conntrack -E ctnetlink 이벤트 엔트리 증감 속도
XDP 액션 bpftrace tracepoint:xdp:* DROP/PASS/TX/REDIRECT 비율
Netfilter 처리 시간 perf CPU 프로파일링 nf_hook_slow, conntrack lookup 비용
TCP RST 수신 bpftrace tracepoint:tcp:tcp_receive_reset RST 폭주 감지
프로덕션 tracing 주의: kprobe는 커널 버전에 따라 함수 이름이 변할 수 있습니다. tracepoint(안정 ABI)를 우선 사용하고, kprobe는 tracepoint가 없는 경우에만 사용하세요. 또한 bpftrace의 map 크기가 커지면 메모리를 소비하므로 interval 블록에서 주기적으로 clear()하는 것이 중요합니다.

네트워크 보안 종합 체크리스트

지금까지 다룬 커널 네트워크 보안 설정을 운영 환경에 적용할 때 빠짐없이 확인해야 할 체크리스트입니다. 서버 역할(웹 서버, 라우터/방화벽, 컨테이너 호스트)에 따라 강조되는 항목이 다릅니다.

네트워크 보안 체크리스트: 배포 프로파일별 우선순위 웹 서버 프로파일 tcp_syncookies=1 somaxconn=8192+ tcp_rfc1337=1 tcp_timestamps=1 rp_filter=1 nf_conntrack_helper=0 accept_redirects=0 tcp_challenge_ack_limit 상향 라우터/방화벽 프로파일 nf_conntrack_max 충분히 확보 ct timeout 서비스별 분리 SYNPROXY (필요 시) rp_filter=1 또는 2 XDP 조기 차단 (고 PPS) INVALID drop 규칙 ip_forward sysctl 보안 검토 fragment threshold 조정 컨테이너 호스트 프로파일 bridge-nf-call-iptables=1 NS별 conntrack 제한 CAP_NET_ADMIN 제한 veth BPF 정책 적용 cgroup BPF 네트워크 제어 NS별 sysctl 하드닝 seccomp 네트워크 syscall 제한 네트워크 정책 (NetworkPolicy)
서버 역할에 따라 강조되는 보안 항목이 다릅니다. 초록 테두리가 최우선, 주황이 권장, 회색이 상황별 적용입니다.
분류sysctl / 설정권장값확인 명령
TCP 하드닝 net.ipv4.tcp_syncookies 1 sysctl net.ipv4.tcp_syncookies
net.ipv4.tcp_timestamps 1 sysctl net.ipv4.tcp_timestamps
net.ipv4.tcp_rfc1337 1 sysctl net.ipv4.tcp_rfc1337
net.ipv4.tcp_synack_retries 2-3 sysctl net.ipv4.tcp_synack_retries
net.ipv4.tcp_fin_timeout 30 sysctl net.ipv4.tcp_fin_timeout
IP 스푸핑 방지 net.ipv4.conf.all.rp_filter 1 (비대칭 시 2) sysctl net.ipv4.conf.all.rp_filter
net.ipv4.conf.all.accept_redirects 0 sysctl net.ipv4.conf.all.accept_redirects
net.ipv4.conf.all.send_redirects 0 (라우터가 아닌 경우) sysctl net.ipv4.conf.all.send_redirects
conntrack 보호 net.netfilter.nf_conntrack_max 서비스 규모에 맞게 sysctl net.netfilter.nf_conntrack_max
net.netfilter.nf_conntrack_helper 0 sysctl net.netfilter.nf_conntrack_helper
net.netfilter.nf_conntrack_tcp_loose 0 (SYNPROXY 사용 시) sysctl net.netfilter.nf_conntrack_tcp_loose
ICMP 보호 net.ipv4.icmp_echo_ignore_broadcasts 1 sysctl net.ipv4.icmp_echo_ignore_broadcasts
net.ipv4.icmp_ratelimit 1000 sysctl net.ipv4.icmp_ratelimit
net.ipv4.icmp_ignore_bogus_error_responses 1 sysctl net.ipv4.icmp_ignore_bogus_error_responses
IPv6 보호 net.ipv6.conf.all.accept_ra 0 (서버) / 1 (호스트) sysctl net.ipv6.conf.all.accept_ra
net.ipv6.conf.all.accept_redirects 0 sysctl net.ipv6.conf.all.accept_redirects
# 네트워크 보안 종합 검증 스크립트
#!/bin/bash

echo "=== TCP 하드닝 ==="
sysctl net.ipv4.tcp_syncookies net.ipv4.tcp_timestamps \
       net.ipv4.tcp_rfc1337 net.ipv4.tcp_synack_retries \
       net.ipv4.tcp_fin_timeout net.ipv4.tcp_max_syn_backlog \
       net.core.somaxconn

echo "=== IP 스푸핑 방지 ==="
sysctl net.ipv4.conf.all.rp_filter \
       net.ipv4.conf.all.accept_redirects \
       net.ipv4.conf.all.send_redirects

echo "=== conntrack 상태 ==="
sysctl net.netfilter.nf_conntrack_max \
       net.netfilter.nf_conntrack_helper \
       net.netfilter.nf_conntrack_tcp_loose
cat /proc/sys/net/netfilter/nf_conntrack_count

echo "=== ICMP 보호 ==="
sysctl net.ipv4.icmp_echo_ignore_broadcasts \
       net.ipv4.icmp_ratelimit \
       net.ipv4.icmp_ignore_bogus_error_responses

echo "=== TCP backlog 관측 ==="
nstat -az TcpExtSyncookiesSent TcpExtListenOverflows \
          TcpExtListenDrops TcpExtTCPReqQFullDoCookies

echo "=== NIC 드롭 ==="
for iface in $(ls /sys/class/net/ | grep -v lo); do
    echo "--- $iface ---"
    ethtool -S $iface 2>/dev/null | grep -E 'rx_dropped|rx_errors|rx_missed'
done

echo "=== listener 상태 ==="
ss -lnt
배포 자동화: 위 체크리스트의 sysctl 값은 /etc/sysctl.d/99-network-security.conf에 영속화하고, 배포 도구(Ansible, Puppet 등)로 관리하는 것을 권장합니다. 수동 sysctl -w는 재부팅 시 초기화됩니다.

sysctl 영속화 템플릿

# /etc/sysctl.d/99-network-security.conf
# 네트워크 보안 하드닝 — 서버 프로파일

# === TCP 하드닝 ===
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_rfc1337 = 1
net.ipv4.tcp_synack_retries = 3
net.ipv4.tcp_max_syn_backlog = 8192
net.core.somaxconn = 8192
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_max_tw_buckets = 200000
net.ipv4.tcp_challenge_ack_limit = 999999999

# === IP 스푸핑 방지 ===
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.all.log_martians = 1

# === ICMP 보호 ===
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.icmp_ratelimit = 1000

# === conntrack ===
net.netfilter.nf_conntrack_helper = 0
net.netfilter.nf_conntrack_tcp_loose = 0

# === IPv6 ===
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_ra = 0
단계적 적용: 모든 설정을 한꺼번에 적용하지 마세요. 변경 전후의 카운터(nstat, ethtool -S, conntrack -S)를 비교하고, 정상 트래픽 손실이 없는지 확인한 뒤 다음 항목으로 넘어가세요. 특히 rp_filterct timeout 변경은 비대칭 경로와 장시간 세션에 영향을 줄 수 있습니다.
설정 검증 필수: 보안 설정을 적용한 후 반드시 다음을 확인하세요:
  • TLS handshake가 정상적으로 완료되는지 (openssl s_client로 테스트)
  • DNS 질의가 정상 응답하는지 (conntrack timeout 영향 확인)
  • 비대칭 경로 서비스가 있다면 rp_filter 설정 후 접근 가능 여부
  • 컨테이너 간 통신이 정상인지 (bridge-nf-call 설정 확인)
  • health check 프로브가 rate limit에 걸리지 않는지

참고자료