접근 로그에 다음이 찍히고 클라이언트는 403 을 받는다.
1741135798.123 0 10.0.0.31 TCP_DENIED/403 4123 GET http://example.com/ - HIER_NONE/- text/html
TCP_DENIED 는 Squid 가 요청을 상위로 보내지 않고 자기가 거절했다는 뜻이다. 즉 대상 서버까지 가 보지도 않았다. HIER_NONE/- 이 그 근거다.
거절의 이유는 거의 언제나 http_access 규칙이다. Squid 는 규칙을 위에서부터 순서대로 보고 처음 일치하는 것을 적용한다. 기본 설정 끝에 다음이 있다.
http_access deny all
여기까지 내려왔다는 것은 그 위에서 허용된 적이 없다는 뜻이다.
로그 형식을 늘려 판단 근거를 남긴다.
logformat detail %ts.%03tu %6tr %>a %Un %Ss/%03>Hs %<st %rm %ru %[un %Sh/%<a %mt
access_log /var/log/squid/access.log detail
디버그 섹션을 켜면 어떤 ACL 에서 걸렸는지 캐시 로그에 나온다. 부하가 크므로 확인이 끝나면 되돌린다.
debug_options ALL,1 28,3
sudo systemctl reload squid
sudo tail -f /var/log/squid/cache.log
클라이언트 대역이 localnet 에 없다. 기본 설정의 localnet 은 사설 대역을 나열해 두지만, 실제 클라이언트 대역이 빠져 있거나 해당 줄이 주석 처리돼 있는 경우가 많다.
acl localnet src 10.0.0.0/8
acl localnet src 172.16.0.0/12
acl localnet src 192.168.0.0/16
http_access allow localnet
http_access deny all
http_access allow localnet 은 반드시 deny all 보다 위에 있어야 한다.
포트가 Safe_ports 에 없다. 표준이 아닌 포트로 나가려 하면 여기서 막힌다.
acl Safe_ports port 8080
acl Safe_ports port 9000
HTTPS 인데 SSL_ports 에 없다. HTTPS 는 CONNECT 메서드로 터널을 요청하고, Squid 는 CONNECT 를 SSL_ports 로만 허용한다.
acl SSL_ports port 443
acl SSL_ports port 8443
http_access deny CONNECT !SSL_ports
비표준 HTTPS 포트를 쓰려면 SSL_ports 에 추가한다.
문법을 먼저 검사한다.
sudo squid -k parse
통과하면 재적재한다. 기존 연결을 끊지 않는다.
sudo squid -k reconfigure
로드밸런서가 Squid 앞에 있을 때, L4 헬스체크가 프록시 프로토콜을 모르고 단순 HTTP GET 을 던지는 경우가 있다. Squid 는 정방향 프록시라 절대 URL 이 아닌 요청을 이해하지 못해 400 이나 403 을 돌려주고, 로드밸런서는 서버를 죽은 것으로 표시한다.
헬스체크 전용 포트를 따로 두는 것이 가장 깔끔하다. Squid 에 헬스체크를 시키지 말고 옆에 가벼운 응답기를 둔다.
sudo dnf install -y nginx
server {
listen 8081;
location /healthz {
access_log off;
return 200 "ok\n";
}
}
로드밸런서의 헬스체크 대상을 8081/healthz 로 두고, 실제 트래픽은 Squid 의 3128 로 보낸다. Squid 프로세스가 죽었는지까지 확인하려면 헬스체크 스크립트에서 3128 에 TCP 연결만 시도해 보는 방식으로 보완한다.
Squid 자체에서 특정 경로에 고정 응답을 내게 만드는 방법도 있으나 정방향 프록시의 용도를 벗어나 설정이 복잡해진다. 별도 포트를 권한다.
Squid 는 HTTP 프록시다. 임의의 TCP 를 그대로 중계하지 않는다. JDBC, 데이터베이스 프로토콜, 사설 바이너리 프로토콜은 Squid 로 통과시킬 수 없다.
CONNECT 터널이 있지만 이것은 클라이언트가 HTTP 프록시 규약을 알고 CONNECT host:port 를 먼저 보낼 수 있을 때만 성립한다. 일반 JDBC 드라이버는 그런 기능이 없다.
용도에 맞는 것을 고른다.
| 필요 | 도구 |
|---|---|
| HTTP·HTTPS 정방향 프록시, URL 단위 통제, 캐시 | Squid |
| 임의 TCP 중계, 부하 분산, 헬스체크 | HAProxy |
| 클라이언트가 목적지를 고르는 범용 프록시 | SOCKS5 (microsocks, dante) |
| 고정된 한 쌍의 포트 전달 | socat, iptables DNAT |
HAProxy 로 TCP 를 중계할 때는 목적지가 고정된다. 클라이언트가 목적지를 스스로 고르게 하려면 SOCKS5 를 쓴다.
frontend db_in
bind *:30002
mode tcp
default_backend db_out
backend db_out
mode tcp
server db1 10.0.0.41:1521 check