-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This tools allows a user to create a (net,mnt,user)-namespace container which can be used to run openvpn. This tools uses sudo to gain privileges for the setup and requires a line like ALL ALL=NOPASSWD: /usr/bin/uvpn start_as_root,/usr/bin/uvpn stop_as_root in the sudoers file. The container will be connected to the guest network. For this the system needs a working vlan.guest0 vlan interface into the guest network. The usage for the user might be along this pattern: uvpn start uvpn exec openvpn ~/.charite-username.ovpn echo -e "search charite.de\nnameserver 141.42.1.1\nnameserver 141.14.16.1" | uvpn exec bash -c 'cat > /etc/resolv.conf' uvpn exec firefox --new-instance --ProfileManager uvpn exec firefox --new-instance -P charite uvpn show uvpn stop_container Because the network of the container is separate from the host system, ip based X11-forward will not work without more setup. So the above example would only work on the local workstation.
- Loading branch information
Showing
2 changed files
with
304 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,303 @@ | ||
#! /bin/bash | ||
|
||
die() { | ||
echo "$@">&2 | ||
exit 1 | ||
} | ||
|
||
die_usage() { | ||
cat <<EOF | ||
usage: | ||
$0 start # start uvpn container | ||
$0 stop # stop uvpn container | ||
$0 exec [cmd...] # execute cmd (default bash) in the container | ||
$0 show [user...] # show processes | ||
$0 start_as_root # sudo callback - internal use | ||
$0 stop_as_root # sudo callback - internal use | ||
EOF | ||
exit 1 | ||
} | ||
|
||
create_dhclient-script() { | ||
cat >/var/run/uvpn/$USER/dhclient-script << '_EOF_' | ||
#!/bin/bash | ||
ip=/sbin/ip | ||
### | ||
### DHCPv4 Handlers | ||
### | ||
if [ x$new_broadcast_address != x ]; then | ||
new_broadcast_arg="broadcast $new_broadcast_address" | ||
fi | ||
if [ x$old_broadcast_address != x ]; then | ||
old_broadcast_arg="broadcast $old_broadcast_address" | ||
fi | ||
if [ x$new_subnet_mask != x ]; then | ||
new_subnet_arg="netmask $new_subnet_mask" | ||
fi | ||
if [ x$old_subnet_mask != x ]; then | ||
old_subnet_arg="netmask $old_subnet_mask" | ||
fi | ||
if [ x$alias_subnet_mask != x ]; then | ||
alias_subnet_arg="netmask $alias_subnet_mask" | ||
fi | ||
if [ x$new_interface_mtu != x ]; then | ||
mtu_arg="mtu $new_interface_mtu" | ||
fi | ||
if [ x$IF_METRIC != x ]; then | ||
metric_arg="metric $IF_METRIC" | ||
fi | ||
if [ x$reason = xBOUND ] || [ x$reason = xRENEW ] || \ | ||
[ x$reason = xREBIND ] || [ x$reason = xREBOOT ]; then | ||
if [ x$old_ip_address != x ] && [ x$alias_ip_address != x ] && \ | ||
[ x$alias_ip_address != x$old_ip_address ]; then | ||
# Possible new alias. Remove old alias. | ||
ifconfig $interface:0- inet 0 | ||
fi | ||
if [ x$old_ip_address != x ] && [ x$old_ip_address != x$new_ip_address ]; then | ||
# IP address changed. Bringing down the interface will delete all routes, | ||
# and clear the ARP cache. | ||
ifconfig $interface inet 0 down | ||
fi | ||
if [ x$old_ip_address = x ] || [ x$old_ip_address != x$new_ip_address ] || \ | ||
[ x$reason = xBOUND ] || [ x$reason = xREBOOT ]; then | ||
ifconfig $interface inet $new_ip_address $new_subnet_arg \ | ||
$new_broadcast_arg $mtu_arg | ||
# Add a network route to the computed network address. | ||
for router in $new_routers; do | ||
if [ "x$new_subnet_mask" = "x255.255.255.255" ] ; then | ||
ip route add $router dev $interface | ||
fi | ||
ip route add default via $router dev $interface $metric_arg | ||
done | ||
else | ||
# we haven't changed the address, have we changed other options | ||
# that we wish to update? | ||
if [ x$new_routers != x ] && [ x$new_routers != x$old_routers ] ; then | ||
# if we've changed routers delete the old and add the new. | ||
for router in $old_routers; do | ||
ip route del default via $router | ||
done | ||
for router in $new_routers; do | ||
if [ "x$new_subnet_mask" = "x255.255.255.255" ] ; then | ||
ip route add $router dev $interface | ||
fi | ||
ip route add default via $router dev $interface $metric_arg | ||
done | ||
fi | ||
fi | ||
if [ x$new_ip_address != x$alias_ip_address ] && [ x$alias_ip_address != x ]; | ||
then | ||
ifconfig $interface:0- inet 0 | ||
ifconfig $interface:0 inet $alias_ip_address $alias_subnet_arg | ||
ip route add $alias_ip_address dev $interface:0 | ||
fi | ||
exit 0 | ||
fi | ||
if [ x$reason = xEXPIRE ] || [ x$reason = xFAIL ] || [ x$reason = xRELEASE ] \ | ||
|| [ x$reason = xSTOP ]; then | ||
if [ x$alias_ip_address != x ]; then | ||
# Turn off alias interface. | ||
ifconfig $interface:0- inet 0 | ||
fi | ||
if [ x$old_ip_address != x ]; then | ||
# Shut down interface, which will delete routes and clear arp cache. | ||
ifconfig $interface inet 0 down | ||
fi | ||
if [ x$alias_ip_address != x ]; then | ||
ifconfig $interface:0 inet $alias_ip_address $alias_subnet_arg | ||
ip route add $alias_ip_address dev $interface:0 | ||
fi | ||
exit 0 | ||
fi | ||
if [ x$reason = xTIMEOUT ]; then | ||
if [ x$alias_ip_address != x ]; then | ||
ifconfig $interface:0- inet 0 | ||
fi | ||
ifconfig $interface inet $new_ip_address $new_subnet_arg \ | ||
$new_broadcast_arg $mtu_arg | ||
set $new_routers | ||
if ping -q -c 1 $1; then | ||
if [ x$new_ip_address != x$alias_ip_address ] && \ | ||
[ x$alias_ip_address != x ]; then | ||
ifconfig $interface:0 inet $alias_ip_address $alias_subnet_arg | ||
ip route add $alias_ip_address dev $interface:0 | ||
fi | ||
for router in $new_routers; do | ||
if [ "x$new_subnet_mask" = "x255.255.255.255" ] ; then | ||
ip route add $router dev $interface | ||
fi | ||
ip route add default via $router dev $interface $metric_arg | ||
done | ||
exit 0 | ||
fi | ||
ifconfig $interface inet 0 down | ||
exit 1 | ||
fi | ||
exit 0 | ||
_EOF_ | ||
chmod +x /var/run/uvpn/$USER/dhclient-script | ||
} | ||
|
||
have_interface() { | ||
local if="$1" | ||
ip link show "$1" >/dev/null 2>&1 | ||
} | ||
|
||
cmd_start() { | ||
test $# -eq 0 || die_usage | ||
|
||
sudo $0 start_as_root || exit 1 | ||
create_dhclient-script | ||
cat >/var/run/uvpn/$USER/resolv.conf<<_EOF_ | ||
search molgen.mpg.de | ||
nameserver 141.14.16.1 | ||
_EOF_ | ||
cmd_exec ip link set lo up | ||
cmd_exec ip link set "uvpn.$USER.1" up | ||
cmd_exec mount --bind /var/run/uvpn/$USER/resolv.conf /etc/resolv.conf | ||
cmd_exec dhclient -4 -v \ | ||
-lf /var/run/uvpn/$USER/dhclient.leases \ | ||
-pf /var/run/uvpn/$USER/dhclient.pid \ | ||
-sf /var/run/uvpn/$USER/dhclient-script \ | ||
uvpn.$USER.1 | ||
exit | ||
} | ||
|
||
cmd_start_as_root() { | ||
test $# -eq 0 || die_usage | ||
|
||
test $(id -u) -eq 0 || die "must be root" | ||
user="$SUDO_USER" | ||
test -n "$user" || die "must be called via sudo" | ||
uid=$(id -u "$user") 2>/dev/null || die "$user: no such user" | ||
umask 022 | ||
have_interface vlan.guest0 || die "vlan.guest0 not available. This system is not prepared to run $0. Please contact helpdesk@molgen.mpg.de" | ||
|
||
findmnt "/run/uvpn/$user/ns" >/dev/null 2>&1 && die "container already started" | ||
|
||
if ! have_interface "uvpn.$user.0"; then | ||
ip link add "uvpn.$user.0" type veth peer name "uvpn.$user.1" | ||
fi | ||
if ! have_interface br.guest0; then | ||
brctl addbr br.guest0 | ||
brctl addif br.guest0 vlan.guest0 | ||
fi | ||
brctl addif br.guest0 "uvpn.$user.0" | ||
ip link set "uvpn.$user.0" up | ||
ip link set vlan.guest0 up | ||
ip link set br.guest0 up | ||
|
||
mkdir -p "/run/uvpn/$user/ns" | ||
chown "$user" "/run/uvpn/$user" | ||
for ns in user net mnt;do | ||
touch "/run/uvpn/$user/ns/$ns" | ||
done | ||
mount --bind "/run/uvpn/$user/ns" "/run/uvpn/$user/ns" | ||
mount --make-private "/run/uvpn/$user/ns" | ||
|
||
pid=$(setuid $uid unshare --mount --user --net bash -c 'sleep 30 > /dev/null&echo $!') | ||
for ns in user net mnt;do | ||
mount --bind /proc/$pid/ns/$ns "/run/uvpn/$user/ns/$ns" | ||
done | ||
echo "0 $uid 1" > /proc/$pid/uid_map | ||
echo "0 $(id -g "$user") 1" > /proc/$pid/gid_map | ||
ip link set "uvpn.$user.1" netns $pid | ||
kill $pid | ||
} | ||
|
||
cmd_stop() { | ||
test $# -eq 0 || die_usage | ||
user=$USER | ||
uid=$(id -u "$user") | ||
if findmnt "/run/uvpn/$user/ns/net" > /dev/null; then | ||
inode_net=$(stat --format %i "/run/uvpn/$user/ns/net") | ||
for pid in $(cd /proc;ls -1d [0-9]*|sort -n); do | ||
inode="$(stat -L --format %i /proc/$pid/ns/net 2>/dev/null)" || continue | ||
test $inode == $inode_net || continue | ||
cmd=$(cat /proc/$pid/cmdline|sed 's/\x0/ /g') | ||
echo "kill --> $pid $cmd" | ||
kill -9 $pid | ||
found_processes=1 | ||
done | ||
test -n "$found_processes" && sleep 1 | ||
fi | ||
sudo $0 stop_as_root | ||
} | ||
|
||
cmd_stop_as_root() { | ||
test $# -eq 0 || die_usage | ||
test $(id -u) -eq 0 || die "must be root" | ||
user="$SUDO_USER" | ||
test -n "$user" || die "must be called via sudo" | ||
uid=$(id -u "$user") 2>/dev/null || die "$user: no such user" | ||
|
||
for ns in user net mnt;do | ||
while umount "/run/uvpn/$user/ns/$ns" 2>/dev/null; do true; done | ||
done | ||
while umount "/run/uvpn/$user/ns" 2>/dev/null; do true; done | ||
ip link del "uvpn.$user.0" 2>/dev/null | ||
} | ||
|
||
cmd_exec() { | ||
findmnt "/run/uvpn/$USER/ns" >/dev/null 2>&1 || die "container not started" | ||
nsenter \ | ||
--wd=$(pwd) \ | ||
--net="/run/uvpn/$USER/ns/net" \ | ||
--user="/run/uvpn/$USER/ns/user" \ | ||
--mount="/run/uvpn/$USER/ns/mnt" \ | ||
"$@" | ||
} | ||
|
||
cmd_show() { | ||
user="$1";shift | ||
test $# -eq 0 || die_usage | ||
test -z "$user" && user=$USER | ||
uid=$(id -u "$user") 2>/dev/null || die "$user: no such user" | ||
findmnt "/run/uvpn/$user/ns/net" >/dev/null 2>/dev/null || { echo "Container not started"; exit; } | ||
inode_net=$(stat --format %i "/run/uvpn/$user/ns/net") | ||
echo "Processes running in this container:" | ||
for pid in $(cd /proc;ls -1d [0-9]*|sort -n); do | ||
inode="$(stat -L --format %i /proc/$pid/ns/net 2>/dev/null)" || continue | ||
test $inode == $inode_net || continue | ||
cmd=$(cat /proc/$pid/cmdline|sed 's/\x0/ /g') | ||
echo " $pid $cmd" | ||
found_processes=1 | ||
done | ||
test -n "$found_processes" || echo " (none)" | ||
} | ||
|
||
cmd="$1";shift | ||
case "$cmd" in | ||
start) | ||
cmd_start "$@" | ||
;; | ||
start_as_root) | ||
cmd_start_as_root "$@" | ||
;; | ||
stop) | ||
cmd_stop "$@" | ||
;; | ||
stop_as_root) | ||
cmd_stop_as_root "$@" | ||
;; | ||
exec) | ||
cmd_exec "$@" | ||
;; | ||
show) | ||
cmd_show "$@" | ||
;; | ||
*) | ||
die_usage | ||
;; | ||
esac |