Objectifs
Le but est de d'obtenir un serveur avec un proxy Squid qui utilise le VPN. Ainsi la navigation apparaît sortir au bout du tunnel VPN, peut-être dans un autre pays.
Un évolution serait de mettre en place un proxy SOCKS v5 pour augmenter les possibilités offertes par ce serveur.
Prérequis
Avant de commencer il vous faut bien entendu un Raspberry PI, un type 1 B est utilisé dans mon cas. Celui-ci doit être installé avec une distribution Raspberry Pi OS ou Ubuntu sans interface graphique ( cela réduit énormément les mise à jour ) avec le réseau de configurer et le SSH actif.
Il vous faut choisir un fournisseur de VPN pour pouvoir monter le tunnel vers un des serveurs de ce fournisseur. Le paramétrage est disponible dans la console de votre compte client du fournisseur de VPN choisit.
Iptables
Afin de paramétrer IPTables, nous allons utiliser les services initd pour configurer le mur de feu. N'ayant qu'une seule interface réseau cela convient tout à fait, il y a d'autres solutions avec les scripts if-up.d et if-down.d. Pour cela nous ajoutons un fichier iptables dans la liste des services et nous l'activons avec la commande update-rc.d :
#!/bin/bash
### BEGIN INIT INFO
# Provides: iptables
# Required-Start:
# Required-Stop:
# X-Start-Before:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Enables and disables firewall rules
# Description: Enables and disables the firewall rules
# using iptables(8)
### END INIT INFO
LOCKFILE=/var/lock/firewall.lock
IPTABLES=/sbin/iptables
IPV4_NETWORK=192.168.0.0/24
IP6TABLES=/sbin/ip6tables
IPV6_NETWORK=2001:861:e040:e2b0::/64
PATH=/sbin:/bin:/usr/sbin:/usr/bin
. /lib/lsb/init-functions
start_firewall() {
if [ -f "${LOCKFILE}" ]; then
log_failure_msg "Lock file exists, firewall is already enabled?"
exit 1
fi
if ! touch ${LOCKFILE} ; then
log_failure_msg "Cannot create a lock file!"
exit 1
fi
log_success_msg "Enabling firewall rules using iptables(8)."
########## Iptables rules ##########
## Remove any existing rules from all chains
${IPTABLES} -F
${IPTABLES} -F -t nat
${IPTABLES} -F -t mangle
${IP6TABLES} -F
${IP6TABLES} -F -t nat
${IP6TABLES} -F -t mangle
## Remove any pre-existing user-defined rules
${IPTABLES} -X
${IPTABLES} -X -t nat
${IPTABLES} -X -t mangle
${IP6TABLES} -X
${IP6TABLES} -X -t nat
${IP6TABLES} -X -t mangle
# Zero the counters
${IPTABLES} -Z
${IP6TABLES} -Z
# ${IPTABLES} -N LOG_DROP
# ${IPTABLES} -A LOG_DROP -j LOG --log-prefix '[IPTABLES DROP] : '
# ${IPTABLES} -A LOG_DROP -j DROP
# ${IP6TABLES} -N LOG_DROP
# ${IP6TABLES} -A LOG_DROP -j LOG --log-prefix '[IPTABLES DROP] : '
# ${IP6TABLES} -A LOG_DROP -j DROP
########## DEFAULT RULES ##########
${IPTABLES} -P INPUT DROP
${IP6TABLES} -P INPUT DROP
${IPTABLES} -P OUTPUT DROP
${IP6TABLES} -P OUTPUT DROP
${IPTABLES} -P FORWARD DROP
${IP6TABLES} -P FORWARD DROP
## Accept looplocal in input.
${IPTABLES} -I INPUT -i lo -j ACCEPT
${IP6TABLES} -I INPUT -i lo -j ACCEPT
${IPTABLES} -I OUTPUT -o lo -j ACCEPT
${IP6TABLES} -I OUTPUT -o lo -j ACCEPT
########## COMMON SECURITY RULES ##########
## Drop XMAS and NULL scans.
${IPTABLES} -A INPUT -p tcp --tcp-flags FIN,URG,PSH FIN,URG,PSH -j DROP
${IP6TABLES} -A INPUT -p tcp --tcp-flags FIN,URG,PSH FIN,URG,PSH -j DROP
${IPTABLES} -A INPUT -p tcp --tcp-flags ALL ALL -j DROP
${IP6TABLES} -A INPUT -p tcp --tcp-flags ALL ALL -j DROP
${IPTABLES} -A INPUT -p tcp --tcp-flags ALL NONE -j DROP
${IP6TABLES} -A INPUT -p tcp --tcp-flags ALL NONE -j DROP
${IPTABLES} -A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j DROP
${IP6TABLES} -A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j DROP
## Drop silently broadcasts.
${IPTABLES} -A INPUT -m pkttype --pkt-type broadcast -j DROP
${IP6TABLES} -A INPUT -m pkttype --pkt-type broadcast -j DROP
########## NORMAL RULES ##########
## Permit open connexion to send packets.
${IPTABLES} -A OUTPUT -m state ! --state INVALID -j ACCEPT
${IP6TABLES} -A OUTPUT -m state ! --state INVALID -j ACCEPT
## Permit open connection to receive packets.
${IPTABLES} -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
${IP6TABLES} -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
## Allow SSH
${IPTABLES} -A INPUT -p tcp -s $IPV4_NETWORK --dport 22 -j ACCEPT
${IP6TABLES} -A INPUT -p tcp -s $IPV6_NETWORK --dport 22 -j ACCEPT
## Allow FTP as client
# ${IPTABLES} -A INPUT -p tcp -s $IPV4_NETWORK --sport 20 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
# ${IP6TABLES} -A INPUT -p tcp -s $IPV6_NETWORK --sport 20 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
## Allow Transmission web interface
${IPTABLES} -A INPUT -p tcp -s $IPV4_NETWORK --dport 9091 -j ACCEPT
${IP6TABLES} -A INPUT -p tcp -s $IPV6_NETWORK --dport 9091 -j ACCEPT
## Allow specified BitTorrrent client port
${IPTABLES} -A INPUT -p tcp --dport 9999 -j ACCEPT
${IP6TABLES} -A INPUT -p tcp --dport 9999 -j ACCEPT
## Allow incoming HTTP traffic to Squid Proxy
${IPTABLES} -A INPUT -p tcp -s $IPV4_NETWORK --dport 8080 -j ACCEPT
${IP6TABLES} -A INPUT -p tcp -s $IPV6_NETWORK --dport 8080 -j ACCEPT
## Log packets in forward.
# ${IPTABLES} -A FORWARD -j LOG
# ${IP6TABLES} -A FORWARD -j LOG
log_success_msg "Firewall rules loaded successfully."
}
reset_firewall() {
log_success_msg "Disabling iptables(8) firewall rules."
# Remove any existing rules from all chains
${IPTABLES} -F
${IPTABLES} -F -t nat
${IPTABLES} -F -t mangle
${IP6TABLES} -F
${IP6TABLES} -F -t nat
${IP6TABLES} -F -t mangle
# Remove any pre-existing user-defined rules
${IPTABLES} -X
${IPTABLES} -X -t nat
${IPTABLES} -X -t mangle
${IP6TABLES} -X
${IP6TABLES} -X -t nat
${IP6TABLES} -X -t mangle
# Zero the counters
${IPTABLES} -Z
${IP6TABLES} -Z
${IPTABLES} -P INPUT ACCEPT
${IPTABLES} -P OUTPUT ACCEPT
${IPTABLES} -P FORWARD ACCEPT
${IP6TABLES} -P INPUT ACCEPT
${IP6TABLES} -P OUTPUT ACCEPT
${IP6TABLES} -P FORWARD ACCEPT
log_success_msg "Firewall shutdown successful."
}
status_firewall() {
if [ -f "${LOCKFILE}" ]; then
log_success_msg "Firewall is enabled."
else
log_success_msg "Firewall is disabled."
fi
}
case "${1}" in
start)
start_firewall
;;
reset)
reset_firewall
;;
stop)
reset_firewall
rm -f "${LOCKFILE}"
;;
status)
status_firewall
;;
reload|restart|force-reload)
reset_firewall
rm -f "${LOCKFILE}"
start_firewall
;;
*)
echo "usage: ${0} {start|stop|reload|restart|force-reload|reset" >&2
;;
esac
OpenVPN
On utilisera la version OpenVPN fournie avec la distribution. Cependant il faut désactiver le service de démarrage du serveur et créer un fichier dans /etc/init.d pour démarrer OpenVPN en tant que client. Les identifiants de connexion doivent être écrits dans le fichier /root/openvpn.auth ( première ligne avec le login et second ligne avec le mot de passe en clair ) dans l'exemple ci-dessous :
#! /bin/sh -e
### BEGIN INIT INFO
# Provides: openvpn-client
# Required-Start: $local_fs $network
# Required-Stop: $local_fs $network
# Default-Start:
# Default-Stop: 0 1 2 3 4 5 6
# Short-Description: Openvpn client
# Description: Connect a openvpn tunnel
#
### END INIT INFO
. /lib/lsb/init-functions
OPENVPN_DIR=/etc/openvpn
OPENVPN_CLIENT=/usr/sbin/openvpn
OPENVPN_AUTHFILE=/root/openvpn.auth
# Source defaults file. Edit that file to configure this script.
if [ -e /etc/default/openvpn-client ]; then
. /etc/default/openvpn-client
fi
if [ "$OPENVPN_CLIENT" = "/usr/sbin/openvpn" ]; then
test -x "$OPENVPN_CLIENT" || exit 0
elif [ ! -x "$OPENVPN_CLIENT" ]; then
log_failure_msg "Openvpn client '$OPENVPN_CLIENT' does not exist or is not" \
"executable."
exit 5
fi
if [ ! -f "$OPENVPN_AUTHFILE" -o ! -r "$OPENVPN_AUTHFILE" ]; then
log_failure_msg "Openvpn authentication '$OPENVPN_AUTHFILE' does not exist or is not" \
"executable."
exit 5
fi
if [ ! -d "$OPENVPN_DIR" ]; then
log_failure_msg "OPENVPN configuration directory '$OPENVPN_DIR' does not exist."
exit 6
fi
if [ ! -z "$OPENVPN_OPTS" ]; then
(echo $OPENVPN_OPTS | grep -- '--daemon' 1>/dev/null) &&
log_warning_msg "\`--daemon' option detected \
on /etc/default/openvpn, this \
can cause problems on openvpn. The option \
will be suppressed"
OPENVPN_OPTS=`echo "$OPENVPN_OPTS" | sed 's/--daemon//g'`
fi
PIDFILE=/var/run/openvpn-client.pid
CONFIGFILE=openvpn.conf
DESC="Openvpn client"
NAME=`basename $OPENVPN_CLIENT`
OPENVPN_OPTS=" --config $OPENVPN_DIR/$CONFIGFILE --writepid $PIDFILE --daemon $NAME --auth-user-pass $OPENVPN_AUTHFILE --script-security 2"
is_running()
{
retval=1
if [ -r $PIDFILE ]; then
pid=`cat $PIDFILE`
if [ -e /proc/$pid ]; then
procname=`/bin/ps h -p $pid`
[ -n "$procname" ] && retval=0
fi
fi
return $retval
}
start()
{
log_begin_msg "Starting $DESC: $NAME"
if is_running; then
log_progress_msg "already running"
else
start-stop-daemon --start --quiet --background --pidfile $PIDFILE \
--make-pidfile \
--exec $OPENVPN_CLIENT -- $OPENVPN_OPTS
fi
log_end_msg 0
if [ "$SCHEDULE" = "1" ]; then
schedule
fi
}
stop()
{
log_begin_msg "Stopping $DESC: $NAME"
if ! is_running; then
log_progress_msg "not running"
else
start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE \
--exec $OPENVPN_CLIENT
# Wait until really stopped - $pid is set from is_running
# (waiting for max 60s (600 cycles at 100ms))
i=0
while kill -0 "$pid" 2> /dev/null; do
if [ $i = '600' ]; then
break;
else
if [ $i = '0' ]; then
echo -n " ... waiting "
elif [ $(($i%10)) = 0 ]; then
echo -n "."
fi
i=$(($i+1))
sleep .1
fi
done
fi
rm -f $PIDFILE
log_end_msg 0
}
status()
{
STATUS="Status of $DESC:"
if is_running; then
log_success_msg "$STATUS running"
else
log_success_msg "$STATUS stopped"
fi
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart|force-reload)
stop
sleep 1
start
;;
status)
status
;;
schedule)
schedule
;;
*)
log_success_msg "Usage: $0 {start|stop|restart|force-reload|status}" >&2
exit 1
;;
esac
Squid
Nous utilisons la version Squid de la distribution utilisée. Le fichier de configure squid.conf doit rester assez simple pour l'usage voulu :
acl localnet src fc00::/7 # RFC 4193 local private network range
acl localnet src XX.XX.XX.XX/?? # Local LAN
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
include /etc/squid/conf.d/*
http_access allow localnet
http_access allow localhost
http_access deny all
http_port 8080
coredump_dir /var/spool/squid
refresh_pattern ^ftp: 1440 20% 10080
refresh_pattern ^gopher: 1440 0% 1440
refresh_pattern -i (/cgi-bin/|\?) 0 u0% 0
refresh_pattern . 0 20% 4320
Celui-ci doit cependant se lancer après le VPN afin de la route par défaut ait déjà été changée. Aussi le service sera lancé depuis un autre script.
Du coup dans le fichier /etc/init.d/squid on prendra soin au début du fichier de modifier les lignes pour obtenir :
# Default-Start:
# Default-Stop: 0 1 2 3 4 5 6
pour ensuite lancer la commande de mise à jour les liens symboliques :
update-rc.d squid defaults
Transmission
Pour transmission la version de la distribution est largement suffisante. Comme pour Squid, celui-ci doit se lancer après le VPN afin que la route par défaut soit celle du VPN. De la même manière in modifiera le fichier /etc/init.d/transmission-daemon
# Default-Start:
# Default-Stop: 0 1 2 3 4 5 6
pour ensuite lancer la commande de mise à jour les liens symboliques :
update-rc.d squid defaults
Pensez à monter un disque externe ou avoir une carte SD suffisamment importante en fonction de ce que voulez faire.
Démarrage des services
Pour lancer tout cela j'ai créé un fichier de lancement des services nommé /etc/init.d/vpnbox :
#!/bin/sh
### BEGIN INIT INFO
# Provides: vpnbox
# Required-Start: $local_fs $network
# Required-Stop: $local_fs $network
# Default-Start: 3 4 5
# Default-Stop: 0 1 2 6
# Short-Description: VPN box services
# Description: Connect an open vpn tunnel and start squid and transmission daemon
#
### END INIT INFO
. /lib/lsb/init-functions
VPN_SERVICE="openvpn-client"
VPN_SERVICE_STATUS_RESPONSE='Openvpn client'
BITTORRENT_SERVICE="transmission-daemon"
BITTORRENT_DESC='Transmission BitTorrent Client'
SQUID_SERVICE="squid"
SQUID_DESC="Squid HTTP Proxy 3.x"
test_vpn_actif(){
content=$(wget https://wtfismyip.com/text -q -O-)
isValid=$(echo $content | sed 's/^\([0-9]\{1,3\}\.\)\{3\}[0-9]\{1,3\}$/1/g')
if [ "$(echo $isValid | grep "^[[:digit:]]*$")" ] && [ 1 -eq $isValid ]; then
return 1
else
return 0
fi
}
test_vpn_connected(){
content=$(ifconfig tun0 | egrep '[[:space:]]*netmask[[:space:]]*([0-9]{1,3}\.){3}([0-9]{1,3})[[:space:]]*destination[[:space:]]*([0-9]{1,3}\.){3}([0-9]{1,3})')
if [ -n "$content" ]; then
return 1
else
return 0
fi
}
wait_vpn_connected(){
test_vpn_connected
isConnected=$?
while [ $isConnected -ne 1 ]; do
sleep 10
test_vpn_connected
isConnected=$?
done
}
start(){
log_daemon_msg "Starting $VPN_DESC" "$VPN_SERVICE"
service $VPN_SERVICE start
wait_vpn_connected
test_vpn_actif
isActif=$?
if [ $isActif -ne 1 ]; then
service $VPN_SERVICE stop
return false
else
log_daemon_msg "Starting $SQUID_DESC" "$SQUID_SERVICE"
service $SQUID_SERVICE start
log_daemon_msg "Starting $BITTORRENT_DESC" "$BITTORRENT_SERVICE"
service $BITTORRENT_SERVICE start
fi
}
stop(){
log_daemon_msg "Stopping $BITTORRENT_DESC" "$BITTORRENT_SERVICE"
service $BITTORRENT_SERVICE stop
log_daemon_msg "Stopping $SQUID_DESC" "$SQUID_SERVICE"
service $SQUID_SERVICE stop
log_daemon_msg "Stopping $VPN_DESC" "$VPN_SERVICE"
service $VPN_SERVICE stop
return 0
}
case "$1" in
start)
log_daemon_msg "Starting $DESC" "$NAME"
if start ; then
log_end_msg $?
else
log_end_msg $?
fi
;;
stop)
log_daemon_msg "Stopping $DESC" "$NAME"
if stop ; then
log_end_msg $?
else
log_end_msg $?
fi
;;
restart)
log_daemon_msg "Restarting $DESC" "$NAME"
stop
if start ; then
log_end_msg $?
else
log_end_msg $?
fi
;;
status)
service $VPN_SERVICE status
val=$?
service $SQUID_SERVICE status
val=$(($?+$val))
service $BITTORRENT_SERVICE status
val=$(($?+$val))
exit $val
;;
check)
test_vpn_connected
isConnected=$?
if [ $isConnected -eq 1 ]; then
test_vpn_actif
isActif=$?
if [ $isActif -ne 1 ]; then
stop
start
fi
else
stop
start
fi;
;;
*)
echo "Usage: /etc/init.d/$NAME {start|stop|restart|status|check}"
exit 3
;;
esac
J'ai conscience qu'il y a des choses à améliorer, un jour peut-être...
Commentaires