dowse |
|
---|---|
Copyright (C) 2013-2014 Dyne.org Foundation Dowse is written by Denis Roio http://jaromil.dyne.org This source code is free software; you can redistribute it and/or modify it under the terms of the GNU Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Please refer to the GNU Public License for more details. You should have received a copy of the GNU Public License along with this source code; if not, write to: Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
#!/usr/bin/env zsh
|
GLOBALS |
VERSION=0.3
DATE="Jan/2014"
QUIET=0
DEBUG=0
SCRIPT=$0
|
standard output message routines |
autoload colors; colors
|
it's always useful to wrap them, in case we change behaviour later |
notice() { if [[ $QUIET == 0 ]]; then print "$fg_bold[green][*]$fg_no_bold[default] $1" >&2; fi }
error() { if [[ $QUIET == 0 ]]; then print "$fg[red][!]$fg[default] $1" >&2; fi }
func() { if [[ $DEBUG == 1 ]]; then print "$fg[blue][D]$fg[default] $1" >&2; fi }
act() {
if [[ $QUIET == 0 ]]; then
if [ "$1" = "-n" ]; then
print -n "$fg_bold[white] . $fg_no_bold[default] $2" >&2;
else
print "$fg_bold[white] . $fg_no_bold[default] $1" >&2;
fi
fi
}
|
honor quiet and debug flags as early as possible |
if [[ ${@} == *-q* ]]; then QUIET=1; fi
if [[ ${@} == *-D* ]]; then DEBUG=1; fi
DIR=${DOWSE:-`pwd`}
|
pid files for our daemons |
pid_tor=$DIR/run/tor.pid
pid_squid=$DIR/run/squid.pid
pid_dnsmasq=$DIR/run/dnsmasq.pid
pid_privoxy=$DIR/run/privoxy.pid
|
read the network configuration of known hosts |
known=`cat conf/network | grep -v '^#'`
|
|
|
CHECKS |
{ test -r conf } || {
error "The dowse script must be run inside its source directory"
return 1 }
{ test -r conf/settings } || {
error "Dowse configuration is missing, create conf/settings"
return 1 }
{ test -r conf/network } || {
error "No network is configured, create conf/network"
return 1 }
notice "Dowse $VERSION - local area network rabdomancy"
cat <<EOF
Copyright (C) 2013-2014 Dyne.org Foundation, License GNU GPL v3+
This is free software: you are free to change and redistribute it
For more informations see http://www.dyne.org/software/dowse
EOF
func "root access"
{ test "$UID" = "0" } || {
error "Dowse needs root privileges to operate."
return 1 }
func "loading configuration from $DIR/conf/settings"
source conf/settings
|
setup dirs |
mkdir -p log
chmod go-rwx log
chown -R $dowseuid:$dowsegid log
mkdir -p run
chmod go-rwx run
chown -R $dowseuid:$dowsegid run
|
create the cache dir in RAM |
mkdir -p /dev/shm/dowse
chown -R $dowseuid:$dowsegid /dev/shm/dowse
chmod go-rwx /dev/shm/dowse
|
|
|
START/STOP DAEMONS |
|
waitpid() |
waitpid() {
|
takes a pid |
pid="$1"
lastnewline=0
while true; do
ps -p "$pid" > /dev/null
if [ $? = 0 ]; then print -n . ; lastnewline=1; sleep 1
else break; fi
|
todo: timeout with kill -9 |
done
|
just because we care to look good on the console |
{ test $lastnewline = 1 } && { print }
}
|
dnsmasq_stop() |
dnsmasq_stop() {
{ test -r $pid_dnsmasq } && {
pid=`cat $pid_dnsmasq`
act "Stopping dnsmasq ($pid)"
kill $pid
waitpid $pid
rm -f $pid_dnsmasq
}
}
|
dnsmasq_start() |
dnsmasq_start() {
act "Preparing to launch dnsmasq..."
|
if running, stop to restart |
dnsmasq_stop
func "dnsmasq --pid-file $DIR/run/dnsmasq.pid -C $DIR/dnsmasq.conf"
dnsmasq --pid-file=$pid_dnsmasq -C $DIR/run/dnsmasq.conf
}
|
squid_stop() |
squid_stop() {
{ test -r $pid_squid } && {
pid=`cat $pid_squid`
ps -p "$pid" > /dev/null
{ test $? = 0 } || {
func "removing stale pid for squid"
rm -f $pid_squid
return 1 }
act "Stopping squid ($pid)"
setuidgid $dowseuid squid3 -f $DIR/run/squid.conf -k shutdown
{ test $? = 0 } || {
error "Error running squid3, the daemon might be left running."
return 1 }
waitpid $pid
rm -f $pid_squid
}
}
|
squid_start() |
squid_start() {
act "Preparing to launch Squid..."
func "setuidgid $dowseuid squid -f $DIR/run/squid.conf"
|
cleanup all previous cache in ram |
rm -rf /dev/shm/dowse/*
|
populate the volatile cache |
setuidgid $dowseuid squid3 -z -f $DIR/run/squid.conf
|
launch the squid |
setuidgid $dowseuid squid3 -f $DIR/run/squid.conf
}
|
privoxy_stop() |
privoxy_stop() {
{ test -r $pid_privoxy } && {
pid=`cat $pid_privoxy`
act "Stopping privoxy ($pid)"
kill $pid
waitpid $pid
rm -f $pid_privoxy
}
}
|
privoxy_start() |
privoxy_start() {
act "Preparing to launch privoxy..."
|
if running, stop to restart |
privoxy_stop
privoxy --user $dowseuid --pidfile $pid_privoxy $DIR/run/privoxy.conf
}
|
tor_stop() |
tor_stop() {
command -v tor >/dev/null
{ test $? = 0 } || { func "tor not found, skipping"; return 1 }
{ test -r $pid_tor } && {
pid=`cat $pid_tor`
act "Stopping tor ($pid)"
kill $pid
waitpid $pid
|
pid file is deleted by tor |
}
}
|
tor_start() |
tor_start() {
command -v tor >/dev/null
{ test $? = 0 } || { func "tor not found, skipping"; return 1 }
act "Preparing to launch tor..."
|
if running, stop to restart |
tor_stop
tor -f $DIR/run/tor.conf
}
|
iptables_flush() |
iptables_flush() {
act "Flushing iptables firewall rules"
iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X
iptables -t mangle -F
iptables -t mangle -X
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
}
|
iptables_start() |
iptables_start() {
notice "Setting up iptables firewall rules"
iptables -P OUTPUT ACCEPT
iptables -P INPUT DROP
iptables -P FORWARD DROP
func "allow only local loopback"
iptables -A INPUT -i eth0 -d 127.0.0.1 -j DROP
iptables -A INPUT -i eth0 -s 127.0.0.1 -j DROP
iptables -A FORWARD -i eth0 -s 127.0.0.1 -j DROP
iptables -A FORWARD -i eth0 -d 127.0.0.1 -j DROP
iptables -A INPUT -s 127.0.0.1 -j ACCEPT
iptables -A INPUT -d 127.0.0.1 -j ACCEPT
|
Block outgoing NetBios (if you have windows machines running on the private subnet). This will not affect any NetBios traffic that flows over the VPN tunnel, but it will stop local windows machines from broadcasting themselves to the internet. iptables -A FORWARD -p tcp --sport 137:139 -j DROP iptables -A FORWARD -p udp --sport 137:139 -j DROP iptables -A OUTPUT -p tcp --sport 137:139 -j DROP iptables -A OUTPUT -p udp --sport 137:139 -j DROP |
|
Check source address validity on packets going out to internet iptables -A FORWARD -s ! ${dowsenet} -i eth0 -j DROP |
|
Allow packets from private subnets |
iptables -A INPUT -s ${dowsenet} -j ACCEPT
iptables -A FORWARD -i ${interface} -s ${dowsenet} -j ACCEPT
|
Allow DHCP service |
iptables -A INPUT -p udp --sport 67:68 --dport 67:68 -j ACCEPT
|
Allow incoming pings (can be disabled) |
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
|
Allow services such as www and ssh (can be disabled) |
iptables -A INPUT -p tcp --dport http -j ACCEPT
iptables -A INPUT -p tcp --dport ssh -j ACCEPT
|
Keep state of connections from local machine and private subnets |
iptables -A OUTPUT -m state --state NEW -o eth0 -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -m state --state NEW -o eth0 -j ACCEPT
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
|
Allow incoming OpenVPN packets. Duplicate the line below for each OpenVPN tunnel, changing --dport n to match the OpenVPN UDP port. In OpenVPN, the port number is controlled by the --port n option. If you put this option in the config file, you can remove the leading '--' If you taking the stateful firewall approach (see the OpenVPN HOWTO), then comment out the line below. iptables -A INPUT -p udp --dport 1194 -j ACCEPT |
func "defend the network from ipv6"
ip6tables -F
ip6tables -P INPUT DROP
ip6tables -P FORWARD DROP
ip6tables -P OUTPUT DROP
}
|
ebtables_flush() |
ebtables_flush() {
command -v ebtables >/dev/null
{ test $? = 0 } || { func "ebtables not found, skipping"; return 1 }
act "flushing ebtables (layer 2 firewall)"
ebtables -P FORWARD ACCEPT
ebtables -P OUTPUT ACCEPT
ebtables -P INPUT ACCEPT
ebtables -F
}
|
ebtables_start() |
ebtables_start() {
command -v ebtables >/dev/null
{ test $? = 0 } || { func "ebtables not found, skipping"; return 1 }
notice "Setting up ebtables rules (layer 2 firewall)"
ebtables -P FORWARD DROP
ebtables -A FORWARD -p IPv4 -j ACCEPT
ebtables -A FORWARD -p ARP -j ACCEPT
ebtables -A FORWARD -p LENGTH -j ACCEPT
ebtables -A FORWARD --log-level info --log-ip --log-prefix EBFW
ebtables -P INPUT DROP
ebtables -A INPUT -p IPv4 -j ACCEPT
ebtables -A INPUT -p ARP -j ACCEPT
ebtables -A INPUT -p LENGTH -j ACCEPT
ebtables -A INPUT --log-level info --log-ip --log-prefix EBFW
ebtables -P OUTPUT DROP
ebtables -A OUTPUT -p IPv4 -j ACCEPT
ebtables -A OUTPUT -p ARP -j ACCEPT
ebtables -A OUTPUT -p LENGTH -j ACCEPT
ebtables -A OUTPUT --log-level info --log-ip --log-arp --log-prefix EBFW -j DROP
act "pinning down known MAC addresses to IP"
for i in ${(f)known}; do
|
check if its a mac address |
echo "$i" | grep '^..:..:..:..:..:..' > /dev/null
{ test $? = 0 } || { continue } # skip if no mac address
mac=${i[(w)1]}
host=${i[(w)2]}
ip=${i[(w)3]}
{ test "$host" = "ignore" } && { continue }
func "$i"
ebtables -A FORWARD -p IPv4 --ip-src ${ip} -s ! ${mac} -j DROP
done
}
|
sysctl_setup() |
sysctl_setup() {
cat <<EOF | sysctl -p -
net.ipv4.tcp_syncookies = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.core.rmem_max = 33554432
net.core.wmem_max = 33554432
net.ipv4.tcp_fin_timeout = 4
vm.min_free_kbytes = 65536
net.netfilter.nf_conntrack_tcp_timeout_established = 7200
net.netfilter.nf_conntrack_checksum = 0
net.netfilter.nf_conntrack_tcp_timeout_syn_sent = 15
net.ipv4.tcp_keepalive_time = 60
net.ipv4.tcp_keepalive_intvl = 10
net.ipv4.tcp_keepalive_probes = 3
net.ipv4.ip_local_port_range = 1025 65530
net.ipv4.tcp_timestamps = 0
EOF
}
|
|
|
DOWSE FUNCTIONS |
|
dowse_check() |
dowse_check() {
act "Checking requirements to run dowse..."
for req in setuidgid dnsmasq iptables privoxy squid3; do
command -v $req >/dev/null
{ test $? != 0 } && {
error "Cannot find $req"
error "You need it to run dowse, please install it."
return 1
}
done
return 0
}
|
dowse_setup() |
dowse_setup() {
act "Configuring our box and connecting it to the Internet..."
func "reading configuration in conf/settings"
func "generating dnsmasq.conf"
cat <<EOF > $DIR/run/dnsmasq.conf
address=/$hostname/$dowse
address=/.i2p/$dowse
address=/.onion/$dowse
bogus-priv
cache-size=1500
conf-dir=/etc/dnsmasq.d
dhcp-range=$dowseguests
addn-hosts=$DIR/run/hosts
dhcp-leasefile=$DIR/run/leases
domain-needed
domain=$lan
expand-hosts
interface=$interface
listen-address=$dowse,127.0.0.1
local=/$lan/
user=$dowseuid
group=$dowsegid
EOF
func "generating privoxy.conf"
cat <<EOF > $DIR/run/privoxy.conf
user-manual /usr/share/doc/privoxy/user-manual
confdir /etc/privoxy
logdir $DIR/log/privoxy
listen-address 0.0.0.0:8118
toggle 1
enable-remote-toggle 0
enable-remote-http-toggle 0
enable-edit-actions 1
enforce-blocks 0
buffer-limit 64000
forwarded-connect-retries 0
accept-intercepted-requests 1
allow-cgi-request-crunching 0
split-large-forms 0
keep-alive-timeout 5
socket-timeout 300
handle-as-empty-doc-returns-ok 1
# DIVIDER
forward-socks4a .onion $dowse:9050 .
# DIVIDER
# DIVIDER
forward $hostname .
filterfile default.filter
actionsfile match-all.action # Actions that are applied to all sites and maybe overruled later on.
actionsfile default.action # Main actions file
actionsfile user.action # User customizations
EOF
func "generating squid.conf"
cat <<EOF > $DIR/run/squid.conf
pid_filename $pid_squid
cache_effective_user $dowseuid
cache_store_log none
cache_log $DIR/log/squid_cache.log
access_log /dev/null
# DIVIDER
# DIVIDER
cache_mem 16 MB
cache_dir aufs /dev/shm/dowse 256 16 256
maximum_object_size 16 MB
maximum_object_size_in_memory 1 MB
minimum_object_size 16 KB
memory_pools off
acl all src all
acl manager proto cache_object
acl localhost src 127.0.0.1/32
acl to_localhost dst 127.0.0.0/8 0.0.0.0/32
acl localnet src $dowsenet
acl SSL_ports port 443 # https
acl Safe_ports port 80 # http
acl Safe_ports port 443 # https
acl purge method PURGE
acl CONNECT method CONNECT
http_access allow manager localhost
http_access deny manager
http_access allow purge localhost
http_access deny purge
http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
http_access allow localnet
http_access allow localhost
http_access deny all
icp_access allow localnet
http_port 3128 transparent
hierarchy_stoplist cgi-bin ?
refresh_pattern ^ftp: 1440 20% 10080
refresh_pattern ^gopher: 1440 0% 1440
refresh_pattern -i (/cgi-bin/|\?) 0 0% 0
refresh_pattern -i (deb|tar|gz|tgz|bz2|zip|rar|msi|exe|rpm)$ 0 90% 1440
refresh_pattern (Release|Packages(.gz)*)$ 0 20% 2880
refresh_pattern . 0 20% 4320
acl shoutcast rep_header X-HTTP09-First-Line ^ICY.[0-9]
# DIVIDER
acl apache rep_header Server ^Apache
# DIVIDER
# DIVIDER
cache_mgr Dowse
hosts_file $DIR/run/hosts
coredump_dir $DIR/log
cache_peer localhost parent 8118 0 default no-query no-digest no-netdb-exchange
never_direct allow all
# DIVIDER
# DIVIDER
EOF
cat <<EOF > $DIR/run/tor.conf
User $dowseuid
PidFile $pid_tor
SocksPort $dowse:9050
SocksPolicy accept 10.0.0.250 # $dowsenet
SocksPolicy reject *
Log notice file $DIR/log/tor.log
RunAsDaemon 1
DataDirectory $DIR/run
ControlPort 9051
CookieAuthentication 1
ExitPolicy reject *:*
EOF
func "Fixing entries for known peers"
rm -f $DIR/run/dnsmasq.network
|
pass through tor for urls.onion |
print "dhcp-option=option:router,$dowse" > $DIR/run/dnsmasq.network
|
pass through i2p for urls.i2p forward .i2p $dowse:4444 forward .i2p 127.0.0.1:4444 |
func "Generating hosts file"
rm -f $DIR/run/hosts
echo "127.0.0.1 localhost" > $DIR/run/hosts
for i in ${(f)known}; do
echo "$i" | grep '^..:..:..:..:..:..' > /dev/null
if [ $? = 0 ]; then # mac address is first
host=${i[(w)2]}
ip=${i[(w)3]}
else # no mac address specified
host=${i[(w)1]}
ip=${i[(w)2]}
fi
{ test "$host" = "ignore" } || {
|
direct access |
print "$ip $host" >> $DIR/run/hosts }
done
func "generating dnsmask.network"
for i in ${(f)known}; do
echo "$i" | grep '^..:..:..:..:..:..' > /dev/null
{ test $? = 0 } || { continue } # skip if no mac address
func "$i"
|
access_log $DIR/log/squid_access.log squid |
mac=${i[(w)1]}
host=${i[(w)2]}
ip=${i[(w)3]}
|
avoid having a physical cache directory |
print "dhcp-host=$mac, $host, $ip" >> $DIR/run/dnsmasq.network
done
|
upgrade_http0.9 deny shoutcast |
cat $DIR/run/dnsmasq.network >> $DIR/run/dnsmasq.conf
|
broken_vary_encoding allow apache |
chown -R $dowseuid:$dowsegid log
chown -R $dowseuid:$dowsegid run
notice "Setup completed in $DIR"
return 0
}
|
extension_methods REPORT MERGE MKACTIVITY CHECKOUT |
dowse_start() {
notice "Setting up the network..."
PGL=`pidof pgld`
{ test "$PGL" = "" } || {
act "PeerGuardian found running, will restart it accordingly"
pglcmd stop }
act "Setting up $interface interface"
ifconfig $interface $dowse netmask $netmask up
route add default gw $wan
func "enable masquerading"
modprobe nf_conntrack_ipv4
sysctl net.netfilter.nf_conntrack_acct=1
|
header_access From deny all |
sysctl_setup
func "enable ip forwarding"
print 1 > /proc/sys/net/ipv4/ip_forward
func "bugfix for routing table weirdness in Linux >3.1"
|
the settings below are restrictive: they grant more privacy but break many websites! header_access Link deny all header_access Server deny all header_access Referer deny all header_access User-Agent deny all header_access WWW-Authenticate deny all |
echo 0 >>/proc/sys/net/ipv4/conf/eth0/accept_redirects
{ test "$firewall" = "no" } || {
ebtables_flush
iptables_flush
}
{ test "$firewall" = "yes" } && {
ebtables_start
iptables_start
}
notice "Setting up masquerading (NAT)"
func "setup route towards wired network"
iptables --table nat --append POSTROUTING --out-interface $interface -j SNAT --to $dowse
func "setup transparent proxy to squid"
iptables -t nat -A PREROUTING -i $interface -s $dowsenet -p tcp --dport 80 -j REDIRECT --to-port 3128
|
this is basically a dnsmasq host configuration file |
dnsmasq_start
|
this is our generated hosts file |
privoxy_start
|
add a line to the hosts list |
squid_start
|
gather configuration into variables, line by line |
tor_start
|
add a line to the dnsmasq host list |
{ test "$PGL" = "" } || { pglcmd start }
}
|
append network settings to dnsmasq conf |
dowse_stop() {
notice "Stopping all services."
tor_stop
squid_stop
privoxy_stop
dnsmasq_stop
}
|
set permissions of setup files |
|
dowse_start() |
dowse_check
{ test $? = 1 } && {
error "Aborting operation for missing requirements."
return 1 }
|
strenghten and optimize a bit the system for networking |
case "$1" in
restart|start) dowse_setup; dowse_stop; dowse_start ;;
release) rm $DIR/run/leases; dowse_setup; dnsmasq_stop; dnsmasq_start ;;
reload) rm -rf /dev/shm/dowse/*; dowse_setup; setuidgid $dowseuid squid3 -f $DIR/run/squid.conf -k rec ;;
stop) dowse_stop ;;
*) echo "dowse: command not found: $1" ;;
esac
|
see https://lkml.org/lkml/2011/11/18/191 and http://www.spinics.net/lists/netdev/msg179687.html |
return 0
|
start the dnsmasq daemon |
|
start the privoxy daemon |
|
start the squid daemon | |
if found, start Tor | |
if PeerGuardian was running, start it again | |
dowse_stop() | |
| |
MAIN | |
we use a very simple argument parser | |
be nice with the environment | |
| |