본문 바로가기

Programming/Spring Boot

Spring Boot 프로젝트 SSL 인증서로 https 적용하기

1. Let's Encrypt Certbot Docker Image로 SSL 인증서 발급 받기

웹 페이지의 보안을 위해 사용되는 https은 SSL 인증서가 필요합니다.

Let's Encrypt는 무료 SSL 발급 인증 기관으로 Certbot Docker Image를 제공하고 있어, 이를 활용하면 SSL 인증서를 발급 받을 수 있습니다.

 

우선, SSL 인증서 발급을 위해 Docker 설치, 서버 인증에 필요한 도메인 준비가 선행되어야 하며, 관련 내용은 생략하고 발급 방법을 알아보겠습니다.

 

참고로, Let's Encrypt를 이용하여 SSL 인증서를 발급받는 방법은 3가지입니다.

① Webroot : 외부 웹 사이트에서 해당 호스트 서버에 접근하여 검증. 도메인 호스트 서버에 사용자(개발자)가 설정한 Webroot 경로에 접근하여 검증

② DNS : 도메인의 DNS 서버에 TXT 필드에 Certbot에서 발급한 임의의 값을 넣어두고, TXT 필드의 값을 검사하여 검증

③ Standalone : 호스트 서버 자체에서 인증을 시도하는 방식

 

Webroot 방식을 이용하여 SSL 인증서를 발급받는 방법만을 살펴보겠습니다.

Webroot 방식은 서버의 root 디렉토리에 Let's Encrypt에서 발급한 임의의 값을 넣어두고, 외부에서 해당 서버에 접근했을때 웹 서버의 root 디렉토리에 임의의 값이 있는지 확인하여 서버 인증을 받는 방식입니다.

Webroot 방식을 사용하면 현재 운영중인 서버에서도 인증과 갱신이 가능하다는 장점이 있습니다.

하지만 Webroot 방식은 와일드카드 도메인 인증이 불가능 합니다.

와일드카드 도메인은 *.domain.com과 같이 sub 도메인을 포함한 모든 도메인을 의미합니다.

와일드카드 인증을 받으면 모든 sub 도메인에서도 Https 설정이 가능하지만, 해당 기능은 DNS 방식만 지원합니다.

 

 

2. nginx 설정파일 작성

Let's Encrypt로 부터 실제 운영 중인 서버라고 인정 받기 위해 외부에서 접근 가능한 서버를 실행시켜야 합니다.

Nginx를 통해 외부에서 서버에 접근할 수 있도록 Nginx를 설정 파일을 작성 하겠습니다.

SSL 인증서를 받기 위해 임의의 디렉토리(/home/유저명/ssl)를 미리 생성한 후, 해당 디렉토리에서 실행하였습니다.

또한 Let's Encrypt 인증은 80 포트와 443 포트만 가능합니다.

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

     server_name domain.com www.domain.com;

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

① server_name : SSL 인증을 받을 도메인 주소입니다. 도메인 위치에 인증받을 도메인을 적어주고, 서브 도메인도 같이 적어줍니다.

② location : Let's Encrypt에서 '/.well-known/acme-challenge' 해당 경로를 통해 인증을 시도합니다. root라고 적혀있는 Nginx의 root 경로로 접속하여 인증을 시도합니다. 그래서 Webroot 라고 불립니다.

③ Nginx의 설정 파일을 모두 작성했으면, /home/유저명/ssl/conf.d/default.conf 경로 생성 및 파일을 저장합니다.
이미 사용중인 Nginx 파일이 있다면 '/.well-known/acme-challenge' 해당 경로에 대한 설정만 추가도 무방합니다.

 

 

3. docker-compose.yml 작성

Nginx와 Certbot docker를 실행시킬 docker-compose.yml 파일을 작성 해줍니다.

version: "3.8"
services:
    web:
        image: nginx:latest
        restart: always
        volumes:
            - /home/유저명/ssl/conf.d:/etc/nginx/conf.d
            - /home/유저명/ssl/certbot/conf:/etc/nginx/ssl
            - /home/유저명/ssl/certbot/data:/var/www/certbot
        ports:
            - 80:80
            - 443:443

    certbot:
        image: certbot/certbot:latest
        command: certonly --webroot --webroot-path=/var/www/certbot --email myemail@example.com --agree-tos --no-eff-email -d domain.com
        volumes:
            - /home/유저명/ssl/certbot/conf:/etc/letsencrypt
            - /home/유저명/ssl/certbot/logs:/var/log/letsencrypt
            - /home/유저명/ssl/certbot/data:/var/www/certbot

Docker volume을 유의하여 docker-compose.yml 파일을 작성합니다.

Certbot command에 있는 --webroot-path와 nginx의 volume의 경로에 유의합니다.

 

command에 대해 자세히 알아보겠습니다

① certonly : SSL 인증서만 발급 받겠다는 것 의미

② --webroot : webroot 방식을 사용한다는 것 의미

③ --webroot-path : 서버의 webroot 경로

④ --email : Let's Encrypt의 알림을 받을 이메일

⑤ --agree-tos : ACME 서버 구독 동의

⑥ --no-eff-email : EFF 재단에 email을 공유하지 않음

⑦ -d : 인증받을 도메인 명

 

 

4. SSL 인증서 인증받기

docker-compose.yml 파일을 모두 작성했으면 docker-compose를 실행시켜 SSL 인증서를 발급받습니다.

$ docker-compose up -d

위 커맨드를 실행하여 인증을 시도합니다.

Creating network "nginx-ssl_default" with the default driver
Pulling web (nginx:latest)…
latest: Pulling from library/nginx
8559a31e96f4: Pull complete
8d69e59170f7: Pull complete
3f9f1ec1d262: Pull complete
d1f5ff4f210d: Pull complete
1e22bfa8652e: Pull complete
Digest: sha256:21f32f6c08406306d822a0e6e8b7dc81f53f336570e852e25fbe1e3e3d0d0133
Status: Downloaded newer image for nginx:latest
Pulling certbot (certbot/certbot:latest)…
latest: Pulling from certbot/certbot
cbdbe7a5bc2a: Pull complete
26ebcd19a4e3: Pull complete
a29d43ca1bb4: Pull complete
979dbbcf63e0: Pull complete
30beed04940c: Pull complete
48a1f8a4d505: Pull complete
4416e9b4bbe0: Pull complete
8173b4be7870: Pull complete
21c8dd124dab: Pull complete
c19b04e11dc7: Pull complete
1b560611cec1: Pull complete
Digest: sha256:568b8ebd95641a365a433da4437460e69fb279f6c9a159321988d413c6cde0ba
Status: Downloaded newer image for certbot/certbot:latest
Creating nginx-ssl_certbot_1 … done
Creating nginx-ssl_web_1     … done

 

위와 같이 Nginx와 Cerbot docker image를 pull 받고 컨테이너를 실행합니다.
인증에 성공했는지 확인해봅시다.

$ docker-compose ps

 

docker-compose의 상태를 확인해서 아래와 같이 Certbot의 상태고 Exit 0으로 표시된다면 정상적으로 발급된 상태입니다.

Name                      Command                State                     Ports
certbot     certbot certonly --webroot …  Exit 0                                           
web         /docker-entrypoint.sh ngin …  Up          0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp

 

만일 Certbot의 상태가 Exit 0이 아니라면 docker-comopse.yml과 log를 확인합니다.

log는 아래 커맨드로 확인 가능합니다.

$ docker-compose logs -f

 

인증에 성공하였으면 root 유저로 전환하고 /home/centos/ssl/certbot/conf/live/도메인명 경로로 이동합니다.

SSL 키로 4개의 pem 파일 cert.pem, chain.pem, fullchain.pem, privatekey.pem 키가 생성된 것을 확인할 수 있습니다.

 

 

5. pkc12 형태로 변경

Spring Boot에서는 pem 파일을 지원하지 않기 때문에, privatekey.pem 파일을 pkc12형태로 변경합니다.

$ openssl pkcs12 -export -inkey privkey.pem -in cert.pem -out keystore.p12

파일 변환 시 keystore.p12에서 사용할 비밀번호를 입력하고 따로 기록을 해둡니다.

생성 완료된 keystore.p12 파일을 Spring Boot 프로젝트의 src/main/resources 내부, application.properties 파일과 동일 경로로 복사합니다.

 

 

6. Spring Boot의 application.properties(.yml) 설정

① application.properties의 경우 다음의 내용을 기입합니다.

server.ssl.key-store= classpath:keystore.p12
server.ssl.key-store-type=PKCS12
server.ssl.key-store-password=pkcs12파일변환시입력한패스워드
server.ssl.key-alias=1
server.port=8443
server.http2.enabled=true

 

② applications.yml의 경우 다음처럼 내용을 기입합니다.

server:
  ssl:
    enabled: true
    key-store: classpath:keystore.p12
    key-store-password: pkcs12파일변환시입력한패스워드
    key-store-type: PKCS12
    key-alias: 1
  port: 8443

 

 

7. https 접속 확인

spring boot 프로젝트를 다시 빌드하고 배포하면 브라우저에서 https 접속이 가능하게 됩니다.

maven의 경우는 maven clean, install하고 target에서 jar파일을 배포할 서버로 복사합니다.

gradle의 경우 run as, gradle task, 프로젝트 선택, bootJar, run하여 build/libs의 jar 파일을 배포할 서버로 복사합니다.
서버에서 jar 파일을 배포합니다.

$ nohup java -jar jar파일명 &

 

배포가 완료되면 브라우저 주소창에서 https 접속을 확인합니다.

 

 

8. 기타 사항

① 인증서 갱신

Let's Encrypt에서 제공하는 인증서의 유효 기간은 90일로, 만료일자 도래 30일 이내부터 재발급 가능합니다.

따라서 Let's Encrypt의 인증서를 사용하려면 90일 마다 인증서를 갱신해주어야 하고, 인증서 발급에 유의할 제약사항을 숙지해야 합니다.

--force-renewal 옵션을 통해 강제 재발급이 가능하지만, 인증서 발급 제한사항에 위배될 가능성이 있어 추천드리지 않습니다.

 

인증서 재발급을 위한 Certbot docker 커맨드는 아래와 같습니다.

$ docker run --rm --name certbot -v '/home/유저명/ssl/certbot/conf:/etc/letsencrypt' -v '/home/유저명/ssl/certbot/logs:/var/log/letsencrypt' -v '/home/유저명/ssl/certbot/data:/var/www/certbot' certbot/certbot renew --server https://acme-v02.api.letsencrypt.org/directory --cert-name domain.com

 

Docker volume과 domain name을 자신의 환경에 맞게 적절히 설정해주시길 바랍니다.

위 커맨드를 실행 한 후 아래 커맨드로 남은 인증서 만료일을 확인하여 정상적으로 갱신되었는지 확인합니다.

$ docker run -it --rm --name certbot -v '/home/유저명/ssl/certbot/conf:/etc/letsencrypt' -v '/home/유저명/ssl/certbot/logs:/var/log/letsencrypt' -v '/home/유저명/ssl/certbot/data:/var/www/certbot' certbot/certbot certificates

 

인증서를 자동으로 갱신하기 위해 Crontab 등을 활용할 수 있습니다.

#!/bin/bash

docker run --rm --name certbot -v '/home/유저명/ssl/certbot/conf:/etc/letsencrypt' -v '/home/유저명/ssl/certbot/logs:/var/log/letsencrypt' -v '/home/유저명/ssl/certbot/data:/var/www/certbot' certbot/certbot renew --server https://acme-v02.api.letsencrypt.org/directory --cert-name domain.com


위 커맨드를 .sh 파일로 저장한 후 chmod 400등으로 실행 권한을 부여합니다.

이후 crontab -e에 갱신 시케줄을 등록합니다.

매일 새벽 2시에 실행하도록 설정하는 예시는 다음과 같습니다.

* 2 * * * /mydir/certbot-renew.sh

자신의 환경에 맞게 /etc/crontab에 실행 환경변수를 설정해줍니다.

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

 

 

② spring boot 배포 시 alias 이름이 잘못되었다는 오류 발생

alias로 이름은 어떤 것으로 작성했었는지 확인하고 application.properties 또는 application.yml에서 key-alias를 변경하고 빌드, 배포하면 해결됩니다.

$ keytool -v -list -keystore keystore.p12


alias로 이름은 어떤 것으로 작성했었는지 확인합니다.



참고 링크

https://velog.io/@hytenic/SSL-Lets-Encrypt-Certbot-docker%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-SSL-%EC%9D%B8%EC%A6%9D%EC%84%9C-%EB%B0%9C%EA%B8%89%EB%B0%9B%EA%B8%B0

https://martinnoh.tistory.com/entry/Spring-Boot-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90-https-%EC%97%B0%EA%B2%B0%EC%9D%84-%EC%9C%84%ED%95%9C-%EB%AC%B4%EB%A3%8C-ssl-%EC%A0%81%EC%9A%A9-%EB%B0%A9%EB%B2%95

https://findstar.pe.kr/2018/09/08/lets-encrypt-certificates-rate-limit/