Computer Science/Setup

[Nginx, Docker-compose] HTTPS 설정, https, wss 연결하기 (with Let's encrypt)

_혀니 2025. 2. 14. 16:12
728x90
반응형

1. https://사용하기

nginx/default.conf 기존 파일, HTTP만 지원

http {
    server {
        listen 80;
        server_name {your_domain};

        # ignore cache frontend
        location ~* (service-worker\.js)$ {
            add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
            expires off;
            proxy_no_cache 1;
        }

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

        location /media/ {
            root /app/;
            autoindex on;
            expires max;
            access_log off;
        }

        client_max_body_size 10M;
    }
}

 

80번 포트를 열어두고 해당 포트로 들어오는 요청대로 정적 파일을 serving하고 있다.

HTTPS 적용 과정

1. certbot 설치

sudo apt update
sudo apt install certbot python3-certbot-nginx

 

2. 인증서 발급

sudo certbot -d {your_domain} # 인증서 발급
sudo certbot --nginx -d {your_domain} # 또는 nginx 옵션 사용하면 현재 서비스에 돌아가는 nginx 설정파일에 자동 적용됨
# 단 nginx를 docker container로 따로 돌리고, ssl 인증서는 호스트에서 받고 있으면 자동 설정 안 됨

 

3. 발급받은 인증서 수동 설정하기

http {
    server {
        listen 80;
        server_name {your_domain};
        return 301 https://$host$request_uri; # 80번 포트로 들어오면 https로 redirect
    }

    server {
        listen 443 ssl; # https는 443번 포트를 사용한다
        server_name {your_domain};
        
        # ssl 키 등록 (마운트한 경로 따라서 등록하면 된다)
        ssl_certificate     /etc/letsencrypt/live/{your_domain}/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/{your_domain}/privkey.pem;
        
        # ssl 보안설정
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers on;

        # ignore cache frontend
        location ~* (service-worker\.js)$ {
            add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
            expires off;
            proxy_no_cache 1;
        }

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

        location /uploads/ {
            alias /app/uploads/;
            autoindex on;
            expires max;
            access_log off;
            # try_files $uri $uri/ =404;
        }

        client_max_body_size 10M;
    }
}

 

이렇게...

인증서를 적용해주면 된다

해당 키 경로는 왜 저기인가? 그 이유는

내가 docker container 안에서 nginx를 돌리고 있어서

저 경로대로 키를 마운트해줬기 때문이다.

docker-compose.yml의 nginx 웹서버 부분을 다음과 같이 지정해서 마운트한다

services:
  client:
    build: 
      context: ./client
      dockerfile: Dockerfile
    container_name: client
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./client:/client
      - /etc/letsencrypt:/etc/letsencrypt:ro 
      # 여기! letsencrypt하면 /etc/letsencrypt밑에 ssl 인증서가 생성되므로 해당 경로 마운트, 
      # 뒤에 :ro는 읽기 전용 옵션이다.
      - ./nginx/default.conf:/etc/nginx/nginx.conf:ro
      - ./media/uploads:/app/uploads

 

 

그리고 자매품..

2. wss:// 연결하기

기본적으로 웹소켓은 ws:// 로, http와 함께 동작한다

하지만 https를 사용하면 웹소켓도 wss://를 지원해주는 것이 맞는데

해당 설정은 리버스 프록시로 해결 가능하다.

worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

events {
    worker_connections 1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    limit_req_zone $binary_remote_addr zone=ddos_req:10m rate=20r/s;
    limit_req_status 503;
    limit_conn_zone $binary_remote_addr zone=ddos_conn:10m;
    
    add_header Content-Security-Policy "default-src 'self'; img-src 'self' data:; script-src 'self'; style-src 'self'; object-src 'none'; media-src 'self' blob:;";
    add_header X-Content-Type-Options "nosniff";
    add_header X-Frame-Options "DENY";
    add_header Referrer-Policy "no-referrer";
    add_header X-XSS-Protection "1; mode=block" always;

    upstream api {
        server server:8080;
    }
    
    server {
        listen 80;
        server_name {your_domain};
        return 301 https://$host$request_uri;
    }

    server {
        listen 443 ssl;
        server_name {your_domain};
        
        ssl_certificate     /etc/letsencrypt/live/{your_domain}/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/{your_domain}/privkey.pem;
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers on;

        # ignore cache frontend
        location ~* (service-worker\.js)$ {
            add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
            expires off;
            proxy_no_cache 1;
        }

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

        location /uploads/ {
            alias /app/uploads/;
            autoindex on;
            expires max;
            access_log off;
            # try_files $uri $uri/ =404;
        }

        location /api/ { # 백엔드 서버
            proxy_pass http://api$request_uri;

            proxy_read_timeout 300s;
            proxy_connect_timeout 75s;

            proxy_http_version  1.1;
            proxy_cache_bypass  $http_upgrade;

            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_set_header X-Forwarded-Host  $host;
            proxy_set_header X-Forwarded-Port  $server_port;
        }
        
        location /ws { ## 여기! url이 /ws로 시작하는 경우, 웹소켓 서버로 프록시해준다. ##
            proxy_pass http://api$request_uri;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";
            proxy_set_header Host $host;
            proxy_buffering off;
            proxy_read_timeout 3600s;
        }
        
        client_max_body_size 10M;
    }
}

 

웹소켓 서버가 백엔드 서버와 동일해서 매핑된 url에 맞춰서 백엔드 서버로 프록시해줬다.

이러면 이제 ws://{your_domain}:{port}/ws 로 보내던 소켓 요청이 wss://{your_domain}/ws 로 변경된다.

728x90
반응형