Knowledge Base

Preserving for the future: Shell scripts, AoC, and more

Setting up a transparent proxy for internal network

Overview

This document explains how to set up a web proxy on the internal network so that it can act as a configured proxy as well as transparent network proxy, including both http and https traffic. Exceptions for sites (destinations) as well as clients can be configured.

Architecture

A dd-wrt router is the heart of the example network, at 192.168.1.2. Proxy server proxy.ipa.example.com at 192.168.1.82 provides both transparent proxy and configured proxy behavior.

Configuring router for transparent proxy

The router is set up with dd-wrt firmware: DD-WRT v3.0-r43055 big (05/05/20) and contains a firewall start script (nvram get rc_firewall) which forces all World Wide Web traffic (tcp ports 80 and 443) to a transparent proxy.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#!/bin/sh
WEB_SERVER=192.168.1.14
PROXY_IP=192.168.1.82
OBI_IP=192.168.1.27
CHROMECAST_IP=192.168.1.29
iptables -t mangle -I PREROUTING 2 -p tcp -m multiport --dports 80,443 -s $OBI_IP -j ACCEPT
iptables -t mangle -I PREROUTING 3 -p tcp -m multiport --dports 80,443 -s $CHROMECAST_IP -j ACCEPT
iptables -t mangle -I PREROUTING 5 -p tcp -m multiport --dports 80,443 -s $PROXY_IP -j ACCEPT
iptables -t mangle -I PREROUTING 6 -p tcp -m multiport --dports 80,443 ! -s $PROXY_IP -j MARK --or 3
iptables -t mangle -I PREROUTING 7 -p tcp -m multiport --dports 80,443 -j CONNMARK --save-mark
ip route add $WEB_SERVER via $WEB_SERVER dev br0 table 2
ip route add default via $PROXY_IP dev br0 table 2
ip rule add fwmark 3 table 2

The MARK rule performs a logical OR to set just a few binary flags, so it does not merely "set" all the flags. See Weblink 7. The web server steps are there to ensure that incoming web traffic get to the web server. Observe that CHROMECAST_IP is granted ACCEPT in the firewall. The Chromecast device is given a reserved IP address in DHCP which is described later. An attempt was made to perform logging on the router level, and while this provides IP addresses, it was not sufficient for the needs of this project. See weblink 12.

Configuring the proxy server

First, configure the firewall. Set contents of file /etc/firewalld/direct.xml.

<?xml version="1.0" encoding="utf-8"?>
<direct>
<rule ipv="ipv4" table="nat" chain="PREROUTING" priority="0">-i eth0 -p tcp --dport 80 -j REDIRECT --to-ports 3130</rule>
<rule ipv="ipv4" table="nat" chain="PREROUTING" priority="0">-i eth0 -p tcp --dport 443 -j REDIRECT --to-ports 3129</rule>
</direct>

I was unable to find any other mechanism within firewalld that works for getting the traffic transparently to squid (i.e., that keeps the client IP address). See weblink 7. Also allow a few services and additional ports.

firewall-cmd --permanent --add-service=http --add-service=https --add-service=squid --add-port=3129/tcp
firewall-cmd --permanent --add-port=3130/tcp --add-port=3129/tcp
firewall-cmd --reload
Configuring squid

Configure squid itself. Fill file /etc/squid/squid.conf with contents.

# Research for a log filter includes:
# tail -f /var/log/squid/access.log | grep -iE '200 [0-9]+ GET https?:\/\/[^ ]+'

acl localnet src 10.0.0.0/8 # RFC1918 possible internal network
acl localnet src 172.16.0.0/12  # RFC1918 possible internal network
acl localnet src 192.168.0.0/16 # RFC1918 possible internal network
acl localnet src fc00::/7       # RFC 4193 local private network range
acl localnet src fe80::/10      # RFC 4291 link-local (directly plugged) machines
acl SSL_ports port 443
acl Safe_ports port 80      # http
acl Safe_ports port 21      # ftp
acl Safe_ports port 443     # https
acl Safe_ports port 70      # gopher
acl Safe_ports port 210     # wais
acl Safe_ports port 1025-65535  # unregistered ports
acl Safe_ports port 280     # http-mgmt
acl Safe_ports port 488     # gss-http
acl Safe_ports port 591     # filemaker
acl Safe_ports port 777     # multiling http
acl CONNECT method CONNECT
http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
http_access allow localhost manager
http_access deny manager
http_access allow localnet
http_access allow localhost
http_access deny all
http_port 3128 ssl-bump \
  cert=/etc/pki/tls/certs/proxy.ipa.example.com.pem \
  key=/etc/pki/tls/private/proxy.ipa.example.com-nopw.key \
  cafile=/etc/pki/tls/certs/ca-ipa.example.com.crt \
  generate-host-certificates=on dynamic_cert_mem_cache_size=4MB
https_port 3129 intercept ssl-bump \
  cert=/etc/pki/tls/certs/proxy.ipa.example.com.pem \
  key=/etc/pki/tls/private/proxy.ipa.example.com-nopw.key \
  cafile=/etc/pki/tls/certs/ca-ipa.example.com.crt \
  generate-host-certificates=on dynamic_cert_mem_cache_size=4MB
http_port 3130 intercept ssl-bump \
  cert=/etc/pki/tls/certs/proxy.ipa.example.com.pem \
  key=/etc/pki/tls/private/proxy.ipa.example.com-nopw.key \
  generate-host-certificates=on dynamic_cert_mem_cache_size=4MB
sslcrtd_program /usr/lib64/squid/ssl_crtd -s /var/lib/ssl_db -M 4MB
sslproxy_cert_error allow all
sslproxy_flags DONT_VERIFY_PEER

acl step1 at_step SslBump1
acl SAFE_sites ssl::server_name .telephony.goog .discord.gg
acl DIRECT_sites_name dstdomain .youtube.com
# DIRECT_sites_ip is primarily for youtube; not from nslookup; only from examining squid logs
acl DIRECT_sites_ip dst 172.217.164.74 64.233.177.106 173.194.219.95 64.233.177.99 31.13.65.1 31.13.65.36 74.125.138.95 64.233.185.139 64.233.185.91 172.217.164.78
acl DIRECT_sites any-of DIRECT_sites_name DIRECT_sites_ip
acl DIRECT_site_clients srcdomain tab1.ipa.example.com
acl WORK_clients srcdomain device1.ipa.example.com

always_direct allow DIRECT_sites DIRECT_site_clients
ssl_bump splice DIRECT_sites DIRECT_site_clients
ssl_bump splice SAFE_sites
ssl_bump splice WORK_clients
ssl_bump peek step1
ssl_bump bump all # needed for youtube somehow
coredump_dir /var/spool/squid
refresh_pattern ^ftp:       1440    20% 10080
refresh_pattern ^gopher:    1440    0%  1440
refresh_pattern -i (/cgi-bin/|\?) 0 0%  0
refresh_pattern .       0   20% 4320
strip_query_terms off    #This will allow checking which youtube URLs were visited by user

visible_hostname proxy.ipa.example.com
logformat squid %ts.%03tu %>a %>A %03>Hs %rm %>ru %[un %<a %mt

The order of these entries matters. Additionally, the Chromecast materials may be out of date, but Youtube it still important. An important consideration is to keep the work computer's vpn uninterrupted. Several comments in this file demonstrate which lines are important to that end. The certificate used here is described in a few steps. The cafile used is the root CA cert for the entire FreeIPA infrastructure in the example network. Without this configuration, it is possible that clients who trust the root CA would still not trust the web traffic because squid would have an incomplete cert chain.

Initializing squid

The dynamic ssl certificate database needs to be initialized outside of squid itself. This only needs to be run once.

/usr/lib64/squid/ssl_crtd -c -s /var/lib/ssl_db
chown -R squid /var/lib/ssl_db
restorecon -Rv /var/lib/ssl_db

Without this step, squid could fail to start, or it could fail with an error about crashing too rapidly after so long. See weblink 3.

Getting a valid ssl certificate for squid

See post Getting a valid subCA certificate for squid from FreeIPA

Operations

It is probable that in the future I will want to make changes. A few possible changes are described below.

Disabling transparent proxy at router

If the proxy server is offline, then all outbound www traffic will fail. The best way to disable the transparent proxy setting entirely is to disable the ip rule on the router. Ssh in to the router or visit the web portal. Run command:

ip rule del fwmark 0x3

To re-enable, run the one ip rule command from the rc_firewall startup script.

ip rule add fwmark 3 table 2

Adding a new exclusion for a client on router

To allow a single client unmonitored access to the www, you can add a new rule to the router. This can be achieved by running a single command, or by updating the rc_firewall script.

iptables -t mangle -I PREROUTING 1 -p tcp -m multiport --dports 80,443 -s 192.168.1.300 -j ACCEPT

Where the IP address here is the target client. To remove a specific rule, run

iptables -t mangle -D PREROUTING 1

But ensure that the rule it will remove is not the main one that allows outbound www traffic from the proxy server.

Adding new sites and clients in squid

To exclude the ssl decryption of a site in squid, add it to the SAFE_sites entry in squid.conf and restart squid. You can confirm the syntax is valid without affecting the running daemon by running the following command.

squid -k parse ; echo $?

To exclude a combination of site and client, you can add a site or client to their respesctive lists uner attributes

  • DIRECT_sites_name
  • DIRECT_sites_ip
  • DIRECT_site_clients

Using the proxy by choice

If for some reason you want to manually configure a proxy, you can set the exact following values.

export http_proxy=http://proxy:3128
export https_proxy=http://proxy:3128

It is worth noting that squid has to act differently when being used as a "transparent proxy" so those ports are 3129 (https) and 3130 (http). But port 3128 can be used explicitly for both protocols. Manually choosing to use the proxy can make troubleshooting easier. For example, mobile devices' applications rarely use the host proxy settings, but it can be easier to whitelist requests.

Assign a reserved IP address in dhcp

The internal network uses ISC dhcpd for assigning IP addresses to clients. To add a host to have a specific reservation, you have to edit the dhcpd.conf through the local mechanism dhcpd-control on host dns1.

sudo dhcpd-control --edit

This will open a cached copy of the dhcpd.conf.combined which will get replicated to host dns2 if any changes are made. The changes must be made between headings:

    • BEGIN POOLS FOR SYNC
    • END POOLS FOR SYNC

Add an entry using the MAC address of the client and the desired reserved IP address.

host Obi200 { hardware ethernet 9c:7e:fe:60:64:f3; fixed-address 192.168.1.27; }

Improvements

Need to discover how to get android to think it has Internet access.

References

Man pages

/usr/share/doc/squid-3.5.20/squid.conf.documented

Weblinks

  1. http://silverskysoft.com/open-stack-xwrpr/2015/09/creating-a-subordinate-certificate-authority-in-freeipa/
  2. https://serverfault.com/questions/624879/ssl-crtd-helpers-are-crashing-too-rapidly-in-squid
  3. https://netfilter.org/documentation/HOWTO//packet-filtering-HOWTO.txt General guide for iptables
  4. https://www.agix.com.au/minimal-transparent-squid-proxy-with-ssl-interception-bumping-on-centos-7/ useful examples about ssl_bump
  5. https://wiki.squid-cache.org/Features/SslPeekAndSplice
  6. https://wiki.dd-wrt.com/wiki/index.php/Squid_Transparent_Proxy "Alternative Solution" to Proxy Server on the LAN Subnet
  7. https://forum.dd-wrt.com/phpBB2/viewtopic.php?p=1217511#1217511 I opened a thread on dd-wrt forum for help, but it turned out to be squid-side firewall problems.
  8. https://docs.mitmproxy.org/stable/howto-transparent/ minor examples of ip rules
  9. http://www.silverhawk.net/2016/07/centos-7-squid-and-firewall.html Ultimately unused examples for firewalld and squid acls
  10. https://elatov.github.io/2019/01/using-squid-to-proxy-ssl-sites/ Great initial reference for establishing squid ssl_bump
  11. https://nlug.ml1.co.uk/2014/01/the-missing-firewall-logs-of-dd-wrt/4528 A modest attempt to log traffic on the router, without regard to www hosts and requests.

Comments