같은 서버에 같은 인증서를 설치했는데 어떤 PC 는 경고 없이 접속되고 어떤 PC 는 ERR_CERT_AUTHORITY_INVALID · 인증서 경로를 빌드할 수 없음 · SEC_ERROR_UNKNOWN_ISSUER 가 뜬다. 모두 같은 이미지로 설치한 새 PC 인데도 결과가 갈린다.
클라이언트는 서버 인증서 → 중간 CA → 루트 CA 를 모두 이어야 신뢰한다고 판단한다. 서버가 보내야 하는 것은 서버 인증서와 중간 CA 이며, 루트는 클라이언트가 이미 가지고 있어야 하는 쪽이다.
서버가 서버 인증서만 내려주면 클라이언트는 중간 CA 를 스스로 구해야 한다. 이때 결과가 PC 마다 갈린다.
즉 "되는 PC 는 운 좋게 중간 CA 를 가지고 있는 PC" 이고, 근본 원인은 클라이언트가 아니라 서버 설정이다.
서버 쪽에서 몇 장을 내려주는지 센다.
openssl s_client -connect <서버>:443 -servername <서버> -showcerts </dev/null 2>/dev/null \
| grep -c 'BEGIN CERTIFICATE'
1 : 서버 인증서만. 이 문서의 상황이다.2 이상 : 중간 CA 를 함께 보내고 있다. 그래도 오류가 나면 클라이언트 신뢰 저장소나 유효기간 · 이름 문제를 본다.쿠버네티스 Ingress 라면 시크릿을 열어 본다.
kubectl get secret <secret> -n <ns> -o jsonpath='{.data.tls\.crt}' | base64 -d > /tmp/tls.crt
grep -c 'BEGIN CERTIFICATE' /tmp/tls.crt
openssl x509 -in /tmp/tls.crt -noout -issuer -subject
openssl x509 -in /tmp/tls.crt -noout -text | egrep -A2 'Authority Information Access|CRL Distribution'
kubernetes.io/tls 시크릿에서 클라이언트에게 전달되는 것은 tls.crt 의 내용 그대로다. 같은 시크릿에 ca.crt 가 들어 있어도 그것은 클라이언트 인증서 검증용이며 서버가 내려주는 체인에 자동으로 붙지 않는다. 이 오해가 "ca.crt 가 있는데 왜 체인이 안 되냐"의 정체다.
Windows 클라이언트에서 실패 원인을 직접 보려면 이벤트 뷰어의 응용 프로그램 및 서비스 로그 → Microsoft → Windows → CAPI2 → Operational 로그를 켜고 재현한다. A certificate chain could not be built, AIA retrieval failed, The revocation function was unable to check revocation 중 무엇인지에 따라 조치가 갈린다.
# 순서: 서버 인증서 -> 중간 CA. 루트는 보통 넣지 않는다
cat server.crt intermediate.crt > fullchain.crt
kubectl delete secret <secret> -n <ns>
kubectl create secret tls <secret> --cert=fullchain.crt --key=server.key -n <ns>
kubectl -n ingress-nginx rollout restart deployment ingress-nginx-controller
nginx 는 ssl_certificate 에 fullchain 파일을, Apache 는 SSLCertificateFile 에 fullchain 을 지정한다(예전 SSLCertificateChainFile 은 2.4.8 이후 필요 없다).
중간 CA 를 새로 만들 필요는 없다. 사내 PKI 에 이미 존재하므로 인증서 발급 담당에게 "기존 중간 CA 를 포함한 체인 파일"을 요청하면 된다. 아래 문구를 그대로 쓰면 의사소통이 빠르다.
현재 서버 인증서가 단일 파일로만 구성되어 있어, 중간 CA 를 가지고 있지 않은
클라이언트에서 인증서 체인 오류가 발생합니다.
서버 인증서와 중간 CA 를 포함한 체인(fullchain) 파일을 제공해 주시기 바랍니다.
서버를 고칠 권한이 없으면 클라이언트에 넣는다. Windows 는 반드시 로컬 컴퓨터 저장소(certlm.msc)에 넣는다. 사용자 저장소(certmgr.msc)에만 넣으면 브라우저는 되는데 서비스 계정으로 도는 프로그램이나 Java · .NET 클라이언트는 여전히 실패한다.
| 인증서 | 넣을 위치 |
|---|---|
| 루트 CA | 신뢰할 수 있는 루트 인증 기관 |
| 중간 CA | 중간 인증 기관 |
이 방법은 새 PC 가 들어올 때마다 반복해야 하므로 근본 해결이 아니다.
Not Before 이전이거나 Not After 이후면 같은 증상처럼 보인다. w32tm /query /status 로 동기화 상태를 본다.ERR_CERT_COMMON_NAME_INVALID 는 체인이 아니라 SAN 문제다. 접속한 이름이 인증서의 SAN 에 없다.NET::ERR_CERT_DATE_INVALID 같은 오류가 HSTS 가 적용된 사이트에서 나면 브라우저가 "무시하고 계속" 을 아예 제공하지 않는다. HSTS 는 그 호스트에 대해 반드시 유효한 HTTPS 만 쓰라고 못 박는 정책이므로, 브라우저가 사용자 예외를 허용하지 않는 것이 의도된 동작이다.
Strict-Transport-Security 헤더를 보내면 브라우저가 지정된 기간 동안 기억한다.chrome://net-internals/#hsts 의 delete domain 항목이다.