{"id":"IL-0173","org":"Devsisters","date":"2024-02-13","docs":[],"title":{"en":"cert-manager's preferredChain logic picked the Let's Encrypt chain through the expired DST Root CA X3, so clients built on an old BoringSSL could not connect to a development server","ko":"Let's Encrypt 가 기본 체인을 바꾼 뒤 cert-manager 의 preferredChain 처리 버그로 만료된 DST Root CA X3 체인 인증서가 발급돼, 구버전 BoringSSL 을 쓰는 클라이언트가 개발 서버에 접속하지 못했다."},"domain":["kubernetes","security"],"impact":{"scope":"Development server reached by clients using a library built on an old BoringSSL; no service impact","users":"unknown","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"A latent bug can stay hidden until an upstream provider changes a default. cert-manager's preferredChain matching gave the intended result while DST Root CA X3 was Let's Encrypt's default chain and the wrong one after the default changed.\n\nAn expired root at the top of a served chain is harmless to most TLS clients, which fall back to the trusted ISRG Root X1 below it, but fatal to an implementation such as an old BoringSSL that checks the trust anchor's validity and insists on verifying the topmost certificate. ACME clients choose the preferred chain with different heuristics, so the same preferredChain value can behave differently between Certbot and cert-manager.","ko":"오래 숨어 있던 버그가 외부 서비스의 기본값 변경으로 갑자기 드러날 수 있다. 이번에는 Let's Encrypt 가 기본 체인을 바꾸면서 cert-manager 의 체인 선택 버그가 드러났다. 여기에 신뢰 앵커의 만료일을 검사하고 최상위 인증서를 무조건 검증하려는 구버전 BoringSSL 을 쓰고 있어서 접속 장애로 이어졌다. ACME 클라이언트마다 체인을 고르는 방식이 달라서, 설정한 preferredChain 과 실제로 발급된 체인이 다를 수 있다."},"related":[],"sources":[{"url":"https://tech.devsisters.com/posts/finding-and-fixing-cert-manager-bug","kind":"blog","license":"unknown","archived":null}],"summary":{"en":"Devsisters encrypts traffic in its Kubernetes environment with Let's Encrypt certificates issued through cert-manager. A Let's Encrypt RSA certificate could chain two ways: DST Root CA X3 to ISRG Root X1 to Let's Encrypt R3, or ISRG Root X1 to Let's Encrypt R3 directly. The DST Root CA X3 cross-sign, valid until 30 September 2024, was kept so that older Android devices, which do not trust ISRG Root X1 but also do not check the expiry of trust anchors, could still connect. Because the old BoringSSL in one of its libraries rejected that chain, Devsisters had set cert-manager's preferredChain to ISRG Root X1.\n\nOn 8 February 2024 Let's Encrypt swapped the two: the ISRG Root X1 chain became the default and the DST Root CA X3 chain the alternate, which an ACME server offers through a Link header (RFC 8555, section 7.4.2). Two design choices in cert-manager's chain selection then picked the wrong chain. It searches only the alternate chains, never the default one, and it compares the requested name with the issuer common name of every certificate in a chain rather than only the one closest to the root. The alternate DST Root CA X3 chain contains a certificate issued by ISRG Root X1, so preferredChain: ISRG Root X1 matched it and cert-manager served the chain through the expired root. Certbot, by comparison, checks only the issuer of the last certificate in each chain.\n\nThe engineer verified the analysis against the ACME specification, Certbot's debug log of a real exchange with the Let's Encrypt ACME server, and the code of both clients. Nothing had yet been reported in the Let's Encrypt community forum or the cert-manager repository, so the engineer sent a pull request to cert-manager, which was merged; the maintainers took care over users who might depend on the old behaviour. The same issue was reported to cert-manager a day after the pull request and shared in the Let's Encrypt community about five days later. Because the problem showed up in a development environment, it had no service impact.","ko":"Let's Encrypt 의 RSA 인증서에는 신뢰 체인이 두 가지 있다. 하나는 DST Root CA X3 이 ISRG Root X1 을 교차 서명한 체인이고, 다른 하나는 ISRG Root X1 에서 바로 시작하는 체인이다. 교차 서명 체인은 ISRG Root X1 을 신뢰하지 않는 안드로이드 7.1 미만 기기를 위해 남겨 둔 것이고, 2024년 9월 30일까지 유효하다. 신뢰 앵커는 체인을 검증할 때 출발점으로 삼는 루트의 공개 키와 그 신원이다. 대부분의 구현체는 신뢰 앵커의 만료일을 검사하지 않아 이 체인을 받아도 ISRG Root X1 까지만 검증해 문제없이 동작하지만, 만료일을 검사하는 구버전 BoringSSL 은 실패한다. 그래서 데브시스터즈는 cert-manager 에 preferredChain: ISRG Root X1 을 지정해 두었다.\n\nLet's Encrypt 는 2월 8일 기본 체인을 DST Root CA X3 체인에서 ISRG Root X1 체인으로 바꾸고, DST Root CA X3 체인을 대체 체인으로 돌렸다. ACME 는 Let's Encrypt 같은 자동 인증서 발급 서비스가 쓰는 API 이고 RFC 8555 에 정의돼 있다. ACME 서버는 기본 체인과 함께 Link 헤더로 대체 체인을 알려 준다. 어느 체인을 쓸지는 클라이언트가 고른다. Certbot 디버그 로그로 확인해 보니 Let's Encrypt 는 의도대로 두 체인을 내주고 있었다.\n\n문제는 cert-manager 의 체인 선택 로직이었다. cert-manager 는 preferredChain 을 찾을 때 기본 체인을 아예 후보에서 빼고, 대체 체인 안의 모든 인증서에서 발급자 CN 을 꺼내 설정값과 비교했다. 2월 8일 이전에는 기본 체인이 DST Root CA X3 체인이어서 이 동작이 문제를 일으키지 않았다. 이제는 원하는 ISRG Root X1 체인이 기본 체인이라 후보에서 빠졌고, DST Root CA X3 체인 안에도 발급자가 ISRG Root X1 인 인증서가 있어 이 체인이 선택됐다. 반면 Certbot 은 체인마다 루트에 가장 가까운 인증서의 발급자 CN 만 비교한다.\n\n담당 엔지니어는 Let's Encrypt 커뮤니티 포럼과 cert-manager 저장소 어디에도 보고된 사례가 없자 바로 cert-manager 에 PR 을 올렸고, PR 은 머지됐다. 다음 날 같은 이슈가 cert-manager 저장소에 올라왔고, 약 5일 뒤 Let's Encrypt 커뮤니티에도 사례가 공유됐다. 데브시스터즈는 개발 환경에서 문제를 발견해 서비스 영향은 없었다."},"trigger":{"en":"On 8 February 2024, in the first phase of retiring the DST Root CA X3 cross-sign, Let's Encrypt made the ISRG Root X1 chain its default chain and began offering the DST Root CA X3 chain as the alternate.","ko":"Let's Encrypt 가 DST Root CA X3 단계적 폐기의 첫 단계로 2024년 2월 8일부터 기본 체인을 ISRG Root X1 체인으로 바꾸고, DST Root CA X3 체인을 대체 체인으로 내주기 시작했다."},"severity":"unknown","symptoms":["tls-certificate-errors"],"timeline":{"detected_after":null,"resolved_after":null},"date_kind":"incident","detection":{"en":"On 13 February, after the Lunar New Year holiday, a request came in to the infrastructure cell that the development server could not be reached; while inspecting the certificate chain the server presented, the team found it serving the chain through the expired DST Root CA X3.","ko":"설 연휴 뒤인 2월 13일 개발 서버에 접속이 안 된다는 요청이 인프라셀에 들어왔다. 서버가 내주는 인증서 체인을 열어 보다가 만료된 DST Root CA X3 체인이 서빙되는 것을 발견했다."},"components":["cert-manager"],"root_cause":"software-bug","remediation":[{"text":{"en":"submitted a pull request fixing cert-manager's preferred chain selection, which was merged","ko":"cert-manager 의 preferredChain 선택 로직을 고치는 PR 을 올렸고, PR 은 머지됐다."}}],"action_items":[],"contributing":[],"recurrence_of":null,"symptoms_text":{"en":"On 13 February 2024, when Devsisters' infrastructure cell came back from the Lunar New Year holiday, it received a request to look into a development server that could not be reached. No one had made a manual change over the holiday.\n\nInspecting the certificate chain the server presented, the team found it was serving a chain anchored at DST Root CA X3, a root certificate that had expired at the end of September 2021. cert-manager had been configured with preferredChain: ISRG Root X1 precisely to avoid that chain, because a library the team used was built on an old version of BoringSSL. With its default settings, that BoringSSL fails to build a trust chain whenever it receives the chain through DST Root CA X3 and DST Root CA X3 is not valid.\n\nTests showed that the chain cert-manager returned depended on the preferredChain value. With no preferredChain set, it returned the ISRG Root X1 chain. With preferredChain: DST Root CA X3, it returned the DST Root CA X3 chain, as expected. With preferredChain: ISRG Root X1, it also returned the DST Root CA X3 chain.","ko":"2024년 2월 13일, 설 연휴를 마치고 돌아온 데브시스터즈 인프라셀에 개발 서버에 접속이 안 된다는 요청이 들어왔다. 연휴 직후라 사람이 손댄 변경은 없었다.\n\n서버가 내주는 인증서 체인을 확인하니, 2021년 9월 말에 만료된 DST Root CA X3 이 서명한 체인이 서빙되고 있었다. 인프라셀은 예전에 겪은 접속 불가 문제 때문에 cert-manager 에 preferredChain: ISRG Root X1 을 지정해 이 체인을 쓰지 않도록 해 두었다.\n\n구버전 BoringSSL 은 기본 설정에서 DST Root CA X3 으로 시작하는 체인을 받으면, DST Root CA X3 이 유효하지 않을 때 신뢰 체인 구성에 무조건 실패한다. 데브시스터즈가 쓰던 라이브러리가 구버전 BoringSSL 을 쓰고 있어 접속이 되지 않았다.\n\n테스트해 보니 preferredChain 값에 따라 동작이 갈렸다. 값을 지정하지 않으면 ISRG Root X1 체인이 나왔고, DST Root CA X3 으로 지정하면 DST Root CA X3 체인이 나왔다. ISRG Root X1 으로 지정하면 오히려 DST Root CA X3 체인이 나왔다."},"updated_at":"2026-11-09T11:55:55.551751Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0173","org_slug":"devsisters"}