Proxy 환경에서 Real IP 설정하기 (AWS ELB, Load Balancer, CloudFlare)

HYEONG HWAN, MUN/ 7월 7, 2019/ 미분류/ 2 comments

https://blog.lael.be/post/8989

검색해 봤는데 잘 정리된 글이 없어서 작성해본다.

이 글에서는 여러 프록시 서버(로드밸런서, AWS Elastic Load Balancer, Web Application Firewall, CloudFlare) 환경에서, 실제 사용자의 IP 를 올바르게 처리하는 방법에 대해 알아볼 것이다.

 

1. Proxy 의 뜻

noun
1. 대리
2. 대리권
adjective
1. 대리의
2. 대리권의

Google 사전에 따르면 Proxy는, 명사, 형용사로 대리, 대리인, 대리권 등을 뜻하는 단어이다.

컴퓨터 기술에서는 대신하여 작업을 처리하는 프로그램 또는 서버를 뜻한다.

 

2. 프록시 서버의 동작 레이어

프록시 서버는 Client <=> Server 사이에서 동작한다. 패킷을 완전히 분석한 후에 동작하는 프로그램/서버 이기 때문에 Application Layer 에서 동작하며, L7 Proxy 라고도 부른다.

프록시 처리에는 서버 자원을 거의 소모하지 않기 때문에, 1:N 프록시의 - Load Balancer 형태로 주로 사용된다. (한대의 프록시 서버가 다수의 서버에 연결하여 동작)

 

 

3. 프록시 서버 사용시 유의 사항

웹 네트워크 통신에서 로드밸런서의 동작 위치는 다음과 같다. (또는 위의 그림을 보아라.)

고객(Client) <=> 로드밸런서(LB) <=> 서버(Server)

서버의 입장에서는 통신의 상대가, 고객이 아니라 로드밸런서(LB)가 된다.

따라서 IP 를 저장하고 처리해야하는 시스템이라면 이러한 Proxy 환경을 위해 추가적인 작업을 하여야 한다.

 

4. X-Forwarded-For 헤더 살펴보기

이 값은 표준 문서에 등록되어 있지 않은 속성이다. 실질적인 표준이라는 표현을 사용한다. 참고로 포워딩 표준 속성인 Forwarded 가 2014년 6월에 정의되었는데 2019년 현재 Forwarded 값으로 동작하는 프록시는 없는 것으로 보인다.
(표준 속성 문서 : https://tools.ietf.org/html/rfc7239#section-4)

모든 프록시 서버가 X-Forwarded-For 로 구현되어 있기 때문에 이 값을 사용하도록 하자. (더 정확히 확인하고 싶다면 사용중인 Proxy 의 헤더를 dump 해서 확인해 보도록 하자.)

 

< 출처 : https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/Forwarded >

 

X-Forwarded-For 규격

참고 : https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/X-Forwarded-For

X-Forwarded-For: <client>, <proxy1>, <proxy2>

프록시를 거칠 때마다 뒤에 [, ] (콤마 스페이스)를 추가한다.

 

1) 일반적인 상황

Client IP : 221.72.10.5
로드밸런서 IP : 34.34.34.34

- 로드밸런서(34.34.34.34)로부터 전달받는 HTTP 요청 헤더

X-Forwarded-For: 221.72.10.5

 

2) 다중 프록시 상황

Client IP : 221.72.10.5
외부 로드밸런서 IP : 34.34.34.34
내부 로드밸런서 IP : 10.10.0.1 ~ 10.10.0.3

- 로드밸런서(10.10.0.1)로부터 전달받는 HTTP 요청 헤더

X-Forwarded-For: 221.72.10.5, 34.34.34.34

 

5. 실제 구성 테스트 및 헤더 분석

블로그 글 쓰기 위해 이렇게 까지 해야하나 싶지만, 위의 예제 값에 대해서 환경을 구성하였고, 실제 테스트를 해 보았다.

테스트는 아주 널리 쓰이는 AWS ELB(Elastic Load Balancer), Cloudflare, 그리고 무료 체험 크래딧이 있길래 TOAST 로드밸런서에서도 테스트 해 보았다.

 

로드밸런서(프록시)에 연결 되어있는 실제 서버에서 TCP를 Dump 하여 확인해 보았다. 참고로 tcp 통신을 확인하는 명령어는 아래와 같다.

# tcpdump -nnAs 0 port 80

 

1) 일반적인 환경

AWS ELB

GET /not-found HTTP/1.1
X-Forwarded-For: 121.131.72.193
X-Forwarded-Proto: https
X-Forwarded-Port: 443

 

CloudFlare

GET /not-found HTTP/1.1
CF-IPCountry: KR
X-Forwarded-For: 121.131.72.193
X-Forwarded-Proto: https
CF-Visitor: {"scheme":"https"}
CF-Connecting-IP: 121.131.72.193

 

TOAST 로드밸런서

GET /not-found HTTP/1.1
X-Forwarded-Port: 80
X-Forwarded-Prot: http
X-Forwarded-Proto: http
X-Forwarded-For: 121.131.72.193

 

2) 다중 프록시 환경

적당히 테스트 환경을 구축하였다. 스크린샷 없이 결과만 붙여 넣겠다.

AWS ELB

GET /not-found HTTP/1.1
X-Forwarded-For: 221.72.10.5, 34.34.34.34, 121.131.72.193
X-Forwarded-Proto: https
X-Forwarded-Port: 443

 

CloudFlare

GET /not-found HTTP/1.1
X-Forwarded-For: 221.72.10.5, 34.34.34.34,121.131.72.193
X-Forwarded-Proto: https
CF-Connecting-IP: 121.131.72.193

proxy 2 의 IP를 comma+space 대신에 comma 만 사용하였다. 문법이 올바르지 않아 보이는데 동작에는 문제가 없더라.

 

TOAST 로드밸런서

GET /not-found HTTP/1.1
Host: 133.186.210.143
X-Forwarded-For: 221.72.10.5, 34.34.34.34
X-Forwarded-Port: 80
X-Forwarded-Prot: http
X-Forwarded-Proto: http
X-Forwarded-For: 121.131.72.193

TOAST 로드밸런서는 조금 이상하다. 공식 개발블로그 (https://meetup.toast.com/posts/163) 에 쓰인 것과 다르게 동작하더라.
haproxy 관련 문서를 찾아봤는데 추가 설정이 필요해 보인다.

 


 

6. 웹서버에서의 바른 설정 방법

코드 단계에서 처리하려면 정말로 잘 처리해야 한다.

pseudo code 로 알고리즘만 설명하자면, 먼저 트래픽이 신뢰하는 로드밸런서로부터 왔는지 체크하고, 맞다면 X-Forwarded-For 헤더 값, 으로 쪼개서 가장 마지막 값 또는 뒤에서 부터 탐색하면서 신뢰하지 않는 최초 값을 선택하도록 하여야 한다.
많은 예제 코드들이 X-Forwarded-For 의 첫번째 값을 신뢰하도록 되어 있는데, 이것은 보안 문제를 일으킬 수 있다. X-Forwarded-For IP 중 하나를 선택해야 한다면 항상 마지막 값을 선택하길 바란다. 물론 X-Forwarded-For IP 가 하나라면 그 하나를 선택하면 된다.

예를 들어 https://codex.wordpress.org/Plugin_API/Filter_Reference/pre_comment_user_ip 코드는 보안 취약점이 있다. (This code is vulnerable.)
올바르게 수정하려면 첫번째 값이 아니라, 마지막 값을 선택해야 한다. (또는 proxy 가 아닌 마지막 값)

 

코드 단계 말고, 웹서버(Nginx, Apache) 단계에서 안전하게 처리하는 방법

웹서버에서 적절히 설정하면, 코드를 변경하지 않아도 된다.

1) NGINX

real_ip_header 라는 속성 구문을 사용해야하는데, NGINX가 -with-http_realip_module 구성 설정되어 있어야 한다.
거의 모든 현재의 NGINX 프로그램이 이미 이 설정을 사용하여 동작하고 있다. (적어도 내가 가이드 한 방법대로 설치했다면..)

 

NGINX 환경 변수 확인

# nginx -V

매의 눈으로 -with-http_realip_module 옵션을 찾아내 보자.
(관련 문서 : http://nginx.org/en/docs/http/ngx_http_realip_module.html)

 

/etc/nginx/nginx.conf 에 구문을 적용해보자.

$remote_addr 값이 real_ip_header 의 마지막 값으로 변경된다.

- AWS ELB 를 사용 중일 경우.
set_real_ip_from 172.31.0.0/16;
real_ip_header X-Forwarded-For;

 

- Cloudflare 를 사용 중일 경우

참고 : https://support.cloudflare.com/hc/en-us/articles/200170786

set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 104.16.0.0/12;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 131.0.72.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2405:8100::/32;
set_real_ip_from 2c0f:f248::/32;
set_real_ip_from 2a06:98c0::/29;
real_ip_header X-Forwarded-For;

 

- TOAST 로드밸런서를 사용 중인 경우
set_real_ip_from 192.168.0.0/16;
real_ip_header X-Forwarded-For;

 

NGINX 서비스 재시작

# service nginx restart

 


 

2) Apache2

Apache 는 많은 모듈이 있고 사용할 수 있다. reverse proxy add forward module 을 설치해 보도록 하자.

ubuntu 의 기본저장소에 있는 libapache2-mod-rpaf 를 사용하려고 했으나, 버전이 너무 낮아서 IP범위(CIDR)을 지정할 수 없더라.
할 수 없이 공식사이트에서 코드를 다운받아서 컴파일하여 설치해야 한다.

참고로 Ubuntu 기본저장소의 모듈 버전은 0.6 이고, 최신 코드의 모듈 버전은 0.8.5 이다.

rpaf 공식사이트 : https://github.com/gnif/mod_rpaf

 

다운로드 후 컴파일 및 생성

# cd /root
# mkdir temp
# cd temp
# git clone https://github.com/gnif/mod_rpaf.git .
# apt install build-essential apache2-dev
# make
# make install

/usr/lib/apache2/modules/mod_rpaf.so 위치에 설치된다.

 

설치 소스파일 삭제

# cd /root
# rm -rf /root/temp

 

 

rpaf 환경설정 파일 생성

# vi /etc/apache2/mods-available/git-rpaf.conf

CloudFlare 일 경우 예제.

<IfModule rpaf_module>
    RPAF_Enable On
    RPAF_SetHostName On
    RPAF_ProxyIPs 173.245.48.0/20 103.21.244.0/22 103.22.200.0/22 103.31.4.0/22 141.101.64.0/18 108.162.192.0/18 190.93.240.0/20 188.114.96.0/20 197.234.240.0/22 198.41.128.0/17 162.158.0.0/15 104.16.0.0/12 172.64.0.0/13 131.0.72.0/22 2400:cb00::/32 2606:4700::/32 2803:f800::/32 2405:b500::/32 2405:8100::/32 2a06:98c0::/29 2c0f:f248::/32
    RPAF_Header X-Forwarded-For
    RPAF_ForbidIfNotProxy   Off
</IfModule>

 

rpaf 모듈 로드 구문 생성

# vi /etc/apache2/mods-available/git-rpaf.load

LoadModule rpaf_module /usr/lib/apache2/modules/mod_rpaf.so

 

 

모듈 등록

# cd /etc/apache2/mods-enabled
# ln -s ../mods-available/git-rpaf.conf .
# ln -s ../mods-available/git-rpaf.load .

 

 

모듈 로드를 위해서 Apache 재시작

#service apache2 restart

 

모듈이 로드된 후에는 git-rpaf.conf 수정 후 service apache2 reload 구문을 사용하여 restart 하지 않고도 변경을 적용할 수 있다.

 


참고 문서

https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/X-Forwarded-For

https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/Forwarded

https://codex.wordpress.org/Plugin_API/Filter_Reference/pre_comment_user_ip

https://www.lesstif.com/pages/viewpage.action?pageId=20775886

https://meetup.toast.com/posts/163

http://nginx.org/en/docs/http/ngx_http_realip_module.html

 

2 Comments

  1. 대충 검색했을때 실제 테스트 결과와 함께 정리된 글은 없어서 직접 정리중이였는데 다시 찾아보니 있었네요..! 도움 많이 받았습니다. 지식 공유 감사 드립니다!

Leave a Comment

작성하신 댓글은 관리자의 수동 승인 후 게시됩니다.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>
*
*