Post

πŸ’₯ LetsEncrypt SSL μΈμ¦μ„œ λ°œκΈ‰ 횟수 μ œν•œ

πŸ’₯ LetsEncrypt SSL μΈμ¦μ„œ λ°œκΈ‰ 횟수 μ œν•œ

문제 상황

  • Nginx의 default.confλ‚˜ Script 파일이 μ œλŒ€λ‘œ μž‘μ„±λ˜μ–΄ μžˆμŒμ—λ„ LetsEncrypt SSL μΈμ¦μ„œ λ°œκΈ‰μ— 계속 μ‹€νŒ¨ν–ˆλ‹€.

문제 원인

1
2
3
4
5
6
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for subdomain.iptime.org
An unexpected error occurred:
AttributeError: can't set attribute
Ask for help or search for solutions at https://community.letsencrypt.org. 
See the logfile /var/log/letsencrypt/letsencrypt.log or re-run Certbot with -v for more details.
  • μΈμ¦μ„œ λ°œκΈ‰ μ‹€νŒ¨ ν›„ docker logs nginx λͺ…λ Ήμ–΄λ₯Ό μž…λ ₯ν•˜λ©΄ 상기 둜그λ₯Ό 확인할 수 μžˆλ‹€.
1
2
3
4
5
6
7
{
Β Β "type": "urn:ietf:params:acme:error:rateLimited",
Β Β "detail": "too many certificates (5) already issued for this exact set of domains in the last 168h0m0s, 
Β Β 	retry after 2025-05-02 07:31:44 UTC: 
Β Β 	see https://letsencrypt.org/docs/rate-limits/#new-certificates-per-exact-set-of-hostnames",
Β Β "status": 429
}
  • docker exec -it nginx /bin/bash λͺ…λ Ήμ–΄λ‘œ Nginx μ»¨ν…Œμ΄λ„ˆλ‘œ λ“€μ–΄κ°€μ„œ /var/log/letsencrypt/letsencrypt.log νŒŒμΌμ„ ν™•μΈν•˜λ©΄ λœλ‹€.
  • 둜그λ₯Ό 보면 μΈμ¦μ„œ λ°œκΈ‰ μš”μ²­μ— λŒ€ν•œ 횟수 μ œν•œμ΄ μžˆμ—ˆμŒμ„ μ•Œ 수 있고, μ–Έμ œ λ‹€μ‹œ μš”μ²­μ΄ κ°€λŠ₯ν•œμ§€ 확인 κ°€λŠ₯ν•˜λ‹€.
  • 결둠적으둜 Nginx의 default.confλ‚˜ Script νŒŒμΌμ„ 계속 μˆ˜μ •ν•˜λ©΄μ„œ, μΈμ¦μ„œ λ°œκΈ‰ μš”μ²­μ— λŒ€ν•œ 횟수 μ œν•œ μ΄μƒμœΌλ‘œ μš”μ²­ν•œ 것이 문제의 μ›μΈμ΄μ—ˆλ‹€.

ν•΄κ²° 방법

  • μ°Ύμ•„λ³΄λ‹ˆ μΈμ¦μ„œ λ°œκΈ‰κ³Ό μΈμ¦μ„œ 갱신에 λŒ€ν•œ ν…ŒμŠ€νŠΈ λͺ…λ Ήμ–΄κ°€ λ”°λ‘œ μžˆλ‹€κ³  ν•œλ‹€.

βœ… μΈμ¦μ„œ λ°œκΈ‰ ν…ŒμŠ€νŠΈ

1
certbot certonly --staging --webroot --webroot-path=/var/www/certbot --email ${CERTBOT_EMAIL} --agree-tos --no-eff-email -d 도메인

  • --staging μ˜΅μ…˜μ€ μ‹€μ œ μΈμ¦μ„œ λ°œκΈ‰ μ„œλ²„κ°€ μ•„λ‹Œ ν…ŒμŠ€νŠΈ μ„œλ²„μ™€ ν†΅μ‹ ν•˜λ„λ‘ μ„€μ •ν•˜λŠ” μ˜΅μ…˜μ΄λ‹€.
  • μ‹€μ œ μΈμ¦μ„œ λ°œκΈ‰λ³΄λ‹€λŠ” 쑰금 λŠμŠ¨ν•œ 기쀀을 μ μš©ν•΄μ„œ μΈμ¦μ„œλ₯Ό λ°œκΈ‰ν•΄ μ€€λ‹€.
  • μ΄λ•Œ λ°œκΈ‰ λ˜λŠ” μΈμ¦μ„œλŠ” μ‹€μ œ μœ νš¨ν•˜μ§€ μ•ŠμœΌλ©°, λΈŒλΌμš°μ € λ“±μ—μ„œ μ‹ λ’° λ˜μ§€ μ•ŠλŠ”λ‹€.
1
2
3
4
5
6
7
8
9
10
11
12
13
2025-05-01 13:32:32,856:DEBUG:certbot._internal.plugins.selection:Requested authenticator webroot and installer <certbot._internal.cli.cli_utils._Default object at 0xffffafdb4390>
2025-05-01 13:32:32,856:DEBUG:certbot._internal.cli:Var staging=True (set by user).
2025-05-01 13:32:32,856:DEBUG:certbot._internal.cli:Var server={'staging', 'dry_run'} (set by user).
2025-05-01 13:32:32,856:DEBUG:certbot._internal.cli:Var account={'server'} (set by user).
2025-05-01 13:32:32,856:DEBUG:certbot._internal.cli:Var staging=True (set by user).
2025-05-01 13:32:32,856:DEBUG:certbot._internal.cli:Var server={'staging', 'dry_run'} (set by user).
2025-05-01 13:32:32,856:DEBUG:certbot._internal.cli:Var authenticator=webroot (set by user).
2025-05-01 13:32:32,859:DEBUG:certbot._internal.cli:Var webroot_path=/var/www/certbot (set by user).
2025-05-01 13:32:32,859:DEBUG:certbot._internal.cli:Var webroot_path=/var/www/certbot (set by user).
2025-05-01 13:32:32,859:DEBUG:certbot._internal.cli:Var webroot_map={'webroot_path'} (set by user).
2025-05-01 13:32:32,860:DEBUG:certbot._internal.storage:Writing new config /etc/letsencrypt/renewal/도메인.conf.
2025-05-01 13:32:32,864:DEBUG:certbot._internal.display.obj:Notifying user:
Successfully received certificate.
  • ν…ŒμŠ€νŠΈμ— μ„±κ³΅ν•˜λ©΄ Nginx μ»¨ν…Œμ΄λ„ˆ λ‚΄ /var/log/letsencrypt/letsencrypt.logμ—μ„œ 상기 둜그λ₯Ό 확인할 수 μžˆλ‹€

βœ… μΈμ¦μ„œ κ°±μ‹  ν…ŒμŠ€νŠΈ

1
certbot renew --dry-run
  • λ§Œμ•½ 만료일 30일 전에 λ„λž˜ν•˜μ§€ μ•Šμ„ 경우 아무 λ™μž‘λ„ ν•˜μ§€ μ•ŠλŠ”λ‹€.
  • 만료일 30일 μ „λΆ€ν„° ν•΄λ‹Ή λͺ…λ Ήμ–΄λ₯Ό 톡해 μΈμ¦μ„œλ₯Ό κ°±μ‹ ν•  수 μžˆλ‹€.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# docker logs nginx
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations, all simulated renewals succeeded:
Β Β /etc/letsencrypt/live/도메인/fullchain.pem (success)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# docker exec -it nginx /bin/bash
# cd /var/log/letsencrypt
# cat letsencrypt.log
2025-05-01 13:46:04,141:DEBUG:certbot._internal.cli:Var dry_run=True (set by user).
2025-05-01 13:46:04,141:DEBUG:certbot._internal.cli:Var server={'dry_run', 'staging'} (set by user).
2025-05-01 13:46:04,141:DEBUG:certbot._internal.cli:Var dry_run=True (set by user).
2025-05-01 13:46:04,141:DEBUG:certbot._internal.cli:Var server={'dry_run', 'staging'} (set by user).
2025-05-01 13:46:04,141:DEBUG:certbot._internal.cli:Var account={'server'} (set by user).
2025-05-01 13:46:04,183:INFO:certbot.ocsp:Cannot extract OCSP URI from /etc/letsencrypt/archive/도메인/cert1.pem
2025-05-01 13:46:04,187:INFO:certbot._internal.renewal:Certificate not due for renewal, but simulating renewal for dry run
2025-05-01 13:46:04,187:INFO:certbot._internal.renewal:Non-interactive renewal: random delay of 95.38242860717801 seconds
  • ν…ŒμŠ€νŠΈμ— μ„±κ³΅ν•˜λ©΄ 둜그λ₯Ό 톡해 성곡 μ—¬λΆ€λ₯Ό 확인할 수 μžˆλ‹€.

βœ… 횟수 μ œν•œμ— κ±Έλ €μžˆλŠ”λ° κΈ‰ν•˜κ²Œ μΈμ¦μ„œκ°€ ν•„μš”ν•˜λ‹€λ©΄?

1
certbot certonly --webroot --webroot-path=/var/www/certbot --email ${CERTBOT_EMAIL} --agree-tos --no-eff-email -d μ„œλΈŒλ„λ©”μΈA -d μ„œλΈŒλ„λ©”μΈB
  • μΈμ¦μ„œ λ°œκΈ‰κ³Ό κ°±μ‹  ν”„λ‘œμ„ΈμŠ€μ—λŠ” λ¬Έμ œκ°€ μ—†κ³  횟수 μ œν•œμ— κ±Έλ € μžˆλŠ”λ°, κΈ‰ν•˜κ²Œ ν•΄λ‹Ή μ„œλ²„λ₯Ό μ΄μš©ν•΄μ•Ό ν•˜λŠ” 상황이라면 λ‹€μŒκ³Ό 같이 μ§„ν–‰ν•  수 μžˆλ‹€.
    1. κ°€λΉ„μ•„ λ“± 도메인 μ„œλΉ„μŠ€μ—μ„œ μ„œλΈŒ 도메인을 μΆ”κ°€ν•˜μ—¬ 동일 IP둜 μ„€μ •
    2. μΈμ¦μ„œ λ°œκΈ‰ μ‹œ μœ„μ˜ λͺ…λ Ήμ–΄ μ‹€ν–‰

회고

  • ν…ŒμŠ€νŠΈλŠ” μ΅œλŒ€ν•œ μ‹€μ œ 운영 ν™˜κ²½μ— κ°€κΉκ²Œ ν•˜μžλŠ” μ£Όμ˜μ—¬μ„œ 쑰금 μ°μ°ν•˜μ§€λ§Œ ChatGPTλŠ” 두 ν…ŒμŠ€νŠΈ λͺ…λ Ήμ–΄λ₯Ό ν†΅κ³Όν•˜λ©΄ μ‹€μ œ 운영 ν™˜κ²½μ—μ„œλ„ μ„±κ³΅μ μœΌλ‘œ μΈμ¦μ„œ λ°œκΈ‰ 및 갱신이 κ°€λŠ₯ν•  것이라고 ν•œλ‹€.
This post is licensed under CC BY 4.0 by the author.