[Spring] 로컬 및 운영 환경에서 HTTPS 적용하기
웹 서비스를 운영할 때 HTTPS(SSL) 적용은 보안의 기본입니다. 따라서 이번 글에서는 개발 환경(로컬)과 운영 환경에서 HTTPS를 적용하는 방법을 정리하려고 합니다.
- 로컬에서는 Self-Signed 인증서를 사용하고,
- 운영 환경에서는 Let's Encrypt + Nginx를 통한 무료 SSL 인증서 적용했습니다.
- 마지막에는 오류 사례와 해결 방법까지 정리해보았습니다.
1. 로컬에서 HTTPS 적용 방법 (Self-Signed 인증서)
1-1. Self-Signed 인증서 생성 (Java Keytool 사용)
로컬에서 테스트용 HTTPS를 적용하려면 자체 서명(Self-Signed) 인증서를 생성해야 합니다.
아래 명령어 실행
keytool -genkeypair -alias local-ssl -keyalg RSA -keysize 2048 -storetype PKCS12 -keystore keystore.p12 -validity 3650
- 생성 시 다음 정보 입력
- 비밀번호
- 이름 / 조직 / 지역 : 대략적으로 입력
- keystore.p12 파일이 생성됨
입력 예시
Enter keystore password:
(예: password12345!)
Re-enter new password:
(예: password12345!
What is your first and last name?
[Unknown]: localhost
What is the name of your organizational unit?
[Unknown]: Dev Team
What is the name of your organization?
[Unknown]: Project Name
What is the name of your City or Locality?
[Unknown]: Seoul
What is the name of your State or Province?
[Unknown]: Seoul
What is the two-letter country code for this unit?
[Unknown]: KR
Is CN=localhost, OU=Dev Team, O=HisCool, L=Seoul, ST=Seoul, C=KR correct?
[no]: yes
각 항목 설명
first and last name | localhost | 테스트용, 로컬 개발 시 localhost 추천 (CN = Common Name) |
organizational unit | Dev Team | 부서명, 자유 입력 |
organization | HisCool | 프로젝트명, 회사명 |
City or Locality | Seoul | 도시 이름 |
State or Province | Seoul | 주/도 이름 |
country code | KR | 국가 코드 (대한민국 = KR) |
이 과정을 마치면 `keystore.p12` 파일이 생성됩니다.
1-2. Spring Boot `application.yml` 설정
application.yml에 다음과 같이 작성:
server:
port: 8443
ssl:
enabled: true
key-store: classpath:keystore.p12
key-store-password: password
key-store-type: PKCS12
1-3. 접속 테스트
- 서버를 실행한 후 https://localhost:8443 에 접속해보세요.
- 브라우저에서 보안 경고가 발생하는데, 이는 Self-Signed 인증이므로 정상
2. 운영 환경에서 HTTPS 적용 방법 (Let's Encrypt)
Let's Encrypt + Nginx 활용
운영 서버에서는 Self-Signed 대신 Let's Encrypt를 사용하는 것이 일반적입니다.
도메인을 준비하고, Nginx와 Certbot을 활용해 무료 SSL 인증서를 발급받는 방법을 알아봅니다.
2-1. 도메인 준비
- 도메인은 가비아나 카페24, AWS Route53 등에서 구매
- 저는 가비아에서 구매하였습니다
- DNS 설정에서 서버의 퍼블릭 IP와 연결
2-2. Nginx 설치
sudo apt update
sudo apt install nginx
2-3. Certbot 설치 및 인증서 발급
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
Let's Encrypt 인증서 발급 시 필수 정보 중 하나인 이메일 주소를 입력해야 합니다.
만약 인증서 갱신이나 보안 경고가 발생하면 해당 이메일로 알림이 온다고 합니다.
Enter email address (used for urgent renewal and security notices)
(Enter 'c' to cancel): ( 예 : gmail@gmail.com )
이후 약관 동의를 물어보는데
- "동의하시겠습니까?" 하면 Y 입력하면 됩니다.
- "이메일 공유 동의" 여부도 물어볼 수 있는데, 이건 선택 사항이라 N을 입력해도 상관없습니다.
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.3-September-21-2022.pdf. You must
agree in order to register with the ACME server. Do you agree to the Terms of
Service? (Y)es/(N)o: Y
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about our work
encrypting the web, EFF news, campaigns, and ways to support digital freedom.
(Y)es/(N)o: N
2-4. Nginx 설정 (/etc/nginx/sites-available/default 수정)
보통 Nginx 설정은 다음 디렉토리에 있음
/etc/nginx/sites-available/
default 파일을 수정
server {
listen 80;
server_name domain.com www.domain.com;
# Let's Encrypt 인증서 발급용 (HTTP-01 챌린지)
location /.well-known/acme-challenge/ {
root /var/www/html;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name domain.com www.domain.com;
ssl_certificate /etc/letsencrypt/live/hischool.shop/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/hischool.shop/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
proxy_pass http://localhost: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;
}
}
2-5. Nginx 테스트 후 적용
설정 오류 확인:
sudo nginx -t
다음과 같이 나오면 성공입니다.
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Nginx 재시작
sudo systemctl restart nginx
2-6. Spring Boot 설정
- 운영 환경에서는 Nginx가 SSL 처리를 담당
- Spring Boot는 HTTP (8080 포트) 로만 실행
- application.yml의 SSL 설정은 필요 없습니다.
2-7. 접속 확인
- `https://domain.com` 접속
- `https://www.domain.com` 접속
2-8. DNS 반영 지연 주의
가비아, AWS Route53 등에서 DNS 설정 후 적용까지 최대 30분~1시간 소요될 수 있습니다.
저는 1시간 정도 걸려서 설정을 잘못했나 걱정했습니다..
설정 방법은 다음 블로그에서 소개드릴 예정입니다!
3. 비고) IP만으로 HTTPS 적용 가능할까?
3-1. Self-Signed 인증서 + Spring Boot
- 도메인 없이 IP 주소를 대상으로 HTTPS를 적용하고 싶다면, Self-Signed 인증서를 생성해서 Spring Boot에서 직접 SSL을 적용할 수 있습니다.
- 다만, 브라우저에서 보안 경고가 계속 발생하고, 실제 운영 서비스에서는 사용자 신뢰성 확보 불가.
- 보안 테스트, 내부망 용도로만 적합.
3-2. 적용 절차 (IP 용 HTTPS)
GPT가 알려준 절차는 다음과 같았지만, 시도해보지는 않음
3-2-1. Selft-Signed 인증서 생성 (IP 용)
IP는 CN (Common Name)으로 넣어서 인증서 생성
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout selfsigned.key \
-out selfsigned.crt \
-subj "/C=KR/ST=Seoul/L=Seoul/O=Test/OU=Dev/CN=YOUR.SERVER.IP.ADDRESS"
3-2-2. PKCS12 키스토어로 변환
openssl pkcs12 -export -in selfsigned.crt -inkey selfsigned.key -out keystore.p12 -name selfsigned
3-2-3. Spring Boot에 적용
`application.yml`
server:
port: 8443
ssl:
enabled: true
key-store: classpath:keystore.p12
key-store-password: [비밀번호]
key-store-type: PKCS12
3-2-4. 접속 테스트
- `https://[서버IP]:8443` 접속 (브라우저 경고 발생 예상)
3-3. 도메인 없이 공인 인증서 발급 → 불가능
- Let's Encrypt, 일반 CA(인증기관)는 IP 주소로는 SSL 인증서를 발급해주지 않아.
- 공인된 SSL 인증서는 도메인 필수.
- 따라서 가비아에서 500원짜리 도메인을 구입하여 진행함
GPT's의 추천
- 테스트/내부망 ➔ Self-Signed 인증서 + Spring Boot 적용 OK
- 운영 서비스 ➔ 도메인 확보 후 Let's Encrypt 추천
- 브라우저 신뢰 필요 ➔ 도메인 없이 불가능 (보안 경고 피할 수 없음)
4. 마무한 오류들 정리
4-1. certbot: error: unrecognized arguments: —nginx
certbot: error: unrecognized arguments: —nginx
원인
`--nginx` 옵션 앞에 있는 하이픈(-)이 잘못 입력됨
해결 방법
--nginx 옵션 앞의 하이픈은 -- (ASCII 하이픈 두 개) 를 사용해야 함
4-2. "default" E212: Can't open file for writing
원인
이 오류는 파일을 수정하려고 했는데, 권한이 부족해서 저장이 안 되는 경우에 발생.
즉, /etc/nginx/sites-available/default 파일을 루트 권한 없이 열어서 저장하려 했기 때문에 발생/
해결 방법
`sudo`로 열어서 수정
Nginx 설정 파일은 시스템 설정 파일이기 때문에 sudo 권한이 필요
파일을 수정할 때 아래 명령어로 실행
sudo nano /etc/nginx/sites-available/default
위 명령어로 열어서 수정하고, 저장 (Ctrl + O), 종료 (Ctrl + X)
4-3. [emerg] duplicate listen [::]:443
원인
- listen [::]:443 설정이 중복되어 있다는 의미야.
- 즉, 동일한 포트(443)에 대해 두 번 이상 listen 지시어를 선언한 상태
해결 방법
중복된 `listen` 지시어 제거
- `default` 파일 안에서 listen 부분을 확인하고
- 중복된 부분을 하나로 통합
4-4. duplicate location "/" in ~ (위와 거의 비슷)
원인
기존 정적 파일용 location /을 삭제하지 않음
location / {
try_files $uri $uri/ =404;
}
해결 방법
기존 정적 파일용 location / 를 제거하면 됨
5. 마무리
로컬 개발 환경에서의 Self-Signed 인증서 적용부터, 운영 환경에서의 Let's Encrypt와 Nginx를 활용한 무료 SSL 인증서 발급 및 적용까지—이번 글에서는 HTTPS 적용의 전 과정을 살펴보았습니다.
요약하자면:
- 개발 환경에서는 Self-Signed 인증서를 통해 빠르게 HTTPS 테스트를 해볼 수 있으며,
- 운영 환경에서는 반드시 도메인을 확보한 후, Let's Encrypt로 무료이자 공인된 SSL 인증서를 발급받아 적용해야 합니다.
이번 글이 SSL 인증서 적용을 망설이는 분들께 실제 적용 방법과 시행착오를 줄일 수 있는 경험담으로 도움이 되길 바랍니다.