지금의 웹사이트를 운영 환경에 배포하면서 HTTPS 적용을 위해 Certbot SSL 인증서를 발급했다. 하지만 첫 시도는 실패했고 원인 파악을 하기까지 꽤나 삽질을 했다. 그 과정을 정리하며 회고해봤다.
1. Certbot 인증 실패 최초의 원인
인증서 발급이 실패의 첫 시작은 DNS 전파 지연으로 추측된다. 도메인 설정 직후 Certbot을 실행했기 때문에 Let's Encrypt 서버가 내 도메인의 A레코드를 인식하지 못했던 것 같다.
EC2에서ping,dig등으로 IP가 정상적으로 노출되어도 Let's Encrypt의 인증 서버는 글로벌 DNS 전파가 덜 된 상태일 수 있다. 즉, 내가 보는 DNS와 Certbot이 보는 DNS는 다를 수 있다.
2. 데드락 무한 루프
이를 모르고 여러 번 인증서를 발급하려고 시도하다보니 certbot 실행 시 nginx가 포트 80을 점유 중이라는 아래의 에러 메시지가 나왔던 것. 이 상태에서는 포트 80이 열려 있어도 nginx가 실행되지 않기 때문에 certbot 인증도 실패하게 된다.
Bind for 0.0.0.0:80 failed: port is already allocated
- 이전에 실행한 nginx 컨테이너가 아직 살아 있을 경우
- 시스템 상의 다른 서비스(Apache, nginx 등)가 포트 80을 점유중인 경우
인증 과정에서 Certbot은 80포트로 HTTP 요청을 받아야 하므로 이 포트를 점유하고 있는 다른 프로세스를 명확히 정리하지 않으면 인증 실패로 이어짐. 이 포트는 인증 시점에 아주 민감하게 사용되므로, 충돌 방지를 위해 인증 단계만 임시 nginx를 띄우는 등 설계적 고려 필요
또한 Let's Encrypt가 도메인으로 HTTP 접속 시도했는데 연결 자체가 거부(Connection refused) 되기도 했다.
Detail: 3.36.199.220: Fetching http://give-it-a-shot.site/.well-known/acme-challenge/...: Connection refused
- nginx가 아직 실행 전이거나
- 방화벽/보안 그룹 설정으로 80 포트 접근 막혔거나
- nginx가 인증서 없어서 부팅에 실패한 상태
Let's Encrypt는 HTTP-01 인증을 위해 요청을 보낼 때 서버가 HTTP(80) 포트를 통해 응답 가능해야함. 그런데 nginx가 인증서 없이 HTTPS로만 부팅을 시도하거나 webroot 인증 디렉토리가 올바르게 매핑되지 않으면 인증에 실패. 인증 시점에서는 반드시 HTTP로 도메인 응답이 가능해야 하며 인증 후에 HTTPS 리디렉션을 거는 것이 안정적
이때부터 nginxproxy 컨테이너는 계속 재시작...
STATUS: Restarting (1) 21 seconds ago Nginx가 HTTPS로 부팅하려고 하는데 지정된 경로에 인증서 파일이 없어서 죽고 있는 상황이 이어졌고..
docker-compose에 restart: always가 설정되어 있어서 무한 루프 진입..
[emerg] cannot load certificate "/etc/letsencrypt/live/give-it-a-shot.site/fullchain.pem":
BIO_new_file() failed: No such file or directory
인증서를 새로 발급받기 위한 명령어를 입력했다.
docker compose run --rm certbot하지만 이전에 생성된 certbot 컨테이너가 종료되지 않고 남아 있어서 새로운 certbot 컨테이너를 띄우려고 할 때 충돌이 발생했다.
Error when allocating new name: Conflict. The container name "/certbot" is already in use
도커 컴포즈 반복 실행 시 잔존 컨테이너 정리(cleanup) 작업 선행 필수!
rm 명령어를 통해 중복 컨테이너를 제거했지만 다시 재시작 루프를 탔고... 아래와 같은 교착 상태에 빠졌다.
인증서 부재 → nginx 부팅 실패 → certbot 인증 실패 → 인증서 미존재 → 다시 nginx 부팅 실패 → 무한 루프결국.. 처음부터 차근차근 다시 해보는 게 낫겠다 싶어 다 밀어버리고 새로 시작
인증서 발급 시 nginx에 의존하는 certbot 방식(webroot 등)을 쓰는 경우에는 nginx가 처음에는 HTTPS 설정 없이 HTTP만 켜져 있어야 한다. 이후 인증 완료되면 nginx를 재시작해 HTTPS 설정을 적용하는 방식으로 흐름을 설계 필요
Docker + Certbot + Nginx 프로세스 재구성
[1] HTTP만 가능한 Nginx 기동 ─────┐
▼
[2] Certbot이 인증 요청 수행 (Webroot)
▼
[3] 인증서 발급 성공
▼
[4] Nginx에 HTTPS 설정 추가 및 재시작
또는…
[자동화 방식] docker-compose.yml에서 certbot 컨테이너에서 인증서가 없으면 먼저 발급 → 이후 nginx가 HTTPS 설정으로 시작하도록 처리하는 스크립트 작성
3. 접근 권한 문제로 인해 인증서가 안 보였던 해프닝
전부 밀어버린 뒤 처음부터 작업하면서 다시 certbot을 통해 인증서를 발급했다. 그러나 certbot-etc/live/ 폴더는 비어 있었고 서버에서 해당 폴더로 접근하려하면 에러메시지가 나왔다.
ls: cannot access 'certbot-etc/live': No such file or directory
인증서 발급이 또 실패한 줄 알았다. 그러다가 drwx—— 3 root root 4096 Jul 15 04:06 live 에러 메시지를 확인해 root 권한으로 아래와 같이 명령어를 입력해 접근해보니 인증서 파일이 정상적으로 발급되어 존재하고 있는 것이 보였다.
sudo ls -al certbot-etc/live/give-it-a-shot.site
해당 디렉토리에는 다음과 같은 심볼릭 링크들이 있었다:
cert.pem -> ../../archive/give-it-a-shot.site/cert1.pem
chain.pem -> ../../archive/give-it-a-shot.site/chain1.pem
fullchain.pem -> ../../archive/give-it-a-shot.site/fullchain1.pem
privkey.pem -> ../../archive/give-it-a-shot.site/privkey1.pem인증서는 처음부터 잘 발급되어 있었고 내가 못 본 건 단지 root 전용 디렉토리였기 때문이었다.
root와 sudo 접근
- 일반 서버 운영: sudo 사용 권장, 보안상 안전, 추적 가능
- 로컬에서 root 전용 작업(시스템 초기 설정 등): 잠시 su -로 root 전환 후 작업
| 항목 | root 접근 (su, root 로그인) | sudo 접근 (sudo 명령) |
|---|---|---|
| 권한 | 전체 권한 (계속 유지) | 필요한 명령에만 일시 부여 |
| 보안 | 위험 (오류 시 전체 영향) | 안전 (로그 추적 가능) |
| 로그 기록 | 기록 안됨 | /var/log/auth.log에 기록 |
| 필요 비밀번호 | root 비밀번호 | 사용자 본인 비밀번호 |
| 권장 여부 | 비권장(서버 환경에서 위험) | 일반적으로 권장 |
4. 삭제해도 되는 파일
이전에는 이를 몰랐기 때문에 임의로 certbot-etc 폴더에 cert.pem, chain.pem fullchain.pem를 삽입해두었다. 이제 인증서가 정상적으로 발급되었음을 확인했으니 불필요한 복제본은 지우기로 했다.
certbot-etc/
├── archive/ # 실제 인증서 파일 저장소
│ └── give-it-a-shot.site/
│ ├── cert1.pem
│ ├── chain1.pem
│ ├── fullchain1.pem
│ └── privkey1.pem
├── live/ # 서비스에 쓰는 최신 인증서 심볼릭 링크
│ └── give-it-a-shot.site/
│ ├── cert.pem -> ../../archive/...
│ ├── chain.pem -> ../../archive/...
│ ├── fullchain.pem -> ../../archive/...
│ └── privkey.pem -> ../../archive/...
├── cert.pem ❗ ← 삭제해도 되는 것
├── chain.pem ❗ ← 삭제해도 되는 것
├── fullchain.pem ❗ ← 삭제해도 되는 것
├── options-ssl-nginx.conf ✔ Certbot이 만든 TLS 설정 파일
├── ssl-dhparams.pem ✔ DH 파라미터, Diffie-Hellman 키 교환 보안 파일
마무리하며
| 항목 | 교훈 |
|---|---|
| DNS 전파 | 설정 후 최소 5~10분 이상 기다릴 것. 즉시 Certbot 실행에 주의 |
| 인증 실패 | 실패 메시지를 잘 해석할 것 *가장 중요! — 진짜 실패가 아닐 수도 있음 |
| 접근 문제 | Permission denied가 뜨면 sudo로 접근해볼 것 |
| 인증서 경로 | Nginx는 live/도메인명/ 경로의 인증서를 참조함 |
| 포트 충돌 | 에러 메시지에 이미 점유 중인 포트가 표시됨. 해당 컨테이너 중지 필요 |
이번 Certbot 인증서 발급 경험 덕분에 DNS 전파의 본질과 Let's Encrypt 인증 흐름, Nginx 설정과 Certbot 디렉토리 구조를 깊이 있게 이해하게 됐다.
천천히 다시 복기하며 과정을 되돌아보니 얻게 되는 교훈이 크다!