Squid MITM proxy with dynamic URL acceptance
It’s not out of box experience, but still possible.
root@user-dev:/etc/squid# cat conf.d/squid-sig.conf
acl localnet src 0.0.0.1-0.255.255.255 # RFC 1122 "this" network (LAN)
acl localnet src 10.0.0.0/8 # RFC 1918 local private network (LAN)
acl localnet src 100.64.0.0/10 # RFC 6598 shared address space (CGN)
acl localnet src 169.254.0.0/16 # RFC 3927 link-local (directly plugged) machines
acl localnet src 172.16.0.0/12 # RFC 1918 local private network (LAN)
acl localnet src 192.168.0.0/16 # RFC 1918 local private network (LAN)
acl localnet src fc00::/7 # RFC 4193 local private network range
acl localnet src fe80::/10 # RFC 4291 link-local (directly plugged) machines
acl localnet src 127.0.0.1 # loopback (including docker healthcheck)
sslcrtd_program /usr/lib64/squid/security_file_certgen
url_rewrite_program /usr/bin/java -Xmx128m -jar /etc/squid/sproxy-filter.jar
url_rewrite_children 1 startup=1 idle=1 concurrency=0 queue-size=1000 on-persistent-overload=ERR
url_rewrite_extras "%>a:%>p %{Proxy-Authorization}>h"
url_rewrite_timeout 30 seconds on_timeout=retry
redirector_bypass off
url_rewrite_bypass off
http_port 8443 ssl-bump generate-host-certificates=on cert=/etc/squid/sproxy/ca.pem key=/etc/squid/sproxy/cakey.pem dynamic_cert_mem_cache_size=128MB options=NO_SSLv3,NO_TLSv1,NO_TLSv1_1 cipher=HIGH:!MEDIUM:!LOW:!SHA1
tls_outgoing_options min-version=1.2 cipher=HIGH:!MEDIUM:!LOW
acl filtered_PASS note clt_conn_tag filtered_PASS
acl filtered_SSL_AV note clt_conn_tag filtered_SSL_AV
acl filtered_SSL note clt_conn_tag filtered_SSL
acl filtered_AV note clt_conn_tag filtered_AV
acl filtered_OK any-of filtered_PASS filtered_SSL_AV filtered_SSL filtered_AV
acl filtered_MITM any-of filtered_SSL filtered_SSL_AV
acl filtered_NO_MITM any-of filtered_PASS filtered_AV
acl step1 at_step SslBump1
ssl_bump splice filtered_NO_MITM
ssl_bump peek step1
ssl_bump bump filtered_MITM
acl filtered_ICAP any-of filtered_SSL_AV filtered_AV
icap_enable on
icap_send_client_ip on
icap_service ICAP_AV respmod_precache icap://127.0.0.1:1344/av bypass=0 on-overload=wait max-conn=20 connection-encryption=off
adaptation_access ICAP_AV allow filtered_ICAP
http_upgrade_request_protocols websocket allow all
http_access allow localnet
http_access deny all
#!/usr/bin/python3
import sys
import base64
auth_by_src = {}
for line in sys.stdin:
tokens = line.rstrip().split(" ")
url = tokens[0]
src = tokens[1]
auth = tokens[2]
user = "-"
if not url.startswith("http"):
user = auth_by_src[src] = base64.b64decode(auth[8:]).decode("utf-8").split(":")[0]
q = ["select from allowed where domain = ? and user = ?", url, user]
with open("/var/log/squid/filter.log", "a+") as log:
log.write("domain "+url+" filtered for "+user+"\n")
if "google" in url:
print("OK clt_conn_tag=filtered_OK", flush=True)
else:
print("OK status=302 url=\"http://www.blocked.org.uk\"", flush=True)
else:
try:
user = auth_by_src[src]
except:
user = "-"
q = ["select from allowed where url = ? and user = ?", url, user]
with open("/var/log/squid/filter.log", "a+") as log:
log.write("url "+url+" filtered for "+user+"\n")
if "google" in url:
print("OK clt_conn_tag=filtered_OK", flush=True)
else:
print("OK status=302 url=\"http://www.blocked.org.uk\"", flush=True)
# end
https_proxy="container:pass@127.0.0.1:8443/" curl -k -vvv https://www.google.com/mail
* Uses proxy env variable https_proxy == 'container:pass@127.0.0.1:8443/'
* Trying 127.0.0.1:8443...
* Connected to 127.0.0.1 (127.0.0.1) port 8443
* CONNECT tunnel: HTTP/1.1 negotiated
* allocate connect buffer
* Proxy auth using Basic with user 'container'
* Establish HTTP proxy tunnel to www.google.com:443
> CONNECT www.google.com:443 HTTP/1.1
> Host: www.google.com:443
> Proxy-Authorization: Basic Y29udGFpbmVyOnBhc3M=
> User-Agent: curl/8.5.0
> Proxy-Connection: Keep-Alive
>
< HTTP/1.1 200 Connection established
<
* CONNECT phase completed
* CONNECT tunnel established, response 200
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519 / id-ecPublicKey
* ALPN: server did not agree on a protocol. Uses default.
* Server certificate:
* subject: CN=www.google.com
* start date: Jul 30 12:50:13 2024 GMT
* expire date: Oct 22 12:50:12 2024 GMT
* issuer: C=EU; O=Security; OU=InternetAccess
* SSL certificate verify result: self-signed certificate in certificate chain (19), continuing anyway.
* Certificate level 0: Public key type EC/secp384r1 (384/192 Bits/secBits), signed using ecdsa-with-SHA256
* Certificate level 1: Public key type EC/secp384r1 (384/192 Bits/secBits), signed using ecdsa-with-SHA384
* using HTTP/1.x
> GET /mail HTTP/1.1
> Host: www.google.com
> User-Agent: curl/8.5.0
> Accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
< HTTP/1.1 301 Moved Permanently
< Location: https://mail.google.com/mail/u/0/
< Cross-Origin-Resource-Policy: cross-origin
< Content-Type: text/html; charset=UTF-8
< X-Content-Type-Options: nosniff
< Date: Fri, 16 Aug 2024 20:28:59 GMT
< Expires: Fri, 16 Aug 2024 20:58:59 GMT
< Cache-Control: public, max-age=1800
< Server: sffe
< Content-Length: 230
< X-XSS-Protection: 0
< Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
< Age: 849
< Cache-Status: user-dev;hit;detail=match
< Via: 1.1 user-dev (squid/6.6)
< Connection: keep-alive
<
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>301 Moved</TITLE></HEAD><BODY>
<H1>301 Moved</H1>
The document has moved
<A HREF="https://mail.google.com/mail/u/0/">here</A>.
</BODY></HTML>
* Connection #0 to host 127.0.0.1 left intact
This is just PoC. In the final code the Python script should connect to a database and check is user is allowed to access domain/url.
How to apply customized proxy for containers? Deploy Spire on Istio (https://github.com/istio/istio/blob/master/samples/security/spire/README.md) and take user context from XFCC HTTP header. Please note that all deployments must use dedicated service accounts.
