Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
executable file 301 lines (275 sloc) 9.86 KB
#!/bin/sh
#
# © 2009 David Woodhouse <dwmw2@infradead.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program 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. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
################
#
# This is a replacement for the standard vpnc-script used with vpnc and
# openconnect. It sets up VPN routing which doesn't screw over the
# _normal_ routing of the box.
#
# It sets up a new network namespace for the VPN to use, and it runs
# a Secure Shell dæmon inside that namespace, with full access to all
# routes on the VPN.
#
# It links the 'real' network namespace of the computer to this new one
# by an IPv6 site-local connection -- you can ssh into the 'VPN namespace'
# by connecting to the host 'fec0::1'.
#
# You don't need any IPv6 configuration or connectivity for this; you
# only need to have IPv6 support in your kernel. The use of IPv6 is purely
# local to your machine.
#
# This gives you effectively the same service as if your company used a
# SSH "bastion host" for access, instead of a VPN. It's just that the
# bastion host is a special network namespace in your _own_ machine.
#
# Since your connection to it is _private_, though, you can run a few
# services that a secure bastion host could not -- like a web proxy,
# for example. You can also just forward certain points so that, for
# example, connections to port 25 on your bastion host are automatically
# forwarded inside the VPN to your internal mail server. There should
# be a sample xinetd.conf with this script which shows how to do that
# using 'netcat' started from xinetd.
#
# It probably helps if you think of the VPN namespace as if it was a
# separate machine. From the network point of view, that's what it is.
# It just happens to share the file system (and a lot of other stuff)
# with your _real_ computer.
#
# You can configure various other services to use this for connections into
# your VPN, as follows...
#
# SOCKS
#
# SSH has a built-in SOCKS server. If you run 'ssh -D 1080 fec0::1', SSH
# will listen on port 1080 and will forward connections through the SSH
# connection and give you full SOCKS access to the VPN.
#
# It might make sense to make this script automatically start a SSH
# connection with SOCKS enabled, if you want that to be available.
#
# SSH
#
# The OpenSSH client is capable of connecting to a SSH server by running
# and arbitrary command and using its stdin/stdout, instead of having to
# make a direct TCP connection to the server.
#
# So you can configure it to SSH into the VPN 'namespace' and use the
# 'netcat' command for certain connections. You can add something like
# this to your ~/.ssh/config:
#
# Host *.example.internal
# ProxyCommand ssh fec0::1 exec nc %h %p
#
# (This also works if your company has made the mistake of overloading the
# public 'company.com' domain for internal purposes, instead of doing the
# sensible thing and using a _separate_ domain.)
#
# MAIL
#
# Like SSH, most decent mail clients are able to run a command to connect
# to their IMAP server instead of being limited to a direct TCP connection.
#
# Commands you might want to use could look like...
# ssh $MAILSRV exec /usr/sbin/dovecot --exec-mail imap
# ssh $MAILSRV exec /usr/sbin/wu-imapd
# ssh fec0::1 openssl s_client -quiet -connect $MAILSRV:993 -crlf 2>/dev/null
#
# Where '$MAILSRV' is the name of your mail server, of course.
#
# Note that the first two assume that you've set up SSH as described above,
# so that SSH connections to the mail server work transparently. For the
# latter, you probably need to redirect stderr to /dev/null to avoid
# spurious output from openssl configuring your mail client (openssl doesn't
# seem to take the -quiet option very seriously).
#
# For mail clients which _cannot_ simply run an external command for their
# connection, first file a bug and then see the 'PORT FORWARDING' section
# below.
#
# WEB
#
# Firefox and most other browsers should understand a 'proxy autoconfig'
# file and that can tell it to use SOCKS (see above) for certain domains.
# A suitable PAC file might look like this:
#
# function FindProxyForURL(url, host)
# {
# if (dnsDomainIs(host, "company.com"))
# return "SOCKS5 localhost:1080";
#
# return "DIRECT";
# }
#
# PORT FORWARDING
#
# You can use SSH to forward certain ports, of course -- but there's another,
# simpler option.
#
# The included example of xinetd configuration will accept connections on
# port 25 and 993 of the host fec0::1, and will automatically forward them
# using netcat to the appropriate hosts within your VPN. This can be extended
# to forward other ports.
#
# OTHER SERVICES
#
# Most other services should also be available through SSH, through the
# SOCKS proxy, or by port forwarding in some way. If all else fails, you
# can just ssh into the vpn namespace (ssh fec0::1) and have a shell with
# complete access.
#
# BREAKING OUT OF THE VPN
#
# If you ssh _into_ your machine from the VPN side, you'll get a shell in
# the VPN namespace. To 'break out' from there, you may want to ssh to
# fec0::2 which is the normal machine.
#
# CONTROLLING ACCESS TO THE VPN
#
# One serious flaw with the _traditional_ VPN setup is that it allows
# _all_ processes and users on the machine to have free access to the
# VPN, instead of only the user who is supposed to have access. The
# approach implemented here allows you to fix that, by running the
# SSHD in the VPN namespace with a separate configuration that allows
# only certain users to connect to it.
#
# (Be aware that using port forwarding or using SSH to run a SOCKS proxy
# will negate that benefit, of course)
#
# David Woodhouse <dwmw2@infradead.org>
# 2009-06-06
IP=/sbin/ip
SCRIPTNAME=`basename $0`
NETNSNAME=$SCRIPTNAME
# XINETDCONF=`dirname $0`/xinetd.netns.conf
PS4=" \$\$+ "
connect_parent()
{
export PARENT_NETNS=$$
$IP link set $TUNDEV down
if ! $IP link set $TUNDEV netns $$; then
echo "Setting network namespace for $TUNDEV failed"
echo "Perhaps you don't have network namespace support in your kernel?"
exit 1
fi
$IP netns delete $NETNSNAME >/dev/null 2>&1
if ! $IP netns add $NETNSNAME; then
echo "Creating network namespace $NETNSNAME failed"
echo "Perhaps you don't have network namespace support in your kernel?"
exit 1
fi
$IP link add dev $TUNDEV-vpnssh%d type veth
# XXX: Assume vpnssh0 and vpnssh1; ip doesn't tell us!
LOCALDEV=$TUNDEV-vpnssh0
export REMOTEDEV=$TUNDEV-vpnssh1
$IP netns exec $NETNSNAME $0 $@ &
CHILDPID=$!
# XXX: If we do this too soon (before the unshare), we're just
# giving it to our _own_ netns. which achieves nothing.
# So give it away until we _can't_ give it away any more.
while $IP link set $REMOTEDEV netns $CHILDPID 2>/dev/null; do
sleep 0.1
done
# Give away the real VPN tun device too
$IP link set $TUNDEV netns $CHILDPID
$IP link set $LOCALDEV up
$IP addr add fec0::2/64 dev $LOCALDEV
echo "VPN now accessible through 'ssh fec0::1'"
if ! grep -q 127.0.0.1 /etc/resolv.conf; then
echo "WARNING: Your host needs to be running a local dnsmasq or named"
echo "WARNING: and /etc/resolv.conf needs to point to 127.0.0.1"
# XXX: We could probably fix that for ourselves...
fi
}
connect()
{
if [ -z "$PARENT_NETNS" ]; then
connect_parent
exit 0
fi
# This is the child, which remains running in the background
# Wait for the tundev to appear in this namespace
while ! ip link show $TUNDEV >/dev/null 2>&1 ; do
sleep 0.1
done
# Set up Legacy IP in the new namespace
$IP link set lo up
$IP link set $TUNDEV up
if [ -n "$INTERNAL_IP4_ADDRESS" ]; then
$IP -4 addr add $INTERNAL_IP4_ADDRESS dev $TUNDEV
$IP -4 route add default dev $TUNDEV
fi
if [ -n "$INTERNAL_IP6_ADDRESS" ]; then
$IP -6 addr add $INTERNAL_IP6_ADDRESS dev $TUNDEV
$IP -6 route add default dev $TUNDEV
fi
if [ "$INTERNAL_IP4_MTU" != "" ]; then
$IP link set $TUNDEV mtu $INTERNAL_IP4_MTU
fi
# Set up the veth back to the real system
$IP link set $REMOTEDEV up
$IP -6 addr add fec0::1/64 dev $REMOTEDEV
# Run dnsmasq to provide DNS service for this namespace.
# The host needs to be running its own local nameserver/dnsmasq and
# /etc/resolv.conf should be pointing to 127.0.0.1 already.
DNSMASQ_ARGS="--port=53 -k -R"
for NS in $INTERNAL_IP4_DNS; do
DNSMASQ_ARGS="$DNSMASQ_ARGS -S $NS"
done
for NS in $INTERNAL_IP6_DNS; do
DNSMASQ_ARGS="$DNSMASQ_ARGS -S $NS"
done
/usr/sbin/dnsmasq $DNSMASQ_ARGS &
DNSMASQ_PID=$!
# Set up sshd
/usr/sbin/sshd -D &
SSHD_PID=$!
XINETD_PID=
if [ "$XINETDCONF" != "" ] && [ -r "$XINETDCONF" ]; then
/usr/sbin/xinetd -dontfork -f $XINETDCONF &
XINETD_PID=$!
fi
# Wait for the veth link to be closed...
while ip link show $REMOTEDEV >/dev/null 2>&1 ; do
sleep 1
done
kill -TERM $DNSMASQ_PID
kill -TERM $SSHD_PID
if [ "$XINETD_PID" != "" ]; then
kill -TERM $XINETD_PID
fi
# Wait a while to avoid tun BUG() if we quit and the netns goes away
# before vpnc/openconnect closes its tun fd.
sleep 1
}
disconnect()
{
# Kill our end of the veth link, leaving the child script to clean up
$IP link del $TUNDEV-vpnssh0
while ! $IP netns delete $NETNSNAME >/dev/null 2>&1 ; do
sleep 0.1
done
}
case $reason in
connect)
connect
;;
disconnect)
disconnect
;;
esac