Notice
Recent Posts
Recent Comments
Link
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
Archives
Today
Total
관리 메뉴

csct3434

Spring Boot 프로젝트에 HTTPS 적용하기(feat. nginx, letsencrypt, certbot) 본문

개발 일지

Spring Boot 프로젝트에 HTTPS 적용하기(feat. nginx, letsencrypt, certbot)

csct3434 2023. 2. 25. 22:43

스프링부트 프로젝트에 Certbot으로 Let's Encrypt에서 발급받은 SSL 인증서를 사용하여 HTTPS를 적용하는 방법이다.

 

서버 환경 : Ubuntu 22.04


1.  Nginx 설치

sudo apt-get update
sudo apt-get install nginx

2. Nginx 리버스 프록시 설정

sudo vi /etc/nginx/conf.d/default.conf
server {
    listen 80;
    server_name {도메인이름.com};	# 도메인 주소 입력

    location / {
        proxy_pass https://localhost:8080;  # 스프링 서버의 포트번호가 8080이 아니면 맞춰서 수정
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
    }
}

vi 에디터로 default.conf 를 작성한다. 아래의 내용을 default.conf 에 붙여넣은 후, :wq로 빠져나온다.

 

< 참고 > 

처음에 proxy_pass 부분에 https가 아닌 http://localhost:8080; 으로 적었다가 다음과 같은 에러가 발생했다.

 - 도메인 주소(80 포트)로 접속 시 : 80 -> 443 포트로 redirect 되지만, 'This combination of host and port requires TLS' 에러 메세지 반환

 - 443 포트로 접속 시 : 'The plain HTTP request was sent to HTTPS port' 에러 메시지 반환

- 8080 포트로 접속 시 : HTTPS가 적용되며 정상적으로 접속됨

이후 proxy_pass의 http를 https로 수정하니까 80, 443, 8080 포트 모두 정상적으로 HTTPS가 적용되며 서버에 연결되었다.

 

3. Certbot 설치

sudo snap install certbot --classic

 

4. Let's Encrypt 인증서 발급

sudo certbot --nginx

 명령어를 실행하면 도메인주소 목록이 뜨는데,  default.conf 에 적어놓은 도메인 주소가 있는지 확인 후, 해당 번호를 입력하면 된다. 

 

정상적으로 실행되면

 - /etc/letsencrypt/live/{도메인주소}/ 에 pem 파일들이 생성되고

 - /etc/nginx/conf.d/default.conf 파일을 certbot이 수정하여, 자동으로 80 -> 443 -> 8080 으로 리다이렉트 하는 내용을 추가해준다.

 

<default.conf> : 최종적인 default.conf 파일 내용

server {
    server_name {도메인주소};

    location / {
        proxy_pass https://localhost:8080;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
    }

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/{도메인주소}/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/{도메인주소}/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}
server {
    if ($host = {도메인주소}) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen 80;
    server_name {도메인주소};
    return 404; # managed by Certbot


}

 

5.  스프링 프로젝트에 인증서 적용하기

cd /etc/letsencrypt/live/{도메인주소}
ls

 : 앞선 과정들이 정상적으로 실행됐다면, 4개의 pem 파일이 생성된 것을 확인할 수 있다.

 

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

 : pem 파일 확인 후, 위의 명령어를 입력합니다.

명령어를 실행하면 EXPORT 비밀번호를 입력하라고 뜨는데, 해당 비밀번호는 프로젝트의 application.yml에 적어둘 예정이니 기억하고 있어야 합니다.

 

이후,  ls 명령어로 현재 폴더에 keystore.p12 파일이 생성되었는지 확인한 후, 이를 프로젝트의 application.yml(혹은 application.properties) 파일이 위치한 디렉토리에 옮겨준다. (ex : src/main/resources)

 

파일을 옮긴 후, application.yml 혹은 application.properties 파일에 아래의 내용을 추가한다.

 

<application.yml>

server:
  ssl:
    enabled: true
    key-store-type: PKCS12
    key-store: classpath:keystore.p12
    key-store-password: {EXPORT_PASSWORD} # 아까 입력한 인증서 암호

<application.properties>

server.ssl.enabled=true
server.ssl.key-store-type=PKCS12
server.ssl.key-store=classpath:keystore.p12
server.ssl.key-store-password={EXPORT_PASSWORD} # 아까 입력한 인증서 암호

 

 

6. 작동 확인

cd {프로젝트 최상위 폴더 경로}
./gradlew build -x test
cp build/libs/{jar파일명} .
sudo service nginx stop
sudo service nginx start
sudo service nginx status  # Active : active (running) 이면 정상
java -jar {jar파일명}

: 프로젝트를 빌드하여 jar 파일을 새로 생성하고, nginx를 재부팅하고 서버를 실행시켜 잘 작동되나 확인합니다.

 

7.  Crontab으로 SSL 인증서 자동 갱신 설정

Let's Encrypt에서 발급받은 인증서는 무료지만, 유효 기간이 3개월이라 주기적으로 갱신시켜야 합니다.

 

sudo certbot renew --dry-run

: 인증서가 갱신되는지 테스트 합니다. success가 뜨는지 확인합니다.

 

sudo crontab -e

: 명령어를 입력하면 에디터를 선택하는 화면이 뜨는데, 저는 2번 vim.basic 을 선택했습니다.

 

이후 주석이 길게 적혀져 있는 화면이 뜨는데, 맨 아래에 다음의 내용을 추가해줍니다.

0 0 1 * * /usr/bin/certbot renew --renew-hook="sudo systemctl restart nginx"

매월 1일에 인증서 갱신 후, nginx를 restart 하는 내용입니다.

 

< 예시 >

# Edit this file to introduce tasks to be run by cron.
#
# Each task to run has to be defined through a single line
# indicating with different fields when the task will be run
# and what command to run for the task
#
# To define the time you can provide concrete values for
# minute (m), hour (h), day of month (dom), month (mon),
# and day of week (dow) or use '*' in these fields (for 'any').
#
# Notice that tasks will be started based on the cron's system
# daemon's notion of time and timezones.
#
# Output of the crontab jobs (including errors) is sent through
# email to the user the crontab file belongs to (unless redirected).
#
# For example, you can run a backup of all your user accounts
# at 5 a.m every week with:
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
#
# For more information see the manual pages of crontab(5) and cron(8)
#
# m h  dom mon dow   command
0 0 1 * * /usr/bin/certbot renew --renew-hook="sudo systemctl restart nginx"

 

이후 매월 1일마다 인증서가 자동으로 갱신됩니다.

 

8. 서버 가동

nohup java -jar {jar파일명} &

 : nohup은 세션연결이 종료되어도 서비스가 작동되도록 하는 명령어이며, '&'는 프로세스가 백그라운드로 실행되는데 사용됩니다.

 

백그라운드로 실행중인 어플리케이션을 종료하려면 'pgrep -f {jar파일명}' 으로 PID를 확인한 후, sudo kill -15 {PID}로 종료합니다.

pgrep -f {jar파일명}
sudo kill -15 {PID}

# 예시
# pgrep -f webapp.jar
# 1997  <- 출력된 프로세스의 PID
# sudo kill -15 1997

 

참고 : https://hudi.blog/https-with-nginx-and-lets-encrypt/