[수미수의 개발 브로구]

[트러블슈팅] AWS 환경에서 클라이언트 IP 검출 이슈 해결 본문

트러블 슈팅

[트러블슈팅] AWS 환경에서 클라이언트 IP 검출 이슈 해결

수미수 2023. 8. 7. 23:46
반응형

들어가기 전

 개발 프로젝트 오픈 지원 과정에서, 운영환경에서 테스트의 필요성으로 인해, 특정 IP 대역에서만 특정 서비스를 현시해야 했다. 일반적인 스프링 기반 웹 어플리케이션에서 클라이언트의 IP 검출 로직을 이용하여 로컬에서 테스트 후, 스테이지 환경에 적용 하였지만, 클라이언트의 IP가 제대로 검출 되지 않는 문제가 발생하였다. 관련 엔지니어분을 통해서 현재 어플리케이션에서 검출하는 IP는 AWS 의 특정 장비인것으로 확인 되었고, 현재 클라우드 인프라 구성환경에서 실제 클라이언트 IP 검출 방법에 대해서 간략하게 설명 하고자 한다.

문제 확인

  일반적으로 스프링의 HttpServletRequest 의 GetRemoteAddr() 을 통해서 클라이언트의 IP를 검출 할 수 있지만, 스테이지 환경 또는 운영 환경에서는 Web Server 또는 Load Balancer, API G/W 를 통해서 프록시 통신하여, 해당 함수를 이용해서 Client IP를 검출 할 수 없다.

  현재 운영중인 어플리케이션에서 클라이언트의 IP를 검출 하는 로직 구성은 아래 소스와 같다.  물론, 더 많은 옵션을 이용하여 좀더 정확성을 높일 수 있으나, 클라이언트 IP 로직이 잘못된 것을 인지 하기 전까지 아래 소스를 이용하여 클라이언트 IP를 검출 하는방법으로 사용하였으며, 해당 방법으로는 현재 운영중인 환경에서 클라이언트의 IP를 검출 할 수 없다는 것을 확인 하였다.

public static String getRemoteIP(HttpServletRequest request){
    String ip = request.getHeader("X-FORWARDED-FOR");
    //proxy 환경일 경우
    if (ip == null || ip.length() == 0) {
        ip = request.getHeader("Proxy-Client-IP");
    }
    //웹로직 서버일 경우
    if (ip == null || ip.length() == 0) {
        ip = request.getHeader("WL-Proxy-Client-IP");
    }
    if (ip == null || ip.length() == 0) {
        ip = request.getRemoteAddr() ;
    }
    return ip;
}

오류 분석

위의 검출 로직을 좀더 많은 옵션을 이용하여, 검출해 보았지만 역시나 클라이언트의 IP를 가지고 오지 못했고, 다른 방법인 고객 ID 기반으로 특정 서비스를 오픈 하는 방법이 있었지만, 노가다 작업이 필요한 상황이였다. 그래서 어플리케이션 내에 클라이언트의 요청부터 어플리케이션으로 들어오는 모든 Http 요청 헤더를 검출 하였고, 우리가 예상했던, x-forwarded-for 에는 역시나, AWS 장비의 IP가 있었다. 그리고, 실제 클라이언트의 IP는 forwarded 헤더 값에, for 부분에 클라이언트의 IP 정의 되어 있었다.

코드 수정

아래 소스는 위의 검출 로직을 포함한 전체 클라이언트 IP 검출 로직이며, 실제 IP 체크 부분은 여러 방법이 있기 때문에 별도로 언급 하지 않지만, https://stackoverflow.com/questions/577363/how-to-check-if-an-ip-address-is-from-a-particular-network-netmask-in-java 부분을 참고하여, IPAddressMatcher 를 사용 요구 사항을 충족 시켰다.

public static String getRemoteIP(HttpServletRequest request){
        String ip = null;

        //forwarded 환경일 경우
        if (ip == null || ip.length() == 0) {
           String forwarded = request.getHeader("forwarded");

            for (String str : forwarded.split(";")) {
                String[] sa = str.split("=");
                if ("for".equals(sa[0])) {
                    ip = sa[1];
                    break;
                }
            }
        }
        
        //proxy 환경일 경우
        if (ip == null || ip.length() == 0) {
            ip = request.getHeader("X-FORWARDED-FOR");
        }

        //proxy 환경일 경우
        if (ip == null || ip.length() == 0) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        //웹로직 서버일 경우
        if (ip == null || ip.length() == 0) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0) {
            ip = request.getRemoteAddr() ;
        }
        return ip;
    }

기타

X-Forwarded-For(XFF)

‌   XFF는 Http 헤더이며, Http Server에 요청한 Client IP를 식별하기 위한 표준이다. Http 프록시나 로드 밸런서를 통해 웹 서버에 접속하는 클라이언트의 원 IP 주소를 식별하는 표준이다. 클라이언트와 서버 중간에서 트래픽이 프록시나 로드 밸런서를 거치면, 서버 접근 로그에는 프록시나 로드 밸런서의 IP 주소만을 담는다. 클라이언트의 원 IP 주소를 보기위해 X-Forwarded-For 요청 헤더가 사용된다.

Forwarded

 Forwarded 헤더는 클라이언트에서 접하고 있는 프록시 서버들이 요청에 대한 연결에 연관되어 있는 상황에서 해당 연결이 변경되거나 잃어버리게 되었을 때, 해당되는 정보를 가지고 있다.

반응형