목요일

React useEffect 무한루프 문제 해결하기

수요일

Nginx SSL 인증서 만료 해결법

🔍 검색 키워드: Nginx SSL 인증서 만료, Let's Encrypt 갱신, TLS 에러, HTTPS 연결 실패, 인증서 업데이트

Nginx SSL 인증서 만료 해결법

증상

브라우저에서 HTTPS 사이트 접속 시:

NET::ERR_CERT_AUTHORITY_INVALID
  ERR_SSL_OBSOLETE_VERSION
  The certificate has expired
  SSL_ERROR_HANDSHAKE_FAILURE_ALERT

또는 서버 로그에:

SSL_ERROR_RX_RECORD_TOO_LONG
  no shared ciphers
  Peer rejected a valid certificate
  certificate verify failed (self signed certificate)

cURL로 확인 시:

$ curl -v https://example.com
  * SSL certificate problem: certificate has expired

원인

  1. 인증서 만료: Let's Encrypt 90일 정책, 또는 상용 인증서 만료
  2. 자동 갱신 미작동: certbot/Nginx 플러그인 설정 오류
  3. Nginx 설정 오류: 잘못된 인증서 경로
  4. 시스템 시간 오류: 서버 시계가 실시간과 다름
  5. 인증서 체인 미완성: 중간 인증서(Intermediate) 누락
  6. 포트 443 차단: Let's Encrypt 갱신 포트 막힘
  7. 권한 오류: 인증서 파일 읽기 권한 부족

해결 방법

방법 1: 현재 인증서 상태 확인

# 인증서 만료 기간 확인
  openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem \
    -noout -dates
    
    # 출력 예:
    # notBefore=Jun  4 12:00:00 2024 GMT
    # notAfter=Sep  2 12:00:00 2024 GMT
    
    # SSL 프로토콜 버전 확인
    openssl s_client -connect example.com:443 -tls1_2
    
    # 인증서 체인 확인
    openssl s_client -connect example.com:443 -showcerts
    
    # 남은 기간 확인 (일 수)
    ssl-cert-check -c /etc/letsencrypt/live/example.com/cert.pem
    
    # Nginx 설정에서 인증서 경로 확인
    grep "ssl_certificate" /etc/nginx/sites-enabled/default

방법 2: Let's Encrypt 인증서 자동 갱신

# 1. certbot 설치 (미설치 시)
  sudo apt-get install certbot python3-certbot-nginx
  
  # 2. 인증서 수동 갱신
  sudo certbot renew
  
  # 3. 특정 도메인만 갱신
  sudo certbot renew --cert-name example.com
  
  # 4. 강제 갱신 (만료 60일 전이 아니라도)
  sudo certbot renew --force-renewal
  
  # 5. 갱신 후 Nginx 재로드
  sudo systemctl reload nginx
  
  # 6. Nginx 설정 테스트
  sudo nginx -t

자동 갱신 설정 (cron 또는 systemd):

# crontab 설정 (매일 오전 3시 확인)
  sudo crontab -e
  # 다음 라인 추가:
  0 3 * * * /usr/bin/certbot renew --quiet && systemctl reload nginx
  
  # 또는 systemd timer (권장)
  sudo systemctl list-timers certbot
  sudo systemctl status certbot.timer

방법 3: Nginx 설정 확인 및 수정

server {
      listen 443 ssl http2;
          server_name example.com www.example.com;
          
              # SSL 인증서 경로 확인
                  ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
                      ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
                      
                          # TLS 버전 명시 (TLS 1.2 이상)
                              ssl_protocols TLSv1.2 TLSv1.3;
                              
                                  # 최신 암호화 알고리즘
                                      ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
                                          ssl_prefer_server_ciphers on;
                                          
                                              # HSTS 설정 (선택사항)
                                                  add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
                                                  
                                                      # 설정 테스트
                                                          # $ sudo nginx -t
                                                              # nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
                                                                  # nginx: configuration file /etc/nginx/nginx.conf test is successful
                                                                  }
                                                                  
                                                                  # HTTP → HTTPS 리다이렉트
                                                                  server {
                                                                      listen 80;
                                                                          server_name example.com www.example.com;
                                                                          
                                                                              location / {
                                                                                      return 301 https://$host$request_uri;
                                                                                          }
                                                                                          }

설정 적용:

sudo nginx -t
  sudo systemctl reload nginx

방법 4: 새 인증서 발급 (기존 만료된 경우)

# 1. certbot 대화형 모드로 새 인증서 발급
  sudo certbot certonly --nginx -d example.com -d www.example.com
  
  # 2. 수동 DNS 검증 방식
  sudo certbot certonly --manual --preferred-challenges dns \
    -d example.com -d www.example.com
    
    # DNS TXT 레코드 추가 후 엔터
    
    # 3. 발급된 인증서 확인
    sudo ls -la /etc/letsencrypt/live/example.com/

방법 5: 시스템 시간 확인 및 수정

# 시스템 시간 확인
  date
  
  # 시간이 틀렸다면 수정
  sudo timedatectl set-ntp true  # NTP 자동 동기화
  sudo timedatectl set-timezone Asia/Seoul
  
  # 수정 확인
  date
  timedatectl
  
  # 시간 동기화 강제 실행
  sudo ntpdate -s ntp.ubuntu.com

방법 6: SSL 점검 및 모니터링

# SSL Labs 온라인 테스트 (브라우저에서)
  # https://www.ssllabs.com/ssltest/analyze.html?d=example.com
  
  # 로컬에서 SSL 테스트
  sudo apt-get install sslscan
  sslscan --no-failed example.com:443
  
  # 인증서 투명성 로그 확인
  curl https://ct.googleapis.com/log/all_logs_list.json
  
  # Nginx 에러 로그 확인
  sudo tail -f /var/log/nginx/error.log
  
  # Certbot 갱신 로그 확인
  sudo tail -f /var/log/letsencrypt/letsencrypt.log

정리표

에러 메시지 원인 해결법
certificate has expired 인증서 만료 certbot renew 실행
SSL_ERROR_HANDSHAKE_FAILURE_ALERT TLS 설정 오류 Nginx SSL 설정 확인
no shared ciphers 암호화 불일치 ssl_ciphers 최신 버전 설정
certificate verify failed 인증서 체인 누락 fullchain.pem 사용 확인
ERR_SSL_OBSOLETE_VERSION 구버전 TLS TLS 1.2 이상으로 설정

: Let's Encrypt는 90일마다 갱신이 필요합니다. certbot renew --dry-run으로 자동 갱신을 사전 테스트하고, 갱신 후 systemctl reload nginx로 무중단 재로드하세요. Nginx 재시작 시 기존 연결은 유지되므로 서비스 중단이 없습니다.