리졸버를 갈아끼웠습니다 — 순단은 3초, 바뀐 것은 인증서 갱신 경로 전부
와일드카드 인증서를 실제로 발급했습니다. 고친 것은 설정 세 줄이고 서비스가 끊긴 시간은 3초인데, 그 3초는 인증서를 받느라 든 시간이 아니라 프록시를 다시 띄우느라 든 시간이었습니다. 그리고 그 세 줄은 리졸버 하나에 붙는 설정이라, 제가 바꾸려던 것보다 넓게 적용됐습니다.
앞의 세 글에서 와일드카드 인증서를 DNS-01로 받는 방법과 그것이 못 덮는 영역, 그리고 주소를 주기 전에 목적을 먼저 정해야 하는 이유를 정리했습니다. 세 글 모두 설계 단계에서 쓴 것이고, 마지막 글은 조건 하나를 확정하지 못한 채 끝났습니다. 3주가 지나 그 조건이 확정됐고, 그래서 실제로 발급했습니다. 이 글은 그 실행 기록입니다.
3편이 미뤄둔 조건이 확정됐습니다 — 서브도메인만, 커스텀 도메인은 없음
지난 글의 결론은 와일드카드 DNS-01이 정답인 구간은 "서브도메인은 필요한데 커스텀 도메인은 로드맵에 없다" 하나뿐이라는 것이었습니다. 다만 그 조건이 참인지는 그때 확인하지 못했습니다. 로드맵에 무엇이 들어 있는지는 제가 정하는 일이 아니었기 때문입니다.
이번에 확인을 받았습니다. 고객사는 자기 도메인을 쓰지 않고, 전부 acme.example.com 형태로 들어옵니다. 3편이 정의한 그 유일한 구간에 실제로 들어온 셈입니다.
이 확인 한 문장이 결정의 전부였습니다. 2편에서 on-demand TLS를 꽤 길게 비교했는데, 그 방식의 가장 큰 장점은 고객 소유 도메인을 서브도메인과 같은 메커니즘으로 처리한다는 것이었습니다. 고객 소유 도메인이 없다면 그 장점이 통째로 사라집니다. 비교표를 다시 열 필요도 없었습니다. 표의 한 열이 아예 필요 없어졌기 때문입니다.
계획서를 어겼습니다 — 리졸버를 갈아끼웠습니다
1편에서 저는 기존 리졸버를 그대로 두고 letsencryptdns를 하나 더 만들겠다고 적었습니다. 이유도 함께 적어뒀습니다. 기존 리졸버를 DNS-01로 갈아끼우면 지금 서비스 중인 인증서들의 갱신 경로가 함께 바뀐다는 것이었습니다.
실행에서는 그렇게 하지 않았습니다. 기존 리졸버에서 HTTP 검증 줄을 빼고 DNS 검증으로 바꿨습니다. 계획서를 쓴 사람과 실행한 사람이 같은데도 그렇게 됐고, 계획서가 경고한 그 부작용은 발급이 끝난 뒤에야 제 눈에 들어왔습니다. 그 이야기는 이 글의 뒤에서 다시 하겠습니다.
- '--certificatesresolvers.letsencrypt.acme.dnschallenge=true'
- '--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=route53'
- '--certificatesresolvers.letsencrypt.acme.dnschallenge.resolvers=1.1.1.1:53,8.8.8.8:53'
첫 줄이 검증 방식을 DNS로 바꾸고, 둘째 줄이 TXT 레코드를 실제로 심을 프로바이더를 지정하고, 셋째 줄이 전파를 확인할 DNS 서버를 지정합니다. 와일드카드가 HTTP 검증으로는 발급되지 않는 이유는 1편에서 다뤘으니 여기서는 넘어가겠습니다.
인증서를 받으려면 «구실»이 필요했습니다
설정에 리졸버를 붙였다고 인증서가 나오지는 않았습니다. 프록시는 "이 호스트를 서빙한다"고 선언한 라우터가 있어야 그 이름으로 인증서를 신청합니다. 발급을 요청하는 주체는 설정이 아니라 라우터입니다.
그래서 임의의 하위 주소를 받는 라우터를 하나 두고, 거기에 와일드카드를 요청하게 했습니다. 우선순위는 일부러 낮췄습니다. 기존 주소들의 명시적인 라우터가 먼저 잡아야 하기 때문입니다. 이 선택이 나중에 두 가지를 설명해줍니다. 하나는 기존 인증서들이 왜 그대로 남았는가이고, 다른 하나는 다음 글의 503입니다.
순단 3초는 발급이 아니라 Traefik 재시작에서 나왔습니다
서두에 3초를 먼저 적었으니 그 3초의 출처를 이어 적겠습니다. 검증 방식은 Traefik이 기동할 때 한 번 읽는 값이라, 파일만 고쳐두면 아무 일도 일어나지 않습니다. 반영시키려면 프록시를 다시 띄워야 하고, 그 사이가 3초였습니다. 뒤집어 말하면 이 설정을 만지는 작업에는 순단이 반드시 따라옵니다. 얼마나 줄이느냐의 문제이지 안 낼 수 있는 성질의 것이 아니었습니다.
인증서 비용은 0원이었습니다. Let's Encrypt이니 당연한 이야기인데, 이 0원이 말해주는 것은 이 작업에서 치른 값이 전부 인증서 바깥에 있었다는 사실입니다. 3편에서 "진짜 비용은 TLS가 아니었다"고 썼던 것이 실행에서도 그대로였습니다.
재시작을 부르는 설정과 안 부르는 설정
같은 프록시의 설정인데 반영되는 방식이 다릅니다. 이 차이를 모르면 증상을 양쪽으로 겪게 됩니다.
| 설정 | 반영되는 방식 | 잘못 알면 생기는 일 |
|---|---|---|
| 검증 방식·프로바이더·리졸버 | 프록시를 다시 띄워야 읽습니다 | 파일만 고쳐두고 "왜 안 바뀌지"로 시간을 씁니다 |
| 라우터·미들웨어 규칙 | 파일이 바뀌면 프록시가 다시 읽습니다 | 굳이 재시작해서 순단을 한 번 더 냅니다 |
Traefik은 이 둘을 각각 static configuration과 dynamic configuration이라고 부릅니다. 이름을 알고 나면 문서에서 어느 쪽인지 바로 확인할 수 있고, 그 확인 하나가 "설정을 바꿨는데 왜 안 되지"와 "왜 또 끊겼지"를 갈라줍니다. 실무라고 부를 만한 것이 있다면 이런 것이라는 생각이 들었습니다.
TXT가 안 보인다고 발급이 안 된 것은 아니었습니다
DNS 검증이 도는 동안 _acme-challenge TXT 레코드가 보이는지 확인했습니다. 아무것도 없었습니다. 그래서 요청이 애초에 나가지 않았다고 단정할 뻔했습니다.
실제로는 검증이 끝나면 그 레코드를 바로 지웁니다. 제가 본 "아무것도 없음"은 실패가 아니라 이미 끝났다는 뜻일 수 있었습니다. 조금 더 기다렸다가 인증서 저장소를 열어보면 될 일이었습니다.
돌이켜 생각해보면 이건 DNS-01만의 함정이 아닙니다. 검증 수단의 흔적이 안 보이는 것과 검증이 일어나지 않은 것은 다릅니다. 증거의 부재는 부재의 증거가 아니라는 말이 그대로 적용되는 자리입니다. 수명이 짧은 중간 산물은 전부 이 성질을 갖습니다. 확인할 대상을 중간 산물이 아니라 최종 산물로 잡았어야 했습니다.
발급은 «추가»였고, 갱신은 «전부»였습니다
기존 인증서는 그대로 자기 것을 씁니다
발급이 끝나고 기존 주소들을 다시 재봤습니다. 전부 자기 개별 인증서를 그대로 쓰고 있었습니다. 저장소를 열어보니 인증서가 5장이었고 와일드카드는 그중 다섯 번째였습니다. 앞의 네 장이 사라지지 않고 그대로 있다는 것이 곧 "와일드카드가 기존 것을 대체하지 않는다"의 증거였습니다.
이유는 앞에서 둔 그 우선순위 낮은 라우터입니다. 명시적인 라우터가 있는 호스트는 자기 이름으로 받은 인증서를 쓰고, 와일드카드가 쓰이는 것은 아무 라우터도 잡지 않는 주소가 들어올 때뿐입니다.
실은 다행이었습니다. 기존 주소들의 동작이 하나도 바뀌지 않았다는 뜻이니까요. 다만 다행이라는 말을 쓰는 게 맞나 싶기도 합니다. 저는 와일드카드가 기존 것을 덮어쓸 수도 있다고 가정한 채 작업하고 있었고, 그렇게 가정하면 필요 이상으로 긴 점검 창을 잡게 됩니다. 예상과 실제가 어긋난 방향이 덜 위험한 쪽이었을 뿐, 확인하지 않고 가정한 것은 마찬가지였습니다.
그런데 인증서 갱신 경로는 함께 바뀝니다
앞 절과 나란히 놓으면 이상하게 들립니다. 발급은 추가인데 갱신은 전부라니요. 갈라지는 지점은 저장 단위였습니다. 인증서는 호스트 단위로 저장되고, 검증 방식은 리졸버 단위로 저장됩니다. 그래서 리졸버의 검증 방식을 바꾸는 순간 그 리졸버로 받은 인증서 갱신 경로가 전부 새 방식으로 갑니다. 와일드카드만 DNS로 가고 나머지는 HTTP로 남지 않습니다.
와일드카드가 DNS 검증으로 실제 발급됐으니 IAM 권한과 프로바이더 배선은 한 번 통과된 셈입니다. 다만 기존 인증서의 첫 갱신을 지켜본 것은 아닙니다. 갱신은 만료 한참 전에 알아서 일어나므로, 제가 확인한 것은 "새 경로로 한 장을 받았다"까지이고 "옛 장들이 새 경로로 갱신된다"는 아직 관측하지 않았습니다. 만료 한 달 전쯤 확인할 일로 남겨뒀습니다.
1편에서 기존 리졸버를 건드리지 않겠다고 적어둔 이유가 정확히 이것이었습니다. 그 문장을 쓴 것도 저이고 어긴 것도 저인데, 어기는 순간에는 그 문장이 떠오르지 않았습니다. 계획서의 경고는 실행하는 사람이 다시 읽을 때에만 경고입니다. 적어두는 것과 지키는 것 사이에 3주가 있었습니다.
정리하면
손이 간 것은 설정 세 줄이고 잰 값은 3초인데, 이 작업이 남긴 가장 큰 것은 "한 달 뒤에 확인할 일"이었습니다. 배운 것을 한 줄로 줄이면 설정 파일 중 무엇이 재시작을 부르고 무엇이 안 부르는지를 아는 것이 실무고, 리졸버 단위 설정은 내가 바꾼 것보다 넓게 적용된다는 것입니다.
그리고 인증서는 나왔고 자물쇠도 잠깁니다. 그런데 그 주소를 브라우저에 쳐보면 503이 뜹니다. 인증서가 나온 다음에 무엇이 남아 있었는지는 다음 글에서 이어 쓰겠습니다.
관련 글
와일드카드 인증서는 HTTP-01으로 못 받습니다 — DNS-01이 강제하는 IAM 결정
멀티테넌트 서브도메인을 위해 와일드카드 인증서를 발급하려다 알게 된 것은, 문제가 TLS가 아니라 DNS 쓰기 권한이라는 점이었습니다. 프록시에 Route53 권한을 주는 순간 생기는 폭발 반경과 그것을 좁히는 방법을 정리했습니다.
와일드카드가 못 덮는 것 — Traefik DNS-01과 Caddy on-demand TLS를 가른 지점
와일드카드 인증서를 손에 넣고 나서야 그것이 한 단계밖에 못 덮는다는 사실이 문제가 됐습니다. 고객사 소유 도메인을 받으려면 애초에 다른 메커니즘이 필요했고, 그 판단이 프록시 선택 자체를 다시 보게 만들었습니다.
무중단 서버 이전: DNS 컷오버와 Let's Encrypt 타이밍 함정
이미지를 옮기고 DNS만 바꾸면 끝일 줄 알았는데, Let's Encrypt 인증서가 발급되지 않았습니다. 무중단 이전의 설계와, TTL을 미리 낮추지 않아 인증서 발급이 지연된 함정을 로그와 함께 정리했습니다.