Skip to Content
Dev Environment & Toolsmkcert로 로컬 HTTPS와 런타임 인증서 설정 맞추기
🛠️ Dev Environment & Tools2023년 1월 12일

mkcert로 로컬 HTTPS와 런타임 인증서 설정 맞추기

#dev-environment-tools#https#mkcert#zero-trust#local-development#proxy

로컬 개발 서버를 HTTP로 실행하는 것만으로는 사내 로그인 흐름을 테스트하기 어려웠습니다. 당시 환경은 HTTPS와 도메인 기반 SSO를 전제로 했고, 로컬 서비스와 개발 서버를 함께 연결해야 하는 경우도 있었습니다.

그래서 로컬에서도 테스트용 도메인과 HTTPS를 사용했습니다. mkcert로 인증서를 만들고, 브라우저뿐 아니라 백엔드와 빌드 도구가 필요한 CA를 신뢰하도록 설정했습니다.

HTTPS와 도메인이 필요했던 이유

SSO의 허용 리디렉션 주소와 쿠키 설정은 애플리케이션 코드 외부의 조건이기도 합니다. 화면만 로컬에 띄워도 로그인 이후 돌아올 주소가 맞지 않으면 전체 흐름을 확인할 수 없었습니다.

HTTPS를 맞춘 뒤에도 도메인과 쿠키 범위, CORS와 SameSite 설정은 각각 확인해야 합니다. 인증서 하나로 이 조건이 모두 해결되는 것은 아닙니다. 로컬 HTTP에 별도 우회 설정을 더하기보다 실제 테스트에 필요한 주소와 HTTPS 조건을 먼저 맞췄습니다.

로컬 CA와 인증서 생성

mkcert는 개발용 CA를 만들고 그 CA로 서버 인증서를 발급합니다. macOS에서는 Homebrew로 설치했습니다.

bash
brew install mkcert mkcert -install

다음은 테스트 도메인으로 app.local.test를 사용하는 예시입니다. 사내 SSO와 연결할 때는 실제로 허용된 테스트 주소를 사용해야 합니다.

bash
mkcert -cert-file app.local.test.pem -key-file app.local.test-key.pem app.local.test

도메인이 로컬 장비를 가리키도록 /etc/hosts에도 항목을 추가합니다.

text
/etc/hosts에 추가
127.0.0.1 app.local.test

발급한 서버 인증서와 개인 키는 개발 서버나 프록시의 HTTPS 설정에 연결합니다. mkcert가 인증서를 만든다고 서버 설정까지 자동으로 바꾸지는 않습니다.

브라우저와 런타임의 신뢰 저장소

브라우저에서 열리는 주소를 Java에서 호출하면 PKIX path building failed 오류가 날 수 있습니다. 같은 서버에 접속하더라도 각 프로그램이 참조하는 인증서 저장소가 다를 수 있기 때문입니다.

mkcert는 시스템 저장소뿐 아니라 조건에 따라 Firefox와 Java 저장소도 지원합니다. Java는 JAVA_HOME 설정을 확인해야 하고, Firefox는 필요한 NSS 도구가 설치돼 있어야 합니다. 실제 애플리케이션이 다른 JDK나 별도 trust store를 사용한다면 그 경로도 확인해야 합니다.

로컬 CA 인증서의 위치는 고정된 경로를 가정하지 않고 다음 명령으로 찾을 수 있습니다.

bash
mkcert -CAROOT

여기에 있는 rootCA.pem은 신뢰 저장소에 등록할 CA 인증서입니다. rootCA-key.pem은 인증서를 발급하는 개인 키이므로 프로젝트나 공용 설정 파일에 넣어 공유하면 안 됩니다.

사내 CA와 로컬 CA 설정 자동화

당시에는 로컬 CA 외에 사내 Zero Trust 환경에서 필요한 인증서도 있었습니다. 개발 서버에 연결하는 Node·Python 도구와 Java 애플리케이션, Gradle에서도 이 인증서를 읽어야 했습니다.

매번 수동으로 설정하기보다 macOS와 각 런타임에서 필요한 경로를 맞추는 셸 스크립트를 만들었습니다. 새 장비나 런타임 변경 후 다시 설정할 수 있도록 로컬 CA와 사내 CA를 모으고, 대상별 신뢰 저장소에 반영하는 방식이었습니다.

다만 모든 런타임이 같은 환경 변수를 읽는 것은 아닙니다. Node의 NODE_EXTRA_CA_CERTS, Python 라이브러리의 CA 번들, Java의 trust store를 각각 확인해야 했습니다. 사내 CA가 들어 있는 번들을 로컬 CA 하나로 덮어쓰지 않는 것도 필요했습니다.

JDK 외부의 Java Trust Store

JDK 내부의 cacerts에만 인증서를 추가하면 JDK를 바꿀 때 설정을 다시 해야 했습니다. 그래서 별도의 trust store를 두고 Java 프로세스가 그 파일을 사용하도록 환경 변수를 설정했습니다.

다음은 trust store 파일을 이미 준비한 뒤 사용할 수 있는 설정 예시입니다. 기존 JAVA_TOOL_OPTIONS 값은 유지하고 경로를 추가합니다.

bash
sample/java-trust-store.sh
export JAVA_TOOL_OPTIONS="${JAVA_TOOL_OPTIONS:+$JAVA_TOOL_OPTIONS }-Djavax.net.ssl.trustStore=\"$HOME/.config/dev-certs/java-cacerts\""

기본 JSSE 설정을 사용하는 Java 도구는 이 시스템 속성으로 trust store 경로를 지정할 수 있습니다. IDE나 이미 실행 중인 Gradle daemon에는 변경된 실행 환경을 다시 적용해야 할 수 있습니다.

별도 파일을 사용하면 JDK 경로와 인증서 설정을 분리할 수 있습니다. 대신 기본 공개 CA가 빠진 빈 저장소를 사용하지 않도록 주의해야 합니다. JDK의 cacerts를 복사해 구성했다면 JDK 업데이트에 포함된 CA 변경도 별도 파일에 반영해야 합니다.

프록시와 연결

서비스마다 HTTPS를 설정하는 대신 앞단 프록시에 서버 인증서를 연결했습니다. 브라우저는 프록시에 HTTPS로 접속하고, 프록시는 로컬 프런트엔드와 API에 요청을 전달하는 구조입니다.

인증서를 신뢰하는지와 로그인 흐름이 정상인지는 따로 확인했습니다. 브라우저에서 화면을 연 뒤 API 요청과 리디렉션을 따라가고, Java와 빌드 도구에서도 필요한 HTTPS 호출이 되는지 확인해야 했습니다.

프록시 구성은 Nginx Proxy Manager로 로컬 HTTPS 접속 주소 통일하기에서 이어서 설명합니다.

참고 자료

Last updated on