infra
Platform

모듈 맵

[Linux] SSL/TLS 인증서 관리

0 / 20 완료

펼치기

Linux-master · 16 / 20

[Linux] SSL/TLS 인증서 관리

openssl과 certbot으로 HTTPS 인증서를 발급·갱신·점검하고, 인증서 만료로 인한 서비스 중단을 예방합니다

🚨INCIDENT ALERT
HIGH

일요일 새벽 3시, 모니터링 알림이 울립니다. "HTTPS 접속 실패 — 인증서 만료." Let's Encrypt 인증서가 어젯밤 자정에 만료됐고 자동 갱신 cron이 환경변수 문제로 90일 전부터 조용히 실패하고 있었습니다. 사용자들은 "보안 연결이 아닙니다" 경고창을 보고 있고, 구매 전환율이 0%입니다. 인증서가 무엇인지, 어떻게 갱신되는지, 그리고 이런 사고를 처음부터 막는 방법을 알아야 합니다.

SSL/TLS 인증서 관리

"보안 연결이 아닙니다" 경고 하나로 사용자가 다 도망갑니다. 더 나쁜 건 인증서가 새벽 3시에 만료되어 서비스가 통째로 다운되는 것입니다. 이 모듈에서는 인증서 발급부터 자동 갱신까지 실무에서 쓰는 방식을 다룹니다.


이번 챕터에서 배울 것

인증서 관리의 핵심은 자동화입니다. 사람이 기억해서 갱신하는 건 언젠가 반드시 실패합니다.

  • 1TLS 인증서 구조 — 공개키, CA 체인, 신뢰 모델
  • 2openssl — 인증서 생성, 조회, 검증
  • 3자체 서명 인증서 — 내부망/개발 환경용
  • 4Let's Encrypt + certbot — 무료 자동 갱신
  • 5인증서 만료 모니터링과 장애 예방
실습 환경 준비

💡개념

TLS 인증서 동작 원리 — 3분 요약

클라이언트 (브라우저)                 서버
        │                              │
        │──── ClientHello ────────────>│
        │<─── ServerHello + 인증서 ───│  ← 서버가 공개 인증서 전송
        │                              │
        │  [인증서 검증]               │
        │  1. 만료일 확인              │
        │  2. 도메인명 일치 확인       │
        │  3. CA 서명 검증 (신뢰 체인) │
        │                              │
        │──── 대칭키 교환 (암호화됨) ─>│
        │<──── 암호화된 통신 시작 ─────│

인증서 파일 구성:

certificate.crt   ← 공개 인증서 (서버가 브라우저에 전송)
private.key       ← 개인키 (절대 외부 유출 금지!)
ca-bundle.crt     ← 중간 CA 체인 (신뢰 연결용)

Nginx 설정 예시:

Nginx
server {
    listen 443 ssl;
    ssl_certificate     /etc/ssl/certs/example.crt;
    ssl_certificate_key /etc/ssl/private/example.key;
    ssl_trusted_certificate /etc/ssl/certs/ca-bundle.crt;
}

💡개념

openssl — 인증서 생성과 조회

로컬 터미널
# ── 자체 서명 인증서 생성 (개발/내부망용) ──

# 1. 개인키 생성 (RSA 2048비트)
openssl genrsa -out server.key 2048

# 2. 인증서 서명 요청 (CSR) 생성
openssl req -new -key server.key \
  -subj "/C=KR/ST=Seoul/O=MyCompany/CN=myserver.internal" \
  -out server.csr

# 3. 자체 서명 인증서 발급 (365일)
openssl x509 -req -days 365 \
  -in server.csr -signkey server.key \
  -out server.crt

# ── 한 번에 생성 (개발환경 빠른 세팅) ──
openssl req -x509 -nodes -days 365 \
  -newkey rsa:2048 \
  -keyout server.key \
  -out server.crt \
  -subj "/CN=localhost"
로컬 터미널
# ── 인증서 정보 조회 ──

# 만료일 확인
openssl x509 -in server.crt -noout -dates
# notBefore=Apr 14 00:00:00 2026 GMT
# notAfter=Apr 14 00:00:00 2027 GMT

# 상세 정보 (발급자, 도메인, 알고리즘)
openssl x509 -in server.crt -noout -text | head -40

# ── 원격 서버 인증서 실시간 조회 ──
openssl s_client -connect google.com:443 -servername google.com < /dev/null 2>/dev/null \
  | openssl x509 -noout -dates

# 만료까지 남은 일수 계산
openssl s_client -connect example.com:443 < /dev/null 2>/dev/null \
  | openssl x509 -noout -checkend 2592000  # 30일(2592000초) 내 만료 여부
# Certificate will not expire  ← 안전
# Certificate will expire      ← 곧 만료!

💡개념

Let's Encrypt + certbot — 무료 자동 갱신

로컬 터미널
# ── Nginx 플러그인으로 자동 발급 + 설정 ──
sudo certbot --nginx -d example.com -d www.example.com

# ── Standalone 모드 (Nginx 없이) ──
sudo certbot certonly --standalone -d example.com
# 발급된 파일 위치:
# /etc/letsencrypt/live/example.com/fullchain.pem  ← 인증서 + 체인
# /etc/letsencrypt/live/example.com/privkey.pem    ← 개인키

# ── 인증서 목록 확인 ──
sudo certbot certificates

# ── 갱신 테스트 (실제 갱신 안 함) ──
sudo certbot renew --dry-run

# ── 수동 갱신 ──
sudo certbot renew

# ── 갱신 후 Nginx 리로드 자동화 ──
sudo certbot renew --deploy-hook "systemctl reload nginx"

자동 갱신 설정 (systemd timer — Ubuntu 20.04+):

서버 터미널
# certbot 설치 시 자동으로 생성됨
systemctl status certbot.timer
systemctl list-timers | grep certbot

# 수동으로 timer 설정 (없을 경우)
# /etc/systemd/system/certbot-renew.service
# /etc/systemd/system/certbot-renew.timer
# → 다음 모듈(systemd-timer)에서 자세히 다룸

로컬 터미널
$ sudo certbot certonly --standalone -d example.com
# ...
# Problem binding to port 80: Could not bind to IPv4 or IPv6.

원인: Standalone 모드는 임시 웹서버를 80 포트에 띄워 도메인 인증을 합니다. Nginx가 이미 80 포트를 점유 중이면 충돌.

해결 방법:

로컬 터미널
# 방법 1: Nginx 플러그인 사용 (권장)
sudo certbot --nginx -d example.com
# Nginx 설정을 자동으로 수정해서 인증 처리

# 방법 2: Nginx 중지 후 standalone
sudo systemctl stop nginx
sudo certbot certonly --standalone -d example.com
sudo systemctl start nginx

# 방법 3: webroot 모드 (Nginx 실행 중 유지)
sudo certbot certonly --webroot \
  -w /var/www/html \
  -d example.com
# Nginx가 /.well-known/acme-challenge/ 를 서빙하면서 인증

webroot 모드 Nginx 설정 추가:

Nginx
location /.well-known/acme-challenge/ {
    root /var/www/html;
}
로컬 터미널
$ curl https://example.com
# curl: (35) OpenSSL SSL_connect: Connection reset by peer

# 또는 브라우저에서 SSL_ERROR_RX_RECORD_TOO_LONG

원인: Nginx가 443 포트에서 SSL 없이 평문 HTTP를 응답하거나, 방화벽이 443을 막고 있음.

로컬 터미널
# 확인 1: Nginx가 443에서 SSL을 켰는지 확인
grep -n "listen 443\|ssl_certificate" /etc/nginx/sites-enabled/*
# listen 443 ssl; 이어야 함 (ssl 빠지면 HTTP로 응답)

# 확인 2: 443 포트 방화벽
sudo ufw status | grep 443
sudo iptables -L -n | grep 443

# 확인 3: 실제로 무엇이 443에서 리스닝하는지
ss -tlnp | grep :443
💼
실무 맥락
현업 패턴

실무 인증서 모니터링 스크립트

로컬 터미널
#!/bin/bash
# /usr/local/bin/check-ssl-expiry.sh
# 인증서 만료 30일 전 알림 (cron으로 매일 실행)

DOMAINS=("example.com" "api.example.com" "admin.example.com")
WARN_DAYS=30
LOG="/var/log/ssl-check.log"

for domain in "${DOMAINS[@]}"; do
    expiry=$(openssl s_client -connect "$domain:443" -servername "$domain" \
      < /dev/null 2>/dev/null | openssl x509 -noout -enddate 2>/dev/null \
      | cut -d= -f2)

    if [ -z "$expiry" ]; then
        echo "[$(date)] ERROR: $domain — 인증서 조회 실패" >> "$LOG"
        continue
    fi

    expiry_epoch=$(date -d "$expiry" +%s)
    now_epoch=$(date +%s)
    days_left=$(( (expiry_epoch - now_epoch) / 86400 ))

    if [ "$days_left" -lt "$WARN_DAYS" ]; then
        echo "[$(date)] WARN: $domain 인증서 ${days_left}일 후 만료 ($expiry)" >> "$LOG"
        # 슬랙/이메일 알림 추가 가능
    else
        echo "[$(date)] OK: $domain ${days_left}일 남음" >> "$LOG"
    fi
done

인증서 관련 실무 체크리스트:

로컬 터미널
# 1. 인증서 파일 권한 확인 (개인키는 root만 읽어야 함)
ls -la /etc/letsencrypt/live/example.com/
# privkey.pem은 600 또는 640이어야 함

# 2. TLS 버전 확인 (TLS 1.0/1.1 차단)
openssl s_client -connect example.com:443 -tls1
# handshake failure → 정상 (구버전 차단됨)

# 3. Mixed content 확인 — HTTPS 페이지에서 HTTP 리소스 로드 없는지
# 브라우저 개발자 도구 > Console에서 확인

인증서 만료로 서비스가 다운되는 사고는 100% 예방 가능합니다. 자동 갱신 + 모니터링 둘 다 있어야 진짜 안전합니다.

지식 확인

퀴즈 — 4문제

Q1

Let's Encrypt 인증서의 유효 기간과 자동 갱신 시점은?

Q2

openssl s_client -connect example.com:443 명령으로 확인할 수 없는 것은?

Q3

자체 서명 인증서(self-signed)와 CA 서명 인증서의 가장 중요한 차이는?

Q4

인증서 갱신 후 Nginx가 여전히 만료된 인증서를 제공하고 있다. 원인으로 가장 가능한 것은?

0 / 4 답변

이것도 배워보세요

linux-master고급 · 75
[Linux] 가상화 기초 — KVM/libvirt
Linux-master 트랙 계속