스프링부트에 Nginx 리버스 프록시(Reverse Proxy) 서버 연결하기

 

 

리버스 프록시를 도입하게된 계기 💭

졸업작품인 코리(Co:RE)의 서버를 개발하면서 실제로 배포를 위한 환경을 만들려고 하다보니,

보안적인 요소를 생각하지 않을 수 없었다.

 

1차적으로 생각이 난 부분은 당시 학습하고 있었던 프록시 서버였다.

프록시 서버에 대해 모르는 사람은 해당 포스팅을 참고해주세요!

 

리버스 프록시 서버를 적용하면 보안상 이점과 여러 장점들을 얻을 수 있는데,

그 중 내가 생각한 장점은 아래와 같았다.

 

 

⚙️ 보안

개발자 도구에 WAS의 주소가 그대로 노출되어 다양한 공격의 가능성이 발생한다.

여기에 리버스 프록시를 적용하게 되면 서버를 직접 노출시키지 않고 클라이언트와의 통신을 중계하므로 서버의 실제 위치를 숨기고 보안을 강화할 수 있다.

프록시 서버로 인해 본래 서버의 IP 주소를 노출시키지 않을 수 있어, 해커들의 DDos 공격과 같은 공격을 막는데 용이하다.


⚙️ 로드 밸런싱

리버스 프록시는 여러 서버로 요청을 분산할 수 있으며, 부하 분산을 구현할 수 있다.

추후 서버를 Scale Out 했을 때 Load Balancing 기능까지 수행할 수 있다.

이는 트래픽이 여러 서버에 고르게 분산되어 서버의 성능을 향상시키고 가용성을 높이는 데 도움이 된다.

 

 


 

작업 환경 💻


AWS EC2 't2.micro'
 'ubuntu 22.04' AMI
 `Nginx 1.18.0`


생략 과정 ✂️

 AWS 계정 생성
EC2 생성 (Ubuntu 22.04 AMI 사용)
보안규칙 설정
    HTTP 80번 포트에 대해 IPv4, IPv6 허용
    HTTPS 443번 포트에 대해 IPv4, IPv6 허용
    SSH 22번 포트 IPv4 허용
볼륨 크기는 EC2 프리티어에서 사용할 수 있는 최댓값인 27GiB 설정
 스프링부트 서버가 죽지 않도록 `Swap Memory` 설정하여 `t2.micro` 환경에서 3GB의 메모리 흭득

 


 

Nginx Reverse Proxy 설정 방법 ⚙️


💻 Shell 명령

# apt-get 업데이트
sudo apt-get update

# Nginx 설치
sudo apt-get install nginx
sudo service start nginx

# nginx Config 파일 열기
sudo vi /etc/nginx/site-available/default

# http 설정 바로 아래에 server 설정을 추가하여 리버스 프록시 설정(사용하지 않는 주석 전부 제거)



💻 /etc/nginx/site-available/default 설정

http {
        server {
                listen 80; # 포트를 필요에 따라 조정하세요.
                #server_name your_domain.com; # 도메인 이름 대신 IP 주소를 사용할 경우 필요 없습니다.

                location / {
                        proxy_pass http://서버_주소:포트; # 백엔드 서버의 IP 주소
						proxy_http_version 1.1;

                        proxy_set_header Connection ""; #504 Bad GateWay 문제 방지
                        proxy_set_header Host $host;
                        proxy_set_header X-Real-IP $remote_addr;
                        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                }
        }

        sendfile on;
        tcp_nopush on;
        types_hash_max_size 2048;

        include /etc/nginx/mime.types;
        default_type application/octet-stream;

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
        ssl_prefer_server_ciphers on;

        access_log /var/log/nginx/access.log;
        error_log /var/log/nginx/error.log;
}

 


⚙️ proxy_set_header Host $host;


이 헤더는 원격 서버로 전달되는 HTTP 요청 헤더 중 하나이다.
`$host` 변수는 클라이언트가 요청한 호스트 헤더 값을 가지고 있다.

호스트 이름은 일반적으로 `도메인 이름`이나 `IP 주소`인데,
이를 통해 리버스 프록시는 요청을 받은 원격 서버에게 어디로부터 온 요청인지 알려준다.

 

 

 


⚙️ proxy_set_header X-Real-IP $remote_addr;


이 헤더는 `클라이언트의 실제 IP 주소`를 리버스 프록시를 통해 전달한다.
이것은 원격 서버가 클라이언트의 실제 IP를 알고자 할 때 사용된다.

 

⚙️ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;


일반적으로 HTTP 요청에는 `X-Forwarded-For` 헤더가 포함되어 있다.
이 헤더는 `클라이언트`가 `어떤 IP 주소를 통해 요청을 보냈는지`를 기록하며, 여러 프록시 서버를 거친 경우 쉼표로 구분된 목록으로 기록된다.

Nginx에서 `$proxy_add_x_forwarded_for` 변수는 이 `X-Forwarded-For` 헤더의 값을 가져와 저장한다.
이를 통해 서버는 실제 클라이언트의 IP 주소를 파악할 수 있다.

 

예를 들어, 클라이언트 A에서 요청이 오고, 그 요청이 Nginx 프록시 서버 를 거쳐 백엔드 서버로 전달되면,
X-Forwarded-For 헤더에는 "A, Nginx_IP" 와 같이 표시된다.

이때 $proxy_add_x_forwarded_for 변수에는 "A, Nginx_IP"와 같은 값이 저장된다.

 

 


연결 완료 ✅

 


`Nginx` EC2의 IP에 `80번 포트`로 접속했는데 `SpringBoot` EC2의 IP에 `8080번 포트`로 접속한 것과 동일한 화면이 출력되는 것을 확인할 수 있다.

또한 `SpringBoot`의 포트인 `8080`번으로 접속하면, 접속할 수 없는 것을 알 수 있다.

이는 우리가 `AWS의 EC2 보안 그룹의 인바운드 규칙`에서 `8080번` 포트를 외부에서 접속하지 가능하도록 설정해주지 않았기 때문이다.
위와 같은 설정들을 통해 무조건 `리버스 프록시`를 거쳐서 접속할 수 있게 된 것이다.

 

 


 

추후 프로젝트 개선 방향 🤔

  • `WAS`에 대한 접근과 정보 노출을 방어했지만 아직 `Nginx`를 통해서 요청하면 `WAS`에 요청하는 것과 똑같이 요청하여 데이터를 흭득할 수 있다. 그렇기에 고의적으로 `리버스 프록시 서버`에 많은 요청을 보내게 되면, 그대로 받아들여 서버에 과부하가 오게 된다.
  • 또한 지금 개발하고 있는 서비스 `Co:RE`는 유저들이 운영 측의 `GPT API Key`를 사용하여 무료로 응답값을 얻는 형식이다. 때문에 더더욱 많은 고의적인 요청이 보내지면 금전적으로 큰 손해를 볼 수 밖에 없다
    • 이에 대한 결론으로 다음 개발 과제는 `과도한 트래픽에 대한 방어`로 잡았다.
    • `Bucket4j` 라이브러리를 사용하면 Token-Bucket 알고리즘을 통해
      유저들에게 API를 요청할 수 있는 `Token`을 주고 Token의 `사용 시간` 또는 `사용 횟수`에 제한을 걸어,
      고의적인 요청을 방어할 수 있다고 한다.
      때문에 이를 공부하여  적용해보고자 한다.