사전 방화벽
개념

방화벽

gabury1고친 사람 github-actions[bot]

방화벽은 네트워크 경계를 지나는 통신을 골라서 들이고 나머지는 막습니다. 무엇을 들일지는 미리 적어 둔 규칙이 정합니다. 웹 프레임워크 가운데에는 인증이 필요한 구간을 정하는 설정을 방화벽이라 부르는 것도 있습니다. 이 문서는 네트워크의 방화벽을 다룹니다.

쉽고 빠른 이해

방화벽은 네트워크 경계에서 오가는 통신을 골라 들이고 나머지를 막습니다. 웹 서버라면 원격 접속이 오는 22번 포트와 웹 요청이 오는 80번·443번 포트만 열고 그 밖은 버리는 식입니다.

컴퓨터마다 스스로 지키게 두면 한 대만 빠뜨려도 뚫립니다. 그래서 망과 망이 만나는 곳 한 군데에서 가립니다.

어떻게 도는가:

  1. 지나가는 패킷의 앞머리에서 주소, 포트 번호, 프로토콜 종류, 방향을 읽습니다
  2. 규칙 목록과 맞춰 보고 걸린 규칙대로 들이거나 버립니다
  3. 어느 규칙에도 안 걸리면 기본 정책을 따릅니다. 들어오는 통신은 버리도록 두기를 권합니다
  4. 많은 방화벽은 지나간 연결을 기억합니다. 내보낸 요청의 답은 따로 안 열어도 들어옵니다

대가도 있습니다. 앞머리만 보는 방화벽은 열어 준 포트로 들어오는 공격을 못 가립니다. 암호로 감싼 내용도 못 읽습니다. 연결을 기억하는 표는 오래 쉰 연결을 지웁니다. 그 연결은 말없이 끊길 수 있습니다.

상세

건물 현관의 경비실을 떠올려 봅니다. 드나드는 사람마다 어디서 왔고 어디로 가는지 묻습니다. 출입 명부에 없으면 돌려보냅니다.

방화벽은 두 네트워크가 만나는 곳에서 오가는 통신을 규칙과 맞춰 보고 들이거나 버리는 기능입니다. 지키는 쪽을 방화벽의 안쪽, 나머지를 바깥쪽이라 합니다. 안쪽은 회사 망일 수도 있고 컴퓨터 한 대일 수도 있습니다.

방화벽은 전용 장비로 놓입니다. 운영체제 안의 소프트웨어로 돌 수도 있습니다.

방화벽이 보는 값

네트워크로 오가는 데이터는 패킷이라는 작은 덩어리로 나뉘어 갑니다. 패킷 앞머리에는 어디서 와서 어디로 가는지가 적혀 있습니다. 이 앞머리가 헤더입니다. 가장 기본인 방화벽은 헤더의 값만 규칙과 맞춥니다.

맞춰 보는 대표적인 값은 이렇습니다.

값 무엇을 가리나
출발지 IP(Internet Protocol, 인터넷 프로토콜) 주소 패킷을 보낸 컴퓨터
목적지 IP 주소 패킷이 닿으려는 컴퓨터
프로토콜 주고받는 방식. TCP(Transmission Control Protocol, 전송 제어 프로토콜) · UDP(User Datagram Protocol, 사용자 데이터그램 프로토콜) · ICMP(Internet Control Message Protocol, 인터넷 제어 메시지 프로토콜)
출발지·목적지 포트 번호 그 컴퓨터의 어느 프로그램이 주고받나. 방화벽에 따라 볼 수도 있는 값입니다. 웹 서버라면 목적지 포트가 TCP 80 입니다
지나는 네트워크 인터페이스와 방향 어느 연결 단자를 지나나. 들어오는 것인가 나가는 것인가

방향을 따로 보는 까닭은 들어오는 것과 나가는 것에 다른 규칙을 걸기 때문입니다. 들어오는 것은 엄하게 막고 나가는 것은 느슨하게 두는 구성이 대개 쓰입니다. 안쪽 사용자가 바깥의 수많은 서비스에 닿아야 해서입니다.

헤더 값만 보는 방화벽은 패킷 하나하나를 따로 판정합니다. 지나간 연결을 기억해 두고 판정에 쓰는 방화벽도 있습니다. 연결마다 상태를 따라가는 이 일을 연결 추적이라 합니다.

규칙을 맞춰 보는 순서

규칙의 모음을 규칙 집합이라 합니다. 규칙 집합을 맞춰 보는 방법은 방화벽마다 다릅니다.

많은 방화벽은 규칙을 위에서부터 차례로 맞춰 봅니다. 처음 걸린 규칙의 처분을 따릅니다. 그 아래는 더 안 봅니다.

이런 방화벽에서는 순서가 뜻을 집니다. 넓게 허용하는 규칙이 위에 있으면 그 아래의 좁은 차단 규칙은 걸릴 일이 없습니다.

다른 방법을 쓰는 방화벽도 있습니다. 거부 규칙을 먼저 다 보고 나서 허용 규칙을 보는 방화벽이 있습니다. 규칙마다 우선순위 숫자를 매겨 걸린 것 가운데 우선순위가 가장 높은 규칙을 따르는 방화벽도 있습니다.

기본 정책

어느 규칙에도 안 걸린 패킷은 기본 정책이 처분합니다. 기본 정책은 들이는 쪽과 막는 쪽으로 갈립니다.

명시로 허용한 것만 지나가게 하고 나머지는 전부 막는 방식을 기본 차단이라 합니다. 영어로 deny by default 입니다. 공격받을 위험이 줄어듭니다. 망이 나르는 통신량도 줄일 수 있습니다.

반대는 금지한 것만 막고 나머지를 들이는 방식입니다. 호스트와 프로토콜과 애플리케이션은 계속 바뀝니다. 금지 목록은 새로 생긴 것을 모릅니다.

기본 차단에서는 새로 생긴 것도 허용 규칙을 더하기 전까지 막힙니다. 서비스를 띄울 때마다 규칙을 더해야 하는 품이 대가입니다.

버림과 거절

막는 방법은 둘로 갈립니다. 둘 다 통신을 끊습니다. 보낸 쪽이 겪는 것이 다릅니다.

버림 거절
하는 일 패킷을 조용히 지웁니다 못 간다는 오류 패킷을 돌려줍니다
보낸 쪽이 겪는 것 답을 기다리다 정해 둔 시간이 지나야 포기합니다. 이것이 타임아웃입니다 기다리지 않고 바로 실패합니다
돌려주는 것 없음 ICMP 오류 메시지나 TCP RST(reset) 패킷. RST 는 연결을 끊으라는 TCP 의 신호입니다

이 차이는 증상을 읽는 단서가 됩니다. 접속이 한참 걸리다 끊기면 어딘가에서 패킷이 버려지고 있을 수 있습니다. 곧바로 거부되면 거절로 답한 쪽이 있습니다.

방화벽이 못 가리는 것

방화벽은 자기가 읽을 수 있는 통신만 제대로 다룹니다. 이해하지 못하는 통신은 막아야 할 것을 들여보낼 수도 있습니다.

암호로 감싼 통신이 대표입니다. TLS(Transport Layer Security, 전송 계층 보안), SSH(Secure Shell, 보안 셸), IPsec(IP Security, IP 보안) 같은 프로토콜은 내용을 숨깁니다. 암호화한 메일이나 손으로 암호화한 파일도 못 읽습니다.

일부 방화벽은 다른 프로토콜 안에 감싸 보내는 터널 통신도 해석하지 못합니다. 암호화하지 않았어도 감싸는 방식을 모르면 내용을 못 읽습니다. 이렇게 못 읽는 통신을 들일지 막을지도 규칙이 정합니다.

헤더만 보는 방화벽은 열어 준 포트로 들어오는 공격도 못 가립니다. 웹 서버의 80번 포트는 열려 있어야 합니다. 그 포트로 온 요청이 애플리케이션의 허점을 찌르는 것이어도 헤더 값은 규칙에 맞습니다. 내용까지 읽는 방화벽은 아래 「갈래」에서 봅니다.

배경

한 네트워크 안에 컴퓨터가 수십 대 있다고 해 봅니다. 컴퓨터마다 누구를 들일지 스스로 챙기게 두면 한 대만 설정을 빠뜨려도 바깥에서 그 컴퓨터로 들어옵니다. 지킬 곳이 컴퓨터 수만큼 늘어납니다.

그래서 망과 망이 이어지는 곳 한 군데에 정책을 모을 장치가 필요해졌습니다. 작은 안쪽 망을 큰 바깥 망과 이어 주는 길목에 서서 드나드는 통신을 규칙대로 가립니다. 안쪽 망은 인터넷과 맞닿은 회사 망 전체일 수도 있습니다. 회사 안에서 회계나 인사처럼 민감한 일을 맡은 망을 나머지 사내 망과 가르는 데도 씁니다.

이름은 건물의 방화벽에서 빌렸습니다. 건물의 방화벽은 한 구획에 난 불이 옆 구획으로 번지지 않게 막는 벽입니다. 네트워크의 방화벽도 바깥의 위협이 안쪽으로 번지지 않게 경계에 섭니다.

예시

성격이 다른 실물 셋을 봅니다. 리눅스 커널에서 도는 규칙 파일 하나와 클라우드 둘이 두는 규칙 목록입니다.

nftables 의 서버 규칙

nftables 는 리눅스에서 방화벽 규칙을 적는 도구입니다. 아래는 웹 서버 한 대를 지키는 규칙 파일에서 들어오는 패킷을 다루는 체인입니다. 체인은 규칙을 차례로 묶은 목록입니다.

# 로 시작하는 줄은 주석입니다. nftables 는 주석을 규칙으로 읽지 않습니다. 이 파일의 주석은 대개 바로 아래 규칙이 하는 일을 적은 설명입니다. 규칙 줄 앞에 # 를 붙여 꺼 둔 것도 있습니다. 이렇게 꺼 두는 것을 주석 처리라 합니다.

chain inbound {

    # By default, drop all traffic unless it meets a filter
    # criteria specified by the rules that follow below.
    type filter hook input priority 0; policy drop;

    # Allow traffic from established and related packets, drop invalid
    ct state vmap { established : accept, related : accept, invalid : drop }

    # Allow loopback traffic.
    iifname lo accept

    # Jump to chain according to layer 3 protocol using a verdict map
    meta protocol vmap { ip : jump inbound_ipv4, ip6 : jump inbound_ipv6 }

    # Allow SSH on port TCP/22 and allow HTTP(S) TCP/80 and TCP/443
    # for IPv4 and IPv6.
    tcp dport { 22, 80, 443} accept

    # Uncomment to enable logging of denied inbound traffic
    # log prefix "[nftables] Inbound Denied: " counter drop
}

줄마다 정하는 것은 이렇습니다.

줄 정하는 것
policy drop 이 체인은 들어오는 패킷을 거릅니다. 아래 규칙에 하나도 안 걸린 패킷은 버립니다. 기본 차단입니다
ct state vmap {…} 연결 상태를 봅니다. 이미 맺어진 연결(established)과 그에 관련된 연결(related)의 패킷은 받습니다. 상태가 어긋난 패킷(invalid)은 버립니다
iifname lo accept 컴퓨터가 자기 자신에게 보내는 루프백 통신은 받습니다
meta protocol vmap {…} IPv4(IP 네 번째 판) 패킷은 inbound_ipv4 체인으로, IPv6(IP 여섯 번째 판) 패킷은 inbound_ipv6 체인으로 넘깁니다
tcp dport { 22, 80, 443} accept 목적지 포트가 TCP 22(SSH) · 80(HTTP, HyperText Transfer Protocol) · 443(HTTPS, HTTP Secure)이면 받습니다

related 가 어떤 연결을 가리키는지는 이 규칙 파일에 없습니다(미확인).

같은 파일에는 체인이 셋 더 있습니다. 위 체인이 넘겨주는 두 체인과 forward 체인입니다.

chain inbound_ipv4 {
    # accepting ping (icmp-echo-request) for diagnostic purposes.
    # However, it also lets probes discover this host is alive.
    # This sample accepts them within a certain rate limit:
    #
    # icmp type echo-request limit rate 5/second accept
}

chain inbound_ipv6 {
    # accept neighbour discovery otherwise connectivity breaks
    #
    icmpv6 type { nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } accept

    # accepting ping (icmpv6-echo-request) for diagnostic purposes.
    # However, it also lets probes discover this host is alive.
    # This sample accepts them within a certain rate limit:
    #
    # icmpv6 type echo-request limit rate 5/second accept
}

chain forward {
    # Drop everything (assumes this device is not a router)
    type filter hook forward priority 0; policy drop;
}

# no need to define output chain, default policy is accept if undefined.

inbound_ipv6 체인은 이웃 탐색 메시지를 받습니다. 이웃 탐색은 IPv6 컴퓨터가 같은 망의 이웃과 라우터를 찾고 알리는 메시지입니다. 주석은 이 메시지를 막으면 연결이 끊긴다고 적습니다. ICMPv6 은 IPv6 에서 쓰는 ICMP 입니다.

ping 은 상대가 살아 있는지 묻는 ICMP 에코 요청입니다. ping 을 받는 줄은 두 체인 모두 주석 처리돼 있습니다. 주석을 풀면 초당 5번까지만 받도록 양을 묶어 받습니다.

ping 을 받으면 진단에 쓸 수 있습니다. 대신 바깥에서 훑는 쪽도 이 컴퓨터가 살아 있다는 것을 알게 됩니다. inbound_ipv4 에는 주석 처리된 ping 줄뿐이라 받는 규칙이 없습니다.

두 체인에서 받히지 않은 패킷은 inbound 의 다음 줄인 포트 규칙으로 갑니다. 그 줄의 주석도 IPv4 와 IPv6 에 함께 건다고 적습니다. 들어오는 패킷 하나가 지나는 길을 그리면 이렇습니다.

flowchart TD
    P["들어오는 패킷"] --> C{"연결 상태"}
    C -->|"established · related"| A["받는다"]
    C -->|invalid| X["버린다"]
    C -->|"셋 다 아니다"| L{"루프백인가"}
    L -->|예| A
    L -->|아니다| V{"IP 판"}
    V -->|IPv6| N{"이웃 탐색 메시지인가"}
    N -->|예| A
    N -->|아니다| T{"목적지가 TCP 22 · 80 · 443 인가"}
    V -->|IPv4| T
    T -->|예| A
    T -->|아니다| X

마지막 「버린다」가 policy drop 입니다. forward 체인은 이 컴퓨터를 거쳐 다른 컴퓨터로 넘어가는 패킷을 다룹니다. 이 컴퓨터가 라우터가 아니라고 보고 policy drop 으로 전부 버립니다.

나가는 패킷을 다루는 output 체인은 아예 없습니다. 체인을 정의하지 않으면 기본 정책이 accept 입니다. 들어오는 것은 막고 나가는 것은 여는 구성입니다.

AWS 보안 그룹

AWS(Amazon Web Services)는 VPC(Virtual Private Cloud, 가상 사설 클라우드) 안의 서버마다 보안 그룹이라는 규칙 목록을 붙입니다. 이 규칙은 nftables 와 몇 가지가 다릅니다.

  • 허용 규칙만 적을 수 있습니다. 거부 규칙은 없습니다
  • 새로 만든 보안 그룹에는 들어오는 규칙이 하나도 없습니다. 규칙을 더하기 전까지 들어오는 통신은 전부 막힙니다
  • 새 보안 그룹에는 나가는 통신을 전부 허용하는 규칙이 하나 들어 있습니다. 이 규칙을 지우고 나가는 규칙도 하나도 없으면 나가는 통신도 막힙니다
  • 한 서버에 보안 그룹을 여럿 붙이면 모든 그룹의 규칙을 하나로 합쳐 판정합니다

규칙 하나는 프로토콜과 포트 범위와 출발지(들어오는 규칙) 또는 목적지(나가는 규칙)로 이뤄집니다. 프로토콜은 번호로 적습니다. 6 이 TCP, 17 이 UDP, 1 이 ICMP 입니다. 포트는 22 처럼 하나를 적거나 7000-8000 처럼 범위를 적습니다.

보안 그룹은 연결 상태를 기억합니다. 서버가 밖으로 요청을 보내면 그 답은 들어오는 규칙과 무관하게 들어옵니다. 들어오도록 허용한 요청의 답은 나가는 규칙과 무관하게 나갑니다.

Google Cloud 의 VPC 방화벽 규칙

Google Cloud 도 VPC 네트워크에 방화벽 규칙을 둡니다. 규칙이 하나도 없는 서버에는 보이지 않는 규칙 둘이 걸립니다. 들어오는 것을 막는 implied deny ingress 와 나가는 것을 여는 implied allow egress 입니다.

외부 IP 가 있는 서버라도 들어오는 규칙이 없으면 인터넷에서 오는 통신은 막힙니다. 그 서버가 밖으로 보낸 통신의 답은 들어옵니다.

규칙이 허용한 연결이면 들어오는 쪽이든 나가는 쪽이든 그 답도 지나갑니다. 답으로 인정받으려면 요청과 다섯 값이 맞아야 합니다. 출발지 IP · 목적지 IP · 출발지 포트 · 목적지 포트 · 프로토콜입니다. 다만 출발지와 목적지의 주소와 포트는 서로 뒤바뀐 꼴입니다.

이 다섯 값의 묶음을 5-튜플이라 합니다. 답 트래픽을 막는 규칙은 아예 만들 수 없습니다.

규칙을 맞춰 보는 방법은 위에서부터가 아니라 우선순위입니다. 걸린 규칙 가운데 우선순위가 가장 높은 것을 따릅니다. 거부 규칙이 허용 규칙을 이기는 것은 두 규칙의 우선순위가 같을 때뿐입니다.

갈래

방화벽은 패킷에서 어디까지 읽고 무엇을 기억하느냐로 갈립니다. 헤더만 읽는 것에서 시작해 연결 상태를 기억하는 것, 애플리케이션이 주고받는 내용까지 읽는 것으로 넓어집니다. 놓이는 곳으로도 한 번 더 갈립니다.

갈래 무엇을 읽나 무엇이 다른가
패킷 필터 헤더의 주소 · 프로토콜 · 포트 · 인터페이스와 방향 연결을 기억하지 않습니다
상태 추적 헤더 값과 TCP 헤더의 몇몇 값 연결마다 상태 표에 한 줄씩 적습니다
애플리케이션 방화벽 애플리케이션 계층의 프로토콜 동작 상태 추적에 프로토콜 분석을 더합니다
애플리케이션 프록시 게이트웨이 애플리케이션 프로토콜의 헤더와 본문 연결을 둘로 갈라 양쪽을 따로 맺습니다
웹 애플리케이션 방화벽 웹 서버로 가는 HTTP 요청 웹 서버 바로 앞에 섭니다

패킷 필터

헤더의 값만 보고 규칙과 맞추는 가장 기본 기능입니다. 패킷 내용에는 관심이 없습니다. 지나가는 흐름의 상태도 기억하지 않습니다. 이렇게 상태를 안 보는 방화벽을 무상태 검사 방화벽이라고도 합니다.

상태를 기억하지 않으니 한 세션 안에서 오간 여러 요청을 서로 엮지 못합니다. 답 트래픽도 저절로 들이지 않습니다. 답이 들어올 길도 규칙으로 따로 열어야 합니다.

대부분의 운영체제와 라우팅하는 장비에 이 기능이 들어 있습니다. 순수한 패킷 필터의 흔한 예는 접근 제어 목록(ACL, Access Control List)을 쓰는 라우터입니다. 팔리는 방화벽 가운데 패킷 필터만 하는 것은 드뭅니다. 그래도 대부분의 방화벽 한가운데에는 패킷 필터가 있습니다.

상태 추적

연결마다 상태를 기억해 두고 예상한 상태에서 벗어난 패킷을 막습니다. 이 기억을 담는 표가 상태 표입니다. 표의 한 줄에는 대개 출발지 IP 주소, 목적지 IP 주소, 포트 번호, 연결 상태가 들어갑니다.

TCP 연결의 상태는 크게 셋으로 나뉩니다. 맺는 중, 쓰는 중, 끝남입니다. 끝남에는 한쪽이 닫자고 한 연결과 오래 아무것도 안 오간 연결이 다 들어갑니다.

새 패킷이 오면 방화벽은 상태 표와 견줘 봅니다. 공격자가 이미 맺어진 연결의 패킷인 척 헤더를 꾸며 보내도 표에 그 연결이 없으면 걸러집니다.

UDP 는 연결을 맺고 끊는 절차가 없습니다. 상태 추적 방화벽 대부분은 UDP 에서 주소와 포트만 기억합니다. 안쪽에서 DNS(Domain Name System, 도메인 이름 시스템) 질의를 내보냈을 때만 바깥에서 온 DNS 응답이 들어옵니다.

방화벽은 UDP 세션이 언제 끝났는지 모릅니다. 미리 정한 시간이 지나면 표에서 그 줄을 지웁니다.

sequenceDiagram
    participant 안 as 안쪽 컴퓨터
    participant 방화벽
    participant 밖 as 바깥 DNS 서버
    안->>방화벽: DNS 질의를 내보낸다
    Note over 방화벽: 상태 표에 주소와 포트를 적는다
    방화벽->>밖: 내보낸다
    밖-->>방화벽: DNS 응답이 온다
    Note over 방화벽: 표에 적힌 질의의 답이다
    방화벽-->>안: 들여보낸다

애플리케이션 방화벽

상태 추적에 애플리케이션 계층의 프로토콜 분석을 더합니다. 벤더가 만든 정상 프로토콜 동작의 틀과 오가는 것을 견줘 벗어난 것을 찾습니다. 일부 벤더는 이것을 심층 패킷 검사라 부릅니다.

포트 번호만 보는 규칙으로는 못 가리는 것을 가립니다. 보통 HTTP 가 쓰는 80번 포트로 메신저가 오가는지 알아챕니다. 조직이 허용하지 않는 첨부 파일, 예를 들어 실행 파일이 붙은 메일을 막습니다. FTP(File Transfer Protocol, 파일 전송 프로토콜)의 put 명령처럼 특정 동작만 골라 막을 수도 있습니다.

애플리케이션 프록시 게이트웨이

두 컴퓨터 사이에 대리인으로 서서 둘이 직접 이어지지 못하게 합니다. 연결 하나가 둘로 갈립니다. 클라이언트와 프록시 사이에 하나, 프록시와 진짜 목적지 사이에 하나입니다.

양 끝은 직접 이어진 것처럼 느끼도록 만들어졌습니다. 바깥 컴퓨터는 프록시하고만 이야기하므로 안쪽 IP 주소가 밖에 드러나지 않습니다. 애플리케이션 프로토콜의 헤더와 본문을 읽고 통과 여부를 정합니다. 통과시키기로 하면 그때 목적지로 넘깁니다.

웹 애플리케이션 방화벽

웹 서버 바로 앞에 놓여 HTTP 를 노린 공격을 막는 애플리케이션 방화벽입니다. 줄여서 WAF(Web Application Firewall)라 부릅니다. HTTP 를 노린 공격에는 웹 사용자의 컴퓨터에 악성 소프트웨어를 심는 것과 사용자를 속여 정보를 빼내는 것이 있습니다. 그런 공격 가운데 많은 수를 웹 애플리케이션 방화벽이 잡아낼 수 있습니다.

전통적인 방화벽은 안쪽 망 전체를 지키려고 망이 만나는 곳에 섭니다. 웹 애플리케이션 방화벽은 서버 하나를 노린 공격을 막으려고 그 웹 서버 앞에 섭니다. 이 차이 때문에 전통적인 방화벽과 아주 다른 물건으로 보는 일이 많습니다.

호스트 방화벽

지금까지의 방화벽은 망의 경계에 섰습니다. 호스트 방화벽은 지키려는 컴퓨터 안에서 도는 소프트웨어입니다. 컴퓨터 한 대로 들어오고 나가는 통신만 다룹니다. 서버에 붙으면 호스트 방화벽, 데스크톱이나 노트북에 붙으면 개인 방화벽이라 합니다.

경계 방화벽이 모든 공격을 알아보지는 못합니다. 안쪽 컴퓨터끼리 오가는 공격은 경계 방화벽을 아예 안 지날 수도 있습니다. 호스트 방화벽에서 꼭 필요한 통신만 허용하면 같은 서브넷(한 네트워크를 잘게 나눈 작은 망)의 컴퓨터에서 오는 공격도 막아 줍니다. 서버에서 나가는 통신을 묶어 두면 어떤 악성 코드가 다른 컴퓨터로 번지는 것을 막는 데 도움이 될 수 있습니다.

flowchart TD
    I["인터넷"] --> B["경계 방화벽"]
    subgraph 안쪽망
        W["웹 서버 · 호스트 방화벽"]
        D["데이터베이스 서버 · 호스트 방화벽"]
    end
    B --> W
    W --> D

경계 방화벽을 지나 들어온 통신도 각 서버의 호스트 방화벽에서 한 번 더 걸립니다. 웹 서버에서 데이터베이스 서버로 가는 통신은 경계 방화벽을 안 지납니다. 이 통신은 두 서버의 호스트 방화벽만 가립니다.

운영

이 절은 방화벽을 굴리는 사람이 만지는 설정 키와 기본값을 봅니다. 대상은 리눅스의 도구 넷과 클라우드 둘입니다. 리눅스 도구는 커널 연결 추적 · nftables · iptables · ufw(Uncomplicated Firewall)입니다. 클라우드는 AWS 와 Google Cloud 입니다.

iptables 는 nftables 처럼 리눅스 커널의 방화벽 규칙을 적는 도구입니다. ufw 는 호스트 방화벽을 설정하는 명령줄 도구입니다.

다루는 물음은 여섯입니다. 상태 표가 언제 줄을 지우나, 표가 차면 어떻게 되나, 보안 그룹 규칙을 바꾸면 기존 연결은 어떻게 되나, 기본 정책을 무엇으로 두나, 버림과 거절을 어떻게 고르나, 막힌 패킷을 어떻게 보나입니다.

연결 추적 표의 유휴 만료

상태 표의 줄은 영원히 남지 않습니다. 패킷이 안 오간 시간이 만료값을 넘으면 지워집니다. 줄 하나가 겪는 일을 그리면 이렇습니다.

stateDiagram-v2
    [*] --> 추적중: 규칙이 허용한 연결이 시작된다
    추적중 --> 추적중: 패킷이 오간다
    추적중 --> 지워짐: 유휴 시간이 만료값을 넘는다
    지워짐 --> [*]

만료값은 이렇게 다릅니다. 단위는 전부 초입니다. EC2(Elastic Compute Cloud)는 AWS 의 가상 서버입니다.

어디 키 · 이름 기본값 바꿀 수 있는 범위
리눅스 커널 nf_conntrack_tcp_timeout_established 432000 (5일) 미확인
리눅스 커널 nf_conntrack_udp_timeout 30 미확인
리눅스 커널 nf_conntrack_udp_timeout_stream 120 미확인
AWS EC2 TCP established timeout Nitro v6 인스턴스 종류(P6e-GB200 제외)는 350, 그 밖은 432000 60 ~ 432000
AWS EC2 UDP timeout 30 30 ~ 60
AWS EC2 UDP stream timeout 180 60 ~ 180

리눅스 커널의 키는 /proc/sys/net/netfilter/ 아래에 있습니다. EC2 는 TCP 만료값을 432000 보다 짧게 두기를 권합니다. EC2 에서 UDP stream 은 요청과 응답이 한 번 넘게 오간 UDP 흐름입니다. 한 번만 오가거나 한쪽으로만 흐른 것은 짧은 쪽 만료값을 받습니다.

Google Cloud 는 키 대신 기준 하나를 둡니다. 10분에 패킷이 하나라도 오가면 추적 상태가 살아 있다고 봅니다.

백엔드 개발자가 이 만료 때문에 겪는 일이 있습니다. 커넥션 풀은 데이터베이스나 바깥 서비스와 맺은 TCP 연결을 열어 둔 채 다시 씁니다.

그 연결이 만료값보다 오래 쉬면 방화벽은 그 연결을 끝난 것으로 칩니다. 줄이 지워진 뒤 그 연결로 패킷을 보내면 표에 맞는 줄이 없습니다. 예상한 상태에서 벗어난 패킷이라 막힐 수 있습니다. 막히면 풀 쪽에서는 쉬던 연결이 다음 요청에서야 끊긴 것으로 드러납니다.

sequenceDiagram
    participant 풀 as 커넥션 풀
    participant 방화벽
    participant DB as 데이터베이스
    풀->>방화벽: 연결을 맺는다
    Note over 방화벽: 상태 표에 줄을 적는다
    방화벽->>DB: 넘긴다
    Note over 풀,DB: 만료값보다 오래 아무것도 안 오간다
    Note over 방화벽: 줄을 지운다
    풀->>방화벽: 쉬던 연결로 다음 요청을 보낸다
    Note over 방화벽: 맞는 줄이 없다 · 막힐 수 있다
    Note over 풀: 막히면 이때서야 끊긴 것을 안다

표가 가득 찰 때

표에 담을 수 있는 줄 수에는 한도가 있습니다.

리눅스 커널에서는 nf_conntrack_max 가 한도입니다. 기본값은 nf_conntrack_buckets 와 같습니다. 연결 하나가 표에 두 번 들어갑니다. 원래 방향으로 한 번, 주소를 뒤집은 답 방향으로 한 번입니다. 한도가 찼을 때 커널이 어떻게 하는지는 미확인입니다.

EC2 는 인스턴스마다 추적할 수 있는 연결 수의 상한을 둡니다. 상한에 닿으면 새 연결을 못 맺으므로 오가는 패킷이 전부 버려집니다. 남은 수는 conntrack_allowance_available 지표로 봅니다.

보안 그룹 규칙을 바꾼 뒤의 기존 연결

EC2 의 보안 그룹은 규칙을 바꿔도 이미 추적 중인 연결을 바로 끊지 않습니다. 기존 연결이 만료될 때까지 패킷을 계속 들입니다.

바로 끊어야 하면 상태를 안 기억하는 필터를 쓸 수 있습니다. AWS 의 네트워크 ACL 은 서브넷에 붙는 무상태 필터입니다. 답 트래픽을 저절로 들이지 않습니다. 한 방향이라도 막는 네트워크 ACL 을 더하면 기존 연결도 끊깁니다.

기본 정책

기본 정책은 도구마다 이름과 처음 값이 다릅니다.

도구 기본 정책을 정하는 손잡이 처음 값
nftables 체인의 policy drop · policy accept 정의하지 않은 체인은 accept
ufw default 뒤에 allow · deny · reject 와 방향 설치 때 incoming deny · forward deny · outgoing allow
AWS 보안 그룹 손잡이 없음. 허용 규칙만 더한다 들어오는 규칙 없음 · 나가는 것 전부 허용 규칙 하나
Google Cloud 손잡이 없음. 보이지 않는 규칙 둘 implied deny ingress · implied allow egress

ufw 의 방향에는 incoming · outgoing · routed 가 옵니다. 기본 정책을 바꿔도 이미 있는 규칙은 손으로 옮겨야 합니다.

설치 직후 ufw 는 꺼져 있습니다. 설치 때 깔리는 규칙은 상태가 어긋난(INVALID) 패킷을 버립니다.

버림과 거절을 고르는 설정

거절을 고르는 손잡이는 도구마다 이름이 다릅니다. 먼저 iptables 입니다.

iptables 에서는 규칙에 걸린 패킷을 어떻게 할지를 대상(target)이라 합니다. DROP 대상은 패킷을 버립니다. IPv4 용 REJECT 대상은 걸린 패킷에 오류 패킷을 돌려보냅니다. 그 밖에는 DROP 과 같습니다.

REJECT 에 걸린 패킷은 그 아래 규칙을 더 거치지 않습니다. 돌려줄 답은 --reject-with 로 고릅니다.

값 돌려주는 것
icmp-port-unreachable (기본) 포트에 닿을 수 없다는 ICMP 오류
icmp-net-unreachable · icmp-host-unreachable · icmp-proto-unreachable 망 · 호스트 · 프로토콜에 닿을 수 없다는 ICMP 오류
icmp-net-prohibited · icmp-host-prohibited · icmp-admin-prohibited 망 · 호스트 · 관리자가 금지했다는 ICMP 오류
tcp-reset TCP RST 패킷. TCP 만 맞추는 규칙에서만 씁니다

icmp-admin-prohibited 를 지원하지 않는 커널에서는 거절 대신 그냥 버립니다.

ufw 에서는 deny 대신 reject 를 쓰면 거절이 됩니다. 보낸 쪽에 막혔다는 것을 알려야 할 때 씁니다. 예를 들어 ufw reject auth 입니다.

막힌 패킷을 로그로 보기

nftables 는 log 동작으로 패킷을 로그에 남깁니다. prefix 는 로그 메시지 앞에 붙는 문자열입니다. 한 규칙에 동작 둘을 함께 적을 수 있습니다. 앞의 서버 규칙 파일 끝줄이 그 꼴입니다.

log prefix "[nftables] Inbound Denied: " counter drop

이 줄의 주석 처리를 풀면 앞선 규칙에 하나도 안 걸린 패킷을 로그에 남기고 버립니다.

규칙은 왼쪽에서 오른쪽으로 평가됩니다. 조건과 log 의 순서가 뜻을 바꿉니다. iif lo log tcp dport 22 accept 는 22번 포트만이 아니라 루프백으로 들어온 패킷을 모두 로그에 남깁니다. log 가 포트 조건보다 앞에 있기 때문입니다.

관련 항목

방화벽이 패킷에서 읽는 값

패킷 · 헤더 · IP 주소 · 포트 번호 · 프로토콜 · TCP · UDP · ICMP · 네트워크 인터페이스 · TCP 플래그 · 5-튜플

방화벽의 하위 종류

패킷 필터링 · 상태 기반 검사 · 심층 패킷 검사 · 애플리케이션 방화벽 · 애플리케이션 프록시 게이트웨이 · 웹 애플리케이션 방화벽 · 호스트 방화벽 · 개인 방화벽 · 경계 방화벽

방화벽이 통과 여부를 정할 때 쓰는 장치

규칙 집합 · 체인 · 접근 제어 목록 · 기본 차단 정책 · 허용 목록 · 연결 추적 · 상태 테이블

방화벽 구실을 하는 제품과 도구

netfilter · iptables · nftables · ufw · pf · firewalld · conntrack

클라우드에서 방화벽을 맡는 기능

보안 그룹 · 네트워크 ACL · VPC 방화벽 규칙 · VPC · AWS · EC2 · Google Cloud

방화벽과 같은 경계에 서는 다른 장비

NAT · 라우터 · 게이트웨이 · 프록시 · 리버스 프록시 · 로드 밸런서 · VPN · DMZ · 서브넷 · 침입 방지 시스템 · 침입 탐지 시스템

방화벽이 내용을 못 읽는 암호화·터널 프로토콜

TLS · SSH · IPsec · SRTP · OpenPGP · 터널링 · IPv6

방화벽 규칙이 따로 다뤄야 하는 IPv6 메시지

ICMPv6 · 이웃 탐색 · ping

방화벽 때문에 생기는 증상과 진단 단서

타임아웃 · 연결 거부 · ICMP 도달 불가 · TCP RST · 포트 스캔 · 커넥션 풀 · 킵얼라이브 · 유휴 타임아웃

방화벽이 지키려는 보안 원칙

최소 권한 · 심층 방어 · 제로 트러스트 · 공격 표면 · 경계 보안

방화벽을 다른 뜻으로 쓰는 웹 프레임워크 설정

Symfony · 웹 프레임워크 · 인증 · 인가

다른 이름: firewall · 파이어월 · 네트워크 방화벽 · 방화벽 규칙