🔒 보안

mTLS

Mutual TLS

양방향 TLS 인증. 서버와 클라이언트 모두 인증서 검증.

📖 상세 설명

mTLS(Mutual TLS, 상호 TLS)는 일반 TLS의 확장으로, 서버뿐만 아니라 클라이언트도 인증서를 제시하여 양방향 인증을 수행합니다. 제로 트러스트 아키텍처의 핵심 구성요소로, 마이크로서비스 간 통신에서 널리 사용됩니다.

일반 TLS에서는 클라이언트가 서버의 인증서만 검증합니다(HTTPS). mTLS에서는 서버도 클라이언트의 인증서를 검증하여, 허가된 클라이언트만 접속할 수 있습니다. API 키나 토큰 없이도 강력한 인증이 가능합니다.

Service Mesh(Istio, Linkerd)에서는 사이드카 프록시가 자동으로 mTLS를 처리합니다. 애플리케이션 코드 수정 없이 모든 서비스 간 트래픽을 암호화하고 인증할 수 있습니다.

인증서 관리가 복잡해지므로, cert-manager나 Vault PKI를 사용해 자동화하는 것이 일반적입니다. SPIFFE/SPIRE는 워크로드 아이덴티티 표준으로, 동적 환경에서 인증서 발급과 로테이션을 자동화합니다.

💻 코드 예제

# Istio: PeerAuthentication으로 mTLS 강제
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default
  namespace: production
spec:
  mtls:
    mode: STRICT  # mTLS 필수

---
# Istio: DestinationRule로 mTLS 설정
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: my-service
spec:
  host: my-service.production.svc.cluster.local
  trafficPolicy:
    tls:
      mode: ISTIO_MUTUAL  # Istio가 발급한 인증서 사용

---
# Nginx: mTLS 설정
server {
    listen 443 ssl;

    # 서버 인증서
    ssl_certificate /etc/nginx/certs/server.crt;
    ssl_certificate_key /etc/nginx/certs/server.key;

    # 클라이언트 인증서 검증 (mTLS)
    ssl_client_certificate /etc/nginx/certs/ca.crt;
    ssl_verify_client on;  # 필수
    ssl_verify_depth 2;

    location / {
        # 클라이언트 인증서 정보를 백엔드에 전달
        proxy_set_header X-Client-Cert-DN $ssl_client_s_dn;
        proxy_pass http://backend;
    }
}

---
# Node.js: mTLS 클라이언트
import https from 'https';
import fs from 'fs';

const options = {
  hostname: 'api.example.com',
  port: 443,
  path: '/data',
  method: 'GET',
  // 클라이언트 인증서 (mTLS)
  cert: fs.readFileSync('client.crt'),
  key: fs.readFileSync('client.key'),
  // 서버 CA 검증
  ca: fs.readFileSync('ca.crt'),
};

const req = https.request(options, (res) => {
  console.log(`Status: ${res.statusCode}`);
});

---
# cert-manager: 인증서 자동 발급
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: my-service-cert
  namespace: production
spec:
  secretName: my-service-tls
  duration: 2160h  # 90일
  renewBefore: 360h  # 만료 15일 전 갱신
  privateKey:
    algorithm: ECDSA
    size: 256
  usages:
    - server auth
    - client auth  # mTLS용
  dnsNames:
    - my-service.production.svc.cluster.local
  issuerRef:
    name: cluster-ca
    kind: ClusterIssuer

🗣️ 실무에서 이렇게 말해요

보안: "서비스 간 통신에 mTLS 적용하면 네트워크 레벨에서 인증이 되니까 API 키 관리 부담이 줄어들어요."

개발: "인증서 만료 관리가 어려울 것 같은데요..."

보안: "cert-manager로 자동 갱신하면 됩니다. 아니면 Istio 쓰면 사이드카가 알아서 처리해요."

면접관: "mTLS와 일반 TLS의 차이점을 설명해주세요."

지원자: "일반 TLS는 클라이언트가 서버 인증서만 검증하는 단방향 인증입니다. mTLS는 서버도 클라이언트의 인증서를 검증하는 양방향 인증으로, 허가된 클라이언트만 통신할 수 있습니다. 마이크로서비스 환경에서 서비스 간 인증에 주로 사용되며, 제로 트러스트 아키텍처의 핵심입니다."

리뷰어: "mTLS 설정에서 인증서 만료일을 확인하는 로직이 없네요. 만료된 인증서로 요청하면 어떻게 되나요?"

작성자: "TLS 핸드셰이크에서 거부됩니다. 근데 만료 임박 알림을 추가하면 좋겠네요."

⚠️ 주의사항

📚 더 배우기