배경
Discourse는 최종 사용자의 실제 IP 주소를 인식해야 합니다.
하지만 최종 사용자는 항상 하나 이상의 상위 웹 서버(Discourse 컨테이너에서 실행되는 nginx)가 중간에 위치해 있기 때문에 Discourse에 직접 연결하지 않습니다. 따라서 이 정보를 Discourse에 신뢰할 수 있는 방식으로 전달하는 방법이 필요합니다.
x-forwarded-for 헤더가 바로 그 해결책입니다. 이 토픽에서는 해당 정보를 올바르게 처리하는 구체적인 메커니즘과 이를 전파하는 방법에 대해 설명하겠습니다.
아래에서 "nginx"라고 언급될 경우, 다른 곳에 존재할 수 있는 nginx가 아니라 Discourse 컨테이너 내부에서 실행되는 nginx를 의미합니다.
discourse_docker 템플릿에서 확인하기
상위 프록시를 위한 다양한 템플릿(예: cloudflare.template.yml 또는 fastly.template.yml)은 텍스트 치환(취약할 수 있음)에 의존하지 않고 아웃렛(outlets)에서 예측 가능한 파일 이름을 사용하도록 업데이트되었습니다.
우리는 향후에도 이 예측 가능한 형식을 계속 사용할 계획이며, 모든 사용자가 이러한 방식으로 커스터마이징을 작성하도록 권장합니다.
파일 이름
server/real-ip-header.conf
이 파일에는 nginx가 신뢰할 수 있는 출처(source of truth)로 사용할 헤더가 포함됩니다. 예시:
real_ip_header x-forwarded-for;
또는 Cloudflare 템플릿에 설정된 경우:
real_ip_header cf-connecting-ip;
server/real-ip-recursive.conf
이 파일이 존재하면 “실제 IP” 헤더 처리 시 재귀(recursion)를 제어합니다. nginx 앞에 헤더를 추가하는 프록시가 하나 이상 있는 경우 이 설정을 활성화해야 합니다.
real_ip_recursive on;
이것이 왜 필요한지 자세한 내용은 아래 “하나 이상의 프록시가 있는 경우?” 섹션을 참고하세요.
server/set-real-ip-from-ENVIRONMENT.conf
이 파일에는 nginx가 신뢰할 IP 주소를 지정하는 지시문(directives)이 포함되며, 필요에 따라 파일과 지시문을 여러 개 가질 수 있습니다.
discourse_docker의 템플릿은 필요에 따라 이러한 파일을 생성합니다(예: set-real-ip-from-cloudflare.conf). 추가적인 요구 사항이 있다면 직접 파일을 추가할 수 있습니다.
예시:
AWS에서 실행 중이고 Discourse 컨테이너 앞에 ALB(Application Load Balancer)가 있는 경우, 컨테이너 정의에 다음을 추가하여 추가 파일을 만들 수 있습니다(환경에 맞게 수정):
run:
- file:
path: /etc/nginx/conf.d/outlets/server/set-real-ip-from-aws.conf
chmod: 644
# AWS VPC는 10.42.0.0/16이며, ALB 네트워크에서 오는 모든 연결을 신뢰
contents: |
set_real_ip_from 10.42.66.0/24;
set_real_ip_from 10.42.67.0/24;
- file:
path: /etc/nginx/conf.d/outlets/server/real-ip-header.conf
chmod: 644
contents: |
real_ip_header x-forwarded-for;
하나 이상의 프록시가 있는 경우?
Discourse 앞에 프록시가 하나 이상 있는 경우, 가장 간단한 해결 방법은 각 프록시가 자신의 상위(upstream)를 신뢰하고 하위(downstream) x-forwarded-for를 적절히 설정하도록 필요한 단계를 수행하는 것입니다.
예시:
Cloudflare → Load Balancer → [(Discourse 컨테이너) nginx → Discourse]
여기서 가장 간단한 옵션은 Load Balancer가 x-forwarded-for(또는 cf-connecting-ip)를 처리하여 최종 사용자의 실제 IP 주소를 결정하고, 자신의 x-forwarded-for를 적절히 설정하는 것입니다. 이렇게 하면 nginx는 Load Balancer만 신뢰하면 되며, Cloudflare이 존재한다는 사실을 알거나 신경 쓸 필요가 없이 정상적으로 작동합니다.
항상 이것이 가능한 것은 아니므로, 때로는 Discourse가 앞에 있는 여러 프록시에 대해 알아야 할 수 있습니다. Load Balancer가 x-forwarded-for에 단순히 값을 추가하는 경우, nginx는 Load Balancer IP에서 HTTP 요청을 수신하며, x-forwarded-for 헤더는 다음과 같이 보입니다:
x-forwarded-for: real_end_user_ip, cloudflare_ip
이를 처리하려면, 먼저 nginx가 연결의 소스 주소(Load Balancer IP)가 신뢰할 수 있는지(set_real_ip_from 참조)를 결정하고, 신뢰할 수 있다면 x-forwarded-for 헤더의 마지막 IP를 처리합니다.
해당 IP 주소가 cloudflare_ip이므로, nginx는 이 과정을 다시 수행하여 cloudflare_ip가 신뢰할 수 있는지 확인하고, 다음 IP 주소인 real_end_user_ip를 사용합니다.