IL-0173 Devsisters · 2024-02-13kubernetessecurity
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 을 지정해 두었다.
Let'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 는 의도대로 두 체인을 내주고 있었다.
문제는 cert-manager 의 체인 선택 로직이었다. cert-manager 는 preferredChain 을 찾을 때 기본 체인을 아예 후보에서 빼고, 대체 체인 안의 모든 인증서에서 발급자 CN 을 꺼내 설정값과 비교했다. 2월 8일 이전에는 기본 체인이 DST Root CA X3 체인이어서 이 동작이 문제를 일으키지 않았다. 이제는 원하는 ISRG Root X1 체인이 기본 체인이라 후보에서 빠졌고, DST Root CA X3 체인 안에도 발급자가 ISRG Root X1 인 인증서가 있어 이 체인이 선택됐다. 반면 Certbot 은 체인마다 루트에 가장 가까운 인증서의 발급자 CN 만 비교한다.
담당 엔지니어는 Let's Encrypt 커뮤니티 포럼과 cert-manager 저장소 어디에도 보고된 사례가 없자 바로 cert-manager 에 PR 을 올렸고, PR 은 머지됐다. 다음 날 같은 이슈가 cert-manager 저장소에 올라왔고, 약 5일 뒤 Let's Encrypt 커뮤니티에도 사례가 공유됐다. 데브시스터즈는 개발 환경에서 문제를 발견해 서비스 영향은 없었다.
2024년 2월 13일, 설 연휴를 마치고 돌아온 데브시스터즈 인프라셀에 개발 서버에 접속이 안 된다는 요청이 들어왔다. 연휴 직후라 사람이 손댄 변경은 없었다.
서버가 내주는 인증서 체인을 확인하니, 2021년 9월 말에 만료된 DST Root CA X3 이 서명한 체인이 서빙되고 있었다. 인프라셀은 예전에 겪은 접속 불가 문제 때문에 cert-manager 에 preferredChain: ISRG Root X1 을 지정해 이 체인을 쓰지 않도록 해 두었다.
구버전 BoringSSL 은 기본 설정에서 DST Root CA X3 으로 시작하는 체인을 받으면, DST Root CA X3 이 유효하지 않을 때 신뢰 체인 구성에 무조건 실패한다. 데브시스터즈가 쓰던 라이브러리가 구버전 BoringSSL 을 쓰고 있어 접속이 되지 않았다.
테스트해 보니 preferredChain 값에 따라 동작이 갈렸다. 값을 지정하지 않으면 ISRG Root X1 체인이 나왔고, DST Root CA X3 으로 지정하면 DST Root CA X3 체인이 나왔다. ISRG Root X1 으로 지정하면 오히려 DST Root CA X3 체인이 나왔다.
증상 ? TLS 인증서 오류
원인 ? 소프트웨어 결함
설 연휴 뒤인 2월 13일 개발 서버에 접속이 안 된다는 요청이 인프라셀에 들어왔다. 서버가 내주는 인증서 체인을 열어 보다가 만료된 DST Root CA X3 체인이 서빙되는 것을 발견했다.
–
오래 숨어 있던 버그가 외부 서비스의 기본값 변경으로 갑자기 드러날 수 있다. 이번에는 Let's Encrypt 가 기본 체인을 바꾸면서 cert-manager 의 체인 선택 버그가 드러났다. 여기에 신뢰 앵커의 만료일을 검사하고 최상위 인증서를 무조건 검증하려는 구버전 BoringSSL 을 쓰고 있어서 접속 장애로 이어졌다. ACME 클라이언트마다 체인을 고르는 방식이 달라서, 설정한 preferredChain 과 실제로 발급된 체인이 다를 수 있다.
–