Showing posts with label bash. Show all posts
Showing posts with label bash. Show all posts

2011/08/22

SSH tunneling for a small VPN

Here is a simple script that sets up a simple VPN between two networks using SSH tunneling. You must have root access to a computer on both networks. You must have 2 IP addresses for the remote network. The remote network is assumed to be /24. The script assumes that tun0 is available on both computers. If you don't understand this or you don't have this, you can't use this script.
#!/bin/bash


HOST=$1
LOCAL_IP=$2
REMOTE_IP=$3

eval $(ipcalc --network $LOCAL_IP/24)
if [[ "NETWORK=$NETWORK" != $(ipcalc --network $REMOTE_IP/24) ]] ; then
echo "Local IP ($LOCAL_IP) and remote IP ($REMOTE_IP) must be on the same network" >&2
exit 3
fi

ip route del $NETWORK/24 dev eth0 2>/dev/null
set -e

set -x
ssh -w 0:0 $HOST "
ip link set tun0 up
ip addr add $LOCAL_IP peer $REMOTE_IP/32 dev tun0
arp -sD $REMOTE_IP eth0 pub
echo 1 >/proc/sys/NETWORK/ipv4/ip_forward
echo OK
while true ; do sleep 3600 ; done
" | (
set +x
read OK;
echo "Connected to $HOST: $OK"
ip link set tun0 up
ip addr add $REMOTE_IP peer $LOCAL_IP/32 dev tun0
ip route add $NETWORK/24 via $REMOTE_IP dev tun0

iptables --append FORWARD --in-interface eth0 -j ACCEPT
iptables --table nat --append POSTROUTING --out-interface tun0 -j MASQUERADE
set +e
read -e -p 'Connected' K iptables --delete FORWARD --in-interface eth0 -j ACCEPT
iptables --table nat --delete POSTROUTING --out-interface tun0 -j MASQUERADE
echo "Use ^C to stop background ssh"
)

Note that the script has very little error checking beyond verifying that REMOTE_IP and LOCAL_IP are on the same /24.

The script is executed as vpn-ssh remote-host local-ip remote-ip. For example:
ssh my-client 192.168.100.130 192.168.100.205
192.168.100.130 is now an IP for the local computer. It may be connected to from the remote computer and anything on the remote network. If you set up your routing properly, other computers on the local network may also connect to the remote network also.

To create a tunnel ssh must run as root and must connect as root on the remote side. You should use have public/private keys set up, as allowing password login for root over ssh is a bad idea. Tunneling and root login are often deactivated by default, so you'll have to turn them on.

2010/10/06

Neat bash tricks

One idiom I like is:
ssh $host "commands;" 2>&1 | while read line ; do

# react to any error messages or messages from commands in $line
done

For instance, say you were running x11vnc on a remote host. x11vnc has the annoying habit of using a port other then the one you specify, if the one you want is already taken. Very annoying. So:
ssh $host "x11vnc ...." 2>&1 | while read line ; do

if [[ $line =~ 'PORT=([[:digit:]]+)' ]] ; then
port=${BASH_REMATCH[1]}
# now set up some sort of port forwarding so that $port is a sane, known port
ssh -L '*:9600:localhost'$port $host
fi
done

This has some problems, in that the second ssh can survive x11vnc exiting. I thought "hey, how about $?" but that has other problems; say the second ssh exits before its time. The $? you saved could be reused. The kill you'd want to do would provoke hilarity. While complaining about this on IRC, a wise soul suggested that I open a lock file, and then any process with that file still open must be killed. I don't need locking, and didn't want to learn about flock in bash right away, so what I roughly did was:
child_kill () {

if [[ ! $LOCKFILE ]] ; then
return 0
fi
lsof -F '' $LOCKFILE | while read ppid ; do
if [[ $ppid =~ '^p([[:digit:]]+)$' ]] ; then
pid=${BASH_REMATCH[1]}
if [[ $pid != $$ ]] ; then
kill -HUP $pid
fi
fi
done
rm -f $LOCKFILE
}

local LOCKFILE=$(mktemp -p /tmp)
trap "child_kill" EXIT
ssh ... | while read line ; do
....
( exec 123>$LOCKFILE
ssh -L ..... $host &
)
done
child_kill

I wish there was a better way to deal with lsof's output, but this works so why complain?

What's more, I wish I could use SSH's ControlMaster to make the second connection that much faster. But the quick testing I did with 4.9p1 failed. Bugger

2010/09/30

Bash and dialog

First, I rant about dialog: WHAT THE HELL WERE THEY ON WHEN THEY WROTE THIS THING?! First, they make keyboard inteaction completely different from what normal users would expect. And then they make it very very hard to actually get the results into a shell script. What's more, every time I write a new script that uses dialog, I realise it lacks many features I need. So I have my own forked / hacked version that I deploy. Thank God for Open Source Code.

Second, I give you some code snippets that will show you how to do simple things with dialog:

Say you want a menulist and want to know which one was selected:
resp="$( dialog ..... 2>&1 >/dev/tty)"

Here we assign a value to the variable resp. The "" preserves whitespace. The $() causes the value to come from a sub command. dialog ... is the "normal" dialog invocation. 2>&1 causes stderr to be sent to sdtout, so that it ends up on resp. >/dev/tty causes dialog's stdout (its original stdout, that is, not the bits of stdout that are coming from stderr) to go straight to the controling TTY, rather then ending up in resp, which would be silly.

Now, say you had a shell function call do_something. This function sets serveral variables as a side effect and takes a while doing so. Maybe you'd want to use dialog's --progressbox or --gauge so the user has something nice to look at while work is going on. If so you might try
do_something | dialog --progressbox

And you would quickly encounter failure; do_something ends up in a sub-shell, whence it can not set the variables in the main shell.

So, you spend time with the BashFAQ and on #bash complaining trying to find an answer. It turns out that FIFOs (aka named pipes) are the only way:
fifo=$(mktemp -u)

mkfifo $fifo
trap "rm -f $fifo" EXIT
dialog --progressbox "Wait" 12 40 <$fifo &
pid=$!
do_something > $fifo
rm -f $fifo
wait $pid

So, we create a randomly named FIFO. Run dialog in the background, reading from the FIFO. Run our function in the foreground, stdout going to the FIFO. When do_something is done, we remove the FIFO and wait for dialog to exit. Which it should, after the fifo has been removed. The trap makes sure the FIFO is deleted even if we exit early.

If you ask me, there's got be a better way, one that doesn't involve mkfifo and does involve exec. But I can't get my head around exec.

EDIT: turns out there is a better way:
do_something > >(dialog .....)

A mixed bag

I ordered a BlacX SATA/IDE/USB docking station today. Should have bought one back when I first discovered them. Would have made all the farting around with Corey a lot less painful.

In other news, I discovered that getting xterm to use TrueType fonts is as simple as -fa NAME-PT. As in:
xterm -r -fa Consolas-17

Consolas, if you didn't know, is a nice little monospaced font that MS commissioned, tuned to LCD screens. The previous link downloads a bloody useless setup.exe. So I downloaded the .ttf from elsewhere.

Also, you might be interested in this review of programming fonts from 2007. Though he seems to think that 11pt is legible.

Finally, a snippet to get the current X resolution. I needed this to adjust the font size based on resolution. My users want the terminal to fill the screen.
read prop Xsize Ysize < <(xprop -notype -root 32cc ' $0 $1\n' _NET_DESKTOP_GEOMETRY)

echo "${DISPLAY:-Display} is ($Xsize,$Ysize)"

If you didn't know about Process Substitution you do now.

2010/09/28

LTSP and x11vnc

A few years back, I wrote a system that ended up replacing about 200 terminals in 5 different offices with LTSP running on VMs. 2 and bit years latter, the last bunch of users were moved from terminals to LTSP. This time, they got FIT-PC2s as opposed to diskless PCs. Lucky them; less noise. But they are complaining that they find the mouse to unwieldy. And because they have locked down desktops, they don't have a config app to tweak the mouse acceleration and threshold. So I poked around and found that the settings are in :
/home/$USER/.config/xfce4/mcs_settings/mouse.xml

Now, that is hardly exciting.

What is exciting: I then wanted to set up x11vnc so that I could see what the users were experiencing. I've used x11vnc often on the same desktop, but my first attempt at doing it with LTSP failed. So I go on #LTSP to complainlook for a solution, which turned out to be -noshm.

They first complained that LTSP-4 (which I'm using) is far to archaic (which it is). They then got me on track for the next little bit of bash:
#!/bin/bash


USER=$1

error () { echo "$*" >&2; exit 5 }

if [[ ! $USER ]] ; then
error "Usage: $0 user"
fi

userpid="$(pgrep -u $USER xfwm4)"
if [[ ! $userpid ]] ; then
error "User $USER isn't logged in"
elif [[ $userpid =~ ' ' ]] ; then
error "User $USER is probably logged on several times: $userpid"
fi

userenv="$(tr '\0' '\n' < /proc/$userpid/environ | egrep '^DISPLAY=|^XAUTHORITY=')"

eval "$userenv"
if [[ ! $XAUTHORITY ]] ; then
XAUTHORITY=/home/$USER/.Xauthority
fi
export DISPLAY XAUTHORITY

echo DISPLAY=$DISPLAY
echo XAUTHORITY=$XAUTHORITY

exec x11vnc -noshm -nopw

All the error checking might be hiding the really interesting bit: I'm pulling DISPLAY and XAUTHORITY from the user's environment, via /proc. Something I'd never thought of doing before.

2010/09/24

Tools

This is a short list of commands and idioms that make life at the command prompt bearable. Some of them are trivial, but if you don't know they are there, you'll never find them. Reading their man pages is recommended.
screen

perl -lane 'short script'
find [....] -print0 | xargs -0 cmd
( set -e ; cmd1; cmd2; cmd3 ) && killall some-daemon
pstree
ps -C cmd
lsof -c cmd
ssh-keygen
pidof
chkconfig
$(( arithmetic ))
${var/match/replace}
lshw
for n in [...] ; do cmd $n ; done
some-command | grep | while read n ; do cmd $n ; done

lshw combines lspci, lsusb, lshal, lspcmcia and more. Forgot what motherboard a server has? lshw knows. What size/manufacture/number of DIMMs you installed? lshw knows. What version of BIOS? lshw might know. Under CentOS you have to get it from RPMForge.

One should learn variable expansion syntax if one spend any time with bash.

And yes, I do type while and for loops at the command line.