⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

SSL/TLS 인증서 설정 완벽 가이드 (Let's Encrypt) - 슬라이드 1/7
A

AI Generated

2025. 11. 26. · 4 Views

SSL/TLS 인증서 설정 완벽 가이드 (Let's Encrypt)

메일 서버 운영에 필수적인 SSL/TLS 인증서 설정 방법을 다룹니다. Let's Encrypt를 활용한 무료 인증서 발급부터 자동 갱신까지, 실무에서 바로 적용할 수 있는 내용을 담았습니다.


목차

  1. STARTTLS_vs_Implicit_TLS_이해
  2. Let_Encrypt_Certbot_설치
  3. 와일드카드_인증서_발급
  4. docker_mailserver에_인증서_적용
  5. 자동_갱신_스크립트_설정
  6. 인증서_상태_모니터링

1. STARTTLS vs Implicit TLS 이해

김개발 씨는 회사에서 메일 서버를 구축하라는 미션을 받았습니다. 설정 파일을 보니 STARTTLS와 Implicit TLS라는 용어가 눈에 들어옵니다.

"둘 다 암호화 아닌가? 뭐가 다른 거지?" 선배 박시니어 씨에게 물어보기로 했습니다.

STARTTLSImplicit TLS는 이메일 통신을 암호화하는 두 가지 방식입니다. 마치 대화를 시작할 때 "비밀 이야기할게요"라고 먼저 말하고 시작하는 것과, 처음부터 비밀 방에서 대화하는 것의 차이와 같습니다.

이 차이를 이해하면 메일 서버 보안 설정을 올바르게 구성할 수 있습니다.

다음 코드를 살펴봅시다.

# STARTTLS 방식 (포트 25, 587)
# 평문으로 연결 시작 후 암호화 업그레이드
openssl s_client -connect mail.example.com:587 -starttls smtp

# Implicit TLS 방식 (포트 465, 993, 995)
# 처음부터 암호화된 연결로 시작
openssl s_client -connect mail.example.com:465

# 연결 테스트 결과 확인
# - Certificate chain 정보
# - SSL handshake 성공 여부
# - 암호화 프로토콜 버전

김개발 씨는 입사 6개월 차 주니어 개발자입니다. 이번에 회사 내부 메일 서버를 구축하는 프로젝트를 맡게 되었습니다.

설정 문서를 보다가 STARTTLS와 Implicit TLS라는 용어에서 막혔습니다. 둘 다 보안 관련인 것 같은데, 정확히 무슨 차이인지 알 수 없었습니다.

선배 개발자 박시니어 씨가 커피를 마시며 다가왔습니다. "메일 서버 설정하고 있구나?

STARTTLS랑 Implicit TLS 때문에 헷갈리지?" 그렇다면 이 두 가지 방식은 정확히 어떻게 다를까요? 쉽게 비유하자면, STARTTLS는 카페에서 일반 대화를 하다가 "이제부터 비밀 이야기할게요"라고 말하고 작은 목소리로 속삭이는 것과 같습니다.

처음에는 평문으로 연결을 시작하고, 클라이언트가 "STARTTLS" 명령을 보내면 그때부터 암호화된 통신으로 업그레이드됩니다. 반면 Implicit TLS는 처음부터 방음이 완벽한 비밀 회의실에 들어가서 대화하는 것과 같습니다.

연결이 시작되는 순간부터 암호화가 적용됩니다. 왜 두 가지 방식이 공존하게 되었을까요?

인터넷 초창기에는 이메일 통신이 모두 평문이었습니다. 보안의 필요성이 커지면서 기존 시스템과의 호환성을 유지하며 암호화를 추가해야 했고, 그래서 STARTTLS가 탄생했습니다.

기존 포트를 그대로 사용하면서 암호화 기능만 추가한 것입니다. 하지만 이 방식에는 치명적인 약점이 있었습니다.

다운그레이드 공격이라는 보안 위협이 바로 그것입니다. 중간자 공격자가 STARTTLS 명령을 가로채서 지원하지 않는 것처럼 위장하면, 클라이언트는 평문으로 통신을 계속하게 됩니다.

이런 문제를 원천적으로 차단하기 위해 Implicit TLS가 등장했습니다. 처음부터 암호화된 별도의 포트를 사용하므로 다운그레이드 공격이 불가능합니다.

각 방식이 사용하는 포트를 정리해보겠습니다. STARTTLS는 SMTP에서 25번과 587번 포트, IMAP에서 143번 포트, POP3에서 110번 포트를 사용합니다.

Implicit TLS는 SMTPS에서 465번 포트, IMAPS에서 993번 포트, POP3S에서 995번 포트를 사용합니다. 현대 메일 서버에서는 보안을 위해 두 방식을 모두 지원하는 것이 권장됩니다.

위의 코드를 살펴보면, openssl 명령어로 두 방식의 차이를 직접 확인할 수 있습니다. 첫 번째 명령어는 587번 포트로 연결한 뒤 -starttls smtp 옵션으로 암호화 업그레이드를 시도합니다.

두 번째 명령어는 465번 포트로 바로 암호화 연결을 시작합니다. 두 명령어 모두 성공하면 인증서 체인과 SSL 핸드셰이크 정보가 출력됩니다.

실제 현업에서는 어떻게 설정할까요? 메일 서버를 운영할 때는 587번 포트로 STARTTLS를, 465번 포트로 Implicit TLS를 모두 열어두는 것이 표준입니다.

구형 메일 클라이언트는 STARTTLS만 지원할 수 있고, 최신 클라이언트는 Implicit TLS를 선호하기 때문입니다. 방화벽에서도 이 포트들을 열어두어야 합니다.

하지만 주의할 점이 있습니다. 25번 포트의 STARTTLS는 서버 간 통신에만 사용해야 합니다.

일반 사용자 인증에는 반드시 587번 또는 465번 포트를 사용해야 합니다. 25번 포트를 사용자 인증에 열어두면 스팸 발송에 악용될 수 있습니다.

박시니어 씨의 설명을 들은 김개발 씨가 고개를 끄덕였습니다. "아, 그래서 포트가 여러 개인 거였군요!

호환성과 보안 사이의 균형 문제였네요." 이제 SSL/TLS의 기본 개념을 이해했으니, 실제로 인증서를 발급받아 보겠습니다.

실전 팁

💡 - 신규 시스템에서는 Implicit TLS(465, 993, 995 포트)를 우선적으로 사용하세요

  • STARTTLS 사용 시 반드시 TLS를 강제하는 설정을 활성화하세요

2. Let Encrypt Certbot 설치

김개발 씨는 이제 실제로 SSL 인증서를 발급받아야 합니다. 예전 같으면 비싼 돈을 주고 인증서를 구매해야 했지만, 요즘은 무료로 발급받을 수 있다고 들었습니다.

바로 Let's Encrypt라는 서비스입니다.

Let's Encrypt는 무료로 SSL/TLS 인증서를 발급해주는 비영리 인증 기관입니다. Certbot은 Let's Encrypt 인증서를 자동으로 발급하고 관리해주는 공식 클라이언트 도구입니다.

마치 은행 창구 직원이 복잡한 서류 작업을 대신 처리해주는 것처럼, Certbot이 인증서 발급의 모든 과정을 자동화해줍니다.

다음 코드를 살펴봅시다.

# Ubuntu/Debian 시스템에서 Certbot 설치
sudo apt update
sudo apt install -y certbot

# 또는 snap을 통한 설치 (권장)
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot

# 설치 확인
certbot --version

# 플러그인 목록 확인
certbot plugins

# DNS 플러그인 설치 (와일드카드 인증서용)
sudo apt install -y python3-certbot-dns-cloudflare

인증서 구매 견적을 알아보던 김개발 씨는 깜짝 놀랐습니다. 유명 인증 기관의 와일드카드 인증서는 연간 수십만 원이 넘었습니다.

예산이 빠듯한 스타트업에서 이 비용은 부담이었습니다. 박시니어 씨가 힌트를 주었습니다.

"Let's Encrypt 써봤어? 무료인데다 자동화도 잘 되어 있어." Let's Encrypt는 2016년에 출범한 비영리 인증 기관입니다.

모질라 재단, 구글, 페이스북 등 거대 IT 기업들이 후원하고 있으며, 웹의 보안 수준을 높이겠다는 목표로 무료 인증서를 제공합니다. 2023년 기준으로 전 세계 웹사이트의 절반 이상이 Let's Encrypt 인증서를 사용할 정도로 신뢰받고 있습니다.

그렇다면 Certbot은 무엇일까요? 쉽게 비유하자면, Let's Encrypt가 인증서를 발급해주는 관청이라면, Certbot은 그 관청에서 모든 서류 작업을 대신 처리해주는 대행 서비스와 같습니다.

도메인 소유권 검증, 인증서 발급 요청, 다운로드, 설치, 갱신까지 모든 과정을 자동화해줍니다. Certbot 설치 방법은 크게 두 가지가 있습니다.

첫 번째는 운영체제의 패키지 매니저를 사용하는 방법입니다. Ubuntu에서는 apt install certbot 명령으로 간단히 설치할 수 있습니다.

하지만 이 방법은 운영체제 버전에 따라 오래된 Certbot이 설치될 수 있다는 단점이 있습니다. 두 번째 방법은 snap을 사용하는 것입니다.

snap은 우분투에서 개발한 패키지 관리 시스템으로, 항상 최신 버전의 소프트웨어를 제공합니다. Let's Encrypt 공식 문서에서도 snap 설치를 권장합니다.

snap으로 설치하면 자동 업데이트도 지원되어 보안 패치를 놓칠 걱정이 없습니다. 코드를 살펴보면, snap 설치 후 심볼릭 링크를 생성하는 부분이 있습니다.

/snap/bin/certbot/usr/bin/certbot으로 연결하면 어디서든 certbot 명령을 사용할 수 있습니다. 설치가 완료되면 certbot --version으로 버전을 확인하고, certbot plugins로 사용 가능한 플러그인을 확인합니다.

플러그인이란 무엇일까요? Certbot은 다양한 웹 서버와 DNS 서비스를 지원하기 위해 플러그인 구조를 채택했습니다.

nginx 플러그인을 사용하면 nginx 설정 파일을 자동으로 수정해주고, apache 플러그인은 아파치 설정을 처리합니다. DNS 플러그인은 와일드카드 인증서 발급에 필수적인데, 이 부분은 다음 장에서 자세히 다루겠습니다.

실제 현업에서는 어떤 설치 방법을 선택할까요? 대부분의 프로덕션 환경에서는 snap 설치를 권장합니다.

특히 Let's Encrypt의 정책이나 프로토콜이 변경될 때 빠르게 대응할 수 있기 때문입니다. 다만 snap을 사용할 수 없는 환경이라면 pip로 설치하는 방법도 있습니다.

김개발 씨가 설치를 마치고 버전을 확인했습니다. "certbot 2.7.4가 설치되었네요!" 이제 실제로 인증서를 발급받을 준비가 되었습니다.

실전 팁

💡 - snap 설치 전에 apt로 설치된 구버전 certbot은 먼저 제거하세요

  • DNS 플러그인은 사용하는 DNS 서비스에 맞는 것을 선택하세요 (cloudflare, route53 등)

3. 와일드카드 인증서 발급

설치를 마친 김개발 씨에게 박시니어 씨가 물었습니다. "mail.example.com, smtp.example.com, imap.example.com...

서브도메인마다 인증서를 따로 발급받을 거야?" 김개발 씨는 당황했습니다. 더 좋은 방법이 있다는 뜻이었습니다.

와일드카드 인증서는 *.example.com처럼 모든 서브도메인을 하나의 인증서로 커버하는 방식입니다. 마치 모든 방을 열 수 있는 마스터키와 같습니다.

Let's Encrypt에서 와일드카드 인증서를 발급받으려면 DNS-01 챌린지를 통과해야 하며, 이를 위해 DNS API 접근 권한이 필요합니다.

다음 코드를 살펴봅시다.

# Cloudflare DNS를 사용하는 경우 API 토큰 설정
sudo mkdir -p /etc/letsencrypt
cat << 'EOF' | sudo tee /etc/letsencrypt/cloudflare.ini
dns_cloudflare_api_token = your_api_token_here
EOF
sudo chmod 600 /etc/letsencrypt/cloudflare.ini

# 와일드카드 인증서 발급
sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
  -d "example.com" \
  -d "*.example.com" \
  --agree-tos \
  --email admin@example.com

# 발급된 인증서 확인
sudo ls -la /etc/letsencrypt/live/example.com/

김개발 씨는 메일 서버에 필요한 도메인을 정리해보았습니다. mail.example.com, smtp.example.com, imap.example.com, pop.example.com...

벌써 네 개나 됩니다. 앞으로 webmail.example.com도 추가해야 할 것 같았습니다.

"서브도메인마다 인증서를 발급받으면 관리가 너무 복잡해지지 않을까요?" 박시니어 씨가 해결책을 알려주었습니다. "와일드카드 인증서를 사용하면 돼.

*.example.com 하나로 모든 서브도메인을 커버할 수 있어." 와일드카드 인증서는 마치 아파트 관리인의 마스터키와 같습니다. 일반 인증서가 특정 호수의 열쇠라면, 와일드카드 인증서는 해당 층의 모든 방을 열 수 있는 마스터키입니다.

*.example.com 인증서 하나로 mail, smtp, imap, webmail 등 어떤 서브도메인이든 사용할 수 있습니다. 새로운 서브도메인을 추가해도 인증서를 다시 발급받을 필요가 없습니다.

하지만 와일드카드 인증서를 발급받으려면 특별한 조건이 있습니다. 일반 인증서는 HTTP-01 챌린지로 발급받을 수 있습니다.

웹 서버에 특정 파일을 배치하면 Let's Encrypt가 이를 확인하고 도메인 소유권을 검증합니다. 하지만 와일드카드 인증서는 반드시 DNS-01 챌린지를 사용해야 합니다.

DNS 레코드에 특정 값을 추가하여 도메인 소유권을 증명하는 방식입니다. 왜 DNS 검증이 필요할까요?

*.example.com은 무한히 많은 서브도메인을 의미합니다. 각각의 서브도메인에 대해 HTTP 검증을 하는 것은 불가능합니다.

따라서 도메인 전체에 대한 제어권을 증명할 수 있는 DNS 검증이 필수입니다. DNS를 수정할 수 있다는 것은 그 도메인의 진정한 소유자라는 강력한 증거가 됩니다.

코드를 단계별로 살펴보겠습니다. 먼저 DNS 서비스의 API 인증 정보를 저장합니다.

예시에서는 Cloudflare를 사용했지만, Route53, DigitalOcean 등 다양한 DNS 서비스를 지원합니다. API 토큰이 담긴 파일은 chmod 600으로 권한을 제한해야 합니다.

다른 사용자가 이 파일을 읽으면 DNS를 마음대로 조작할 수 있기 때문입니다. certbot 명령어의 옵션을 살펴보겠습니다.

--dns-cloudflare는 Cloudflare DNS 플러그인을 사용한다는 의미입니다. -d 옵션으로 도메인을 지정하는데, 주의할 점이 있습니다.

와일드카드 인증서(*.example.com)는 서브도메인만 커버하고 루트 도메인(example.com)은 커버하지 않습니다. 따라서 둘 다 사용하려면 두 개의 -d 옵션을 함께 지정해야 합니다.

발급이 완료되면 인증서 파일들이 생성됩니다. /etc/letsencrypt/live/example.com/ 디렉토리에 네 개의 파일이 생깁니다.

privkey.pem은 개인키, cert.pem은 인증서, chain.pem은 중간 인증서, fullchain.pem은 인증서와 중간 인증서를 합친 파일입니다. 대부분의 서비스에서는 fullchain.pem과 privkey.pem 두 파일을 사용합니다.

실무에서 자주 하는 실수가 있습니다. Cloudflare API 토큰을 생성할 때 Zone:DNS:Edit 권한만 부여하는 것이 좋습니다.

전체 계정 권한을 부여하면 보안 위험이 커집니다. 또한 인증서 경로의 파일들은 심볼릭 링크입니다.

실제 파일은 archive 디렉토리에 있고, live 디렉토리는 항상 최신 인증서를 가리킵니다. 김개발 씨가 명령어를 실행하자 잠시 후 "Congratulations!"라는 메시지가 나타났습니다.

와일드카드 인증서 발급에 성공한 것입니다.

실전 팁

💡 - DNS 전파에 시간이 걸릴 수 있으니 --dns-cloudflare-propagation-seconds 옵션으로 대기 시간을 늘려보세요

  • API 토큰은 최소 권한 원칙에 따라 DNS 편집 권한만 부여하세요

4. docker mailserver에 인증서 적용

인증서를 발급받은 김개발 씨는 이제 실제 메일 서버에 적용해야 합니다. 회사에서는 docker-mailserver라는 도커 기반 메일 서버를 사용하기로 했습니다.

도커 환경에서 호스트의 인증서를 어떻게 연결할 수 있을까요?

docker-mailserver는 Postfix, Dovecot, SpamAssassin 등 메일 서버에 필요한 모든 구성요소를 하나의 도커 이미지로 패키징한 솔루션입니다. 마치 레고 블록처럼 필요한 기능이 모두 조립되어 있어 별도 설정 없이도 바로 사용할 수 있습니다.

SSL 인증서는 볼륨 마운트를 통해 컨테이너에 전달합니다.

다음 코드를 살펴봅시다.

# docker-compose.yml 설정
version: '3.8'
services:
  mailserver:
    image: ghcr.io/docker-mailserver/docker-mailserver:latest
    container_name: mailserver
    hostname: mail.example.com
    ports:
      - "25:25"    # SMTP
      - "465:465"  # SMTPS (Implicit TLS)
      - "587:587"  # Submission (STARTTLS)
      - "993:993"  # IMAPS
    volumes:
      # Let's Encrypt 인증서 마운트
      - /etc/letsencrypt:/etc/letsencrypt:ro
    environment:
      - SSL_TYPE=letsencrypt
      - SSL_DOMAIN=example.com
      - ENABLE_CLAMAV=0
      - ENABLE_SPAMASSASSIN=1

docker-mailserver를 선택한 이유가 있었습니다. 전통적인 메일 서버 구축은 Postfix, Dovecot, OpenDKIM, SpamAssassin 등 여러 소프트웨어를 개별적으로 설치하고 설정해야 했습니다.

각각의 설정 파일을 맞추는 것만 해도 며칠이 걸리는 작업이었습니다. docker-mailserver는 이 모든 것을 하나의 컨테이너에 담았습니다.

마치 조립 완료된 PC를 구매하는 것과 같습니다. 부품 호환성을 고민할 필요 없이, 전원만 연결하면 바로 사용할 수 있습니다.

메일 서버도 마찬가지로, docker-compose 파일 하나로 완전한 메일 서버를 구동할 수 있습니다. SSL 인증서를 적용하는 방법은 매우 간단합니다.

docker-mailserver는 Let's Encrypt와의 통합을 기본으로 지원합니다. SSL_TYPE=letsencrypt라는 환경변수 하나만 설정하면, 컨테이너가 자동으로 /etc/letsencrypt/live/ 디렉토리에서 인증서를 찾습니다.

SSL_DOMAIN으로 도메인을 지정하면 해당 도메인의 인증서를 사용합니다. 코드의 volumes 섹션을 자세히 살펴보겠습니다.

/etc/letsencrypt:/etc/letsencrypt:ro에서 :ro는 읽기 전용(read-only)을 의미합니다. 컨테이너가 인증서를 읽을 수만 있고 수정할 수는 없습니다.

이는 보안을 위한 중요한 설정입니다. 만약 컨테이너가 침해되더라도 인증서 파일을 변조할 수 없습니다.

포트 설정도 중요합니다. 첫 번째 장에서 배웠던 내용이 여기서 적용됩니다.

25번 포트는 다른 메일 서버와의 통신용, 465번은 Implicit TLS, 587번은 STARTTLS, 993번은 IMAPS입니다. 각 포트가 어떤 용도인지 이해하고 있으니 설정이 훨씬 명확하게 느껴집니다.

environment 섹션의 다른 설정도 살펴보겠습니다. ENABLE_CLAMAV=0은 바이러스 검사를 비활성화합니다.

ClamAV는 메모리를 많이 사용하므로 리소스가 제한된 서버에서는 끄는 것이 좋습니다. ENABLE_SPAMASSASSIN=1은 스팸 필터를 활성화합니다.

메일 서버 운영에서 스팸 차단은 필수이므로 이 기능은 켜두는 것이 좋습니다. 실제 운영 환경에서는 추가 설정이 필요합니다.

DKIM, SPF, DMARC 같은 이메일 인증 설정도 중요합니다. 이 설정들이 없으면 발송한 메일이 스팸으로 분류될 수 있습니다.

docker-mailserver는 이러한 설정도 환경변수로 쉽게 구성할 수 있습니다. 주의할 점이 있습니다.

인증서가 갱신되면 컨테이너도 새 인증서를 인식해야 합니다. 다행히 docker-mailserver는 인증서 변경을 자동으로 감지하는 기능이 있습니다.

하지만 일부 버전에서는 컨테이너를 재시작해야 할 수도 있습니다. 이 부분은 자동 갱신 스크립트에서 다루겠습니다.

김개발 씨가 docker-compose up -d 명령을 실행하자 메일 서버가 구동되었습니다. openssl 명령으로 465번 포트에 연결해보니 와일드카드 인증서가 제대로 적용되어 있었습니다.

실전 팁

💡 - 처음 설정할 때는 docker-compose logs -f mailserver로 로그를 실시간 모니터링하세요

  • SSL_TYPE=manual을 사용하면 인증서 경로를 직접 지정할 수도 있습니다

5. 자동 갱신 스크립트 설정

메일 서버가 잘 돌아가는 것을 확인한 김개발 씨는 안심하고 다른 업무에 집중했습니다. 그런데 3개월 후, 갑자기 메일 발송이 안 된다는 신고가 들어왔습니다.

원인은 인증서 만료였습니다. Let's Encrypt 인증서는 90일마다 갱신해야 한다는 사실을 깜빡한 것입니다.

Let's Encrypt 인증서는 90일 유효기간을 가지며, 만료 30일 전부터 갱신이 가능합니다. 수동으로 갱신하면 잊어버리기 쉬우므로, cron이나 systemd timer를 사용해 자동 갱신을 설정하는 것이 필수입니다.

갱신 후에는 메일 서버에 새 인증서를 적용하기 위한 후처리도 필요합니다.

다음 코드를 살펴봅시다.

#!/bin/bash
# /usr/local/bin/renew-certs.sh
# Let's Encrypt 인증서 자동 갱신 스크립트

LOG_FILE="/var/log/certbot-renew.log"

echo "$(date): 인증서 갱신 시작" >> $LOG_FILE

# 인증서 갱신 시도
certbot renew --quiet --deploy-hook "docker restart mailserver"

if [ $? -eq 0 ]; then
    echo "$(date): 인증서 갱신 성공" >> $LOG_FILE
else
    echo "$(date): 인증서 갱신 실패" >> $LOG_FILE
fi

# cron 등록 (매일 새벽 3시에 실행)
# sudo crontab -e
# 0 3 * * * /usr/local/bin/renew-certs.sh

인증서 만료 사태를 겪은 김개발 씨는 크게 반성했습니다. 다행히 빠르게 수동 갱신으로 복구했지만, 같은 실수를 반복하고 싶지 않았습니다.

박시니어 씨가 조언했습니다. "자동화하지 않은 것은 결국 잊어버리게 돼 있어.

자동 갱신 스크립트를 만들어두자." 왜 Let's Encrypt는 유효기간을 90일로 짧게 설정했을까요? 보안 관점에서 인증서 유효기간이 짧을수록 좋습니다.

만약 개인키가 유출되더라도 피해 기간을 최소화할 수 있기 때문입니다. 또한 짧은 유효기간은 자동화를 강제합니다.

90일마다 수동으로 갱신하는 것은 현실적으로 불가능하므로, 자연스럽게 자동 갱신 시스템을 구축하게 됩니다. 이것이 바로 Let's Encrypt의 의도입니다.

certbot renew 명령은 매우 똑똑하게 동작합니다. 이 명령을 실행하면 certbot은 시스템에 설치된 모든 인증서를 확인합니다.

그리고 만료까지 30일 이내인 인증서만 갱신합니다. 아직 유효기간이 충분한 인증서는 건드리지 않습니다.

따라서 매일 이 명령을 실행해도 불필요한 갱신이 발생하지 않습니다. 스크립트를 단계별로 살펴보겠습니다.

먼저 로그 파일 경로를 지정합니다. 자동화된 작업은 반드시 로그를 남겨야 합니다.

문제가 발생했을 때 원인을 추적할 수 있어야 하기 때문입니다. $(date) 명령으로 시간을 기록하면 언제 갱신이 시도되었는지 알 수 있습니다.

핵심은 --deploy-hook 옵션입니다. 이 옵션은 인증서 갱신이 성공했을 때만 실행되는 명령을 지정합니다.

예시에서는 docker restart mailserver로 메일 서버 컨테이너를 재시작합니다. 갱신에 실패하면 후크가 실행되지 않으므로, 불필요한 서버 재시작을 방지할 수 있습니다.

--quiet 옵션도 중요합니다. 이 옵션은 에러가 아닌 일반 출력을 숨깁니다.

cron 작업에서 출력이 있으면 시스템 관리자에게 메일이 발송될 수 있습니다. 매일 실행되는 스크립트에서 불필요한 알림이 쏟아지면 정작 중요한 알림을 놓칠 수 있습니다.

따라서 에러만 출력되도록 설정하는 것이 좋습니다. cron 설정을 살펴보겠습니다.

**0 3 * * ***는 매일 새벽 3시에 실행한다는 의미입니다. 새벽 시간을 선택한 이유는 트래픽이 적을 때 서버를 재시작하기 위해서입니다.

Let's Encrypt 공식 문서에서는 하루에 두 번 실행하는 것을 권장합니다. 0시와 12시처럼 12시간 간격으로 설정하면 더 안정적입니다.

systemd timer를 사용하는 방법도 있습니다. 최신 리눅스 배포판에서는 cron 대신 systemd timer를 사용하는 추세입니다.

certbot을 snap으로 설치했다면 systemd timer가 자동으로 등록됩니다. systemctl list-timers 명령으로 확인할 수 있습니다.

snap 설치의 또 다른 장점입니다. 주의할 점이 있습니다.

docker restart는 컨테이너를 중지했다가 다시 시작합니다. 이 과정에서 몇 초간 서비스가 중단됩니다.

메일 서버의 경우 짧은 중단은 큰 문제가 없지만, 더 안전한 방법을 원한다면 docker-mailserver의 인증서 자동 감지 기능을 활용하거나, docker exec로 서비스만 재로드하는 방법도 있습니다. 김개발 씨가 스크립트를 작성하고 cron에 등록했습니다.

이제 인증서 만료 걱정 없이 편히 잠들 수 있게 되었습니다.

실전 팁

💡 - certbot renew --dry-run으로 실제 갱신 없이 테스트할 수 있습니다

  • 갱신 실패 시 슬랙이나 이메일로 알림을 보내는 로직을 추가하면 더 안전합니다

6. 인증서 상태 모니터링

자동 갱신을 설정한 김개발 씨는 한결 마음이 놓였습니다. 하지만 박시니어 씨가 한마디 덧붙였습니다.

"자동화를 믿으면 안 돼. 자동화가 제대로 작동하는지 감시하는 시스템도 필요해." 자동 갱신이 실패하면 어떻게 알 수 있을까요?

모니터링은 시스템이 정상적으로 작동하는지 지속적으로 확인하는 과정입니다. 인증서의 경우 만료일까지 남은 기간을 추적하고, 특정 임계값 이하로 떨어지면 알림을 보내는 시스템이 필요합니다.

외부 서비스를 활용하거나 직접 스크립트를 작성할 수 있습니다.

다음 코드를 살펴봅시다.

#!/bin/bash
# /usr/local/bin/check-cert-expiry.sh
# 인증서 만료일 모니터링 스크립트

DOMAIN="mail.example.com"
WARN_DAYS=14
SLACK_WEBHOOK="https://hooks.slack.com/services/xxx/yyy/zzz"

# 인증서 만료일까지 남은 일수 계산
EXPIRY_DATE=$(echo | openssl s_client -servername $DOMAIN \
    -connect $DOMAIN:465 2>/dev/null | \
    openssl x509 -noout -enddate | cut -d= -f2)

EXPIRY_EPOCH=$(date -d "$EXPIRY_DATE" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( ($EXPIRY_EPOCH - $NOW_EPOCH) / 86400 ))

echo "인증서 만료까지 ${DAYS_LEFT}일 남음"

# 임계값 이하면 슬랙 알림 발송
if [ $DAYS_LEFT -lt $WARN_DAYS ]; then
    curl -X POST -H 'Content-type: application/json' \
        --data "{\"text\":\"경고: ${DOMAIN} 인증서가 ${DAYS_LEFT}일 후 만료됩니다!\"}" \
        $SLACK_WEBHOOK
fi

자동화 시스템도 실패할 수 있습니다. 네트워크 문제로 Let's Encrypt 서버에 접속하지 못할 수도 있고, DNS API 토큰이 만료되었을 수도 있습니다.

cron 데몬 자체가 죽어있을 수도 있습니다. 자동 갱신만 믿고 있다가 인증서가 만료되면 서비스 장애로 이어집니다.

모니터링의 핵심은 다중 방어입니다. 마치 은행 금고에 여러 겹의 잠금장치가 있는 것처럼, 인증서도 여러 단계의 안전장치가 필요합니다.

자동 갱신이 첫 번째 방어선이라면, 모니터링은 두 번째 방어선입니다. 갱신이 실패하더라도 모니터링이 이를 감지하면 수동으로 대응할 시간이 생깁니다.

스크립트의 동작 원리를 살펴보겠습니다. 먼저 openssl s_client 명령으로 실제 서버에 SSL 연결을 시도합니다.

여기서 중요한 것은 certbot이 관리하는 파일이 아니라 실제 서비스 중인 인증서를 확인한다는 점입니다. 파일은 갱신되었지만 서버에 적용되지 않은 경우도 감지할 수 있습니다.

날짜 계산 부분을 자세히 보겠습니다. openssl x509 명령의 출력에서 만료일을 추출합니다.

그리고 date +%s로 유닉스 타임스탬프(초 단위)로 변환합니다. 현재 시간과의 차이를 86400(하루의 초)으로 나누면 남은 일수가 됩니다.

쉘 스크립트에서 날짜 계산은 이렇게 타임스탬프를 활용하는 것이 가장 확실합니다. WARN_DAYS=14로 설정한 이유가 있습니다.

Let's Encrypt는 만료 30일 전부터 갱신이 가능합니다. 자동 갱신이 제대로 작동한다면 만료 30일 전에 이미 갱신되어 있어야 합니다.

14일 전에도 갱신되지 않았다면 무언가 문제가 있다는 신호입니다. 이때 알림을 받으면 수동으로 조치할 충분한 시간이 있습니다.

슬랙 웹훅을 사용한 알림 발송을 살펴보겠습니다. 슬랙은 많은 기업에서 사용하는 협업 도구입니다.

웹훅 URL로 POST 요청을 보내면 지정된 채널에 메시지가 전송됩니다. 비용이 들지 않고 설정도 간단합니다.

물론 이메일, SMS, PagerDuty 등 다른 알림 채널을 사용해도 됩니다. 외부 모니터링 서비스도 활용할 수 있습니다.

SSL Labs, UptimeRobot, Pingdom 같은 서비스들은 SSL 인증서 만료를 모니터링하는 기능을 제공합니다. 특히 외부에서 모니터링하면 서버 자체에 문제가 생겼을 때도 알림을 받을 수 있습니다.

무료 플랜으로도 기본적인 모니터링이 가능합니다. 운영 환경에서는 더 정교한 모니터링이 필요할 수 있습니다.

Prometheus와 Grafana를 사용한다면 blackbox_exporter로 SSL 인증서 메트릭을 수집할 수 있습니다. 대시보드에서 모든 도메인의 인증서 상태를 한눈에 볼 수 있고, AlertManager로 조건부 알림도 설정할 수 있습니다.

규모가 큰 조직에서는 이런 방식을 권장합니다. 김개발 씨가 모니터링 스크립트까지 설정을 마쳤습니다.

이제 인증서 관련 걱정은 끝났습니다. 자동 갱신이 작동하고, 혹시 실패하더라도 알림을 받을 수 있으니까요.

박시니어 씨가 엄지를 치켜세웠습니다. "이제 진짜 운영 가능한 시스템이 됐네!"

실전 팁

💡 - 모니터링 스크립트는 자동 갱신과 다른 서버에서 실행하면 더 안전합니다

  • SSL Labs API를 활용하면 인증서 등급까지 모니터링할 수 있습니다

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#SSL#TLS#LetsEncrypt#Certbot#Docker#SSL,Security

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.