Nginx를 사용한 mTLS 설정하기
결제 프로세스 구현을 위해 PG사, 카드사, 앱카드사, 은행 서버를 개발하고 있습니다. 이러한 금융 시스템에서 데이터 보안은 그 무엇보다 중요한 요소이며, 특히 결제 정보와 같은 민감한 데이터를 처리할 때는 안전한 통신 방식의 구현이 필수적입니다.
왜 mTLS가 필요한가?

금융 거래에서는 단순한 암호화를 넘어, 통신하는 양측의 신원을 확실하게 검증하는 양방향 인증이 필요합니다. 특히 한국에서는 결제 시스템의 보안에 대한 법적 요구사항이 엄격하며, 이는 다음과 같은 법률과 규정에 명시되어 있습니다.
- 개인정보 보호법(PIPA): 카드 번호와 같은 고유 식별 정보의 전송 시 암호화를 포함한 보안 조치를 의무화하고 있습니다.
- 전자금융거래법(EFTA): PG사, 카드사, 은행 간 통신을 포함한 모든 전자금융거래에서 안전성을 보장하기 위한 조치를 요구합니다.
- PCI DSS(Payment Card Industry Data Security Standard): 카드 결제를 처리하는 모든 기관은 카드 소지자 데이터 전송 및 저장 시 암호화를 필수로 요구하고 있습니다.
이러한 요구사항을 충족시키기 위해 단순한 HTTPS 통신을 넘어서, 클라이언트와 서버가 서로의 신원을 확인하는 mTLS(mutual TLS) 방식이 금융 시스템에서 널리 사용되고 있습니다. mTLS는 한쪽의 인증서만 확인하는 일반 TLS와 달리, 통신하는 양측 모두가 인증서를 제출하고 검증하기 때문에 보안성이 훨씬 더 강화됩니다.
이 블로그 포스트에서는 실제 결제 시스템에서 활용할 수 있는 mTLS 통신 환경을 NGINX를 통해 구축하는 방법을 상세히 안내합니다.
주요 용어 설명
- CA (Certificate Authority): 인증서를 발급하고 서명하는 신뢰할 수 있는 기관입니다. 클라이언트나 서버의 신원을 보증해줍니다. 이 가이드에서는 테스트 용도로 직접 CA를 만들어 사용할 것입니다.
- CSR (Certificate Signing Request): 인증서를 발급받기 위해 요청하는 데이터입니다. 인증을 받고자 하는 주체(예: 서버)는 자신의 공개키, 조직 정보 등을 담아 CSR을 생성하고, 이를 CA에 제출합니다.
- 인증서 서명 (Certificate Signing): CA가 인증 요청(CSR)의 내용을 검토한 후, 자신의 비밀키로 서명해서 인증서에 신뢰성을 부여하는 과정입니다. 이 서명을 통해 인증서의 위변조 여부를 검증할 수 있습니다.
- mTLS (mutual TLS): 클라이언트와 서버가 서로의 인증서를 검증해 신뢰를 확인하는 양방향 TLS 인증 방식입니다. 일반 TLS는 서버만 인증하지만, mTLS는 클라이언트도 인증받습니다.
- 키 페어 (Key Pair): 서로 연관된 공개키와 비밀키 한 쌍입니다. 일반적으로 비밀키로 서명하고, 그 서명을 공개키로 검증합니다. 인증서에는 공개키가 들어가 있고, 비밀키는 절대 외부에 노출되지 않습니다.
- NGINX: 가볍고 성능이 뛰어난 웹 서버이자 리버스 프록시로, 많은 기업에서 API 게이트웨이 역할을 담당합니다. 보안 설정이 간편하고 효율적이어서 mTLS 구현에 자주 활용됩니다.
이번 포스트에서는 Google Cloud Platform(GCP) 인스턴스에서 test.woorifisa.site 도메인을 사용해 mTLS(Mutual TLS) 통신을 설정하고,
NGINX를 통해 백엔드 서비스를 띄우는 과정을 상세히 설명하겠습니다. mTLS는 내부 API 통신이나 금융 시스템 같은 보안이 중요한 환경에서 특히 유용합니다.
실습 진행 환경
아래와 같은 환경에서 실습을 진행했습니다.
- GCP 인스턴스: Ubuntu 24.10 운영체제 (
test.woorifisa.site에 매핑된 공인 IP를 가진 인스턴스) - 도메인:
test.woorifisa.site(GCP 인스턴스의 공인 IP와 연결된 DNS A 레코드 설정 필요) - 로컬 환경: MacBook (인증서 생성용)
- 도구:
- OpenSSL (MacBook에 기본 설치됨)
- NGINX (GCP 인스턴스에 설치)
- curl (테스트용)
- SSH 액세스 (GCP 인스턴스 접속용)
1. 로컬에서 인증서 생성하기 (MacBook)
이제 본격적으로 시작하겠습니다. 가장 먼저 할 일은 인증서 생성입니다. 인증서는 mTLS 통신의 핵심이라고 할 수 있습니다. 서버와 클라이언트 모두 인증서가 필요하며, 이들은 모두 신뢰할 수 있는 CA의 서명을 받아야 합니다.
1.1. 디렉토리 생성
먼저 인증서 파일들을 관리할 디렉토리를 만들어보겠습니다.
mkdir -p ./certs1.2. CA 인증서 생성
모든 인증서 과정의 시작점인 CA(인증 기관)를 만들어보겠습니다.
실제 환경에서는 Let's Encrypt 같은 공인 CA를 사용하지만, 테스트 환경에서는 직접 CA를 만들어도 괜찮습니다.
-
CA 키를 먼저 생성해보겠습니다. 이 키는 CA의 비밀키로, 다른 인증서에 서명할 때 사용됩니다.
cd ./certs openssl genpkey -algorithm RSA -out ca.key -
이제 인증서 서명에 사용할 CA 인증서를 생성합니다. 이 인증서는 CA의 공개키를 포함하며, 유효기간은 1년으로 설정했습니다.
cd ./certs openssl req -new -x509 -key ca.key -out ca.crt -days 365 -subj "/CN=TestCA"이렇게 생성된 CA 인증서는 나중에 클라이언트와 서버 모두가 신뢰할 기준이 됩니다.
1.3. 서버 인증서 생성
다음으로 서버 인증서를 만들어보겠습니다. 서버 인증서는 클라이언트가 접속했을 때 서버의 신원을 증명하는 용도로 사용됩니다. HTTPS 웹사이트에 접속할 때 보는 자물쇠 아이콘과 관련된 부분입니다.
-
서버에서 사용할 키(비밀키)를 생성합니다.
cd ./certs openssl genpkey -algorithm RSA -out server.key이 키는 절대 외부에 노출되면 안 되는 서버의 비밀 정보입니다.
-
서버의 인증서 서명 요청(CSR)을 생성하겠습니다. 이것은 “저는 test.woorifisa.site라는 도메인의 서버입니다”라고 CA에게 말하는 것과 같습니다.
cd ./certs openssl req -new -key server.key -out server.csr -subj "/CN=test.woorifisa.site" -
방금 만든 CA로 서버 인증서에 서명합니다. 이 과정을 통해 CA가 “이 서버는 진짜 test.woorifisa.site가 맞습니다”라고 보증하는 것입니다.
cd ./certs openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 365
1.4. 클라이언트 인증서 생성
마지막으로 클라이언트 인증서를 만들어보겠습니다. 일반 HTTPS와 다르게 mTLS에서는 클라이언트도 자신의 신원을 증명해야 하기 때문에 인증서가 필요합니다. 이 부분이 mTLS의 핵심입니다.
-
클라이언트용 키를 만들어보겠습니다.
cd ./certs openssl genpkey -algorithm RSA -out client.key -
클라이언트의 CSR을 생성합니다. 여기서는 식별을 위해 ‘client.woorifisa.site’라는 이름을 사용했습니다.
cd ./certs openssl req -new -key client.key -out client.csr -subj "/CN=client.woorifisa.site" -
CA로 클라이언트 인증서에도 서명합니다. 이제 서버는 이 인증서를 통해 클라이언트가 신뢰할 수 있는 주체인지 확인할 수 있습니다.
cd ./certs openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 365
여기까지 완료하셨다면, 인증서 생성은 모두 끝났습니다. 이제 ./certs 디렉토리에는 CA, 서버, 클라이언트 각각의 인증서와 키가 준비되어 있을 것입니다.
2. GCP 인스턴스 설정하기
이제 인증서를 준비했으니, 실제 서버 환경을 구성해보겠습니다. GCP 인스턴스에 접속해서 필요한 설정들을 해보겠습니다.
2.1. GCP 인스턴스 접속하기
먼저 GCP 인스턴스에 접속해야 합니다. Google Cloud 콘솔에서 SSH를 사용하거나, gcloud 명령어를 통해 접속할 수 있습니다.
GCP Console에서 인스턴스의 공인 IP를 확인해 아래 명령어를 사용해 SSH로 접속해보세요.
ssh [USERNAME]@[INSTANCE_IP]혹은 GCP Console에서 인스턴스 이름과 존을 확인한 후, 아래 명령어를 사용해 SSH로 접속해주세요.
gcloud compute ssh [INSTANCE_NAME] --zone [ZONE]예를 들면: gcloud compute ssh instance-mtls-test --zone asia-northeast3-c 이렇게 입력하시면 됩니다.
2.2. 필수 도구 설치하기
NGINX는 경량화된 고성능 웹 서버로, 리버스 프록시 기능이 특히 뛰어납니다. mTLS 설정도 간편하게 할 수 있어 API 게이트웨이로 자주 활용됩니다. 그럼 NGINX를 설치해보겠습니다.
-
패키지 목록을 최신화하고 NGINX를 설치합니다.
sudo apt-get update -y sudo apt-get upgrade -y sudo apt-get install -y nginx이 명령어들은 시스템 패키지를 최신 상태로 업데이트하고, NGINX 웹 서버를 설치합니다.
-
NGINX가 제대로 설치됐는지 확인해주세요.
nginx -v버전 정보가 출력된다면 설치가 잘 된 것입니다.
2.3. 인증서 업로드하기
로컬에서 생성한 인증서를 GCP 인스턴스로 옮겨야 합니다. 서버 인증서와 CA 인증서가 필요한데, 클라이언트 인증서는 API를 호출하는 측에서 사용할 것입니다.
-
GCP 인스턴스에 인증서를 저장할 디렉토리부터 만들어보겠습니다.
sudo mkdir -p /mtls-demo/certs -
MacBook에서 생성한 인증서 파일들을 GCP 인스턴스로 전송해보겠습니다.
아래 명령어를 사용해 인증서 파일들을 GCP 인스턴스로 복사해주세요.
sudo scp -i ~/.ssh/[YOUR_SSH_KEY] ./certs/{ca.crt,server.crt,server.key} [USERNAME]@[INSTANCE_IP]:/tmp/혹은 gcloud 명령어를 사용해도 됩니다.
sudo gcloud compute scp ./certs/{ca.crt,server.crt,server.key} [INSTANCE_NAME]:/tmp/ --zone [ZONE] -
파일이 제대로 전송됐는지 확인해보겠습니다.
ls /tmpGCP 인스턴스에 정상적으로 전송되었다면
/tmp경로에ca.crt,server.crt,server.key파일들이 보일 것입니다. -
이제 인증서 파일들을
/mtls-demo/certs디렉토리로 이동해보겠습니다.sudo mv /tmp/{ca.crt,server.crt,server.key} /mtls-demo/certs/
2.4. 방화벽 설정하기
mTLS는 HTTPS를 사용하기 때문에 443 포트가 열려 있어야 합니다. GCP 인스턴스의 방화벽 규칙을 설정해보겠습니다.

GCP 콘솔에서 해당 인스턴스를 선택하고 수정 버튼을 클릭한 다음, 방화벽 섹션에서 HTTPS 트래픽 허용 체크박스를 선택해주세요. 이렇게 하면 외부에서 443 포트로 접근할 수 있게 됩니다.
3. 백엔드 서비스 설정하기
NGINX는 리버스 프록시 역할을 할 것입니다. 실제로 요청을 처리할 간단한 백엔드 서비스가 필요합니다. 여기서는 Python의 Flask를 사용해 간단한 API 서버를 만들어보겠습니다.
3.1. Flask 설치 및 설정하기
-
Python과 Flask 패키지를 설치해보겠습니다.
sudo apt-get install -y python3 python3-pip python3-flask -
간단한 API 서버 코드를 작성하겠습니다.
sudo mkdir -p /mtls-demo/backend sudo vi /mtls-demo/backend/app.py -
에디터가 열리면 아래 내용을 입력해주세요. 이 코드는 루트 경로(’/‘)로 요청이 오면 인사말을 반환하는 아주 간단한 웹 서버입니다.
from flask import Flask app = Flask(__name__) @app.route('/') def hello(): return 'Hello from test.woorifisa.site backend!' if __name__ == '__main__': app.run(host='0.0.0.0', port=8080) -
이제 Flask 서버를 백그라운드로 실행해보겠습니다.
nohup python3 /mtls-demo/backend/app.py &nohup과&를 사용하면 서버가 백그라운드에서 계속 실행됩니다.
3.2. Flask 앱 테스트하기
백엔드 서버가 제대로 실행됐는지 확인해보겠습니다.
curl http://localhost:8080모든 것이 정상이라면, Hello from test.woorifisa.site backend!라는 메시지가 보일 것입니다. 이제 내부적으로 API 서버가 잘 작동하고 있습니다.
4. NGINX로 mTLS 설정하기
이제 가장 중요한 부분입니다. NGINX를 설정해서 mTLS를 활성화하고, 백엔드 서버로 요청을 프록시해보겠습니다. NGINX는 mTLS 처리와 백엔드 라우팅을 모두 담당하게 될 것입니다.
4.1. NGINX 설정 파일 작성하기
-
먼저 NGINX 설정 파일을 저장할 디렉토리를 만들어보겠습니다.
sudo mkdir -p /mtls-demo/nginx -
이제 설정 파일을 작성해보겠습니다.
sudo vi /mtls-demo/nginx/nginx.conf -
다음 내용을 입력해주세요. 이 설정은 mTLS를 활성화하고 백엔드 서비스로 요청을 전달합니다.
worker_processes auto; # NGINX 워커 프로세스 수를 자동으로 설정 events { worker_connections 1024; # 각 워커 프로세스가 처리할 수 있는 최대 연결 수 } http { ssl_session_cache shared:SSL:10m; # SSL 세션 캐시 설정 ssl_session_timeout 10m; # SSL 세션 타임아웃 설정 server { listen 443 ssl; # 443 포트(HTTPS)로 들어오는 요청을 수신 server_name test.woorifisa.site; # 도메인 이름 설정 # 서버 인증서 및 키 설정 (서버가 자신을 클라이언트에게 증명) ssl_certificate /mtls-demo/certs/server.crt; ssl_certificate_key /mtls-demo/certs/server.key; # 클라이언트 인증서 검증 설정 (mTLS의 핵심부분!) ssl_client_certificate /mtls-demo/certs/ca.crt; # 클라이언트 인증서를 검증할 CA 인증서 ssl_verify_client on; # 클라이언트 인증서 검증 활성화 ssl_protocols TLSv1.2 TLSv1.3; # 허용할 TLS 프로토콜 버전 ssl_ciphers HIGH:!aNULL:!MD5; # 허용할 암호화 알고리즘 # 로그 설정 access_log /mtls-demo/nginx/access.log; error_log /mtls-demo/nginx/error.log; # 백엔드 프록시 설정 (들어온 요청을 Flask 앱으로 전달) location / { proxy_pass http://localhost:8080; # 요청을 Flask 서버로 전달 proxy_set_header Host $host; # 원본 Host 헤더 유지 proxy_set_header X-Real-IP $remote_addr; # 실제 클라이언트 IP 전달 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } }이 설정에서 중요한 부분은 SSL 인증서 관련 설정들입니다.
ssl_certificate와ssl_certificate_key는 서버 자신의 인증서 정보를 설정하고,ssl_client_certificate와ssl_verify_client on은 클라이언트 인증서를 검증하도록 지시하는 부분입니다. 이 두 가지 설정이 있어야 양방향 인증(mTLS)이 가능해집니다.
4.2. NGINX 실행하기
설정 파일을 작성했으니, 이제 NGINX를 실행해보겠습니다.
-
먼저 설정 파일에 오류가 없는지 테스트해보겠습니다.
sudo nginx -t -c /mtls-demo/nginx/nginx.confsyntax is ok와test is successful이 출력되면 설정에 문제가 없는 것입니다. -
혹시 이미 실행 중인 NGINX가 있다면 종료해보겠습니다.
sudo pkill nginx -
이제 우리가 작성한 설정으로 NGINX를 시작해보겠습니다.
sudo nginx -c /mtls-demo/nginx/nginx.conf이 명령어가 오류 없이 실행되면, NGINX가 성공적으로 시작된 것입니다. 이제 mTLS가 설정된 웹 서버가 443 포트에서 요청을 기다리고 있을 것입니다.
5. mTLS 통신 테스트하기
자, 이제 모든 설정이 완료되었습니다. 실제로 mTLS 통신이 잘 작동하는지 테스트해볼 차례입니다. 클라이언트 인증서를 사용한 경우와 사용하지 않은 경우를 모두 테스트해보겠습니다.
5.1. 클라이언트 인증서로 테스트하기
MacBook(로컬 환경)에서 클라이언트 인증서를 사용해 테스트해보겠습니다:
curl --cert ./certs/client.crt --key ./certs/client.key --cacert ./certs/ca.crt https://test.woorifisa.site이 명령어는 curl에게 세 가지 인증 관련 파일을 제공합니다.
--cert: 클라이언트 인증서 (서버가 클라이언트를 인증하는 데 사용)--key: 클라이언트 비밀키 (클라이언트가 소유권을 증명하는 데 사용)--cacert: CA 인증서 (서버 인증서를 검증하는 데 사용)
모든 게 잘 설정되었다면, Hello from test.woorifisa.site backend! 메시지가 보일 것입니다. 이것은 mTLS 통신이 성공적으로 이루어졌다는 의미입니다.
5.2. 인증서 없이 테스트하기 (실패 예상)
이번에는 클라이언트 인증서 없이 요청해보겠습니다.
curl https://test.woorifisa.sitemTLS가 제대로 적용되었다면, 이 요청은 실패해야 합니다. NGINX는 클라이언트 인증서 검증에 실패하고 다음과 같은 오류를 반환할 것입니다: SSL certificate problem: unable to get local issuer certificate
이 오류는 정상입니다. mTLS가 제대로 동작하고 있다는 증거입니다. 클라이언트가 유효한 인증서를 제공하지 않으면 API 접근이 차단되는 것입니다.
마무리하며
이렇게 해서 mTLS 설정이 모두 완료되었습니다. 이제 클라이언트와 서버 간의 안전한 양방향 인증이 가능해졌고, API 통신이 훨씬 더 안전해졌습니다.
mTLS는 높은 보안이 요구되는 시스템에서 매우 유용한 기술입니다. 내부 API 통신, 금융 서비스, 의료 정보 시스템 등 데이터가 민감한 환경에서 특히 중요하게 활용됩니다.