MTLS(Mutual TLS) 란?
- 상호 인증을 위한 방법으로 간단히 말해 양방향 TLS 통신을 하는 것이라고 보면 된다.
- WebtoB *SSL 절, ReverseProxy를 사용한다면 *ProxySSL 절을 통해 설정할 수 있다.
- 기존 WebtoB에 등록된 SSL 인증서 + MTLS를 사용할 대상 CA 인증서가 필요하다.
*SSL 절 설정 가이드
ssl1 CertificateFile="/tmax/webtob/ssl/aaa.bbb.com.crt", CertificateKeyFile="/tmax/webtob/ssl/aaa.bbb.com.key", CACertificateFile="/tmax/webtob/ssl/chainca.crt", CertificateChainFile="/tmax/webtob/ssl/tossca.crt", // 토스 CA 인증서 추가 CACertificatePath="/tmax/webtob/ssl/", VerifyClient = 2, VerifyDepth = 3, Options = "ExportClientCert", Protocols="-SSLv3, -SSLv2, -TLSv1, TLSv1.1, TLSv1.2, TLSv1.3" |
VerifyClient 값에 따라 인증 절차가 달라진다. 해당 값에 대한 설명은 아래 내용을 참고한다.
CertificateChainFile 의 경우 인증서를 1개 밖에 설정 할 수 없다.
mTLS 인증서를 추가적으로 설정한다면 SSL절을 하나 더 만들어야 한다.
VerifyClient
- 종류: Numeric
- 기본값: 0
- 사용자에게 요청할 인증 레벨을 설정한다.
- 다음은 인증 레벨에 대한 설명이다.
레벨설명0 아무런 인증 요청을 하지 않는다. 1 사용자는 사용 가능한 인증을 서버에게 보여 주어야 한다. 2 사용 가능한 인증을 반드시 서버에게 보여 주어야 한다. 3 사용자는 사용 가능한 인증을 보여 주어야 하며 만일 서버가 인증서를 가지고 있지 않은 상황에서는 인증서 인증 과정이 필요 없다.
VerifyDepth
- 종류: Numeric
- 범위: 0 ~ INT_MAX
- 기본값: 0
- 실제 업무에 적용되는 경우에 인증에서 개입할 부분은 순서대로 다른 CA에 의해서 서로를 인증하는 CA에 관한 것이다. VerifyDepth 항목은 얼마나 깊은 레벨로 연결된 CA들을 추적하여 인증할 것인지를 설정한다. 단 하나의 인증 CA만 필요하다면 1로 설정한다.
Options
- 종류: Literal
- 범위: 255자 이내
- 다음은 설정 가능한 옵션에 대한 설명이다.
- 옵션내용
StdEnvVars SSL Handshake 과정에 사용된 기본 정보를 CGI/PHP, JEUS, Reverse Proxy 내부 서버에서 사용 가능하도록 SSL 환경변수 또는 HTTP Request Header 값을 생성한다.
- CGI/PHP 서버에서 사용 가능한 환경변수: SSL_SESSION_ID(SSL session id), SSL_CIPHER(사용된 cipher), SSL_CIPHER_USEKEYSIZE(사용된 cipher bit)이다.
- JEUS 또는 Reverse Proxy 내부 서버에서 사용 가능한 HTTP Request Header: WJP-SSL-SESSION-ID(SSL session id), WJP-SSL-CIPHER(사용된 cipher), WJP-SSL-CIPHER-USEKEYSIZE(사용된 cipher bit)이다.
다음은 각 언어별 예이다.
- CGI
- PHP
- JSP
ExportClientCert 클라이언트 인증을 실행하는 경우(VerifyClient = 2 설정을 적용한 경우) 클라이언트 인증서를 CGI/PHP, JEUS, Reverse Proxy 내부 서버에서 사용 가능하도록 SSL 환경변수 또는 HTTP Request Header 값을 생성한다. 클라이언트 인증서의 확장필드(Extension Fields - Key Usage, Certificate Policies, CRL Distribution Points 등)를 처리하기 위해서는 해당 설정으로 클라이언트 인증서 정보를 얻어 파싱 후 사용해야 한다.
- CGI/PHP 서버에서 사용 가능한 환경변수: SSL_CLIENT_CERT(클라이언트 인증서-PEM 인코딩된 String 값)이다.
- JEUS 서버 또는 Reverse Proxy 내부 서버에서 사용가능한 HTTP Request Header: WJP-SSL-CLIENT-CERT(클라이언트 인증서-PEM 인코딩된 String 값)이다.
다음은 각 언어별 예이다.
- CGI
- PHP
- JSP
호출 테스트 (--cert, --key 옵션을 통해 인증서를 넘겨야 함, MTLS)
curl https://aaa.bbb.com -v --resolve aaa.bbb.com:443:127.0.0.1 --cert /tmp/tls.crt --key /tmp/tls.key -k
* Added aaa.bbb.com:443:127.0.0.1 to DNS cache
* Hostname aaa.bbb.com was found in DNS cache
* Trying 127.0.0.1:443...
* Connected to aaa.bbb.com (127.0.0.1) port 443
* ALPN: curl offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Request CERT (13):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Certificate (11):
* (304) (OUT), TLS handshake, CERT verify (15):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-CHACHA20-POLY1305-SHA256
* ALPN: server did not agree on a protocol. Uses default.
* Server certificate:
* subject: C=KR; ST=Seoul; L=Jung-gu; O=SHINSEGAE DF Inc.; CN=*.bbb.com
* start date: Dec 13 00:00:00 2023 GMT
* expire date: Jan 3 23:59:59 2025 GMT
* issuer: C=US; O=DigiCert Inc; OU=www.digicert.com; CN=Thawte RSA CA 2018
* SSL certificate verify ok.
* using HTTP/1.x
> GET / HTTP/1.1
> Host: aaa.bbb.com
> User-Agent: curl/8.4.0
> Accept: */*
>
< HTTP/1.1 400 Bad Request
< Connection: Close
< Transfer-Encoding: chunked
< Set-Cookie: JSESSIONID=UrMCYaU56vL7AKPwYnlPDPJ3MkXDd33v1d1RzGSxfSpFRN5WnthkERS5WPPQXPrl.amV1c19kb21haW4vZGV2X29wZW5BcGlfMw==; Path=/; HttpOnly
< cache-control: no-store
< pragma: no-cache
< Date: Mon, 05 Feb 2024 05:32:12 GMT
< Content-Type: text/html;charset=UTF-8
<
* Closing connection
02 request
400코드는 AP 로직에 의해 발생하는 것으로 정상 호출이다.
VerifyClient 값이 2 인 경우 TLS 호출은 Error가 발생 한다.
인증서를 태우지 않은 경우(TLS 통신 시도)
curl https://aaa.bbb.com -v --resolve aaa.bbb.com:443:127.0.0.1 -k
* Added aaa.bbb.com:443:127.0.0.1 to DNS cache
* Hostname aaa.bbb.com was found in DNS cache
* Trying 127.0.0.1:443...
* Connected to aaa.bbb.com (127.0.0.1) port 443
* ALPN: curl offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Request CERT (13):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Certificate (11):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-CHACHA20-POLY1305-SHA256
* ALPN: server did not agree on a protocol. Uses default.
* Server certificate:
* subject: C=KR; ST=Seoul; L=Jung-gu; O=SHINSEGAE DF Inc.; CN=*.bbb.com
* start date: Dec 13 00:00:00 2023 GMT
* expire date: Jan 3 23:59:59 2025 GMT
* issuer: C=US; O=DigiCert Inc; OU=www.digicert.com; CN=Thawte RSA CA 2018
* SSL certificate verify ok.
* using HTTP/1.x
> GET / HTTP/1.1
> Host: aaa.bbb.com
> User-Agent: curl/8.4.0
> Accept: */*
>
* LibreSSL SSL_read: LibreSSL/3.3.6: error:1404C45C:SSL routines:ST_OK:reason(1116), errno 0
* Closing connection
curl: (56) LibreSSL SSL_read: LibreSSL/3.3.6: error:1404C45C:SSL routines:ST_OK:reason(1116), errno 0
* 참고 사항
- 호출 시 /etc/ssl/cert.pem 파일을 OS 에서 기본적으로 보는 것으로 확인되어 해당 경로에 파일을 생성해줘야 한다. (ROOT 계정 필요)
- /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem 파일을 복사하여 사용
curl https://aaa.bbb.com -v --resolve aaa.bbb.com:443:127.0.0.1 --cert toss.pem --key toss.key -k
* Added aaa.bbb.com:443:127.0.0.1 to DNS cache
* Hostname aaa.bbb.com was found in DNS cache
* Trying 127.0.0.1:443...
* Connected to aaa.bbb.com (127.0.0.1) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/cert.pem
* CApath: none