@@ -3,13 +3,13 @@
# === INFO ===
# NoVPN
# Description: Bypass VPN tunnel for applications run through this tool.
VERSION=" 2 .0.0"
VERSION=" 3 .0.0"
# Author: KrisWebDev
# Requirements: Linux with kernel > 2.6.4 (released in 2008).
# This version is only tested on Ubuntu 19.10 with bash.
# This version is tested on Ubuntu 14.04 and 19.10 with bash.
# Main dependencies are automatically installed.
# Script will guide you for iptables 1.6.0 install if needed.
# Note: This script will disable IPv6 (enable with --clean)
# Script will guide you for iptables upgrade if needed.
# Note: For security, this script will disable IPv6, even after --clean.
# === LICENSE ===
# This program is free software: you can redistribute it and/or modify
@@ -26,25 +26,28 @@ VERSION="2.0.0"
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# === CONFIGURATION ===
# Find your real interface with: route | grep '^default' | grep -o '[^ ]*$' | grep -v tun
real_interface=" enp34s0"
# Find your real (non-VPN) interface manually with: ip route
# Guess with: ip route | grep "default via " | sed -n '/^.* dev \([-_[:alnum:]]\{1,\}\).*$/s//\1/p' | grep -v tun
real_interface=" enp0s3"
# Warn if real interface guessed at startup doesn't match above setting
real_interface_check_warn=true
# === ADVANCED CONFIGURATION ===
cgroup_name=" novpn" # Better keep it with purely lowercase alphabetic & underscore
net_cls_classid=" 0x00110011" # Anything from 0x00000001 to 0xFFFFFFFF
ip_table_fwmark=" 11" # Anything from 1 to 2147483647
ip_table_number=" 11" # Anything from 1 to 252
ip_table_name=" $cgroup_name "
# === CODE ===
real_interface_gateway=` ip route | grep " dev ${real_interface} " | awk ' /^default/ { print $3 }' `
# real_interface_ip=`ip addr show "$real_interface" | awk '$1 == "inet" {gsub(/\/.*$/, "", $2); print $2}'`
# Handle options
action=" command"
background=false
skip=false
check_sudo_conf=false
init_nb_args=" $# "
cgroup_version=1
while [ " $# " -gt 0 ]; do
case " $1 " in
@@ -53,17 +56,20 @@ while [ "$#" -gt 0 ]; do
-i|--inside) action=" inside" ; shift 1;;
-l|--list) action=" list" ; shift 1;;
-s|--skip) skip=true; shift 1;;
-f|--force) check_sudo_conf=true; shift 1;;
--sudok) sudok=true; shift 1;;
-g|--cgroup) cgroup_name=" $2 " ; shift 1; shift 1;;
-2|--cgv2) cgroup_version=2; shift 1;;
-I|--info) action=" info" ; shift 1;;
-c|--clean) action=" clean" ; shift 1;;
-h|--help) action=" help" ; shift 1;;
-v|--version) echo " novpn v$VERSION " ; exit 0;;
--sudok) sudok=true; shift 1;;
-* ) echo " Unknown option: $1 . Try --help." >&2 ; exit 1;;
* ) break ;; # Start of COMMAND or LIST
esac
done
if [ " $init_nb_args " -lt 1 ] || [ " $action " = " help" ] ; then
if { [ " $# " -lt 1 ] && [ " $action " == " command " ] ; } || [ " $action " = " help" ] ; then
me=` basename " $0 " `
echo -e " Usage : \e[1m$me [\e[4mOPTIONS\e[24m] [\e[4mCOMMAND\e[24m [\e[4mCOMMAND PARAMETERS\e[24m]]\e[0m"
echo -e " or : \e[1m$me [\e[4mOPTIONS\e[24m] { --outside | --inside } \e[4mLIST\e[24m\e[0m"
@@ -74,15 +80,22 @@ if [ "$init_nb_args" -lt 1 ] || [ "$action" = "help" ] ; then
echo -e " \e[1m-o, --outside \e[4mLIST\e[24m\e[0m Move running process \e[4mLIST\e[24m outside tunnel. \e[1mBROKEN!\e[0m"
echo -e " \e[1m-i, --inside \e[4mLIST\e[24m\e[0m Move back running process \e[4mLIST\e[24m inside tunnel."
echo -e " \e[1m-l, --list\e[0m List processes going outside tunnel."
echo -e " \e[1m-s, --skip\e[0m Don't setup system config & don't ask for root;\n just perform public routing test and run \e[4mCOMMAND\e[24m."
echo -e " \e[1m-s, --skip\e[0m Don't setup system config (never ask for root);\n just perform public routing test and run \e[4mCOMMAND\e[24m."
echo -e " \e[1m--sudok\e[0m Drop sudo rights with \" sudo -K\" before executing \e[4mCOMMAND\e[24m."
echo -e " \e[1m-f, --force\e[0m Force setup of system config (always ask for root)."
echo -e " \e[1m-g, --cgroup \e[4mNAME\e[24m\e[0m Specify cgroup name (cgroup v1) or relative path (cgroup v2). Default: \" $cgroup_name \" ."
echo -e " \e[1m-2, --cgv2\e[0m Use Control Groups version 2 if supported."
echo -e " \e[1m-I, --info\e[0m Display debug information and exit."
echo -e " \e[1m-c, --clean\e[0m Move back all proceses to initial routing settings and remove system config."
echo -e " \e[1m-v, --version\e[0m Print this program version."
echo -e " \e[1m-h, --help\e[0m This help."
echo
echo -e " \e[1m\e[4mLIST\e[0m: List of process ID or names separated by spaces."
echo -e " \e[1m\e[4mLIST\e[0m: List o f process ID or names separated by spaces."
exit 1
fi
ip_table_name=" $cgroup_name "
# This program can't ask for root outside terminal
if [ ! -t 1 ] && [ " $( id -u) " -ne 0 ]; then
skip=true
@@ -93,18 +106,74 @@ if [ "$skip" = true ]; then
echo -e " \e[31mCan't use --skip with --clean. Aborting.\e[0m" >&2
exit 1
fi
if [ " $check_sudo_conf " = true ]; then
echo -e " \e[31mCan't use --skip with --force. Aborting.\e[0m" >&2
exit 1
fi
fi
# Interface check
if [ " $real_interface_check_warn " = true ]; then
real_interface_guess=" $( ip route | grep " default via " | sed -n ' /^.* dev \([-_[:alnum:]]\{1,\}\).*$/s//\1/p' | grep -v tun) "
if [ ! -z " $real_interface_guess " ] && [ " $real_interface " != " $real_interface_guess " ]; then
echo -e " \e[93mWarning: Guessed real interface is \" $real_interface_guess \" but this script is configured to use \" $real_interface \" ."
echo -e " Manually edit the script settings if needed.\e[0m" >&2
fi
fi
# Helper functions
# Find/Check cgroup system folders
cgroup_v1_net_cls=" /sys/fs/cgroup/net_cls"
cgroup_v2_root=" /sys/fs/cgroup"
cgroup_base=" /sys/fs/cgroup"
find_root (){
# Find cgroup v2 root filesystem
if [ " $cgroup_version " = 2 ]; then
[ -f " $cgroup_v2_root " " /cgroup.procs" ] || cgroup_v2_root=" /sys/fs/cgroup/unified"
[ -f " $cgroup_v2_root " " /cgroup.procs" ] || cgroup_v2_root=" $( mount -t cgroup2 | head -n1 | grep -oP ' ^cgroup2 on \K\S+' ) "
if [ ! -f " $cgroup_v2_root " " /cgroup.procs" ]; then
echo -e " \e[31mCan't find a valid cgroup v2 mounted filesystem. Aborting.\e[0m" >&2
exit 1
fi
echo " Using cgroups v2. Found valid cgroup v2 filesystem at $cgroup_v2_root "
cgroup_base=" $cgroup_v2_root "
else
# Find cgroup v1 net_cls folder
[ -f " $cgroup_v1_net_cls " " /cgroup.procs" ] || cgroup_v1_net_cls=" $( mount -t cgroup | grep net_cls | head -n1 | grep -oP ' ^cgroup on \K\S+' ) "
if [ ! -f " $cgroup_v1_net_cls " " /cgroup.procs" ]; then
echo -e " \e[31mCan't find a valid cgroup v1 net_cls folder. Aborting.\e[0m" >&2
exit 1
fi
echo " Using cgroups v1. Found valid cgroup v1 net_cls folder at $cgroup_v1_net_cls "
cgroup_base=" $cgroup_v1_net_cls "
fi
}
# Prepare variables
if [ " $cgroup_version " = 2 ]; then
iptables_arg=" --path $cgroup_name "
else
iptables_arg=" --cgroup $net_cls_classid "
fi
# Helper functions
# Check the presence of required system packages
check_install_package (){
nothing_installed=1
for package_name in " $@ "
do
if ! dpkg -s " $package_name " & > /dev/null; then
if [ " $package_name " = " cgroup-tools" ]; then
if [ -x " $( command -v cgexec) " ]; then
continue
fi
if [ " $( apt-cache search --names-only ' ^cgroup-tools$' | wc -l) " -eq 0 ]; then
echo " cgroups-tools is not available in apt cache, falling back to cgroup-bin install"
package_name=" cgroup-bin"
fi
fi
echo " Installing $package_name "
read -p " Press enter to continue or Ctrl+C to cancel"
sudo apt-get install " $package_name "
nothing_installed=0
fi
@@ -116,198 +185,225 @@ check_package(){
for package_name in " $@ "
do
if ! dpkg -s " $package_name " & > /dev/null; then
# echo "Installing $package_name"
# sudo apt-get install "$package_name"
return 0
if [ " $package_name " = " cgroup-tools" ]; then
# Ignore if cgexec is available (e.g. through cgroup-bin)
if [ ! -x " $( command -v cgexec) " ]; then
true
return
fi
else
true
return
fi
fi
done
return 1
false
}
# List processes bypassing the VPN
list_outside (){
return_status=1
echo -e " PID" " \t" " CMD"
while read task_pid
do
echo -e " ${task_pid} " " \t" " ` ps -p ${task_pid} -o comm=` " ;
return_status=0
done < /sys/fs/cgroup/net_cls/${cgroup_name} /tasks
return $return_status
# Main functions
# Check/Install dependencies
install_dependencies (){
if check_install_package cgroup-lite traceroute; then # Removed cgroup-tools
if check_package cgroup-lite traceroute; then # Removed cgroup-tools
echo " Required packages not properly installed. Aborting." >&2
exit 1
fi
fi
iptables_version=$( iptables --version | grep -oP " iptables v\K[0-9.]+" )
if dpkg --compare-versions " $iptables_version " " lt" " 1.6" ; then
echo -e " \e[31mYou need iptables 1.6.0+. Please install manually. Aborting.\e[0m" >&2
echo " Find latest iptables at http://www.netfilter.org/projects/iptables/downloads.html" >&2
echo " Commands to install iptables 1.8.4:" >&2
echo -e " \e[34msudo apt-get install dh-autoreconf bison flex
cd /tmp
curl https://netfilter.org/projects/iptables/files/iptables-1.8.4.tar.bz2 | tar xj
cd iptables-1.8.4
./configure --prefix=/usr \\
--sbindir=/sbin \\
--disable-nftables \\
--enable-libipq \\
--with-xtlibdir=/lib/xtables \\
&& make \\
&& sudo make install
iptables --version\e[0m" >&2
exit 1
fi
}
# Check and setup iptables - requires root even for check
iptable_checked=false
setup_iptables (){
setup_config (){
# if [ "$cgroup_version" = 1 ]; then
if [ ! -d " $cgroup_base /$cgroup_name " ]; then
echo " Creating control group at \" $cgroup_base /$cgroup_name \" " >&2
sudo mkdir -p " $cgroup_base /$cgroup_name "
sudo chown -R " $USER " :" ` id -g -n " $USER " ` " " $cgroup_base /$cgroup_name "
check_sudo_conf=true
fi
cgroup_owner=` stat -c " %U" " $cgroup_base /$cgroup_name /cgroup.procs" `
if [ " $cgroup_owner " != " ` id -g -n " $USER " ` " ] && [ " $EUID " -ne 0 ]; then
echo -e " \e[93mWARNING: Folder \" $cgroup_base /$cgroup_name /cgroup.procs\" already exists, it is owned by someone else ($cgroup_owner ) and you are not root.\e[0m" >&2
fi
# Redundant
# if [ -z "`lscgroup net_cls:$cgroup_name`" ] || [ `stat -c "%U" "$cgroup_v1_net_cls/${cgroup_name}/tasks"` != "$USER" ]; then
# echo "Creating cgroup net_cls:${cgroup_name}. User $USER will be able to move tasks in it without root permissions." >&2
# sudo cgcreate -t "$USER":"$USER" -a `id -g -n "$USER"`:`id -g -n "$USER"` -g net_cls:"$cgroup_name"
# check_sudo_conf=true
# fi
if [ " $cgroup_version " = 1 ] && [ ` cat " $cgroup_v1_net_cls /$cgroup_name /net_cls.classid" | xargs -n 1 printf " 0x%08x" ` != " $net_cls_classid " ]; then
echo " Applying net_cls class identifier $net_cls_classid to cgroup $cgroup_name " >&2
echo " $net_cls_classid " | sudo tee " $cgroup_v1_net_cls /$cgroup_name /net_cls.classid" > /dev/null
check_sudo_conf=true
fi
if ! grep -E " ^${ip_table_number} \s+$ip_table_name " /etc/iproute2/rt_tables & > /dev/null; then
if grep -E " ^${ip_table_number} \s+" /etc/iproute2/rt_tables; then
echo " ERROR : Table ${ip_table_number} already exists in /etc/iproute2/rt_tables with a different name than $ip_table_name " >&2
echo -e " \e[31mERROR : Table ${ip_table_number} already exists in /etc/iproute2/rt_tables with a different name than $ip_table_name .\e[0m " >&2
exit 1
fi
echo " Creating ip routing table: number=$ip_table_number name=$ip_table_name " >&2
echo " $ip_table_number $ip_table_name " | sudo tee -a /etc/iproute2/rt_tables > /dev/null
check_iptables =true
check_sudo_conf =true
fi
if ! ip rule list | grep " lookup $ip_table_name " | grep " fwmark " & > /dev/null; then
echo " Adding rule to use ip routing table $ip_table_name for packets with firewall mark $ip_table_fwmark " >&2
sudo ip rule add fwmark " $ip_table_fwmark " table " $ip_table_name "
check_iptables =true
check_sudo_conf =true
fi
if [ -z " ` ip route list table " $ip_table_name " default via $real_interface_gateway dev ${real_interface} 2> /dev/null` " ]; then
echo " Adding default route in ip routing table $ip_table_name via $real_interface_gateway dev $real_interface " >&2
sudo ip route add default via " $real_interface_gateway " dev " $real_interface " table " $ip_table_name "
# Useless?
echo " Flushing ip route cache" >&2
sudo ip route flush cache
check_iptables =true
check_sudo_conf =true
fi
if [ " ` cat /proc/sys/net/ipv4/conf/all/rp_filter` " != " 0" ] && [ " ` cat /proc/sys/net/ipv4/conf/all/rp_filter` " != " 2" ]; then
echo " Unset reverse path filtering for interface \" all\" " >&2
echo 2 | sudo tee " /proc/sys/net/ipv4/conf/all/rp_filter" > /dev/null
check_iptables =true
check_sudo_conf =true
fi
if [ " ` cat /proc/sys/net/ipv4/conf/${real_interface} /rp_filter` " != " 0" ] && [ " ` cat /proc/sys/net/ipv4/conf/${real_interface} /rp_filter` " != " 2" ]; then
echo " Unset reverse path filtering for interface \" ${real_interface} \" " >&2
echo 2 | sudo tee " /proc/sys/net/ipv4/conf/${real_interface} /rp_filter" > /dev/null
check_iptables=true
fi
if ! sudo iptables -t mangle -C OUTPUT -m cgroup --cgroup " $net_cls_classid " -j MARK --set-mark " $ip_table_fwmark " 2> /dev/null; then
echo " Adding iptables MANGLE rule to set firewall mark $ip_table_fwmark on packets with class identifier $net_cls_classid " >&2
sudo iptables -t mangle -A OUTPUT -m cgroup --cgroup " $net_cls_classid " -j MARK --set-mark " $ip_table_fwmark "
check_sudo_conf=true
fi
if ! sudo iptables -t nat -C POSTROUTING -m cgroup --cgroup " $net_cls_classid " -o " $real_interface " -j MASQUERADE 2> /dev/null; then
echo " Adding iptables NAT rule to force the packets with class identifier $net_cls_classid to exit through $real_interface " >&2
sudo iptables -t nat -A POSTROUTING -m cgroup --cgroup " $net_cls_classid " -o " $real_interface " -j MASQUERADE
fi
iptable_checked=true
# Only check iptables configuration if another configuration item was missing or if the test fails, as it requires root rights
if [ " $check_sudo_conf " = true ]; then
if ! sudo iptables -t mangle -C OUTPUT -m cgroup $iptables_arg -j MARK --set-mark " $ip_table_fwmark " 2> /dev/null; then
echo " Adding iptables MANGLE rule to set firewall mark $ip_table_fwmark on packets with class identifier $net_cls_classid " >&2
sudo iptables -t mangle -A OUTPUT -m cgroup $iptables_arg -j MARK --set-mark " $ip_table_fwmark "
fi
if ! sudo iptables -t nat -C POSTROUTING -m cgroup $iptables_arg -o " $real_interface " -j MASQUERADE 2> /dev/null; then
echo " Adding iptables NAT rule to force the packets with class identifier $net_cls_classid to exit through $real_interface " >&2
sudo iptables -t nat -A POSTROUTING -m cgroup $iptables_arg -o " $real_interface " -j MASQUERADE
fi
iptable_checked=true
fi
if [ " ` cat /proc/sys/net/ipv6/conf/all/disable_ipv6` " != " 1" ] || [ " ` cat /proc/sys/net/ipv6/conf/" " $real_interface " " /disable_ipv6` " != " 1" ] || [ " $check_sudo_conf " = true ]; then
echo " Disabling IPv6 (not supported/implemented)"
sudo ip -6 route add blackhole default metric 1
echo 1 | sudo tee " /proc/sys/net/ipv6/conf/all/disable_ipv6" > /dev/null
fi
}
fcgexec () {
" $@ "
}
# Test if config is working, IPv4 only
testresult=true
test_routing (){
exit_ip=" $( cgexec -g net_cls:" $cgroup_name " traceroute -n -m 1 8.8.8.8 | sed -n ' 2{p;q}' | grep -oE ' [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -1) "
if [ -z " $exit_ip " ]; then
# exit_ip="$(fcgexec -g net_cls:"$cgroup_name" traceroute -n -m 1 8.8.8.8 | sed -n '2{p;q}' | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -1)"
exit_ip=" $( fcgexec traceroute -n -m 1 8.8.8.8 | sed -n ' 2{p;q}' | grep -oE ' [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -1) "
if [ -z " $exit_ip " ]; then
# Old traceroute
exit_ip=" $( cgexec -g net_cls:" $cgroup_name " traceroute -m 1 8.8.8.8 | sed -n ' 2{p;q}' | grep -oE ' [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -1) "
if [ -z " $exit_ip " ]; then
# exit_ip="$(fcgexec -g net_cls:"$cgroup_name" traceroute -m 1 8.8.8.8 | sed -n '2{p;q}' | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -1)"
exit_ip=" $( fcgexec traceroute -m 1 8.8.8.8 | sed -n ' 2{p;q}' | grep -oE ' [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -1) "
if [ -z " $exit_ip " ]; then
echo -e " \e[31mTest failed: Unable to determine source exit IP (found \" $exit_ip \" ).\e[0m" >&2
if [ " $skip " = true ]; then
echo -e " \e[31mYou should remove --skip option to perform setup.\e[0m" >&2
fi
testresult=false
fi
return 0
fi
fi
if [ -z " $real_interface_gateway " ]; then
echo -e " \e[31mTest failed: Unable to determine real interface gateway IP (found \" $real_interface_gateway \" ).\e[0m" >&2
testresult=false
return 0
return 0
fi
if [ " $exit_ip " == " $real_interface_gateway " ]; then
echo -e " \e[32mTest OK. Trafic exits with IP \" $exit_ip \" .\e[0m" >&2
testresult=true
return 0
return 0
else
echo -e " \e[31mTest failed: Trafic exits with \" $exit_ip \" instead of \" $real_interface_gateway \" . Aborting.\e[0m" >&2
testresult=false
return 1
echo -e " \e[31mTest failed: Trafic exits with \" $exit_ip \" instead of \" $real_interface_gateway \" .\e[0m" >&2
return 1
fi
}
# Reconfigure routing
reroute (){
if [ -z " $real_interface_gateway " ]; then
echo -e " \e[31mCan't find default gateway of real interface \" ${real_interface} \" . Is it up?\e[0m" >&2
echo -e " \e[31mCan't find default gateway of real interface \" ${real_interface} \" . Is it up?\e[0m" >&2
echo -e " \e[31mAborting.\e[0m" >&2
exit 1
fi
if [ " $skip " = false ]; then
if [ -z " ` lscgroup net_cls:$cgroup_name ` " ] || [ ` stat -c " %U" /sys/fs/cgroup/net_cls/${cgroup_name} /tasks` != " $USER " ]; then
echo " Creating cgroup net_cls:${cgroup_name} . User $USER will be able to move tasks in it without root permissions." >&2
sudo cgcreate -t " $USER " :" $USER " -a ` id -g -n " $USER " ` :` id -g -n " $USER " ` -g net_cls:" $cgroup_name "
check_iptables=true
fi
if [ " $check_iptables " = true ]; then
setup_iptables
fi
echo " Disabling IPv6 (not supported/implemented)"
sudo ip -6 route add blackhole default metric 1
echo 1 | sudo tee " /proc/sys/net/ipv6/conf/lo/disable_ipv6" > /dev/null
echo 1 | sudo tee " /proc/sys/net/ipv6/conf/all/disable_ipv6" > /dev/null
echo 1 | sudo tee " /proc/sys/net/ipv6/conf/default/disable_ipv6" > /dev/null
setup_config
fi
# MOVE ourself, as program caller, to cgroup
echo $$ | sudo tee " $cgroup_base /${cgroup_name} /cgroup.procs" > /dev/null
# TEST
test_routing
testresult=$?
if [ " $skip " = false ]; then
if [ " $testresult " = false ]; then
if [ " $iptable_checked " = false ] && [ " $skip " = false ]; then
echo -e " Trying to setup iptables and redo test..." >&2
setup_iptables
check_sudo_conf=true
setup_config
test_routing
testresult=$?
fi
fi
if [ " $testresult " = false ]; then
echo -e " \e[31mAborting.\e[0m" >&2
exit 1
fi
fi
fi
if [ " $testresult " -ne 0 ]; then
echo -e " \e[31mAborting.\e[0m" >&2
exit 1
fi
}
# List processes bypassing the VPN
list_outside (){
return_status=1
echo -e " PID" " \t" " CMD"
cat " $cgroup_base /${cgroup_name} /cgroup.procs" | \
while read task_pid
do
echo -e " ${task_pid} " " \t" " ` ps -p ${task_pid} -o comm=` " ;
return_status=0
done
return $return_status
}
check_iptables=false
# Check/Install DEPENDENCIES
if [ " $action " = " command" ] || [ " $action " = " outside" ]; then
if [ " $skip " = false ]; then
echo " Checking/Installing dependencies" >&2
install_dependencies
fi
fi
# SETUP config
if [ " $skip " = false ]; then
echo " Checking/setting forced routing config (skip with $0 -s ...)" >&2
if check_install_package cgroup-lite traceroute cgroup-tools; then
if check_package cgroup-lite traceroute cgroup-tools; then
echo " Required packages not properly installed. Aborting." >&2
exit 1
fi
fi
iptables_version=$( iptables --version | grep -oP " iptables v\K[0-9.]+" )
if dpkg --compare-versions " $iptables_version " " lt" " 1.6" ; then
echo -e " \e[31mYou need iptables 1.6.0+. Please install manually. Aborting.\e[0m" >&2
echo " Find latest iptables at http://www.netfilter.org/projects/iptables/downloads.html" >&2
echo " Commands to install iptables 1.6.0:" >&2
echo -e " \e[34msudo apt-get install dh-autoreconf bison flex
cd /tmp
curl http://www.netfilter.org/projects/iptables/files/iptables-1.6.0.tar.bz2 | tar xj
cd iptables-1.6.0
./configure --prefix=/usr \\
--sbindir=/sbin \\
--disable-nftables \\
--enable-libipq \\
--with-xtlibdir=/lib/xtables \\
&& make \\
&& sudo make install
iptables --version\e[0m" >&2
exit 1
fi
if [ ! -d " /sys/fs/cgroup/net_cls/$cgroup_name " ]; then
echo " Creating net_cls control group $cgroup_name " >&2
sudo mkdir -p " /sys/fs/cgroup/net_cls/$cgroup_name "
check_iptables=true
fi
if [ ` cat " /sys/fs/cgroup/net_cls/$cgroup_name /net_cls.classid" | xargs -n 1 printf " 0x%08x" ` != " $net_cls_classid " ]; then
echo " Applying net_cls class identifier $net_cls_classid to cgroup $cgroup_name " >&2
echo " $net_cls_classid " | sudo tee " /sys/fs/cgroup/net_cls/$cgroup_name /net_cls.classid" > /dev/null
fi
fi
# Find cgroup filesystem
find_root
if [ " $action " = " command" ]; then
reroute
fi
# SETUP novpn routing
if [ " $action " = " command" ] || [ " $action " = " outside" ]; then
echo " Checking/Setting up system configuration" >&2
reroute
fi
# RUN command
@@ -320,10 +416,12 @@ if [ "$action" = "command" ]; then
exit 1
fi
if [ " $background " = true ]; then
cgexec -g net_cls:" $cgroup_name " --sticky " $@ " & > /dev/null &
# cgexec -g net_cls:"$cgroup_name" --sticky "$@" &>/dev/null &
fcgexec " $@ " & > /dev/null &
exit 0
else
cgexec -g net_cls:" $cgroup_name " --sticky " $@ "
# cgexec -g net_cls:"$cgroup_name" --sticky "$@"
fcgexec " $@ "
exit $?
fi
@@ -341,20 +439,20 @@ elif [ "$action" = "outside" ]; then
do
if [ " $process " -eq " $process " ] 2> /dev/null; then
# Is integer (PID)
echo " $process " | sudo tee /sys/fs/cgroup/net_cls/ ${cgroup_name} /tasks > /dev/null
echo " $process " | sudo tee " $cgroup_base / ${cgroup_name} /cgroup.procs " > /dev/null
exit_code=0
else
# Is process name
pids=$( pidof " $process " )
for pid in $pids
do
echo " $pid " | sudo tee /sys/fs/cgroup/net_cls/ ${cgroup_name} /tasks > /dev/null
echo " $pid " | sudo tee " $cgroup_base / ${cgroup_name} /cgroup.procs " > /dev/null
exit_code=0
done
fi
done
echo -e " \e[31mWARNING : Moving running processes outside the VPN tunnel DOES NOT WORK.\e[0m" >&2
echo -e " \e[31mYou should start new processes and beware processes that have already opened windows: they may reuse existing PID.\e[0m" >&2
echo -e " \e[93mWARNING : Moving running processes outside the VPN tunnel DOES NOT WORK.\e[0m" >&2
echo -e " \e[93mYou should start new processes and beware processes that have already opened windows: they may reuse existing PID.\e[0m" >&2
echo " List of processes bypassing tunnel:"
list_outside
@@ -368,13 +466,13 @@ elif [ "$action" = "inside" ]; then
do
if [ " $process " -eq " $process " ] 2> /dev/null; then
# Is integer (PID)
echo " $process " | sudo tee /sys/fs/ cgroup/net_cls/tasks > /dev/null
echo " $process " | sudo tee " $cgroup_base / cgroup.procs " > /dev/null
else
# Is process name
pids=$( pidof " $process " )
for pid in $pids
do
echo " $pid " | sudo tee /sys/fs/ cgroup/net_cls/tasks > /dev/null
echo " $pid " | sudo tee " $cgroup_base / cgroup.procs " > /dev/null
done
fi
done
@@ -384,44 +482,82 @@ elif [ "$action" = "inside" ]; then
# INFO
elif [ " $action " = " info" ]; then
echo -e " \e[2msudo iptables -L -v --line-numbers\e[0m"
sudo iptables -L -v --line-numbers
echo
echo " Displaying information for cgroup v$cgroup_version "
echo
echo -e " \e[2mcat /etc/iproute2/rt_tables | grep --color \" ^${ip_table_number} \s\|\\ $\" \e[0m"
cat /etc/iproute2/rt_tables | grep --color " ^${ip_table_number} \s\|\$ "
echo
echo -e " \e[2mip route show table all | grep -v 'table local' | grep --color 'table\|$'\e[0m"
ip route show table all | grep -v ' table local' | grep --color ' table\|$'
echo
echo -e " \e[2mip rule list | grep --color 'fwmark\|$'\e[0m"
ip rule list | grep --color ' fwmark\|$'
echo
echo -e " \e[2msudo iptables -t mangle -L -v --line-numbers | grep --color 'cgroup\|$'\e[0m"
sudo iptables -t mangle -L -v --line-numbers | grep --color ' cgroup\|$'
echo
echo -e " \e[2msudo iptables -t nat -L -v --line-numbers | grep --color 'cgroup\|$'\e[0m"
sudo iptables -t nat -L -v --line-numbers | grep --color ' cgroup\|$'
echo
echo -e " \e[2mls -l ${cgroup_base} / | grep --color \" ${cgroup_name} \|\\ $\" \e[0m"
ls -l ${cgroup_base} / | grep --color " ${cgroup_name} \|\$ "
echo
echo -e " \e[2mls -l ${cgroup_base} /${cgroup_name} / | grep --color \" ${USER} \|\\ $\" \e[0m"
ls -l ${cgroup_base} /${cgroup_name} / | grep --color " ${USER} \|\$ "
echo
echo -e " End of debug information."
# CLEAN the mess
elif [ " $action " = " clean" ]; then
echo -e " Cleaning forced routing config generated by this script."
echo -e " Cleaning forced routing config generated by this script for cgroup version $cgroup_version ."
echo -e " Note: Cleaning partially cleans/breaks the custom configuration for the other cgroup version."
echo -e " You should also clean the other cgroup version using the -2 (v2) or no (v1) flag."
echo -e " Note: If you used a custom cgroup name through --group, you must use it with --clean."
echo -e " Note: Use --info and --info -2 afterwards to check."
echo -e " Don't bother with errors meaning there's nothing to remove."
# Remove tasks
if [ -f " /sys/fs/cgroup/net_cls/${cgroup_name} /tasks" ]; then
while read task_pid; do echo ${task_pid} | sudo tee /sys/fs/cgroup/net_cls/tasks > /dev/null; done < " /sys/fs/cgroup/net_cls/${cgroup_name} /tasks"
if [ -f " $cgroup_base /${cgroup_name} /cgroup.procs" ]; then
cat " $cgroup_base /${cgroup_name} /cgroup.procs" | \
while read task_pid
do
echo ${task_pid} | sudo tee " $cgroup_base /cgroup.procs" > /dev/null
done
fi
# Delete cgroup
if [ -d " /sys/fs/cgroup/net_cls/${cgroup_name} " ]; then
sudo find " /sys/fs/cgroup/net_cls/${cgroup_name} " -depth -type d -print -exec rmdir {} \;
fi
if [ -d " $cgroup_base /${cgroup_name} " ]; then
echo -n " Are you sure you want to delete cgroup $cgroup_base /${cgroup_name} ? [y/n] "
old_stty_cfg=$( stty -g)
stty raw -echo ; answer=$( head -c 1) ; stty $old_stty_cfg
echo
if echo " $answer " | grep -iq " ^y" ; then
sudo find " $cgroup_base /${cgroup_name} " -depth -type d -print -exec rmdir {} \;
fi
fi
# This can cause issues if reverse path filtering is normally disabled on the system
echo 1 | sudo tee " /proc/sys/net/ipv4/conf/all/rp_filter" > /dev/null
echo 1 | sudo tee " /proc/sys/net/ipv4/conf/${real_interface} /rp_filter" > /dev/null
sudo iptables -t mangle -D OUTPUT -m cgroup --cgroup " $net_cls_classid " -j MARK --set-mark " $ip_table_fwmark "
sudo iptables -t nat -D POSTROUTING -m cgroup --cgroup " $net_cls_classid " -o " $real_interface " -j MASQUERADE
sudo iptables -t mangle -D OUTPUT -m cgroup $iptables_arg -j MARK --set-mark " $ip_table_fwmark "
sudo iptables -t nat -D POSTROUTING -m cgroup $iptables_arg -o " $real_interface " -j MASQUERADE
sudo ip rule del fwmark " $ip_table_fwmark " table " $ip_table_name "
sudo ip rule del fwmark " $ip_table_fwmark " table " $ip_table_name "
sudo ip route del default table " $ip_table_name "
sudo sed -i ' /^${ip_table_number}\s\+${ip_table_name}\s*$/d' /etc/iproute2/rt_tables
echo 1 | sudo tee " /proc/sys/net/ipv6/conf/lo/disable_ipv6" > /dev/null
sudo sed -i " /^${ip_table_number} \s/d" /etc/iproute2/rt_tables
echo " Disabling IPv6 (still - too risky if not managed by VPN)"
# sudo ip -6 route del blackhole default metric 1
echo 1 | sudo tee " /proc/sys/net/ipv6/conf/all/disable_ipv6" > /dev/null
echo 1 | sudo tee " /proc/sys/net/ipv6/conf/default/disable_ipv6" > /dev/null
if [ -n " ` lscgroup net_cls:$cgroup_name ` " ]; then
sudo cgdelete net_cls:" $cgroup_name "
fi
# Redundant
# if [ -n "`lscgroup net_cls:$cgroup_name`" ]; then
# sudo cgdelete net_cls:"$cgroup_name"
# fi
echo " All done."
@@ -431,4 +567,4 @@ fi
# ./novpn.sh traceroute www.google.com
# Note: 1 firefox profile = 1 process only
# killall firefox; ./novpn.sh --background firefox https://ipleak.net/
# ip=$(./novpn.sh curl 'https://wtfismyip.com/text' 2>/dev/null); echo "$ip"; whois "$ip" | grep -E "inetnum|route|netname|descr"
# ip=$(./novpn.sh curl 'https://wtfismyip.com/text' 2>/dev/null); echo "$ip"; whois "$ip" | grep -E "inetnum|route|netname|descr"