[Nginx] HTTPS 적용

2024. 8. 4. 20:49BE/Nginx

1. HTTPS 필요

 

Nginx SSL Termination이란 Nginx 서버가 클라이언트와의 SSL/TLS 연결을 종료하고, 그 이후의 트래픽을 암호화 없이 백엔드 서버로 전달하는 과정을 의미한다.

 

이를 통해 백엔드 서버의 부담을 줄이고, SSL/TLS 관리와 인증을 Nginx가 처리할 수 있도록 한다.

 

외부와의 통신은 보안이 중요한 만큼 HTTPS를 사용하여 데이터를 암호화한다.

 

외부에서 제3자가 데이터가 전송되는 동안 데이터를 들여다볼 수 없게 만든다.

 

서버 내부에서는 암호화의 필요성이 줄어들기 때문에 효율성을 위해 HTTP를 사용하여 통신한다.

 

이렇게 하면 백엔드 서버에서 복호화하는데 필요한 부담을 줄일 수 있다.

 


2. 원리

 

 

(HTTPS 통신에 대한 간단한 설명 참고)

 


3. 로컬 개발 환경에서 HTTPS

 

로컬에서 HTTPS가 적용된 환경에서 개발과 테스트를 진행하고 싶은 경우 어떻게 해야 할까?

 

로컬 SSL 인증서를 생성하고 nginx의 설정을 업데이트하여 HTTPS를 지원하도록 해야 한다.

 


가. SSL 인증서 및 키 생성

 

로컬 환경에서 HTTPS를 테스트하기 위해서는 자체 서명된 SSL 인증서를 생성한다.

 

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/certs/localhost.key -out /etc/nginx/certs/localhost.crt -subj "/C=KR/ST=Gyeongsangbuk-do/L=Gumi/O=SSAFY/OU=SSAFY/CN=localhost";
  • OpenSSL을 사용하여 인증서와 키를 생성.

  • -x509
    : 인증서 서명 요청(CSR)을 생성하는 대신 자체 서명된 인증서를 직접 생성하는 옵션.
    : 인증 기관(CA)에 제출할 필요 없이 자체 서명된 SSL 인증서를 만듦.

  • -subj "/C=KR/ST=Gyeongsangbuk-do/L=Gumi/O=SSAFY/OU=SSAFY/CN=localhost";
    : 인증서의 주체 세부 정보를 지정
    : 국가(KR), 주(Gyeongsangbuk-do), 지역(Gumi), 조직(SSAFY), 조직 부서(SSAFY), 공통 이름(localhost)로 설정.

 


나. nginx 설정 업데이트

server {
    listen 80;
    server_name localhost;

    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name localhost;

    ssl_certificate /path/to/localhost.crt;
    ssl_certificate_key /path/to/localhost.key;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    location / {
        root /usr/share/nginx/html;
        index index.html;
    }

    # 추가적인 설정은 여기에 추가
}
  • listen 443 ssl;
    : 443 포트로 들어오는 HTTPS 요청을 처리한다.

  • ssl_certificate /path/to/localhost.crt;
    : 서버의 공개키를 포함한 SSL 인증서 파일의 경로를 지정.

  • ssl_certificate_key /path/to/localhost.key;
    : SSL 인증서에 대응하는 개인 키 파일의 경로를 지정.
    : 서버가 암호화된 데이터를 복호화하는 데 사용.


다. nginx 재시작

sudo systemctl restart nginx
# 또는
sudo service nginx restart

설정을 저장한 후 nginx를 재시작하여 변경 사항을 적용.

 

 


4. 배포 단계에서 HTTPS

 

실제 도메인 이름으로 nginx에서 HTTPS를 설정하려면, CA에서 발급한 SSL 인증서를 사용해야 한다.

 

무료로 인증서를 발급해 주는 Let’s Encrypt를 사용했다.

 


가. Certbot

 

Certbot은 Let’s Encrypt에서 제공하는 무료 SSL/TLS 인증서를 자동으로 발급하고 갱신해 주는 도구다.

 

패키지 매니저를 통해 직접 설치할 수도 있지만 docker 환경에선 certbot docker image를 사용하는 것이 더 편하다.

(어떻게 알았냐고요? 저도 알고 싶지 않았어요…)

 

# docker-compose.yml
services:
  nginx:
    image: nginx:latest
    container_name: nginx
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./data/nginx:/etc/nginx/conf.d
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot

  certbot:
    image: certbot/certbot
    command: certonly --webroot --webroot-path=/var/www/certbot --email your_email@email.com --agree-tos --no-eff-email -d your_domain.com
    volumes:
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
    depends_on:
      - nginx

docker-compose 파일을 작성한 다음 /data/nginx 아래에 nginx 설정 파일을 추가한다.

 

server {
    listen 80;
    server_name example.com; #본인 도메인

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

잊지 말고 nginx.conf에서 해당 파일을 include하자. (~~~.conf로 이름 붙이면 기본적으로 include 됨)

 

docker compose up -d

 

정상적으로 동작했다면 certbot 컨테이너가 data/certbot/conf에 인증서를 생성해서 저장한다.

 


나. Nginx 설정 파일 수정

 

진짜 서비스용 Nginx 설정 파일을 추가한다.

 

 

GitHub - h5bp/server-configs-nginx: Nginx HTTP server boilerplate configs

Nginx HTTP server boilerplate configs. Contribute to h5bp/server-configs-nginx development by creating an account on GitHub.

github.com

 

아래 예시는 설명을 위해서 장황하게 작성했지만 실제 프로젝트에선 템플릿 참고해서 빠르게 완성하기.

1. h5bp/tls/certificate_files.conf에 https 인증서 추가

2. 작성한 설정 파일 conf.d에 추가

 

server {
    listen 80;  # IPv4
    listen [::]:80; # IPv6

    server_name example.com;

        # .well-know/acme-challenge 폴더 위치 표시
    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }
    #위 코드를 snippets에서 관리할 수도 있음 참고
    #include /etc/nginx/snippets/letsencrypt.conf;

    # 모든 요청을 https로 리다이렉트
    location / {
        return 301 https://$host$request_uri;
    }

    # Disallow all bots
    location /robots.txt {
        return 200 "User-agent: *\nDisallow: /"; # Disallow all bots
    }
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    ### 선택적인 추가 설정 참고 ###
    #SSL 보안설정 ssl.conf에서 따로 관리 가능
    #include /etc/nginx/snippets/ssl.conf; 

    location / {
        root /app/build;
        try_files $uri /index.html;
    }

    location /api/v1/ {
        # docker network 내부에서 통신
        proxy_pass http://backend:8080;
        # proxy 요청에 원래 호스트 헤더를 설정
        proxy_set_header Host $host;
        # 클라이언트의 실제 IP를 X-Real-IP 헤더에 설정. 백엔드 서버가 실제 클라이언트의 IP를 알기 위함. -> 로깅, IP 제한, 지리적 위치 서비스 등에 사용.
        proxy_set_header X-Real-IP $remote_addr;
        # X-Forwarded-For 클라이언트의 IP와 경유한 프록시 서버의 IP 목록을 전달. 백엔드 서버가 전체 경로를 추적할 수 있게 함.
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        # 프로토콜을 X-Forwarded-Proto 헤더에 설정. 이유는 프로토콜이 http인지 https인지 알기 위함 -> 보안 분석, 트래픽 분석 등에 사>용.
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # Disallow all bots
    location /robots.txt {
        return 200 "User-agent: *\nDisallow: /";
    }
}

이제 진짜 Nginx 설정 파일을 작성해서 /data/nginx 아래에 위치시키면 된다.

 

참고 : ssl.conf

어차피 내용은 상황에 따라 계속해서 업데이트될 테니 어떤 기능들이 있는지만 가볍게 참고하자.

# 서버 버전 정보를 보내지 않도록 설정한다.
# 해커들이 서버의 버전 정보를 바탕으로 취약점을 찾아서 공격할 수 있기 때문이다.
server_tokens off;

# 세션의 유효 기간을 1일로 설정합니다.
ssl_session_timeout 1d;

# 공유된 SSL 세션 캐시를 50MB로 설정합니다.
ssl_session_cache shared:SSL:50m;

# SSL 세션 티켓을 비활성화합니다.
ssl_session_tickets off;

# TLS 1.2와 TLS 1.3 프로토콜만 사용하도록 설정합니다.
ssl_protocols TLSv1.2 TLSv1.3;

# 2048비트 길이의 Diffie-Hellman 파라미터 파일을 사용합니다.
# 암호키 별도로 생성해야 함
ssl_dhparam /etc/nginx/dhparam-2048.pem;

# 사용할 암호화 스위트를 정의합니다.
ssl_ciphers 'ECDHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA:ECDHE-ECDSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA384:DHE-RSA-AES128-SHA:DHE-RSA-CHACHA20-POLY1305';

# 사용할 ECDH 곡선을 secp384r1로 설정합니다.
ssl_ecdh_curve secp384r1;

# 서버에서 클라이언트보다 암호화를 우선 선택하도록 설정합니다.
ssl_prefer_server_ciphers on;

# SSL 스태플링을 활성화합니다.
ssl_stapling on;

# SSL 스태플링 검증을 활성화합니다.
ssl_stapling_verify on;

# DNS 해석기를 Google의 공개 DNS 서버로 설정합니다.
resolver 8.8.8.8 8.8.4.4;

# HTTP Strict Transport Security 헤더를 추가하여 브라우저가 일정 기간 동안 HTTPS를 강제하도록 설정합니다.
add_header Strict-Transport-Security "max-age=15768000; includeSubdomains; preload";

# 브라우저가 MIME 타입을 감지하지 못하게 하는 헤더를 추가합니다.
add_header X-Content-Type-Options nosniff;

# 같은 도메인에서만 iframe을 허용하도록 설정합니다.
add_header X-Frame-Options "SAMEORIGIN";
# 모든 도메인에서 iframe 사용을 금지하도록 설정합니다.
#add_header X-Frame-Options DENY;
# 특정 도메인에서만 iframe을 허용하도록 설정합니다.
#add_header X-Frame-Options "ALLOW-FROM <https://example.com>";
  • ssl_protocols TLSv1.2 TLSv1.3;
    : 서버에서 허용할 SSL/TLS 프로토콜 버전을 정의.
    : 최신 버전인 TLS 1.2와 TLS 1.3만 허용.
    : 24년 8월 기준 그 이하 버전은 브라우저에서 ERR_SSL_VERSION_OR_CIPHER_MISMATCH 에러 발생.
  • ssl_ciphers HIGH:!aNULL:!MD5;
    : 서버에서 사용할 수 있는 암호화 스위트(Cipher suite)를 정의.

 


다. SSL 인증서 갱신

 

Let’s Encrypt 인증서는 90일 동안 유효하기 때문에 주기적으로 갱신해야 한다.

sudo certbot renew --dry-run

갱신 전에 테스트하고 정상적으로 동작한다면 실제로 갱신하자.

 

certbot renew
systemctl restart nginx

이 과정을 수동으로 할 수도 있지만 cron에 등록해서 자동화할 수 있다.

 

 

2022-06-28 Cron_실행

이 글은 목소리가 감미로우신 이고잉님의 오픈튜토리얼 리눅스 강의를 듣고 정리한 내용입니다. Cron 출처 : https://terms.naver.com/entry.naver?docId=4125592&categoryId=59321&cid=59321 cron은 백업과 같이 주기적

ramen4598.tistory.com

 


  • 참고
 

NGINX SSL Termination

클라이언트의 HTTPS 트래픽을 Terminate하여 Upstream 웹 및 애플리케이션 서버의 SSL/TLS 암호화에 따른 연산 부하를 덜어줍니다. 이 문서에서는 NGINX 및 NGINX Plus 에서 HTTPS 서버를 구성하는 방법을 설명

nginxstore.com

 

certbot 으로 let's encrypt 인증서 발급받기

certbot 을 통해 let's encrypt 인증서를 발급받아보자. 현재로서는 가장 간단한 방법인 것 같다. 공식 사이트 가이드를 따라 진행한다. 아래 페이지를 참고했다. certbot 가이드: https://certbot.eff.org/instruc

bitgadak.tistory.com

 

https 사용하기(Let's Encrypt 인증서 사용) - 데브보노의 블로그

저번 글 '도메인 구매하고 도메인 연결하기(go daddy에서 구입)'을 성공적으로 따라 하셨다면 외부인터넷에서 도메인을 입력해서 라즈베리파이 워드프레스에 접속하실 수 있게 되었을 겁니다.

devbono.com

 

 

SSL 인증서 발급 및 갱신(with Let's Encrypt)

예전에 했던 프로젝트에서 1년이 지나 인증서 만료로 갱신해야 한다고 하는데 다른 분이 하셔서 잘 모른다😭 이참에 아예 확실하게 알아두자.. Ubuntu 20.04 운영체제에서 웹서버로 Nginx 사용 시 무

velog.io

 

 

 

'BE > Nginx' 카테고리의 다른 글

[Nginx] 리버스 프록시  (0) 2024.08.04
[Nginx] Nginx란?  (0) 2024.08.03