안녕하세요
클라우드 서버 전환 후에 발생된 문제에 대해 정리를 해보려고 합니다.
하고 있는 시스템의 개발과 운영 서버가 원래는 물리적인 장치로 이루어진 온프레미스 서버에서 도커& 리눅스 환경으로 구성되어 있었어요. 그리고 시장 트렌드상으로, 시스템들이 점차 클라우드 서버로 옮겨가고 있어서, 제가 있는 곳에서도 내부적으로 클라우드 전환을 시작하고 있었어요. 말이 나온 김에 클라우드의 장단점을 간단하게 언급하면 아래와 같아요.
장점 : 가격이 저렴하다. 더불어 공간적인 여유도 생긴다.
단점 : 좀 더 복잡한 세팅 작업이 필요하다. 중앙 컨트롤러가 마비되면 예하 시스템이 모두 마비된다.
그래도 IT는 트렌드를 따라가는 업종이기 때문에, 기존의 방식보다 가격과 기술적인 측면들을 고려했을 때, 클라우드 전환의 장점이 무척 많다고 봤어요. 그래서 이번에 클라우드 전환을 하고 시스템을 테스트해보다가 테더링을 이용한 외부망 테스트시 아이피에 대한 에러가 발생되어 정리를 하게 되었습니다.
이전 상황 :
온프레미스 서버에서는 휴대폰 테더링으로 외부망 접속을 해도 IP는 IPv4형태로 시스템에서 인식되었다.
이후 상황 :
클라우드 전환된 서버에서는 휴대폰 테더링으로 외부망 접속시 IPv6형태로 시스템에서 인식되었다.
발생했던 에러 :
1. DB에 접속IP를 저장하는 컬럼의 길이가 VARCHAR2(20)보다 커서
ORA-12899: value too large for column 에러가 발생.
=> IPv6으로 들어오는 모바일 아이피 길이를 감안해서 VARCHAR2(50)으로 길이를 늘렸어요.
2. 기존의 내부망 IPv4와 현재 접속한 외부망 IPv6 서로 다른 두 IPAddress 객체 간의 Contains 포함여부 에러
=> 정해진 내부망 IPv4는 null 또는 IPv6과 포함여부를 할 수 없기때문에 접속IP의 null체크
public boolean isPrivateNetWorkChk(){
return 클래스명.isContainSubnet(내부망Ip, getRemoteIP());
}
public static String getRemoteIP(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-RealIP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("REMOTE_ADDR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
if ("0:0:0:0:0:0:0:1".equals(ip)) {
ip = "127.0.0.1";
}
return ip;
}
public static boolean isContainSubnet(List<String> subnetmasks, String ipAddress) {
if (ipAddress == null || ipAddress.isEmpty()) {
return false;
}
IPAddress targetAddress;
try {
targetAddress = new IPAddressString(ipAddress).getAddress();
if (targetAddress == null) {
return false;
}
} catch (Exception e) {
return false;
}
for (String subnetmask : subnetmasks) {
IPAddress subnetAddress = new IPAddressString(subnetmask).getAddress();
IPAddress subnet = subnetAddress.toPrefixBlock();
boolean result = subnet.contains(targetAddress);
if (result) {
return true;
}
}
return false;
}
알게 된 내용 :
사용자의 PC가 IPv4로 고정IP가 등록이 되어있는 경우,
온프레미스 서버에 IPv4로 전환되도록 세팅이 되어있는 경우,
휴대폰 테더링을 통해 외부망으로 접속을 해도 요청된 아이피는 IPv4로 전환되어 표시된다.
하지만, 사용자의 PC가 고정 IP가 아닌 경우,
Azure Cloud로 모바일의 SKT, KT LTE망 등을 통해 접속하는 경우,
휴대폰 테더링을 통해 외부망으로 접속을 하면 요청된 아이피는 모바일을 따라 IPv6형식이 된다.
(=요청된 IP를 필터링 부분의 X-Forwarded-For에 방화벽 IP가 최대 55자로 들어오게 된다)
원래 Tomcat RemoteIpValue에서 요청된 *.*.*.* 형태의 IPv4 아이피가 HAProxy로 들어온 것은 X-Forwarded-For의 맨 앞IP로 remoteAddr을 교체하게 되어있다. 하지만, Azure Cloud에 외부망을 통해 접속시 방화벽 IP가 넘어오면서 RemoteIpValue가 파싱되지 못하는 문제가 생긴다. 여기서 RemoteIpValue란, 서버에서 클라이언트의 IP를 나타내는 변수를 의미한다.
때문에 Azure Cloud 서버의 Tomcat Server.xml에서 RemoteIpValue에 Proxy IP를 추가하여 정상적으로 (IPv6이 55자로) IP를 반환할 수 있다. 또 IP관련된 컬럼의 길이를 늘리고 기존IP형태와 비교하는 로직도 수정을 해야한다.
<!--Azure Cloud Tomcat Server.xml에 RemoteIpValue에 Proxy Ip 추가설정-->
<Valve className="org.apache.catalina.valves.RemoteIpValve"
internalProxies="${InternalProxies}"
remoteIpHeader="x-forwarded-for"
proxiesHeader="x-forwarded-by"
protocolHeader="x-forwarded-proto"
/>
'Error 모음' 카테고리의 다른 글
[Oracle] ORA-00054 리소스가 사용 중이어서 NOWAIT가 지정되었거나 시간 초과가 만료된 상태로 획득합니다. (0) | 2023.12.07 |
---|---|
[이클립스] Publishing failed with multiple errors. (0) | 2023.12.01 |
[Spring] Mapped Statements collection does not contain value for. (0) | 2023.11.29 |
[Oracle] Insert ~ Select시 PK를 Max로 추출시 값이 동일한 문제 (0) | 2023.11.29 |
[Oracle] ORA-06576 함수 또는 프로시저 이름이 부적합합니다 (0) | 2023.11.29 |