[Nginx] 특정 IP만 접근 가능하도록 허용, 그 이외는 점검중 페이지로 redirect

TO-BE

배포 후 팀원들이 테스트하는 동안에 허용된 IP만 접근 가능하도록 하고, 그 이외의 IP 접근은 점검중 페이지로 가도록 하고 싶다.

Trouble Shooting

1. $remote_addr

  1. $remote_addr를 $maintenance에 mapping해준다.
  2. location 기본 설정에서 maintenace가 on일경우 에러 코드 503(Service Unavailable)을 반환한다.
  3. 아래 error_page에서 503 코드를 받아, /maintenance.html을 응답한다.
# nginx.conf
http {
  ...
  
  map $remote_addr $maintenance {
      default		on;
      127.0.0.1		off;	# 원하는 IP 기입
  }
  
  server {
  	...
    proxy_intercept_errors on;
    
    location / {
    	...
        
        if ($maintenance = on) {
        	return 503;
        }
    }
    
    error_page 503 /maintenance.html;
    
    location = /maintenace.html {
    	root /home/ec2-user/web;
        internal;
    }
  }
}

결과

  1. 차단이 잘 되는지 확인하기 위해 default 설정을 on으로 하고 테스트 해보았다. 점검 중 페이지로 redirect가 잘 되는 것을 확인할 수 있었다.
  2. 내 ip로 들어갈 경우에는, 정상적으로 접속이 잘 되어야 하기 때문에, ipconfig를 통해 찍히는 IPv4 주소를 off로 설정하고 재접속했다. 로그인 페이지가 나타나야하는데 점검 중 페이지로 redirect 되었다.

=> 결론: default 설정은 잘 적용됨. 내 ip를 잘못 기입한 것 같았다.

2. $http_x_forwarded_for

  1. IP를 찾아서
    서칭을 하다 보니, ipconfig에 찍히는 ip랑, https://whatismyipaddress.com/ 에 찍히는 ip가 달랐다. 후자의 ip는 ISP의 ip, 즉 라우터의 ip인것 같았다. 이곳에 찍히는 ip를 nginx설정에 추가해주었지만 역시 제대로 동작하지 않았다.
    netstat 명령어도 찍어보며 이리저리 찾아보았지만, 제대로 동작하지 않았다.

  2. access_log
    nginx로 들어오는 로그를 찍어봐야겠다!라고 생각하고 log를 찍는 법을 찾아보았다. 로깅은 아래와 같이 설정해주면 되었다.

# nginx.conf
http {
    ...
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
                      
    access_log  /var/log/nginx/access.log  main;
    ...
}
  1. $remote_addr과 $http_x_forwarded_for 그리고 ELB
    (로깅하는 방법을 찾고 설정하러 들어가니 이미 nginx.conf파일에 로깅이 설정되어 있는 것을 뒤늦게 발견했다.)
    로그 파일로 가서 $remote_addr를 확인했는데, 두가지 IP로만 요청이 들어오고 있었다. 실제 클라이언트 IP는 $http_x_forwarded_for에 찍히고 있었고, 여기서 찍힌 ip는 https://whatismyipaddress.com/ 이곳에서 확인한 ip와 동일했다. $remote_addr이 두가지로 정해져서 들어오는 것이 AWS 설정과 관련이 있을거라 생각하고 서칭을 해보자, AWS 환경에서 웹서버의 앞단의 ELB 를 사용한 것과 연관이 있었다.
    로드밸런서는 클라이언트의 ip를 자신의 ip로 변경해서 서버로 요청하기 때문에, nginx의 access_log에서 $remote_addr은 로드밸런서의 IP로 찍히고 있었던 것이다. 따라서, 로드밸런서나 프록시를 사용할 경우, 클라이언트의 원래 IP주소를 확인하려면 X-Forwarded-For 헤더를 사용해야 한다는 것을 알게되었다.

참고 Nginx에서 클라이언트 IP를 확인하는 법 ($http_x_forwarded_for)
1) Proxy 모드 로드밸런서 이용시 클라이언트 IP 로깅하기(TOAST 서비스 들여다보기)
2) ELB를 기반으로 하는 웹 서버 로그에서 클라이언트 IP 주소를 캡처하려면 어떻게 해야 합니까?

결론

nginx 설정파일에서 $remote_addr를 $http_x_forwarded_for로 바꾸니 잘 동작하는 것을 확인할 수 있었다.

최종 해결 코드

# nginx.conf
http {
  ...
  
  map $http_x_forwarded_for $maintenance {
      default		on;
      127.0.0.1		off;	# 원하는 IP 기입
  }
  
  server {
  	...
    proxy_intercept_errors on;
    
    location / {
    	...
        
        if ($maintenance = on) {
        	return 503;
        }
    }
    
    error_page 503 /maintenance.html;
    
    location = /maintenace.html {
    	root /home/ec2-user/web;
        internal;
    }
  }
}
남은 궁금증
  1. 클라이언트 IP 주소가 라우터의 IP로 찍히는데, 기기별 ip 차단은 불가능한 것인가?

자동화

위의 TO-BE를 위해서는, 빌드 후 배포 전에 nginx.conf 설정에 mapping을 넣어주고, 배포 후 테스트 후에는 nginx.conf 설정에 mapping을 빼줘야 한다.

# 배포 전에는 이 부분을 넣고, 테스트 후에는 이 부분을 빼줘야 한다.
  map $http_x_forwarded_for $maintenance {
      default		on;
      127.0.0.1		off;	# 원하는 IP 기입
  }

빌드 -> 특정 IP 접근만 허용하는 nginx.conf 설정 -> 배포 -> 테스트 -> IP 제한 정보가 빠진 기본 nginx.conf 설정

고민하다가 nginx.conf 파일을 교체해주는 쉘 스크립트를 짜서, jenkins로 실행해주기로 했다.

좋은 웹페이지 즐겨찾기