Cloudflare의 역할

Cloudflare는 한 마디로 말해서 내 서버 앞에 세워지는 든든한 교환원 역할을 한다. 무료로도 쓸만한 기본 기능을 제공하는데, 그 기능은 다음 글에서 알아보면 좋을 것 같고, 당장은 Reverse Proxy 역할을 한다고 생각하면 된다. 많은 방문자가 와도 캐시 서버 역할을 해주어 내 서버의 응답 빈도를 줄여서 자원에 여유를 둘 수 있게 하며, 파도처럼 밀어닥치는 악의적인 공격에 대해서는 알아서 판별하여 지연시간을 주어 공격이 실패하게 만든다.

이런 깔끔한 보안관 역할까지 겸해주는 장점이 있는 한편, 잘 모르고 쓰면 문제가 될 요소도 몇 가지 있다. 이 글은 앞으로도 발견하는대로 추가해나가면서 쓰면 좋을 것 같아서 일단 알아낸 것만 나열해보려고 한다.

부정확한 방문자 IP 로깅 해결

Nginx의 기본 설정된 로깅 포맷은 combined이며, log_format 지시자를 써서 별도의 로그 포맷을 선언할 수 있다.

combined, 즉 기본 로깅 포맷은 다음과 같다.

log_format  combined '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent"';

줄바꿔도 이어서 긴 한 줄이 등록되도록 3줄에 걸쳐서 선언되어 있다. 이는 주석 처리되어 있는데, 실제로 설정 파일에 없어도 이렇게 기록된다.

$remote_addr가 있으니 방문자 IP가 기록되어야 하지만, 올바르게 기록되지 않고 미지의 IP가 실제로 기록된다. 이는 Cloudflare의 서버 IP인데, 이렇게 되면 내 서버 로그를 분석해도 방문자 정보는 얻을 수 없게 된다. 방문자 정보는 어디로 갔을까?

X-Forwarded-For 헤더가 설정되어 있다

수제 Reverse Proxy 서버를 구성해본 사람이라면 자연스럽게 이 키워드가 떠올랐을지 모른다. X-Forwarded-For (이하 XFF) 헤더는 HTTP 프록시 서버에서도 쓰이는 유서깊은 동작 방식으로, 본래의 IP 출처를 이곳에 나열식으로 여러 개 추가해나가는 형식을 갖고 있다.

Cloudflare 또한 이 원리에 충실해서 XFF 헤더에 방문자 IP를 넣어준다. 하지만 변경이 아니라 추가를 해준다. 프록시를 거쳐서 내 서버를 방문하려 한 사람이라면 XFF가 콤마로 구분되어 여럿이 되어 있을 것이다. 대략 다음과 같은 형태가 될 것이다.

X-Forwarded-For: 113.152.12.34,58.92.94.20

이는 로그 파일을 불규칙한 구성으로 만들어버려서 분석하기 어렵게 한다. 우리는 방문자 IP 하나를 딱 찝어서 알아내고 싶다.[1]물론 Cloudflare 입장에서 방문객이므로, 프록시를 거쳐서 Cloudflare의 내 서버 주소에 도달한 사람들은 프록시 서버가 Cloudflare의 방문객으로서 보인다. 혹시 XFF도 필요한 사람은 그래서 로그 파일 형태 유지를 위해서라도 가장 마지막에 추가하는 기본 주석에 있는 형태가 가장 좋겠다.

log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

Cloudflare는 더 많은 정보를 제공한다

관련된 설명서를 찾아보면, Cloudflare를 거쳐온 HTTP 요청에는 꽤 많은 헤더가 포함되어 있음을 알 수 있다.

CF-IPCountry

두 글자의 방문자 IP 기반 국가 코드이다. Cloudflare IP Geolocation 옵션을 Network 패널에서 켰어야 유효한 정보가 들어온다. 알 수 없는 국가인 경우에는 XX가 정해진다고 한다. 값을 실제로 보니 KR, US, JP 같은 것이 들어가는 것으로 보인다.

Cf-Ipcountry: US

CF-Connecting-IP

클라이언트가 Cloudflare에 접속한 IP 주소다. Cloudflare에 접속하는 시점에 알려진 IP이기 때문에, 클라이언트가 프록시를 썼다면 프록시 IP가 될 것으로 보인다.

CF-Connecting-IP: 203.0.113.1
더미 IP를 받을 수도 있다고? Pseudo IPv4에 대한 설명

한국은 전혀 요원한 이야기지만, IPv6 환경이 보급된 나라에서는 노출된 IP가 IPv6인 경우가 많다. Cloudflare의 IPv6 설정이 켜있는 경우 자연히 이 IPv6를 받아야 하는데 서버는 IPv4만 지원하는 경우가 있다. (특히 AWS lightsail을 비롯하여 다른 상당수 VPS가 그럴 것이다)

Cloudflare는 이런 혼란스러운 상황에 서버에 그대로 IPv6 주소를 보낼지, 일관성을 위해 IPv4를 만들어서 보낼지, 결정을 관리자에게 맡기고 있다.

가짜 IPv4 설정이 Cloudflare > Network에 들어가면 있다. 기본값은 Off.
  • Off: IPv6 그대로 CF-Connecting-IP에 담겨서 온다.
  • Add header: CF-Pseudo-IPv4, CF-Connecting-IPv6 헤더가 추가되어 각각 담겨서 온다.
  • Overwrite headers: CF-Connecting-IP, X-Forwarded-For 모두 CF-Pseudo-IPv4로 치환당한다.

기본값이 Off이므로 가장 이상적이다. 괜히 건들여서 왜 IPv6가 그대로 넘어오지 않는지 의아했다. 로그에 치명적인 문제가 있어서 안 쓸 정보로 넣겠다면 모를까, 오류도 없는데 더미 정보를 써서 무의미한 값을 늘릴 필요는 없겠다.

X-Forwarded-For

위에서 알아본 XFF 정보로, 기존의 XFF 정보의 끝에 CF-Connecting-IP를 하나 더 추가한 꼴을 갖고 있다. XFF가 없었다면, CF-Conecting-IP와 완전히 동일하다.

X-Forwarded-Proto

Flexible SSL의 경우 클라이언트와 Cloudflare는 SSL 통신을 하지만, 서버와 Cloudflare는 비보안 통신을 하게 되는 경우가 있어서 클라이언트의 프로토콜을 알 수 없을 때가 있다. 처음의 클라이언트 접속이 http인지, https인지 알려준다.

X-Forwarded-Proto: https

CF-Ray

Cloudflare 데이터 센터에서 방문자의 요청을 관리하는 용도의 해시 인코딩된 일련번호다. 기업 유저가 Cloudflare에 지원을 요청할 때 이 번호로 클라이언트 요청을 구분할 수 있다고 한다.

CF-Ray: 230b030023ae2822-SJC

CF-Visitor

JSON 형식으로 입력된 스키마 정보다. 결과적으로 X-Forwarded-Proto와 같은 내용을 담고 있다.

Cf-Visitor: { \"scheme\":\"https\"}

True-Client-IP (엔터프라이즈 요금제 전용)

방문자의 실제 IP를 제공하는데, CF-Connecting-IP와 똑같은 정보를 담고 있다. 다만 일부 기업 고객의 장비가 True-Client-IP만 확인하기 때문에 이를 별도 요금제로 분리하여 추가로 입력해주는 것이다.

True-Client-IP: 203.0.113.1

CDN-Loop

반복되는 요청으로 블록되기 위해 몇 번이나 Cloudflare 네트워크 진입을 요청할 수 있는지 알려준다.

CDN-Loop: cloudflare

다음 단계

이런 헤더를 이용하여 어떤 것을 할 수 있는지 알아보기 위해 우선 다음 글에서 방문자 IP 로깅을 추가해보겠다.

주석

주석
1 물론 Cloudflare 입장에서 방문객이므로, 프록시를 거쳐서 Cloudflare의 내 서버 주소에 도달한 사람들은 프록시 서버가 Cloudflare의 방문객으로서 보인다.

2년차 개발자 이진백입니다. 현재 일본에서 웹 프로그래머로 근무중입니다. 주로 Java, Vue (Nuxt), TypeScript 언어를 활용하고 있습니다. 취미로 C#과 Windows Universal Store App을 오래 만져왔고, 개인 서버 운영에도 관심이 많습니다. 최신 이슈를 알기 쉽게 전달해드리도록, 더욱 더 노력하겠습니다.