IT 공부/주절주절 일기 같은 곳

apache에서 DNS 2개 SSL 설정 해보기 (feat. L4 헬스 체크)

수박한암살자 2022. 5. 19. 19:27

시작에 앞서...

개발도 잘 못하는데 네트워크에 대한 이해도도 잼병인 사람이 겪은 이야기라는 걸 명심하시기 바랍니다.
그래도 다른 사람들에게 도움이 되길 바라며 공유합니다.

또한 도메인을 비롯한 설정들을 실제 작성한 것과 다르게 치환하면서 약간 잘못된 부분이 있을 수 있습니다.
이해하고 넘어가주시면 감사하겠습니다.

 

배경

목표는 아래 2개의 도메인을 서버에 함께 세팅하는 것입니다.

  • a.example.com
  • b.test.net

두 도메인은 서버 두 대를 함께 사용합니다.

서버 두 대에 L4 바인딩을 하여 두 도메인이 각각 VIP를 할당 받았습니다.

  • a.example.com의 VIP : 111.111.111.123
  • b.test.net의 VIP : 222.222.222.234

L7 헬스 체크는 아래와 같습니다.

  • 모니터링 방식 : https / 443
  • 모니터링 경로
    • a.example.com/monitor/example/l7check
    • b.test.net/monitor/test/l7check

두 도메인 모두 SSL 인증서 설정을 합니다.

 

시작

단순하게 하나의 서버에 apache로 멀티 도메인 SSL 설정하는 것은 쉽습니다(?).

  • 구글링하면 일부 글에서는 포트가 달라야한다고 하지만 다 옛날 이야기입니다.
  • 구형 브라우저(ex: IE 구버전)에서는 인식이 제대로 안 된다는 이야기가 있긴 합니다.
  • NameVirtualHost *:443 을 넣어야한다고도 하지만 apache 2.2 버전 이후로 필요없는 문법입니다.

아래는 처음 설정한 httpd-ssl.conf 파일 내의 VirtualHost 설정입니다.
일부 로그 설정이나 경로 등은 임의로 넣어 조금 틀릴 수 있습니다.
httpd.conf 의 설정은 따로 적지 않겠습니다(80 포트로 들어온 설정에 대해 443 으로 리다이렉트 하는 것 외에 없습니다).

 

Listen 443

# example.com 
<VirtualHost *:443>
    ServerName a.example.com
    ServerAdmin example@example.com

    LogLevel debug
    LogFormat "%v %l %u %t \"%r\" %>s %b" comonvhost
    CustomLog /home/apache/logs/access_log comonvhost

    SSLEngine on
    SSLCertificateFile "/home/cert/example/cert.pem"
    SSLCertificateKeyFile "/home/cert/example/cert.key"
    SSLCertificateChainFile "/home/cert/example/cert_chain.crt"
    SSLProtocol ALL -SSLv2 -SSLv3

    CustomLog "| /home/apache/logs/rotatelogs -l /home/apache/logs/ssl_request.log.%Y%m%d 86400" \
              "%t %v %H %h %>s EXAMPLE \"%r\" %b \"%{Referer}i\" \"%{User-Agent}i\""

    ProxyPreserveHost On
    SSLProxyEngine On
    ProxyRequests Off
    RequestHeader set Front-End-Https "On"

    # apache에서 L7check에 대해 200응답을 임의로 보내줌
    ErrorDocument 200 "OK"
    RewriteEngine On
    RewriteRule ^/monitor/example/l7check - [R=200]

</VirtualHost>

# test.net 
<VirtualHost *:443>
    ServerName b.test.net
    ServerAdmin test@test.net

    LogLevel debug
    LogFormat "%v %l %u %t \"%r\" %>s %b" comonvhost
    CustomLog /home/apache/logs/access_log comonvhost

    SSLEngine on
    SSLCertificateFile "/home/cert/test/cert.pem"
    SSLCertificateKeyFile "/home/cert/test/cert.key"
    SSLCertificateChainFile "/home/cert/test/cert_chain.crt"
    SSLProtocol ALL -SSLv2 -SSLv3

    CustomLog "| /home/apache/logs/rotatelogs -l /home/apache/logs/ssl_request.log.%Y%m%d 86400" \
              "%t %v %H %h %>s TEST \"%r\" %b \"%{Referer}i\" \"%{User-Agent}i\""

    ProxyPreserveHost On
    SSLProxyEngine On
    ProxyRequests Off
    RequestHeader set Front-End-Https "On"

    # apache에서 L7check에 대해 200응답을 임의로 보내줌
    ErrorDocument 200 "OK"
    RewriteEngine On
    RewriteRule ^/monitor/test/l7check - [R=200]

</VirtualHost>

중간 점검

적당히 잘 세팅된 것만 같아 보입니다.
우선 제 로컬 PC에 각 서버의 ip에 대해 도메인을 직접 /etc/hosts에 넣어서 브라우저로 접근해봅니다.


https로 잘 인식되고 인증서도 잘 노출이 됩니다.

 

L4에서 헬스 체크하는 부분도 확인합니다.
이런, L4 헬스 체크가 실패하고있네요. ssl_request 로그를 봅니다.

  • [14/May/2022:00:37:53 +0900] a.example.com HTTP/1.1 121.121.123.456 403 EXAMPLE "GET /monitor/example/l7check HTTP/1.1" 337 "-" "-"
  • [14/May/2022:00:37:54 +0900] a.example.com HTTP/1.1 121.121.123.456 403 EXAMPLE "GET /monitor/test/l7check HTTP/1.1" 337 "-" "-"

중간에 403 이 찍혀있습니다. 403 Forbidden 응답이 나가고있네요.
제가 직접 요청을 보내봤습니다.(로컬 /etc/hosts 설정을 통해 도메인으로 요청 가능)

  • curl https://a.example.com/monitor/example/l7check
  • curl https://b.test.net/monitor/test/l7check

두 요청 모두 200 OK 응답이 옵니다.
여기서 일단 답답해집니다.
나는 성공하는데 왜 L4가 보내는 L7 헬스 체크는 실패하지..?ㅠㅠ

일단 로그상에서 봤을 때 주목할 만한 부분이 조금 보입니다.

  1. a.example.com과 b.test.net이 L7 헬스 체크를 각각 하고는 있지만 로그에는 b.example.com 도메인만 찍히고있습니다.
  2. 응답코드가 403입니다. 다들 아시겠지만 그래도 어떤 응답인지 정의를 봅시다.
    • 작동중인 서버에 클라이언트의 요청이 도달했으나, 서버가 클라이언트의 접근을 거부할 때 반환하는 HTTP 응답 코드이자 오류 코드이다. (출처 : 킹무위키)
    • 요청은 왔으나 제가 작업한 설정의 어떠한 설정으로 인해 403이 나가고 있다. 라고 해석할 수 있습니다.

간단하게 체크해본 것들

  • 경로에 대한 오타 -> 없었습니다.
  • rewrite rule이 잘못된 건 아닐까 -> 그렇다면 제가 직접 요청한 것도 실패해야합니다.

 

삽질을 하자

결국 의심하게 되는 것은 두 가지 뿐입니다.

  1. L4에서 L7 헬스 체크 설정이 잘못되어있다.
  2. 내가 apache 설정을 잘못했다.

응답이 403인 부분을 생각해보면 2번의 가능성이 훨씬 높아보입니다.
자괴감을 무릅쓰고 열심히 설정을 만져봅니다. 안됩니다. 이제 다른 일도 밀립니다.
이럴려고 선임이 되었나 하는 자괴감이 듭니다.

결국 L4 바인딩을 해주신 분께 질문을 드려봅니다.


저 "(조심스래) 바인딩 설정에 문제는 없을까요?"
담당자 "바인딩 설정에는 문제가 없습니다. 어디선가 L4 IP를 차단하는 설정이 있는 건 아닐까요?"


아시다시피 apache에는 allow, deny 설정이 존재합니다.
담당자 분께 감사함을 표하고 httpd-vhost와 httpd-ssl 파일을 다 살펴봅니다.
근데 deny하는 부분에 L4 IP를 막을 만한 설정은 보이지 않습니다.

아는 친구에게 물어보니
"형 그냥 다 allow from all 해서 해보시죠."

무식하지만 좋은 방법입니다.
어차피 서비스를 올린 상황은 아니니 잠깐만 열어보기로 결정하고 다 풀어봅니다. 이러면 200응답 나가겠지?

...안됩니다.

다시 네트워크 담당자분께 물어봅니다.
이번엔 지난 로그를 바탕으로 다른 질문을 드려봤습니다.


저 "여전히 안됩니다. 혹시 L7 health check를 각각 하고는 있지만 로그에는 example.com 도메인만 찍히는 부분은 왜 그런지 알 수 있을까요?"
담당자 "L4에서 도메인 주소로 체크하지 않습니다. vip로 요청하여 체크하고 있습니다."

 

원인을 찾아내다

지금까지 제가 삽질한 이유 중 하나는 test 도메인에 대한 헬스 체크 요청도 example 도메인으로 들어가고 있던 부분인데
ip로 요청을 준다면 이야기가 좀 달라집니다. 다시 한번 VirtualHost를 봅니다.

# example.com 
<VirtualHost *:443>
    ServerName a.example.com
    ...

# test.net 
<VirtualHost *:443>
    ServerName b.test.net
    ...

VirtualHost 앞에 *:443으로 되어있네요.
apache 공식 홈페이지의 설명을 보겠습니다.

 

# Ensure that Apache listens on port 80
Listen 80
<VirtualHost *:80>
    DocumentRoot "/www/example1"
    ServerName www.example.com

    # Other directives here
</VirtualHost>

<VirtualHost *:80>
    DocumentRoot "/www/example2"
    ServerName www.example.org

    # Other directives here
</VirtualHost>


The asterisks match all addresses, so the main server serves no requests.
Due to the fact that the virtual host with ServerName www.example.com is
first in the configuration file, it has the highest priority and can be seen as
the default or primary server.
That means that if a request is received that does not match
one of the specified ServerName directives,
it will be served by this first <VirtualHost>.

별표는 모든 주소를 가리키므로, 주서버는 어떤 요청도 서비스하지 않는다.
www.example.com이 설정파일에 처음으로 나오므로 가장 높은 우선순위를 가지며,
기본 혹은 초기 서버가 된다. 어떤 ServerName 지시어에도
해당되지않는 요청은 첫번째 VirtualHost가 서비스한다.

여기서 핵심은 어떠한 ServerName directive에도 해당되지 않는다면 첫번째 VirtualHost가 서비스했을 거라는 부분입니다.
L4에서의 L7 health check는 vip로 요청이 오므로 제가 작성한 두 개의 VirtualHost 중 첫번째가 다 받고 있는 중입니다.

a.example.com의 vip 111.111.111.123 에 대한 L7 check와
b.test.net의 vip 222.222.222.234 에 대한 L7 check 둘 다 말이죠.

그러니 로그도 a.example.com의 VirtualHost의 로그로만 찍히고 있게 됩니다.

 

해결

VirtualHost 설정을 아래와 같이 바꿔봤습니다.

# example.com 
<VirtualHost 111.111.111.123:443>
    ServerName a.example.com
    ...

# test.net 
<VirtualHost 222.222.222.234:443>
    ServerName b.test.net
    ...

VirtualHost directive에 각각 도메인에 바인딩된 VIP를 넣어줬습니다.
이제 L7 헬스 체크 로그를 확인해봅니다.

  • [18/May/2022:18:51:02 +0900] a.example.com HTTP/1.1 121.121.123.456 200 EXAMPLE "GET /monitor/example/l7check HTTP/1.1" 337 "-" "-"
  • [18/May/2022:18:51:04 +0900] b.test.net HTTP/1.1 121.121.123.456 200 TEST "GET /monitor/test/l7check HTTP/1.1" 337 "-" "-"

200으로 응답이 나갑니다!
L4 헬스 체크하는 서비스에서도 정상적으로 STATUS가 UP으로 표시가 됩니다!!

 

하지만...

문제는 해결이 됐지만 조금 의아한 점은 있습니다.

첫번째 <VirtualHost *:443>이 다 받는다고 쳤을 때, example.com에 대한 L7 헬스 체크는 성공했어야하지 않을까?

  • 이 의문이 생긴 이유는 SSL을 이용한 443 포트로 체크를 하지 않고 그냥 일반 80 포트로 시도하니 a.example.com에 대해서는 200 OK 응답이 갔기 떄문입니다.
  • b.test.net에 대해서는 여전히 403 응답이 나갔습니다.


개인적인 예상으로는 443 포트로 오는 요청에 대해서 첫번째 <VirtualHost *:443>이 기본적으로 처리하지만
두번째 VirtualHost 역시 *:443 으로 되어있고 첫번째와는 다른 도메인 인증서를 쓰고 있으므로
요청을 처리하는 과정에서 어떤 인증서로 요청이 처리되어야하는지 충돌 같은게 발생하는 것은 아닐까? 하는 추측을 하는 중입니다.

 

# example.com 
<VirtualHost 111.111.111.123:443>
    ServerName a.example.com
    ...

# test.net 
<VirtualHost 222.222.222.234 111.111.111.123:443>
    ServerName b.test.net
    ...

두번째 VirtualHost에 a.example.com의 vip를 넣어줬더니 a.example.com에 대한 L7 헬스 체크는 다시 실패합니다.
b.test.net은 온전히 두번째 VirtualHost에서만 처리하므로 L7 헬스 체크는 성공합니다.


이 부분에 대한 명확한 apache 문서는 찾지 못했습니다. 혹시 아시는 분이 있다면 댓글 부탁드립니다.

 

더불어 현재 설정에서 만약 또 다른 시스템에서 별도의 ip로 요청을 하는 것이 있다면 문제가 발생할 여지가 있습니다.
예를 들어 서버 RIP로 요청이 들어올 경우 200 응답을 못 줄 것으로 예상합니다.

 

개인적인 생각

해결하는 순간까지 'apache는 어렵다 어려워...' 라고 생각했지만
L4의 L7 헬스 체크 방식에 대해 이해하고 있고 기본적인 VirtualHost 문서를 읽어봤다면 훨씬 빠를게 해결이 가능했을 거라 생각합니다.

 

그럼에도 일부 주변 사람들과 이야기했을 때 해당 이슈는 처음이라는 반응을 봐서 공유해봅니다.
부족한 내용, 잘못된 내용, 추가할 만한 내용에 대해서는 언제든지 이야기 부탁드립니다.

 

참조

반응형