# Tomcat SSL/TLS 설정 가이드 (Apache/Nginx 연동) ## 1. 개요 이 가이드는 **Tomcat**으로 실행되는 웹 애플리케이션에 **SSL/TLS (HTTPS)를 적용**하는 다양한 방법을 설명합니다. 특히, 사내 인프라(`192.168.0.61`과 같은 중앙 프록시)와 유사한 환경 및 클라우드 배포 환경 모두에서 적용할 수 있는 표준적인 방법을 다룹니다. 다양한 조합(Docker/Bare-metal, Apache/Nginx, Let's Encrypt/자체 서명 인증서)에 따른 설정 방법을 체계적으로 안내합니다. 이 가이드의 핵심 전략은 **리버스 프록시(Reverse Proxy)를 이용한 SSL 오프로딩(SSL Offloading)**입니다. ## 2. 핵심 전략: SSL 오프로딩 (TLS Termination) SSL 오프로딩은 클라이언트와의 암호화된 통신(HTTPS)을 백엔드 애플리케이션(Tomcat) 앞단의 리버스 프록시(Nginx, Apache)가 전담하여 처리하는 구조입니다. **장점:** - **중앙 집중식 SSL 관리**: 인증서 관리를 리버스 프록시 한 곳에서 처리하므로 관리가 쉽고, 여러 Tomcat 인스턴스가 있어도 개별적으로 SSL을 설정할 필요가 없습니다. - **성능 향상**: SSL 암호화/복호화 연산은 CPU를 많이 사용합니다. 웹 서버(Nginx/Apache)가 이를 전담함으로써 Tomcat은 비즈니스 로직 처리에 더 많은 리소스를 사용할 수 있습니다. - **유연성 및 보안**: 리버스 프록시가 로드 밸런싱, 캐싱, 요청 필터링, 로깅 등 다양한 기능을 수행할 수 있으며, Tomcat 서버가 외부에 직접 노출되지 않아 보안이 강화됩니다. 이 구조에서 Tomcat은 SSL 설정을 할 필요 없이 일반 HTTP 또는 AJP 프로토콜로 실행됩니다. ## 3. 1단계: SSL 인증서 준비 리버스 프록시에 적용할 SSL 인증서를 준비합니다. 환경에 따라 두 가지 방법 중 하나를 선택합니다. ### 3.1. 개발 환경: 자체 서명 인증서 (Self-Signed Certificate) 로컬이나 개발 서버에서 테스트 용도로 사용합니다. `openssl` 명령어로 생성합니다. ```bash # 인증서를 저장할 디렉토리 생성 mkdir -p certs # 2048비트 RSA 키와 365일 유효한 자체 서명 인증서 생성 openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout certs/self-signed.key \ -out certs/self-signed.crt \ -subj "/C=KR/ST=Seoul/L=Gangnam/O=MyOrg/OU=MyDept/CN=localhost" ``` - `CN` (Common Name)에는 접속할 도메인(예: `dev.mydomain.com` 또는 `localhost`)을 입력합니다. - 생성된 `self-signed.key`(개인키)와 `self-signed.crt`(인증서) 파일을 리버스 프록시 서버의 특정 경로에 저장합니다. ### 3.2. 운영 환경: Let's Encrypt 인증서 (Certbot 사용) 신뢰할 수 있는 무료 인증 기관인 Let's Encrypt의 인증서를 사용합니다. `certbot` 클라이언트를 통해 발급 및 자동 갱신을 설정하는 것이 일반적입니다. **사전 준비:** - 서버를 가리키는 공인 도메인 (예: `www.your-domain.com`) - 서버의 80번 포트(HTTP)가 외부에 개방되어 있어야 합니다. (Certbot이 도메인 소유권을 확인하기 위해 사용) **Certbot 설치 (Ubuntu/Debian 기준):** ```bash sudo snap install --classic certbot sudo ln -s /snap/bin/certbot /usr/bin/certbot ``` **인증서 발급 (Nginx/Apache 중단 방식):** 리버스 프록시를 잠시 중단하고 독립 실행형(standalone) 모드로 인증서를 발급받는 것이 가장 간단합니다. ```bash # Nginx 사용 시 sudo systemctl stop nginx # Apache 사용 시 sudo systemctl stop apache2 # Certbot 실행 sudo certbot certonly --standalone -d your-domain.com --email your-email@example.com --agree-tos # 프록시 재시작 sudo systemctl start nginx # 또는 apache2 ``` - 인증서는 `/etc/letsencrypt/live/your-domain.com/` 디렉토리 내에 `fullchain.pem`(인증서)과 `privkey.pem`(개인키)로 생성됩니다. - Certbot은 자동으로 갱신을 시도하도록 cron job 또는 systemd timer를 설정합니다. `sudo certbot renew --dry-run`으로 갱신을 테스트할 수 있습니다. **Docker 환경에서의 Certbot:** 이전 `docker_ssl_setup_guide.md`의 `certbot` 서비스 부분을 참고하여 구성할 수 있습니다. `certbot` 컨테이너가 Nginx와 볼륨을 공유하여 인증서를 발급하고 갱신하도록 설정합니다. ## 4. 2단계: 리버스 프록시 설정 (SSL 적용) 준비된 인증서를 사용하여 Nginx 또는 Apache에 SSL을 설정하고, Tomcat으로 요청을 전달합니다. ### 4.1. Nginx로 설정하기 #### Bare-metal 환경 `/etc/nginx/sites-available/`에 다음과 같은 설정 파일을 작성합니다. (`your-domain.com.conf`) ```nginx # /etc/nginx/sites-available/your-domain.com.conf # HTTP (80) -> HTTPS (443) 리디렉션 server { listen 80; server_name your-domain.com; return 301 https://$host$request_uri; } server { listen 443 ssl http2; server_name your-domain.com; # 1. SSL 인증서 설정 # Let's Encrypt 사용 시 ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem; # 자체 서명 인증서 사용 시 # ssl_certificate /path/to/certs/self-signed.crt; # ssl_certificate_key /path/to/certs/self-signed.key; # SSL 보안 강화 옵션 ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; location / { # 2. Tomcat으로 요청 전달 (HTTP) # Tomcat 서버가 10.0.0.5:8080에서 실행 중이라고 가정 proxy_pass http://10.0.0.5:8080; # 3. 프록시 관련 헤더 설정 (Tomcat에서 클라이언트 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; } } ``` **설정 적용:** ```bash # 심볼릭 링크 생성 sudo ln -s /etc/nginx/sites-available/your-domain.com.conf /etc/nginx/sites-enabled/ # 설정 테스트 sudo nginx -t # Nginx 재시작 sudo systemctl restart nginx ``` #### Docker 환경 (Nginx + Tomcat 연동) `docker-compose.yml` 파일과 `nginx.conf` 파일을 프로젝트 루트에 구성합니다. **`docker-compose.yml`** ```yaml version: '3.8' services: tomcat: image: tomcat:9.0-jdk11-temurin # server.xml 등을 마운트 할 수 있음 # volumes: # - ./tomcat/conf/server.xml:/usr/local/tomcat/webapps/conf/server.xml expose: - "8080" # 컨테이너 내부 8080 포트 networks: - my-network nginx: image: nginx:latest ports: - "80:80" - "443:443" volumes: # nginx.conf 마운트 - ./nginx.conf:/etc/nginx/conf.d/default.conf # 인증서 마운트 (letsencrypt 또는 self-signed) - /etc/letsencrypt:/etc/letsencrypt:ro # 또는 - ./certs:/etc/nginx/certs:ro depends_on: - tomcat networks: - my-network networks: my-network: driver: bridge ``` **`nginx.conf`** - Bare-metal 설정과 거의 동일하지만, `proxy_pass` 부분이 다릅니다. - Docker의 내부 DNS를 사용하여 서비스 이름(`tomcat`)으로 요청을 전달합니다. ```nginx # ... (server 블록 및 ssl 설정은 위와 동일) ... location / { # 서비스 이름 'tomcat'과 노출된 포트 '8080'으로 전달 proxy_pass http://tomcat:8080; proxy_set_header Host $host; # ... (나머지 헤더 설정은 동일) ... } # ... ``` ### 4.2. Apache로 설정하기 #### Bare-metal 환경 `mod_ssl`, `mod_proxy`, `mod_proxy_http` (또는 `mod_proxy_ajp`) 모듈이 활성화되어 있어야 합니다. ```bash # 모듈 활성화 (Debian/Ubuntu) sudo a2enmod ssl proxy proxy_http # AJP 연동을 원할 경우 # sudo a2enmod proxy_ajp ``` `/etc/apache2/sites-available/`에 다음과 같은 가상 호스트 설정 파일을 작성합니다. (`your-domain-ssl.conf`) ```apache # /etc/apache2/sites-available/your-domain-ssl.conf # HTTP -> HTTPS 리디렉션 ServerName your-domain.com Redirect permanent / https://your-domain.com/ ServerName your-domain.com # 1. SSL 설정 SSLEngine on SSLCertificateFile /etc/letsencrypt/live/your-domain.com/fullchain.pem SSLCertificateKeyFile /etc/letsencrypt/live/your-domain.com/privkey.pem # 2. Tomcat으로 요청 전달 (선택 1: HTTP 프록시) ProxyPreserveHost On ProxyPass / http://10.0.0.5:8080/ ProxyPassReverse / http://10.0.0.5:8080/ # 2. Tomcat으로 요청 전달 (선택 2: AJP 프록시) # Tomcat의 server.xml에 AJP Connector가 활성화(보통 8009 포트)되어 있어야 함 # ProxyPass / ajp://10.0.0.5:8009/ # ProxyPassReverse / ajp://10.0.0.5:8009/ # 3. 프록시 관련 헤더 설정 (HTTP 프록시 사용 시) RequestHeader set X-Forwarded-Proto "https" RequestHeader set X-Forwarded-Port "443" ``` **설정 적용:** ```bash # 사이트 활성화 sudo a2ensite your-domain-ssl.conf # 설정 테스트 sudo apache2ctl configtest # Apache 재시작 sudo systemctl restart apache2 ``` #### Docker 환경 (Apache + Tomcat 연동) 공식 `httpd` 이미지를 사용하여 구성할 수 있습니다. `apache.conf`와 `docker-compose.yml`이 필요합니다. **`docker-compose.yml`** ```yaml version: '3.8' services: tomcat: # ... (Nginx 예시와 동일) ... apache: image: httpd:2.4 ports: - "80:80" - "443:443" volumes: - ./apache.conf:/usr/local/apache2/conf/extra/httpd-ssl.conf - /etc/letsencrypt:/etc/letsencrypt:ro # httpd.conf를 수정하여 ssl.conf를 include 하거나 직접 마운트 # ... 복잡하므로 Bare-metal을 더 권장 ... depends_on: - tomcat # ... ``` *참고: Docker에서 Apache SSL을 설정하는 것은 Nginx보다 다소 복잡합니다. `httpd.conf`를 직접 수정하여 `mod_ssl` 로드, `Listen 443`, `Include conf/extra/httpd-ssl.conf` 등의 지시어를 관리해야 하기 때문입니다.* ## 5. 3단계: Tomcat 설정 (`server.xml`) 리버스 프록시 뒤에서 Tomcat이 올바르게 동작하도록 `server.xml` 파일을 설정합니다. 핵심은 Tomcat이 클라이언트의 원래 요청(HTTPS, 도메인 등)을 인지하도록 만드는 것입니다. **`RemoteIpValve`를 사용하는 것이 가장 표준적이고 권장되는 방법입니다.** **`conf/server.xml`** ```xml remoteIpHeader="x-forwarded-for" protocolHeader="x-forwarded-proto" /> ``` - `internalProxies`: 신뢰할 수 있는 프록시 서버의 IP 주소 목록입니다. 여기에 등록된 IP가 보낸 `x-forwarded-*` 헤더만 신뢰합니다. **중앙 프록시(`192.168.0.61`), Docker의 게이트웨이 IP 등을 등록해야 합니다.** - `remoteIpHeader`: 클라이언트의 실제 IP가 담긴 헤더 이름입니다. (`X-Real-IP` 또는 `X-Forwarded-For`) - `protocolHeader`: 원래 프로토콜(http/https)이 담긴 헤더 이름입니다. (`X-Forwarded-Proto`) `RemoteIpValve`를 사용하면, `ProxyPass` 설정 시 `proxyName`, `proxyPort`, `scheme` 같은 속성을 Connector에 일일이 설정할 필요가 없어 구성이 매우 깔끔해집니다. Tomcat의 Connector는 SSL 설정 없이 일반 HTTP/AJP로 유지합니다. **HTTP Connector:** ```xml ``` **AJP Connector (AJP 연동 시):** ```xml ``` ## 6. 결론 Tomca.t 애플리케이션에 SSL을 적용하는 가장 현대적이고 효율적인 방법은 **리버스 프록시를 사용한 SSL 오프로딩**입니다 1. `openssl` 또는 `certbot`으로 **인증서를 준비**합니다. 2. **Nginx/Apache 프록시 서버에 SSL을 설정**하고 Tomcat으로 요청을 전달합니다. (Docker/Bare-metal 환경에 맞게) 3. **Tomcat의 `server.xml`에는 `RemoteIpValve`를 설정**하여 프록시 환경에 대응합니다. 이 구조를 통해 SSL 인증서 관리와 트래픽 제어를 프록시 서버에 위임하고, Tomcat은 비즈니스 로직에 집중하여 안정적이고 확장성 있는 서비스 아키텍처를 구축할 수 있습니다. ## 7. 고급 구성: End-to-End 암호화 (Re-Encryption) 지금까지 설명한 **SSL 오프로딩**이 가장 일반적이고 효율적인 방법이지만, 내부망 보안 규정과 같은 특수한 요구사항으로 인해 리버스 프록시와 Tomcat 서버 간의 통신까지 암호화해야 하는 경우가 있습니다. 이 구성을 "End-to-End 암호화" 또는 "Re-Encryption"이라고 부릅니다. **구조:** `Client --(HTTPS)--> Nginx/Apache --(HTTPS)--> Tomcat` 이 방식은 보안을 극대화하지만, 다음과 같은 추가 작업이 필요하며 약간의 성능 저하가 발생할 수 있습니다. 1. Tomcat 자체에 SSL/TLS를 설정해야 합니다. 2. 리버스 프록시가 Tomcat의 HTTPS 포트(예: 8443)로 요청을 전달하도록 구성해야 합니다. ### 7.1. 1단계: Tomcat에 SSL 인증서 적용 Tomcat이 HTTPS 요청을 처리할 수 있도록 `conf/server.xml` 파일의 ``를 설정합니다. Tomcat 8.5 이상 버전은 Let's Encrypt에서 발급받은 PEM 형식의 인증서를 직접 사용할 수 있어 편리합니다. **`conf/server.xml` 예시 (PEM 인증서 사용):** ```xml ``` - `certificateKeyFile`: 개인키 파일 (`privkey.pem`)의 경로입니다. - `certificateFile`: 인증서와 체인 인증서가 포함된 파일 (`fullchain.pem`)의 경로입니다. - Docker 환경에서는 `docker-compose.yml` 등을 통해 인증서 파일들을 Tomcat 컨테이너의 `conf/certs/`와 같은 경로에 마운트해야 합니다. ### 7.2. 2단계: 리버스 프록시가 Tomcat(HTTPS)으로 요청을 전달하도록 수정 프록시 서버가 Tomcat의 HTTPS 포트(예: 8443)로 요청을 전달하도록 `proxy_pass` (Nginx) 또는 `ProxyPass` (Apache) 설정을 변경합니다. #### Nginx 설정 변경 `proxy_pass` 지시어의 주소를 `https://...`로 변경합니다. ```nginx # /etc/nginx/sites-available/your-domain.com.conf # ... (server 블록 및 ssl 설정은 동일) ... location / { # Tomcat HTTPS 백엔드로 프록시 # Docker 환경: proxy_pass https://tomcat:8443; proxy_pass https://10.0.0.5:8443; # ... (프록시 헤더 설정은 동일) ... } ``` - **백엔드 인증서 검증**: Nginx는 기본적으로 백엔드 서버(Tomcat)의 인증서를 검증합니다. - **공인 인증서**: Tomcat에 Let's Encrypt와 같은 공인 인증서를 사용했다면 추가 설정이 필요 없습니다. - **자체 서명 인증서**: Tomcat에 자체 서명 인증서를 사용한 경우, Nginx가 해당 인증서를 신뢰하도록 `proxy_ssl_trusted_certificate`를 설정해야 502 오류를 방지할 수 있습니다. ```nginx # Tomcat의 자체 서명 인증서(또는 CA 인증서)를 Nginx가 신뢰하도록 설정 proxy_ssl_trusted_certificate /etc/nginx/certs/tomcat-self-signed.crt; # 개발 환경에서만 검증을 끌 수 있습니다 (보안상 비권장). # proxy_ssl_verify off; ``` #### Apache 설정 변경 `mod_ssl`의 `SSLProxyEngine`을 활성화하고 `ProxyPass` 지시어를 `https://...`로 변경합니다. ```apache # /etc/apache2/sites-available/your-domain-ssl.conf ServerName your-domain.com # ... (SSLEngine on, SSLCertificateFile 등은 동일) ... # 백엔드 SSL 프록시 기능 활성화 SSLProxyEngine on # Tomcat HTTPS 백엔드로 요청 전달 ProxyPass / https://10.0.0.5:8443/ ProxyPassReverse / https://10.0.0.5:8443/ # 자체 서명 인증서 사용 시 신뢰 설정 # SSLProxyCACertificateFile /path/to/tomcat-self-signed.crt # 개발 환경에서만 검증 비활성화 # SSLProxyVerify none ``` 이처럼 End-to-End 암호화는 각 컴포넌트(Tomcat, Nginx/Apache)에 모두 SSL 설정을 적용해야 하므로 전체적인 아키텍처 이해가 중요합니다. - [Configure SSL for Docker (Kendis Help Center)](http://help.kendis.io/en/articles/3382550-configure-ssl-for-docker) - [SSL for docker apps (Docker Community Forums)](https://forums.docker.com/t/ssl-for-docker-apps/102903) - [Let's Encrypt](https://letsencrypt.org/) - [Certbot](https://certbot.eff.org/) - [Apache Module mod_proxy](https://httpd.apache.org/docs/2.4/mod/mod_proxy.html) - [Tomcat Remote IP Valve](https://tomcat.apache.org/tomcat-9.0-doc/config/valve.html#Remote_IP_Valve)