Building a Squid HTTP(S) proxy to route traffic through a VPN from inside a FreeBSD jail

In this post I’m going to cover how to build a Squid proxy server to route traffic out securely through your VPN using the openVPN client. Best of all we’re going to run the whole thing inside a FreeBSD jail using the VIMAGE virtualised networking stack. I’m using CBSD instead of ezjail for this post – more on that to come. Anyway, let’s get started:

Prep

First we need to compile our kernel with support for VIMAGE – FreeBSD’s virtulized network stack:

# cd /usr/src/sys/amd64/conf/

Now your going to want to edit your kernel config, probably called GENERIC. Add these two devices:

...
device          epair
device          if_bridge

and enable VIMAGE:

...
options         VIMAGE

Now navigate back to the sources dir, build and then install your new kernel:

# cd /usr/src
# make buildkernel KERNCONF=GENERIC
# make installkernel KERNCONF=GENERIC

 

Reboot into your new kernel:

# shutdown -r now

As we’re going to use the virtualised network stack, don’t worry about creating a loopback device or IP alias, all we need is a devfs rule to allow our jail access to tunnel devices, net devices and the Berkley packet filter. Create a new section in your CBSD devfs.rules:

 

[devfsrules_jail_with_bpf=7]
add include $devfsrules_hide_all
add include $devfsrules_unhide_basic
add include $devfsrules_unhide_login
add path 'bpf*' unhide
add path 'net*' unhide
add path 'tun*' unhide
Building the jail with cbsd

Now use the tui to create the jail:

# cbsd jconstruct-tui

Most of the options are pretty self explanatory so I’m not going to run through it all. Make sure you set these two as a minimum:

devfs_ruleset    6
vnet             [X]

That’s it – start the jail up:

# cbsd jstart jailname
Inside the jail

Do your usual prep for /­­etc/resolv.conf and /­etc/hosts, then add the IP address you created earlier to your new virtual interface in /­etc/rc.conf:

ifconfig_eth0="inet 192.168.0.107 netmask 255.255.255.0"
defaultrouter="192.168.0.1"

next up install openvpn:

# cd /usr/ports/*/openvpn
# make install

enable openvpn and set the interface in your /­etc/rc.conf:

# echo 'openvpn_if="tun"' >> /­etc/rc.conf
# echo 'openvpn_enable="YES"' >> /­etc/rc.conf

add your tunnel:

# kldload if_tun
OpenVPN

Now we can start configuring openvpn, grab your VPN providers cert – I use VyprVPN by Golden Frog:

# cd /usr/local/­etc/openvpn
# fetch http://www.goldenfrog.com/downloads/ca.vyprvpn.com.crt

Now for the openvpn config, here’s mine, make sure you call it openvpn.conf:

port 1194
client
dev tunN
proto udp
remote uk1.vyprvpn.com
resolv-retry infinite
nobind
persist-key
persist-tun
persist-remote-ip
ca ca.vyprvpn.com.crt
tls-remote uk1.vyprvpn.com
auth-user-pass vyprvpn.pas
comp-lzo
verb 4
keepalive 10 60
route-noexec
script-security 2
up /usr/local/­etc/openvpn/set_routes.sh

You will need to tweak these to fit – make sure you create a file with your username and password in this dir, I’ve called mine vyprvpn.pas.

Now you’ll note for the last 3 options I have disabled creating the routes, and enabled a script at openvpn startup, I had problems getting openvpn to create the routes for me so I ended up writing a shell script to read /var/log/messages and pull out the route info and set it after openvpn has connected:

option1=$(tail -r /var/log/messages | grep "Peer Connection Initiated with" | head -n 1 | grep -o "[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*")
option2=$(tail -r /var/log/messages | grep "PUSH: Received control message:" | head -n 1 | grep -o "route-gateway [0-9]*\.[0-9]*\.[0-9]*\.[0-9]*" | awk '{print $2}')
/sbin/route add -net $option1 192.168.0.1 255.255.255.255
/sbin/route add -net 0.0.0.0 $option2 128.0.0.0
/sbin/route add -net 128.0.0.0 $option2 128.0.0.0

I’m going to re-write this when I get time, as it was just a quick-fix and the info to set the routes actually exists in some variables set by openvpn.

Now, you should be connected! As I am using VyprVPN I’ll use their page to check my IP address:

# fetch --no-verify-peer -o - https://www.goldenfrog.com/whatismyipaddress | grep -i "public ip" | grep -o "[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*"
Squid

Install it – review all the options carefully when you compile:

# cd /usr/ports/*/squid
# make install

Here is my config, I don’t have any interest in caching anything for now, I just want to route all my http traffic through my VPN, but in a later post we’ll be setting up caching for the FreeBSD and various linux sites. The config is located in /usr/local/­etc/squid/squid.conf:

acl local_net src 192.168.0.0/24
http_access allow local_net
http_access allow localhost
cache_access_log /var/log/squid/access.log
cache_log /var/log/squid/cache.log
cache_store_log /var/log/squid/store.log
http_port 8081
visible_hostname Hermes.core.net
cache_effective_user root
cache_effective_group wheel
cache_dir null /tmp
cache deny all
Sharing the config

You can have clients on your network automatically pick up your proxy server by using a pac file. You’ll need to host it on a web server. Set it using DHCP option 252:

dhcp-option=252,http://artemis.core.net/proxy.pac

The contents of my proxy.pac are:

function FindProxyForURL(url, host)
{
if (isInNet(host, "192.168.0.0", "255.255.0.0")) {
     return "DIRECT";
  } else {
     if (shExpMatch(url, "http:*"))
        return "PROXY Hermes:8081" ;
     if (shExpMatch(url, "https:*"))
        return "PROXY Hermes:8081" ;
     return "DIRECT";
  }
}

You should have a proxy server set up, being automatically configured on your clients by your DHCP server and routing all of your traffic through your VPN.

If you have any issues grab me on the FreeBSD forums – username tabs.

Published by

Guy Tabrar

*NIX Admin.