AJP 커넥터 설정과 보안
AJP(Apache JServ Protocol)는 Apache HTTPD와 Tomcat 사이의 고속 바이너리 프로토콜입니다. 이 챕터에서는 AJP의 동작 원리, 최신 보안 설정(Ghostcat 취약점 대응), 그리고 실무에서 AJP를 사용해야 할 상황과 대안을 다룹니다.
AJP 프로토콜 개요
AJP는 웹 서버(Apache HTTPD)와 WAS(Tomcat) 간의 통신을 위해 설계된 바이너리 프로토콜입니다.
[클라이언트] ← HTTP/HTTPS → [Apache HTTPD :80/443]
↓ AJP (바이너리)
[Tomcat :8009]
↓
[Java 웹 애플리케이션]
HTTP 프록시 vs AJP 비교
| 항목 | HTTP 프록시 | AJP |
|---|---|---|
| 프로토콜 | HTTP (텍스트) | 바이너리 |
| 성능 | 보통 | 높음 (파싱 오버헤드 적음) |
| 설정 복잡도 | 낮음 | 중간 |
| 보안 이슈 | 표준 HTTP 보안 | Ghostcat 취약점 주의 |
| 현재 권장 | 권장 | 레거시 시스템 전용 |
현대적 권장사항: 신규 시스템에서는 AJP 대신 HTTP 프록시(
mod_proxy_http)를 사용합니다. AJP는 Apache+Tomcat 구성의 레거시 방식입니다.
Ghostcat 취약점 (CVE-2020-1938)
2020년 2월 발견된 Ghostcat(유령 고양이)은 AJP 커넥터를 통해 Tomcat 웹 애플리케이션 내의 임의 파일을 읽을 수 있는 심각한 취약점입니다.
영향 범위
| Tomcat 버전 | 취약한 버전 | 패치 버전 |
|---|---|---|
| Tomcat 9 | 9.0.0.M1 ~ 9.0.30 | 9.0.31+ |
| Tomcat 8.5 | 8.5.0 ~ 8.5.50 | 8.5.51+ |
| Tomcat 7 | 7.0.0 ~ 7.0.99 | 7.0.100+ |
대응 방법
1순위: AJP 커넥터 비활성화 (가장 안전)
<!-- server.xml — AJP 커넥터 주석 처리 -->
<!-- 아래 줄을 주석 처리하거나 삭제 -->
<!-- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443"/> -->
2순위: secretRequired 강제 (AJP가 반드시 필요한 경우)
<Connector protocol="AJP/1.3"
address="127.0.0.1"
port="8009"
redirectPort="8443"
secretRequired="true"
secret="MyS3cr3tAJPK3y!2024"/>
3순위: 접근 IP 제한
<Connector protocol="AJP/1.3"
address="127.0.0.1" <!-- localhost만 허용 -->
port="8009"
redirectPort="8443"
secretRequired="true"
secret="MyS3cr3tAJPK3y!"/>
AJP 커넥터 설정 (보안 적용)
Tomcat 쪽 설정
<!-- server.xml -->
<Connector protocol="AJP/1.3"
address="127.0.0.1"
port="8009"
redirectPort="8443"
maxThreads="150"
minSpareThreads="5"
connectionTimeout="10000"
secretRequired="true"
secret="YourSecureSecretHere"
allowedRequestAttributesPattern=".*"/>
| 속성 | 설명 | 보안 설정 |
|---|---|---|
address | 수신 IP 주소 | 127.0.0.1 (로컬만) |
port | AJP 포트 | 8009 (기본) |
secretRequired | 시크릿 필수 여부 | true |
secret | 공유 시크릿 | 강력한 무작위 문자열 |
allowedRequestAttributesPattern | 허용 요청 속성 패턴 | 필요한 것만 허용 |
Apache HTTPD 쪽 설정 (mod_proxy_ajp)
# 필요 모듈 활성화
sudo a2enmod proxy
sudo a2enmod proxy_ajp
sudo systemctl reload apache2
# /etc/apache2/sites-available/myapp.conf
<VirtualHost *:80>
ServerName example.com
ProxyRequests Off
ProxyPreserveHost On
# AJP 프록시 (시크릿 포함)
<Proxy "ajp://localhost:8009/?secret=YourSecureSecretHere">
Require all granted
</Proxy>
ProxyPass / ajp://localhost:8009/?secret=YourSecureSecretHere
ProxyPassReverse / ajp://localhost:8009/?secret=YourSecureSecretHere
</VirtualHost>
AJP vs HTTP 프록시 — 언제 무엇을 쓸까
HTTP 프록시 사용 권장 상황
# mod_proxy_http 사용 (권장)
ProxyPass /api/ http://localhost:8080/api/
ProxyPassReverse /api/ http://localhost:8080/api/
# 장점:
# - 설정 단순
# - SSL/TLS 종료 관리 용이
# - Ghostcat 취약점 없음
# - 다양한 로드밸런서와 호환
AJP 사용이 유리한 상황
- 아주 레거시한 Apache+Tomcat 구성을 유지해야 할 때
- 네트워크 대역폭이 극히 제한적인 내부 환경
- 기존
mod_jk(오래된 AJP 모듈) 사용 중 점진적 전환 기간
mod_jk에서 mod_proxy_ajp로 마이그레이션
오래된 시스템에서 mod_jk를 사용 중이라면 mod_proxy_ajp로 전환하는 것이 권장됩니다.
# 기존 mod_jk 설정 (레거시)
# LoadModule jk_module modules/mod_jk.so
# JkWorkersFile /etc/httpd/conf/workers.properties
# JkLogFile /var/log/httpd/mod_jk.log
# JkMount /* worker1
# 전환: mod_proxy_ajp 방식
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
ProxyPass / ajp://localhost:8009/?secret=MySecret
ProxyPassReverse / ajp://localhost:8009/?secret=MySecret
AJP 커넥터 상태 확인
# AJP 포트 리스닝 확인
ss -tlnp | grep 8009
# AJP 연결 테스트 (netcat)
echo -e "\x12\x34\x00\x01\x02" | nc localhost 8009
# Tomcat 로그에서 AJP 관련 메시지 확인
tail -f /opt/tomcat/latest/logs/catalina.out | grep -i ajp
완전한 보안 구성 예시
Tomcat server.xml (AJP 비활성화 권장)
<?xml version="1.0" encoding="UTF-8"?>
<Server port="-1" shutdown="SHUTDOWN">
<!-- ... listeners ... -->
<Service name="Catalina">
<!-- HTTP 커넥터만 활성화 -->
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
address="127.0.0.1"/> <!-- 로컬만 허용 -->
<!-- AJP 커넥터 비활성화 (Ghostcat 방지) -->
<!--
<Connector protocol="AJP/1.3"
address="127.0.0.1"
port="8009"
redirectPort="8443"
secretRequired="true"
secret="ChangeMe!"/>
-->
<Engine name="Catalina" defaultHost="localhost">
<!-- ... -->
</Engine>
</Service>
</Server>
Apache HTTPD (HTTP 프록시 방식)
<VirtualHost *:80>
ServerName example.com
Redirect permanent / https://example.com/
</VirtualHost>
<VirtualHost *:443>
ServerName example.com
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
ProxyRequests Off
ProxyPreserveHost On
# HTTP 프록시 (AJP 대신 권장)
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/
# 실제 IP 전달
RequestHeader set X-Forwarded-Proto "https"
RequestHeader set X-Forwarded-For "%{REMOTE_ADDR}s"
</VirtualHost>
Summary
| 항목 | 권장 설정 |
|---|---|
| Ghostcat 대응 | AJP 커넥터 비활성화 (주석 처리) |
| 불가피한 AJP 사용 | secretRequired="true" + 강력한 시크릿 |
| AJP 수신 IP | address="127.0.0.1" (로컬만) |
| 신규 시스템 | HTTP 프록시(mod_proxy_http) 권장 |
레거시 mod_jk | mod_proxy_ajp로 전환 권장 |