프로그래밍 노트/인프라

[nginx] Proxy 환경에서 client IP 얻기 (정확한 ip 얻기)

깡냉쓰 2020. 9. 2. 20:12
728x90
반응형

문제점

프로젝트에서 nginx를 사용하기로 하여, nginx에 .vue 파일을 위치시키고 그 뒤에 springboot를 위치시켰다.

  1. nginx는 reverse proxy 역할을 하며, 클라이언트의 모든 요청을 받는다.
  2. 요청 URI가 /api로 시작하면 뒷단 springboot(proxy_pass 이용)로 요청을 보내고, /api로 시작하지 않는다면 vue router에게 URI에 해당하는 vue 페이지를 리턴하도록 한다.

nginx.conf

server{
    location ^~/api{
      proxy_pass http://127.0.0.1:8080;
    }
}

이런식으로 구성을 하여 프로젝트를 진행하였는데, 문제가 발생했다.
바로 springboot 에서 request.getRemoteAddress(); 를 호출하여 client_ip를 얻으면 127.0.0.1 이 찍히는 것이다.
client ip --> nginx(80포트) --127.0.0.1:8080요청--> springboot(8080포트)
그러다보니, client_ip를 LOG에 찍거나 DB에 저장을 할때 모든 ip가 127.0.0.1로 저장되는 문제가 발생하였다.

해결책

구글링을 하니, 가장 많이 나오는게 http header "X-Forwarded-For" 에 실제 client_ip를 넣어서 보내는 것이다.

X-Forwarded-For: client_ip, proxy1_ip, proxy2_ip

이런식으로 porxy를 거칠때마다 뒤에 ip를 붙여서 사용하는 것이다.

우리는 porxy가 하나밖에 없으므로 X-Forwarded-For에 client_ip만을 설정하기로 했다. nginx에서 X-Forwarded-For 헤더를 설정하는 방법은 다음과 같다.

server{
    location ^~/api{
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $http_host;
      proxy_pass http://127.0.0.1:8080;
    }
}

nginx를 재기동시킨 후 springboot에서 헤더를 찍어보면 "x-forwarded-for" 이름의 헤더에 실제 client_ip가 나오는 것을 알 수 있다.
이제 실제 client_ip를 얻을 수 있다.

public String getIp(HttpServletRequest request){
    String ip = request.getHeader("x-forwarded-for");
    return ip != null ? ip : request.getRemoteAddr();
}

대충 이런식으로 사용할 수 있겠다.

XFF가 표준이 아니기 때문에 제품에 따라 헤더의 이름이 다를 수도 있다고하니, 참고하길 바란다.
Filter 를 이용해서 ServletRequest를 상속받은 RequestWrapper를 만들어 getRemoteAddr를 오버라이드한다면, 어느 곳에서나 getRemoteAddr();을 호출해도 실제 client_ip를 얻어올 수 있다.

더 좋은 방법이 있다면 댓글 부탁드립니다. (__)

728x90
반응형