[Co Labor] 백엔드 HTTPS 배포하기 - Nginx, CloudFront

2024. 11. 22. 09:50프로젝트: Co Laobr

프론트를 HTTPS로 변경했더니 백으로 HTTP 요청을 보내는게 작동하지 않았다. HTTPS 페이지에서 HTTP 리소스를 로드하려 하면 브라우저가 차단하는 등 보안적인 문제가 있었다.

 

이를 해결하기 위해 백엔드 전체를 HTTPS로 전환하거나 Nginx와 같은 프록시를 사용하여 HTTPS 요청을 HTTP 백엔드로 전달할 수 있다.

백엔드를 HTTPS로 전환하려면 인증서를 발급받거나 AWS의 ACM을 사용하고 EC2 인스턴스에 인증서를 설치해야 한다. 이 인증서는 유효기간도 있고 과정이 복잡하기에 Nginx를 사용하기로 했다.

Nginx를 사용해도 인증서가 필요하긴 하지만 Nginx 설정에 명시할 수 있다. 하지만 EC2 서버에 ACM을 설치할 수 없기에 Let's Encrypt 인증서를 Nginx에 설치해서 사용하기로 결정했다.

 

Nginx는 경량 웹서버로 리버스 프록시 서버로 활용된다.

프록시는 클라이언트 측에서 사용되고 클라이언트의 요청을 서버로 대신 전달하는 역할을 한다.

리버스 프록시는 서버측에서 클라이언트의 요청을 받아 백엔드 서버로 전달하는 서버의 역할이기에 리버스 프록시라고 부른다.

 

즉, 포워드 프록시는 클라이언트가 프록시에 요청하면 프록시가 인터넷을 타고 서버로 연결한다면, 리버스 프록시는 클라이언트가 인터넷을 통해 프록시에 요청하면 프록시가 서버에 요청한다.

 

프론트에서 요청 흐름을 살짝 알아보면 프론트는 API 엔드포인트로 똑같이 요청을 보낸다. DNS 설정에 따라 Nignx로 요청이 라우팅되고 Nginx에서 백엔드 서버로 전달하는 것이다.

 

리버스 프록시 외에도 로드 밸런싱, SSL/TLS 종단의 역할을 한다. 특히 SSL/TLS 종단은 HTTPS 연결을 처리하고 백엔드로의 통신은 HTTP로 할 수 있어서 서버 부하를 줄일 수 있다.

 

Cetrbot을 사용하면 Let’s Encrypt 인증서 발급 및 갱신을 자동화할 수 있다.

 

이제 한 번 사용해보자.

먼저 EC2 인스턴스에 ssh로 접속해준다.

  1. apt 업데이트
    sudo apt-get update
  2. letsencrypt 설치
    sudo apt-get install letsencrypt -y
  3. certbot 설치
    sudo apt install certbot python3-certbot-nginx
  4. nginx 실행
    sudo service nginx start
    이제 서버 IP 주소 혹은 도메인으로 들어가면 다음과 같은 nginx 화면이 떠야 한다.

5. cerbot으로 인증서 받기
sudo certbot --nginx -d 도메인주소
여기서 도메인 주소는 13.209.22.1과 같은 IP 주소가 아니라 DNS에서 관리하는 도메인 주소가 되어야 한다.
메일 주소를 입력하고 인증서를 발급하려 했는데…!! 문제가 생겼다.

현재 프론트에서 CloudFront 인증서를 사용하고 있기에 Nginx에서는 SSL/TLS 인증서 관련된 기능은 수행할 필요 없고 리버스 프록시 기능만 수행하면 된다.

 

6. Nginx 설정 수정

/etc/nginx/sites-available/default

현재 설정은 다음과 같다.

server {
        listen 80 default_server;
        listen [::]:80 default_server;

     

        root /var/www/html;

        index index.html index.htm index.nginx-debian.html;

        server_name _;

        location / {
 
                try_files $uri $uri/ =404;
        }


}

아래와 같은 방식으로 수정하면 된다.

server {
    listen 80;
    server_name colabor.site;

    location / {
        proxy_pass http://서버주소:8080;  # 스프링 부트 애플리케이션 포트
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

 

7.nginx 설정 테스트 및 재시작
sudo nginx -t sudo

systemctl restart nginx

 

HTTP 요청을 리버스 프록시하는 것 까지는 잘 됐는데 여전히 HTTPS는 작동하지 않았다.

지금 상황을 정리해보면 다음과 같다.

  1. 현재 페이지가 CloudFront를 통해 HTTPS로 로드된다.
  2. 페이지 내에서 HTTP API 요청을 보낸다.
  3. 브라우저가 Mixed Content를 차단한다.
  4. 따라서 Nignx가 리버스 프록시 역할을 수행하기 전에 차단되므로 CloudFront 설정을 조정하고 프론트엔드 코드를 수정해야 한다.

문제점은 listen하고 있을 포트에 443을 추가하고 있지 않았고 CloudFront의 Behavior 탭에서 스프링 서버를 추가하지 않았다. 둘 다를 모두 해줬더니 GET은 잘 되지만 POST만 동작하지 않았다.

 

조금 더 알아보니 분명히 CORS 설정은 다 해줬지만 사실 OPTION 요청에 대해 처리하지 않았던 것이었다.

server {
    listen 80;
    listen [::]:80;
    listen 443;
    listen [::]:443;
    server_name colabor.site;

    # 정적 파일 서빙을 위한 새로운 location 블록
    location /images/ {
        alias /home/ubuntu/path/to/save/images/;
        expires 30d;
        add_header Cache-Control "public, no-transform";
    }

    location / {
        proxy_pass http://localhost:8080;  # 스프링 부트 애플리케이션이 실행 중인 포트
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;

        # CORS 설정
        if ($request_method = 'OPTIONS') {
            add_header 'Access-Control-Allow-Origin' '$http_origin' always;
            add_header 'Access-Control-Allow-Methods' 'GET, POST, DELETE, PATCH, OPTIONS' always;
            add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
            add_header 'Access-Control-Allow-Credentials' 'true' always;
            add_header 'Access-Control-Max-Age' 1728000;
            add_header 'Content-Type' 'text/plain; charset=utf-8';
            add_header 'Content-Length' 0;
            return 204;
        }

        add_header 'Access-Control-Allow-Origin' '$http_origin' always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, DELETE, PATCH, OPTIONS' always;
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
        add_header 'Access-Control-Allow-Credentials' 'true' always;
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
    }
}