본문 바로가기
프로젝트 일지/BoWS

Nginx에서 뒷단의 서비스와 Https 통신하려 할 때 겪는 문제들

by 데브겸 2024. 8. 30.

nginx에서 라우팅하는 A서비스 도메인의 인증서로 B서비스와 통신하려 한다?

 

문제상황

bows.co.kr이면 docker compose로 띄운 certbot과 volume을 통해 ssl 인증을 하고, bows.co.kr 도메인이 아니면 뒷단으로 https 그대로 넘겨버려서 뒷단의 k8s의 cert-manager와 traefik이 알아서 ssl을 처리하도록 설정한다. 아래 아키텍쳐를 보면 조금 직관적으로 이해가 가능하다.

 

 

문제는 뒷단으로 넘겼을 때 브라우저가 예를 들어 k8s에 naver.com 도메인으로 띄운 앱과 ssl 인증을 하려고 할 때 cert-manager와 traefik이 가지고 있는 naver.com인증서가 아니라, nginx 설정 앞단의 bows.co.kr의 인증서를 가지고 인증을 하려 한다는 사실을 발견.

Treafik의 external ip로 직접 공유기 포트포워딩해서 보면 배포하고자 하는 도메인에 대한 인증서로 잘 https 통신을 하고 있다.

 

 

하지만 nginx를 거치는 순간, ‘team09~’가 아니라 ‘bows.co.kr’ 도메인에 대한 인증서로 통신하려고 한다…?

 

 

 

원인 분석

nginx에서 proxy_pass의 엔드포인트와 https로 통신하는 경우, nginx가 클라이언트가 되어 활용가능한 인증서로 패킷을 암호화하여 보내게 된다… 즉 서비스의 사용자가 보낸 request 그냥 그대로 https로 넘겨주는게 아니라 nginx에서 해당 패킷을 복호화하고, nginx가지고 있는 인증서로 암호화해서 넘겨주는 거였다. 그러니까 최종 도착하는 인증서에 뜬금없이 bows.co.kr에 대한 인증서(nginx가 volume에서 가져와서 사용하는듯)가 나타나는 것이었다. 좀 더 자세한 내용은 아래 공식문서로도 확인 가능하다.

https://docs.nginx.com/nginx/admin-guide/security-controls/securing-http-traffic-upstream/

 

Securing HTTP Traffic to Upstream Servers | NGINX Documentation

Securing HTTP Traffic to Upstream Servers Secure HTTP traffic between NGINX or F5 NGINX Plus and upstream servers, using SSL/TLS encryption. This article explains how to encrypt HTTP traffic between NGINX and a upstream group or a proxied server. Prerequis

docs.nginx.com

 

 

해결방법

SSL 패스스루(SSL Passthrough)방식을 사용한다. stream 블록을 활용해 tcp/udp 레이어에서의 로드밸런싱을 해버리는 것이다. 다만 도메인 이름에 대한 정보는 그 위 레이어에 존재하기 때문에 ssl_preread 옵션을 켜서 ssl/tls를 종료시키지 않고 client hello에서 필요한 정보들을 추출한다. $ssl_preread_server_name을 읽어오고 도메인에 맞는 방식으로 로드밸런싱을 진행한다.

user nginx;
worker_processes auto;
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections 1024;
}

stream {
    map $remote_addr$proxy_protocol_addr$server_port$ssl_preread_server_name $backend {
        # HTTP 트래픽 (80 포트)
         ~^.*80bows\\.co\\.kr$ 127.0.0.1:8081;
        ~^.*80 127.0.0.1:8082;
        
        # HTTPS 트래픽 (443 포트)
        ~^.*443bows\\.co\\.kr$ 127.0.0.1:8443;
        ~^.*443 ${INGRESS_CONTROLLER_EXTERNAL_IP}:443;
    }

    server {
        listen 80;
        listen 443;
        ssl_preread on;
        proxy_pass $backend;
    }
}

http {
    server {
        listen 127.0.0.1:8081;
        server_name bows.co.kr;

        location /.well-known/acme-challenge/ {
            allow all;
            root /var/www/certbot;
        }

        location / {
            return 301 https://$host$request_uri;
        }
    }

    server {
        listen 127.0.0.1:8082;
        server_name _;

        location /.well-known/acme-challenge/ {
            proxy_pass http://${INGRESS_CONTROLLER_EXTERNAL_IP};
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }

        location / {
            return 301 https://$host$request_uri;
        }
    }

    server {
        listen 127.0.0.1:8443 ssl;
        server_name bows.co.kr;
        server_tokens off;

        ssl_certificate /etc/letsencrypt/live/bows.co.kr/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/bows.co.kr/privkey.pem;
        include /etc/letsencrypt/options-ssl-nginx.conf;
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

        location / {
            proxy_pass <http://frontend>;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }

        location /api/ {
            proxy_pass <http://backend:8080>;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}

 

 

결국 nginx의 역할과 layer4, layer7 로드밸런싱 개념, SSL/TLS 개념이 약했기 때문인 것 같다. 공부하자