웹 서비스를 운영하면서 가장 기본이 되는 보안 요소 중 하나는 바로 SSL 인증서입니다. 오늘날 HTTPS 프로토콜은 선택이 아닌 필수가 되었으며, 주요 브라우저들은 SSL이 적용되지 않은 웹사이트에 대해 보안 경고를 표시합니다. 특히 서브도메인을 포함한 여러 도메인을 관리할 때는 SSL 인증서 설정에 주의가 필요합니다. 이 글에서는 Let’s Encrypt를 사용한 SSL 인증서 발급과 Nginx 서버 설정, 그리고 자동 갱신 관리에 대해 심층적으로 알아보겠습니다.
1. SSL 인증서와 서브도메인 문제
웹사이트를 운영하다 보면 흔히 발생하는 문제 중 하나가 서브도메인의 SSL 인증서 문제입니다. 예를 들어, 메인 도메인 example.com
에 SSL 인증서를 적용했지만 www.example.com
에 접속하면 다음과 같은 오류가 발생할 수 있습니다:
Visiting an untrustworthy website has been prevented
This website has one or more invalid certificates, so we can't guarantee its authenticity.
이런 오류는 주로 두 가지 원인에서 발생합니다:
- 인증서가 해당 서브도메인(www)을 포함하지 않음
- Nginx 설정에서 해당 서브도메인에 대한 처리가 누락됨
특히 Safari와 같은 브라우저는 Chrome보다 SSL 인증서 검증에 더 엄격한 경향이 있어, Chrome에서는 문제가 없어 보여도 Safari에서는 오류가 발생할 수 있습니다.
2. Let’s Encrypt 인증서 발급 및 확장하기
Let’s Encrypt는 무료로 SSL 인증서를 발급받을 수 있는 인증 기관입니다. Certbot이라는 도구를 사용하면 쉽게 인증서를 발급받고 관리할 수 있습니다.
기본 인증서 발급
sudo certbot certonly --nginx -d example.com -d www.example.com
이 명령어는 nginx 플러그인을 사용하여 도메인 소유권을 확인하고 인증서를 발급합니다.
기존 인증서에 서브도메인 추가
이미 인증서가 발급되어 있고 서브도메인을 추가하고 싶다면:
sudo certbot certonly --nginx -d example.com -d www.example.com -d subdomain.example.com
이 명령어를 실행하면 Certbot은 기존 인증서를 확장할지 물어봅니다:
You have an existing certificate that contains a portion of the domains you requested
It contains these names: example.com
You requested these names for the new certificate: example.com, www.example.com, subdomain.example.com
Do you want to expand and replace this existing certificate with the new certificate?
(E)xpand/(C)ancel:
여기서 ‘E’를 선택하면 기존 인증서가 확장됩니다.
3. Nginx 서버 설정 최적화
SSL 인증서가 발급되었다면, 이제 Nginx 서버 설정을 최적화해야 합니다.
기본 SSL 설정
server {
listen 80;
server_name example.com www.example.com;
# HTTP를 HTTPS로 리다이렉션
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# SSL 보안 설정
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH;
ssl_ecdh_curve secp384r1;
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
# 웹사이트 루트 디렉토리 및 기타 설정
root /var/www/example.com;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ /index.php?$args;
}
# PHP 설정 (PHP를 사용할 경우)
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php/php-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
# .htaccess 파일 접근 방지
location ~ /\.ht {
deny all;
}
}
여러 서브도메인 및 포트 설정
다양한 서브도메인과 포트를 사용하는 경우(예: API 서버, 관리자 페이지 등), 각각에 대한 별도의 server 블록을 구성할 수 있습니다:
# API 서버 (8000 포트)
server {
listen 8000 ssl;
listen [::]:8000 ssl;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
error_page 497 https://$host:8000;
# SSL 보안 설정
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://localhost:8008; # 내부 서비스 포트
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;
}
}
# 8000 포트에 대한 HTTP → HTTPS 리다이렉션
server {
listen 8000;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
4. SSL 인증서 자동 갱신 설정
Let’s Encrypt의 인증서는 90일간 유효하므로 자동 갱신 설정이 필수입니다. Certbot을 설치하면 기본적으로 자동 갱신을 위한 systemd 타이머나 cron 작업이 설정됩니다.
자동 갱신 상태 확인
systemctl list-timers | grep certbot
기본 설정은 일반적으로 하루에 두 번(00:00, 12:00) 실행되도록 되어 있으며, 서버 부하를 분산하기 위한 랜덤 지연이 추가됩니다:
# 기본 타이머 설정
[Unit]
Description=Run certbot twice daily
[Timer]
OnCalendar=*-*-* 00,12:00:00
RandomizedDelaySec=43200
Persistent=true
[Install]
WantedBy=timers.target
갱신 주기 변경
서버 리소스를 절약하기 위해 갱신 주기를 변경할 수 있습니다. 예를 들어, 매주 월요일에만 실행하도록 설정하려면:
- override 파일 생성:
sudo mkdir -p /etc/systemd/system/certbot.timer.d
sudo nano /etc/systemd/system/certbot.timer.d/override.conf
- 다음 내용 입력:
[Timer]
OnCalendar=
OnCalendar=Mon *-*-* 02:00:00
RandomizedDelaySec=3600
- 설정 적용:
sudo systemctl daemon-reload
sudo systemctl restart certbot.timer
- 확인:
systemctl list-timers | grep certbot
자동 갱신 테스트
자동 갱신이 제대로 작동하는지 확인하려면 드라이런(dry-run) 테스트를 실행할 수 있습니다:
sudo certbot renew --dry-run
이 명령어는 실제 인증서를 갱신하지 않고 갱신 프로세스만 시뮬레이션합니다.
5. 자동 갱신 작동 방식
Certbot의 자동 갱신은 사용자 상호작용 없이 작동하도록 설계되어 있습니다. 인증서를 처음 발급받을 때나 도메인을 추가할 때는 Yes/No 입력이 필요하지만, 자동 갱신 과정에서는 필요하지 않습니다.
자동 갱신은 다음과 같은 특성을 가집니다:
- 만료 30일 이내인 인증서만 갱신
- 원래 발급 시 사용한 것과 동일한 설정 및 도메인 사용
- 상호작용 없이 실행 (non-interactive)
- 갱신 성공 시 자동으로 웹 서버 재시작 (설정된 경우)
6. SSL 보안 강화를 위한 추가 설정
보안을 더욱 강화하기 위한 추가 Nginx 설정을 소개합니다:
HSTS(HTTP Strict Transport Security) 설정
HSTS는 브라우저가 항상 HTTPS를 통해 사이트에 접속하도록 강제합니다:
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
OCSP Stapling 설정
OCSP Stapling은 클라이언트의 인증서 유효성 검사 속도를 향상시킵니다:
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
강력한 Diffie-Hellman 파라미터 설정
더 강력한 암호화를 위해 Diffie-Hellman 파라미터를 생성하고 설정할 수 있습니다:
sudo openssl dhparam -out /etc/nginx/dhparam.pem 4096
그리고 Nginx 설정에 추가:
ssl_dhparam /etc/nginx/dhparam.pem;
7. 몇 가지 일반적인 문제 해결
인증서가 발급되지 않는 경우
- 도메인이 현재 서버를 가리키고 있는지 확인
- 포트 80, 443이 방화벽에서 열려있는지 확인
- Let’s Encrypt의 rate limit 확인 (https://letsencrypt.org/docs/rate-limits/)
www 및 비-www 접속 통합
사용자가 www와 비-www 중 어느 방식으로 접속하든 일관된 경험을 제공하기 위해 하나의 버전으로 리다이렉션하는 것이 좋습니다:
# non-www를 www로 리다이렉션
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
return 301 https://www.example.com$request_uri;
}
# 또는 www를 non-www로 리다이렉션
server {
listen 443 ssl;
server_name www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
return 301 https://example.com$request_uri;
}
결론
웹 서버 관리에서 SSL 인증서 설정은 보안과 사용자 신뢰를 위한 필수 요소입니다. Let’s Encrypt와 같은 무료 인증 기관을 통해 인증서를 발급받고 Nginx를 통해 적절히 설정하면, 사용자에게 안전한 웹 환경을 제공할 수 있습니다.
특히 여러 서브도메인을 관리할 때는 인증서가 모든 서브도메인을 포함하는지 확인하고, 적절한 Nginx 설정을 통해 모든 접속 경로가 안전하게 처리되도록 해야 합니다. 자동 갱신 설정을 통해 인증서 만료로 인한 서비스 중단을 방지하고, 추가적인 보안 설정을 통해 더욱 강화된 보안을 제공할 수 있습니다.
SSL 인증서 관리는 처음에는 복잡해 보일 수 있지만, 한 번 제대로 설정해 놓으면 Let’s Encrypt의 자동 갱신 기능 덕분에 지속적인 관리의 부담이 크게 줄어듭니다. 웹 서비스 운영자라면 반드시 이러한 보안 설정에 관심을 기울여야 할 것입니다.
답글 남기기