Linux 서버에서 자동으로 HTTPS 인증서 갱신 안 될 때 해결하는 방법

조회수: 0

인증서 자동 갱신 실패 시 나타나는 증상 정리

HTTPS 인증서 자동 갱신이 제대로 안 되면 보통 이런 흐름으로 문제를 인지하게 된다

  • 브라우저에서 사이트 접속 시 보안 경고가 뜬다
  • 인증서가 만료됐다는 메시지가 나오고 사용자가 추가 단계를 거쳐야 한다
  • 일부 클라이언트는 아예 접속을 차단해 트래픽이 눈에 띄게 줄어든다
  • 서버 로그에 인증서 관련 에러가 쌓이기 시작한다

이 상황에서 서버를 재부팅하거나 웹 서버만 재시작한다고 해서 문제가 해결되지는 않는다
먼저 인증서가 정말 만료된 것인지, 자동 갱신이 왜 멈췄는지 차근차근 확인하는 것이 중요하다

첫 단계 인증서 상태와 certbot 기본 정보 확인

가장 먼저 해야 할 일은 현재 인증서 상태를 정확히 파악하는 것이다

브라우저와 openssl로 만료일 확인

브라우저에서 사이트 주소 창에 자물쇠 아이콘을 눌러 인증서 정보를 보면 만료일과 발급자를 대략 확인할 수 있다
서버에서 직접 확인하려면 openssl 명령을 사용할 수 있다

echo | openssl s_client -servername 도메인명 -connect 도메인명:443 2>/dev/null | openssl x509 -noout -dates -issuer -subject

이 명령 결과의 notAfter 부분이 인증서 만료일이다
이미 날짜가 지났거나 며칠 안 남았다면 갱신이 제대로 동작하지 않은 것이 확실하다

주의할 점은 인증서를 갱신해도 웹 서버가 새 파일을 아직 사용하지 않을 수 있다는 것이다
그래서 갱신 명령과 웹 서버 재로딩까지 모두 점검해야 한다

certbot 버전과 설치 방식 확인

대부분의 Linux 서버에서는 Lets Encrypt 기반 certbot을 사용해 HTTPS 인증서를 자동 갱신한다
먼저 certbot이 어디에서 어떻게 설치됐는지 확인해 보자

certbot --version
which certbot

패키지 관리자로 설치했는지
스냅 패키지로 설치했는지
직접 파이썬 패키지로 설치했는지

에 따라 갱신 방식과 서비스 이름이 조금씩 달라진다
같은 서버에 예전 버전과 새 버전이 섞여 있는 경우도 있어, 어떤 certbot이 실제로 사용 중인지부터 분명히 해 두는 것이 좋다

systemd timer 혹은 cron이 실제로 돌고 있는지 확인

자동 갱신이 안 된다는 말은 대부분 갱신 명령을 실행해 주는 타이머나 크론 작업이 멈춰 있다는 뜻이다

systemd timer 사용하는 경우

요즘 배포판은 certbot 타이머를 systemd로 관리하는 경우가 많다

sudo systemctl list-timers | grep certbot
sudo systemctl status certbot.timer

여기에서 확인해야 할 포인트는

  • certbot 타이머가 활성 상태인지
  • 마지막 실행 시각이 언제였는지
  • 다음 실행 예정 시각이 잡혀 있는지

이다

타이머가 비활성 상태라면 다음 명령으로 다시 켤 수 있다

sudo systemctl enable --now certbot.timer

status 출력에서 실패 로그가 보인다면

sudo journalctl -u certbot.service

로 좀 더 자세한 에러 메시지를 확인해 본다

cron으로 갱신하는 경우

조금 오래된 환경이나 수동 설치한 certbot은 cron을 통해 갱신하는 경우가 많다

sudo crontab -l
sudo ls /etc/cron.daily

여기에서 certbot 관련 스크립트가 있는지, 실행 권한이 있는지 확인한다
또한 cron 데몬 자체가 제대로 돌고 있는지도 확인해야 한다

sudo systemctl status cron

타이머나 cron이 전혀 잡혀 있지 않다면 애초에 자동 갱신 설정이 빠져 있었을 가능성이 높다

certbot 갱신 명령을 직접 돌려 보고 에러 확인

타이머 이야기만 계속해도 감이 잘 안 잡힐 수 있다
이럴 때는 직접 certbot 갱신 명령을 돌려 보는 것이 가장 빠르다

드라이런 모드로 테스트

실제 인증서에 영향을 주지 않으면서 전체 흐름을 검증하려면 dry run 옵션을 쓴다

sudo certbot renew --dry-run

여기에서 성공하면 자동 갱신 자체는 문제없는 상태다
타이머나 cron만 바로잡으면 된다

반대로 에러가 나온다면

  • 어떤 도메인에서 실패했는지
  • 인증 방식이 http 검증인지 dns 검증인지
  • 포트나 권한, rate limit 관련 메시지는 없는지

를 꼼꼼히 읽어 본다
certbot이 친절하게 원인을 설명해 주는 편이라 로그만 잘 보면 대부분의 문제를 좁혀 갈 수 있다

웹 서버와 포트 문제로 인증서 검증이 막힌 경우

Lets Encrypt는 인증서 발급이나 갱신 시 도메인이 해당 서버를 제대로 가리키는지 검증한다
가장 흔한 방식이 http 검증이고 이때는 다음 조건이 필수다

  • 도메인의 A 혹은 AAAA 레코드가 서버 공인 아이피를 가리킬 것
  • 서버에서 포트 80으로 들어오는 요청을 차단하지 않을 것
  • 웹 서버 혹은 certbot이 해당 포트에서 챌린지 요청을 처리할 것

여기에서 하나라도 안 맞으면 갱신이 실패한다

포트 80이 막혀 있는 경우

보안상 이유로 http를 모두 차단하고 https만 연 서버에서 의외로 많이 생기는 문제다
인증서 갱신 시점에만큼은 포트 80을 열어줘야 한다

  • 방화벽에서 80 포트 허용
  • 클라우드 보안 그룹에서 80 포트 허용
  • 공유기 포트포워딩도 80을 서버로 연결

을 확인한다

포트가 제대로 열려 있는지는 다른 외부 서버에서 다음과 같이 확인할 수 있다

curl -I http://도메인명

리턴 코드가 정상적으로 나온다면 최소한 네트워크 경로는 열린 것이다

웹 서버 설정과 webroot 경로 문제

webroot 플러그인을 사용해 갱신하는 경우
도메인의 문서 루트 아래에 잘 알려진 경로를 만들어 검증 파일을 올린다

  • certbot 설정 파일에서 webroot 경로가 올바른지
  • nginx나 apache 설정에서 해당 경로로 접근을 정상적으로 처리하는지

를 확인해야 한다

리버스 프록시나 여러 서버가 앞단에 껴 있는 구성에서는 특정 경로만 프록시에서 바로 처리해 버려 챌린지 파일이 실제 서버까지 도달하지 않는 경우도 많다

권한과 경로 문제로 certbot 자체가 실패하는 경우

갱신 로그를 보면 파일 시스템 권한이나 경로 관련 에러가 나오는 경우도 있다

자주 보이는 패턴을 정리해 보면

  • etc letsencrypt 디렉터리 권한이 꼬여 있는 경우
  • 홈 디렉터리 기반 수동 설치와 패키지 설치가 섞인 경우
  • sudo 없이 certbot을 실행해 필요한 파일을 건드리지 못하는 경우

등이 있다

기본적으로는 certbot은 root 권한으로 실행하는 것이 안전하다

sudo certbot renew --dry-run

으로 테스트했을 때 권한 에러가 없다면 대체로 문제없다
etc letsencrypt 아래 디렉터리는 소유자와 권한을 임의로 변경하지 않는 편이 좋다

DNS 설정 오류와 dns 검증 방식 문제

와일드카드 인증서나 특정 환경에서는 dns 검증을 사용한다
이 경우 자동 갱신이 실패하면 원인은 대개 다음 가운데 하나다

  • 도메인 제공업체 API 키가 만료되거나 삭제된 경우
  • dns 플러그인 설정 파일 경로가 바뀐 경우
  • 도메인명이 변경됐는데 renewal 설정 파일을 업데이트하지 않은 경우

dns 검증을 쓰는지 확인하려면 etc letsencrypt renewal 디렉터리 아래 설정 파일을 열어 보면 된다
어느 플러그인을 쓰는지
어떤 환경 변수를 참조하는지
를 확인하고 해당 자격 증명이 여전히 유효한지 점검해야 한다

인증서는 갱신됐는데 웹 서버가 옛 파일을 쓰는 경우

certbot 로그에는 갱신이 성공했다고 나오는데 브라우저에서는 여전히 예전 만료일이 보이는 상황도 있다
이럴 때는 대개 웹 서버를 적절히 재로딩하지 않아서다

apache 혹은 nginx 재로딩 연동

일부 배포판은 certbot이 자체적으로 웹 서버와 연동돼 있어 갱신 후 자동으로 재로딩을 해 준다
하지만 수동 설정이나 컨테이너 환경에서는 post hook을 직접 정의해 줘야 하는 경우가 많다

예를 들어 renewal 설정 파일에 다음과 같이 후처리 명령을 정의할 수 있다

post_hook = systemctl reload nginx

혹은 certbot 실행 시 인자로 전달할 수도 있다

sudo certbot renew --post-hook "systemctl reload nginx"

핵심은 인증서 파일이 바뀌었다는 사실을 웹 서버에게 알려 새 파일을 다시 읽게 만드는 것이다

certbot 로그를 통해 근본 원인 찾기

여러 가지를 추측하기보다 certbot 로그를 직접 보는 것이 정확하다
기본 위치는 다음과 같다

sudo ls /var/log/letsencrypt
sudo less /var/log/letsencrypt/letsencrypt.log

로그 안에 에러 메시지와 함께

어떤 도메인을 처리하다 실패했는지
어떤 단계에서 네트워크나 권한 문제가 있었는지

가 상세히 기록돼 있다
최근 로그만 보는 것보다 몇 개 이전 로그까지 같이 보면 언제부터 문제가 발생했는지도 알 수 있다

재발 방지를 위한 체크리스트 정리

한 번 인증서 만료 사고를 겪고 나면 다시는 경험하고 싶지 않다
마무리로 재발 방지를 위해 점검할 항목을 정리해 보자

  • certbot 타이머나 cron이 활성 상태인지 정기적으로 확인
  • 도메인이나 서버 아이피를 변경할 때 renewal 설정 파일도 함께 검토
  • 방화벽이나 보안 그룹을 수정할 때 포트 80과 443 정책을 함께 점검
  • 갱신 후 웹 서버 재로딩이 자동으로 이뤄지는지 확인
  • 모니터링 도구를 통해 인증서 만료일을 주기적으로 체크하고 알림 설정

이 몇 가지만 꾸준히 관리해도 HTTPS 인증서 만료로 인한 장애를 대부분 피할 수 있다

마무리 자동 갱신이 안 될 때의 기본 접근법

Linux 서버에서 HTTPS 인증서 자동 갱신이 안 될 때는

  • 타이머나 cron이 정말 실행되고 있는지
  • certbot 갱신 명령이 정상 동작하는지
  • 포트와 웹 서버 설정이 검증 조건을 만족하는지
  • 권한과 dns 설정이 올바른지

를 순서대로 점검하는 것이 핵심이다
한 번 전체 흐름을 손으로 따라가 보면서 설정을 정리해 두면 이후에는 인증서 관리를 거의 잊고 지낼 수 있을 정도로 안정적인 상태를 만들 수 있다