[AWS] Nginx + Docker (+docker compose) 환경에서 SSL 적용하기

Sungjin Cho·2024년 9월 3일

AWS

목록 보기
6/7
post-thumbnail

letsencrypt 장단점 (by claude)

장점

무료: 비용 부담 없이 SSL 인증서를 발급받을 수 있다.
자동화: 인증서 발급 및 갱신 과정을 자동화할 수 있다.
널리 지원됨: 대부분의 웹 브라우저와 디바이스에서 인식된다.
오픈소스: 투명성과 신뢰성이 높다.
간편한 설치: 다양한 툴과 플러그인을 통해 쉽게 설치할 수 있다.

단점

짧은 유효기간: 90일마다 갱신해야 한다 (자동화로 해결 가능).
제한된 기능: EV(Extended Validation) 인증서 등 고급 기능은 제공되지 않는다.
도메인 검증만 가능: 조직 검증(OV) 또는 확장 검증(EV)은 제공되지 않는다.
기술 지원 제한: 공식적인 기술 지원이 제한적일 수 있다.
와일드카드 인증서 발급의 복잡성: DNS 챌린지 방식을 사용해야 하므로 설정이 복잡할 수 있다.

결론적으로 무료 SSL 인증서인 letsencrypt를 docker + nginx 환경에 적용하여 데이터를 암호화 하는 것이 보안 측면에서 좋다. 하지만 데이터가 암호화 된다고 하더라도 무료 인증서이기 때문에 데이터의 유출과 같은 상황에서 보증 보험이나 법적 책임을 지지 않는다는 단점이 있다.

1. 기존 nginx/Dockerfile, nginx/nginx.conf, docker-compose.yml 수정하기

Dockerfile

FROM nginx:alpine
COPY ./nginx.conf /etc/nginx/nginx.conf
EXPOSE 80 443
CMD ["nginx", "-g", "daemon off;"]

expose 443 추가

nginx.conf

events {
    worker_connections 1024;
}

http {
    server {
        listen 80;
        server_name www.gajapos.com gajapos.com www.gajapos.kr gajapos.kr pos.gajapos.com pos.gajapos.kr;
        server_tokens off;

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

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

    server {
        listen 443 ssl;
        server_name www.gajapos.com gajapos.com www.gajapos.kr gajapos.kr pos.gajapos.com pos.gajapos.kr;
        server_tokens off;
        client_max_body_size 20M;

        ssl_certificate /etc/letsencrypt/live/gajapos.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/gajapos.com/privkey.pem;
        include /etc/letsencrypt/options-ssl-nginx.conf;
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

        location /api2 {
            proxy_pass http://spring:8081;
            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;
        }

        location /gazapos {
            proxy_pass http://jepetto: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;
        }

        location / {
            proxy_pass http://react:3000;
            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;
        }
    }
}

80 내용을 443으로 옮기고 80 에는
server_tokens off;

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

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

내용 추가

docker-compsoe.yml

services:
  spring:
#    build:
#      context: ./gazapos_db
#      dockerfile: Dockerfile
    image: sungnij/gazapos-spring:latest
    container_name: gazaposspring
    networks:
      - gazapos_network
    ports:
      - "8081:8081"
    volumes:
      - ./gazapos_db/pos.db:/app/sqlite/pos.db
    restart: always

  react:
#    build:
#      context: ./pos4phill-web
#      dockerfile: Dockerfile
    image: sungnij/gazapos-react:latest
    container_name: gazaposreact
    networks:
      - gazapos_network
    expose:
      - "3000"
    depends_on:
      - spring
    restart: always

  nginx:
#    build:
#      context: ./nginx
#      dockerfile: Dockerfile
    image: sungnij/gazapos-nginx:latest
    container_name: gazaposnginx
    networks:
      - gazapos_network
    ports:
      - "80:80"
      - "443:443"
    volumes:   # volume 추가
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
    depends_on:
      - react
      - spring
      - jepetto
    restart: always

  certbot: # certbot image 관련 내용 추가
    image: certbot/certbot
    container_name: gazaposcertbot
    volumes:
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
    entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"

  jepetto:
#    build:
#      context: .
#      dockerfile: gazapos/Dockerfile
    image: sungnij/gazapos-jepetto:latest
    container_name: gazaposjepetto
    networks:
      - gazapos_network
    ports:
      - "8080:8080"
    volumes:
      - ./gazapos_db/pos.db:/app/sqlite/pos.db
    environment:
      - CATALINA_BASE=/app/tomcat
      - CATALINA_HOME=/app/tomcat
      - JEPETTO_PROPERTIES=/app/tomcat/conf/jepetto.properties
      - WTP_DEPLOY=/app/tomcat/webapps
    restart: always

networks:
  gazapos_network:
    driver: bridge

2. 수정한 nginx image build 후 push

docker build -t sungnij/gazapos-nginx:latest ./nginx

docker push sungnij/gazapos-nginx:latest

3. SSH 서버에서 빌드한 이미지 pull

sudo docker pull sungnij/gazapos-nginx:latest

4. init-letsencrypt.sh 실행 파일 만들기

curl -L <https://raw.githubusercontent.com/wmnnd/nginx-certbot/master/init-letsencrypt.sh> > init-letsencrypt.sh
chmod +x init-letsencrypt.sh
sudo nano init-letsencrypt.sh // 도메인, 이메일, 디렉토리 수정
sudo ./init-letsencrypt.sh // 인증서 발급  

만약 curl 명령어가 실행이 안된다면 현재 디렉토리의 쓰기 권한을 체크 후 권한을 부여한다. sudo chmod u+w ~/gazapos

그리고 sudo nano init-letsencrypt.sh 로 확인해보면 domain(필수), email을 수정해준다.

5. init-letsencrypt.sh 실행하기

sudo ./init-letsencrypt.sh 로 스크립트 실행

이 경우, 실행 중에 에러가 발생한 경우이다. 이 에러를 고치지 못해서 한참 헤맸는데, /nginx/nginx.conf 파일이 제대로 마운트 되지 않아서 설정이 적용이 되지 않은 문제때문이었다.

분명 로컬에서 nginx.conf 를 수정하고 이미지를 빌드 했으니 당연히 이 설정은 적용되어 빌드되었다고 생각을 하였고, 실제로 그렇게 적용되어서 빌드된건 맞지만, ssh 서버 상에 이미 존재하던 /nginx/nginx.conf 파일이 있었기 때문에 이 파일을 마운트해서 사용하였기 때문에 에러가 발생하는 문제였다. (docker-compose.yml 에서 volume nginx.conf 마운트 설정이 이미 존재하는 파일을 바라보는 문제)

즉, 빌드 될 때는 로컬의 수정된 파일을 바탕으로 빌드 되지만 docker-compose 에서 볼륨에 마운트하는 nginx.conf 파일은 수정이 안된 nginx.conf 파일이었기 때문에 문제가 발생한 것이다.

따라서 SSH 서버의 nginx.conf 파일을 제대로 수정하고 나면

에러 없이 잘 실행이 완료된 것을 볼 수 있다.

6. docker 컨테이너 실행

letsencrypt가 잘 실행 되었다면 기존 컨테이너를 모두 내리고 새로 실행을 시켜준다.

sudo docker-compose down
sudo docker-compose up -d

7. https 접속

두 url 모두 성공적으로 ssl 이 적용되었고 http 로 접속하려 해도 https로 리다이렉트 되는 것을 확인 할 수 있었다.

0개의 댓글