| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | ||
| 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 |
| 27 | 28 | 29 | 30 |
Tags
- DataGridView
- design pattern
- 마이바티스
- mybatis
- Winform
- VOA
- Kotlin
- docker
- AWS
- kubernetes
- 코틀린
- Spring
- Python
- c#
- machine-learning
- 리액트
- 쿠버네티스
- Java
- 스프링
- springboot
- 파이썬
- github
- 도커
- 리팩토링
- 자바
- MySQL
- git
- 스프링부트
- Spring Boot
- react
Archives
- Today
- Total
보뇨 다이어리
사설 인증서 로컬 신뢰 처리 본문
반응형
사설 인증서 로컬 신뢰 처리 (macOS)
Chrome에서 NET::ERR_CERT_AUTHORITY_INVALID 가 뜨는 내부 클러스터 도메인(*.idc1.ten1010.io 등)을 로컬에서 신뢰시키는 절차. 클러스터 인증서가 재발급될 때마다 반복한다.
아래에서 HOST 는 대상 도메인(예: aipub.cluster3.idc1.ten1010.io)이다.
export HOST=example.com
export KC=~/Library/Keychains/login.keychain-db
Phase 1. 서버가 주는 인증서 확인
echo | openssl s_client -connect $HOST:443 -servername $HOST 2>/dev/null \
| openssl x509 -noout -subject -issuer -fingerprint -sha256 -dates
확인할 것:
- subject: 보통 CN=*.clusterN.idc1.ten1010.io (와일드카드)
- issuer: CN=*.idc1.ten1010.io (사설 CA, 서버가 체인에 안 실어줌)
- notBefore: 최근 날짜면 재발급된 것
Phase 2. Keychain에 이미 있는지 비교
security find-certificate -a -c "$(echo | openssl s_client -connect $HOST:443 -servername $HOST 2>/dev/null \
| openssl x509 -noout -subject | sed 's/^subject=CN *= *//')" -p $KC \
| openssl x509 -noout -fingerprint -sha256 -dates
- 결과 없음 → 처음 신뢰하는 클러스터. Phase 3만 실행.
- 결과 있고 fingerprint가 Phase 1과 다름 → 재발급된 것. Phase 3에서 옛것 삭제 후 추가.
- fingerprint 같음 → 인증서 문제 아님. Chrome 재시작 / HSTS 캐시(Phase 5) 확인.
Phase 3. 인증서 교체 + 신뢰
# 서버 leaf 저장
echo | openssl s_client -connect $HOST:443 -servername $HOST 2>/dev/null \
| openssl x509 -outform PEM > /tmp/$HOST.pem
# (재발급인 경우만) 옛 인증서 삭제 — Phase 2에서 나온 fingerprint의 콜론 제거해서 넣는다
security delete-certificate -Z <OLD_SHA256_NO_COLON> $KC
# 새 인증서 추가 + 루트로 신뢰 (로그인 keychain, sudo 불필요, GUI 인증창 뜰 수 있음)
security add-trusted-cert -r trustAsRoot -k $KC /tmp/$HOST.pem
Phase 4. 검증
curl -sS -o /dev/null -w "HTTP %{http_code} ssl_verify=%{ssl_verify_result}\n" https://$HOST/
ssl_verify=0 이면 성공. 0이 아니면 Phase 1의 fingerprint와 keychain 것이 같은지 다시 확인.
Phase 5. Chrome 반영
- Chrome 완전 종료 후 재시작 (keychain 신뢰 캐시).
- 그래도 경고 나오면 chrome://net-internals/#hsts → Delete domain security policies 에 HOST 입력 → Delete.
- 접속 재시도.
자주 겪는 상황
증상원인조치
| 잘 되던 도메인이 갑자기 ERR_CERT_AUTHORITY_INVALID | 클러스터 cert 재발급 | Phase 1~4 (옛것 삭제 포함) |
| 새 클러스터 처음 접속 | keychain에 없음 | Phase 1, 3, 4 |
| curl은 OK인데 Chrome만 경고 | Chrome 캐시 / HSTS | Phase 5 |
| add-trusted-cert 권한 오류 | System keychain 지정함 | $KC 가 login.keychain-db 인지 확인 |
근본 해결
leaf를 클러스터별로 신뢰하는 방식은 재발급마다 반복된다. 인프라 팀에서 CA 인증서(CN=*.idc1.ten1010.io)를 받아 그것 하나만 trustAsRoot 로 넣으면 하위 클러스터 cert가 바뀌어도 다시 할 필요 없다.
사실 하다가 그냥 쉘스크립트로 만들면 좋겠다 싶어서 아래와 같이 만듦.
#!/usr/bin/env bash
# Trust a private-CA-signed TLS certificate for a host in the macOS login keychain.
#
# Usage:
# scripts/trust-cert.sh <host>[:port]
# scripts/trust-cert.sh https://aipub.cluster3.idc1.ten1010.io/
#
# Steps: fetch leaf cert -> compare with keychain -> replace stale copy -> trust as root -> verify with curl.
set -euo pipefail
KEYCHAIN="${KEYCHAIN:-$HOME/Library/Keychains/login.keychain-db}"
usage() {
echo "usage: $0 <host>[:port] | <https://url>" >&2
exit 2
}
log() { printf '\033[1;34m[%s]\033[0m %s\n' "$1" "$2"; }
ok() { printf '\033[1;32m ✔\033[0m %s\n' "$1"; }
warn() { printf '\033[1;33m !\033[0m %s\n' "$1"; }
die() { printf '\033[1;31m ✘\033[0m %s\n' "$1" >&2; exit 1; }
[[ $# -eq 1 ]] || usage
[[ "$(uname)" == "Darwin" ]] || die "macOS only (uses security(1))."
# --- normalize input: strip scheme / path, split host:port ---------------------
input="${1#*://}"
input="${input%%/*}"
HOST="${input%%:*}"
PORT="${input##*:}"
[[ "$PORT" == "$HOST" ]] && PORT=443
[[ -n "$HOST" ]] || usage
TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT
LEAF="$TMP/leaf.pem"
# --- Phase 1: fetch served certificate ---------------------------------------
log "1/5" "Fetching certificate from $HOST:$PORT"
if ! echo | openssl s_client -connect "$HOST:$PORT" -servername "$HOST" 2>/dev/null \
| openssl x509 -outform PEM > "$LEAF" 2>/dev/null || [[ ! -s "$LEAF" ]]; then
die "Could not fetch certificate from $HOST:$PORT"
fi
SUBJECT_CN="$(openssl x509 -in "$LEAF" -noout -subject | sed -E 's/^subject=.*CN *= *//; s/,.*$//')"
ISSUER="$(openssl x509 -in "$LEAF" -noout -issuer | sed -E 's/^issuer=//')"
NEW_FP="$(openssl x509 -in "$LEAF" -noout -fingerprint -sha256 | sed 's/.*=//; s/://g')"
NOT_BEFORE="$(openssl x509 -in "$LEAF" -noout -startdate | sed 's/.*=//')"
NOT_AFTER="$(openssl x509 -in "$LEAF" -noout -enddate | sed 's/.*=//')"
ok "subject CN : $SUBJECT_CN"
ok "issuer : $ISSUER"
ok "sha256 : $NEW_FP"
ok "valid : $NOT_BEFORE -> $NOT_AFTER"
# --- Phase 2: compare with keychain ------------------------------------------
log "2/5" "Checking login keychain for existing '$SUBJECT_CN'"
EXISTING_FPS="$(security find-certificate -a -c "$SUBJECT_CN" -Z "$KEYCHAIN" 2>/dev/null \
| awk '/SHA-256 hash:/ {print $3}' || true)"
ALREADY_TRUSTED=0
STALE_FPS=()
if [[ -z "$EXISTING_FPS" ]]; then
ok "not present -> first-time trust"
else
while IFS= read -r fp; do
[[ -z "$fp" ]] && continue
if [[ "$fp" == "$NEW_FP" ]]; then
ALREADY_TRUSTED=1
ok "identical certificate already in keychain"
else
STALE_FPS+=("$fp")
warn "stale certificate found: $fp"
fi
done <<< "$EXISTING_FPS"
fi
# --- Phase 3: replace + trust --------------------------------------------------
log "3/5" "Updating keychain"
for fp in "${STALE_FPS[@]:-}"; do
[[ -z "$fp" ]] && continue
security delete-certificate -Z "$fp" "$KEYCHAIN"
ok "removed stale $fp"
done
if [[ $ALREADY_TRUSTED -eq 1 ]]; then
ok "skip add (already present); re-applying trust setting"
fi
# add-trusted-cert is idempotent on the same cert; may prompt a GUI auth dialog.
security add-trusted-cert -r trustAsRoot -k "$KEYCHAIN" "$LEAF"
ok "trusted as root in $(basename "$KEYCHAIN")"
# --- Phase 4: verify -----------------------------------------------------------
log "4/5" "Verifying with curl"
RESULT="$(curl -sS -o /dev/null -w '%{http_code} %{ssl_verify_result}' --max-time 15 "https://$HOST:$PORT/" 2>&1 || true)"
HTTP_CODE="${RESULT%% *}"
SSL_VERIFY="${RESULT##* }"
if [[ "$SSL_VERIFY" == "0" ]]; then
ok "HTTP $HTTP_CODE, ssl_verify=0"
else
die "TLS verification still failing: $RESULT"
fi
# --- Phase 5: browser hints ----------------------------------------------------
log "5/5" "Done. Browser steps:"
echo " 1. Fully quit and restart Chrome (keychain trust is cached)."
echo " 2. If the warning persists (HSTS): chrome://net-internals/#hsts"
echo " -> 'Delete domain security policies' -> enter '$HOST' -> Delete."
echo
echo " Note: issuer is '$ISSUER'. Trusting that CA once would avoid repeating this per cluster."반응형
'컴퓨터 관련 > 환경 정보' 카테고리의 다른 글
| Unexpected method 'appcast' called on Cask 에러 발생시 해결 (0) | 2024.08.14 |
|---|---|
| AWS 내에 IAM 계정들 생성하기 (0) | 2023.10.13 |
| script maven 컴파일시 maven-compiler-plugin:3.8.1:compile 에러 해결 (0) | 2023.03.22 |
| VScode - Auto fix eslint on save (0) | 2021.04.18 |
| cannot start process the working directory ... 에러 (0) | 2020.11.04 |