client의 IP주소를 알아내야 한다.
IP를 사용하여 누가 나를 호출했는지 알수 있다면, 로깅에서 편리할 수 있습니다. 사내에서 사용하는 API 들은 별도의 인증과정을 두지 않는 대신 network 로 호출, 차단을 관리합니다. 따라서, 인증이 없는것은 편리함이 따라오지만 누가 나를 불렀는지 애매해지는 경우가 많습니다.
elastic search 를 사용해 로그 모니터링을 하게 되면서, 어떤 서버가 무슨 API 를 호출을 언제 하였고, 결과가 어땟는지 확인하는 과정이 필요했습니다.
또한, 외부에선 호출이 불가능하더라도 우리 회사 IP라면 authorization 과정을 생략하는데도 IP를 사용합니다.
그러면 client 의 IP address 어디에 있을까요??
HttpRequestServlet
보통 controller 에서 HttpRequestServlet 을 매개변수로 받아오는 경우가 있습니다
public IdolData getList(@RequestParam String name, HttpRequestServlet request) {
request.getRemoteAddr(); // IP 주소?
}
Java
복사
이렇게 해서 IP 주소를 받아온다면, 로컬에서는 IPv6 로 IP 주소를 반환합니다.
하지만, 서버에서는 127.0.0.1 을 받아오는 이슈가 있었습니다.
우리는 대부분 proxy 환경, docker 등에서 API 어플리케이션을 띄어두기 때문에 발생하는 이슈입니다.
이럴 때 사용하는것이 X-Forwared-For 입니다.
X-Forwared-For: <client>, <proxy 1>, <proxy 2>
Java
복사
•
client 의 IP 주소 입니다.
•
바로 우리가 찾던 그것!!
<proxy 1>, <proxy 2>
•
하나의 요청이 proxy 들을 거치면서 IP 주소들이 차례로 열거됩니다.
◦
가장 오른쪽 IP 주소가 가장 마지막에 거친 proxy IP 주소.
◦
가장 왼쪽 IP 주소가 최초 proxy 주소
•
application 앞에 있는 proxy 의 IP 주소를 반환합니다.
X-Forwarded-For: 2001:db8:85a3:8d3:1319:8a2e:370:7348
X-Forwarded-For: 203.0.113.195
X-Forwarded-For: 203.0.113.195, 70.41.3.18, 150.172.238.178
Java
복사
그런데 이런 header 뿐만이 아니라
•
X-Forwarded-For
•
Proxy-Client-IP
•
WL-Proxy-Client-IP
•
HTTPXFORWARDED_FOR
•
HTTPXFORWARDED
•
HTTPXCLUSTERCLIENTIP
•
HTTPCLIENTIP
•
HTTPFORWARDEDFOR
•
HTTP_FORWARDED
•
HTTP_VIA
•
REMOTE_ADDR
이러한 header 들도 봐둬야 할 필요가 있습니다. (각 header 들에 대한 설명은 필요해지면 하는 걸로)
그래서 저는 다음과 같은 Util class를 만들어 보았어요
public class HttpReqRespUtils {
private static final String[] IP_HEADER_CANDIDATES = {
"X-Forwarded-For",
"Proxy-Client-IP",
"WL-Proxy-Client-IP",
"HTTP_X_FORWARDED_FOR",
"HTTP_X_FORWARDED",
"HTTP_X_CLUSTER_CLIENT_IP",
"HTTP_CLIENT_IP",
"HTTP_FORWARDED_FOR",
"HTTP_FORWARDED",
"HTTP_VIA",
"REMOTE_ADDR"
};
public static String getClientIpAddressIfServletRequestExist() {
if (Objects.isNull(RequestContextHolder.getRequestAttributes())) {
return "0.0.0.0";
}
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
for (String header: IP_HEADER_CANDIDATES) {
String ipFromHeader = request.getHeader(header);
if (Objects.nonNull(ipFromHeader) && ipFromHeader.length() != 0 && !"unknown".equalsIgnoreCase(ipFromHeader)) {
String ip = ipFromHeader.split(",")[0];
return ip;
}
}
return request.getRemoteAddr();
}
}
Java
복사
그 안에서 사용하는 것은
/**
* Return the RequestAttributes currently bound to the thread.
* @return the RequestAttributes currently bound to the thread,
* or {@code null} if none bound
*/
@Nullable
public static RequestAttributes getRequestAttributes() {
RequestAttributes attributes = requestAttributesHolder.get();
if (attributes == null) {
attributes = inheritableRequestAttributesHolder.get();
}
return attributes;
}
Java
복사
이 함수를 사용해 RequestAttributes 라는 interface 를 가져옵니다.
ServletRequestAttributes 를 가져와 thread 에 bound 된 servlet request 의 데이터를 받아와
이 thread 로 요청을 보낸 client 의 정보를 수집합니다.
제 서버 상황에서는 X-Forwared-For 의 데이터를 반환받지만, 또 다른 환경이고 다른 상황이라면 다른 header 의 값을 가져와 확인하겠네요
결론
무조건 controller 에서 받아온 Request header 의 remote address 를 확인해선 안된다.
내 서버 상황에 맞는 (proxy 유무) 를 확인하여 필요한 header 의 값을 가져오자!!