Techniques — Simple Linux networking
Back to techniques
http://pobox.com/~rudolf/techniques/linuxnet.html

Based on Ubuntu 10.04 (a variety of Debian Linux).

Get a basic firewall up

sudo ufw default deny
# sudo ufw allow http # for web servers; this is port 80
# sudo ufw allow https # for web servers; this is port 443
sudo ufw allow ssh # this is port 22
sudo ufw logging on
sudo ufw enable
sudo ufw status

Basic network information

# Interface information
netstat -i -e
# Info for "eth0"
sudo ethtool eth0
# Other diagnostics
netstat --tcp [--listening] [--numeric] [--programs]
nmap -v localhost

Send e-mail via a TLS-secured e-mail host

sudo apt-get install sendemail

sendEmail \
        -s EMAILSITE.DOMAIN \
        -xu USERNAME \
        -xp PASSWORD \
        -o tls=yes \
        -f "NICE-SENDER-NAME <REPLY-TO-ADDR>" \
        -t "TO-USER-NICE-NAME <SEND-TO-ADDR>" \
        -u "SUBJECT" \
        -m "MESSAGE"

Set up a reverse SSH connection

For example: you have a public-facing host with SSH access, called publichost; you have a private (e.g. conventional ISP) host called privatehost; you want to be able to log in to privatehost from publichost.

  1. Install the autossh package.
  2. Call the following script e.g. /usr/local/bin/autossh_tunnel_publichost on privatehost and run /usr/local/bin/autossh_tunnel_publichost start regularly (e.g. every 10 minutes) from privatehost's /etc/crontab. That gets the connection going. Download as autossh_tunnel_PUBLICHOST or see below:
    #! /bin/sh
    #
    # Author:	Andreas Olsson <andreas@arrakis.se>
    # Version:	@(#)autossh_tunnel.foo  0.1  27-Aug-2008  andreas@arrakis.se
    # Modified by RNC 14/10/9
    #
    # For each tunnel; make a uniquely named copy of this template.
    
    ## SETTINGS
    #
    # autossh monitoring port (needs to be unique on publichost)
    MPORT=5124 ### amend as necessary
    # the ssh tunnel to setup (also needs to be unique on publichost)
    TUNNEL="-R 5024:localhost:22" ### amend the "5024" bit as necessary
    # remote user
    RUSER="myusername" ### amend as necessary
    # remote server
    RSERVER="publichost" ### amend as necessary
    # keyfile
    KEYFILE="/home/myusername/.../mykeyfile" ### amend to point to a real SSH private key (which will be accepted by publichost for this username)
    # You must use the real autossh binary, not a wrapper.
    DAEMON=/usr/lib/autossh/autossh
    #
    ## END SETTINGS
    
    PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
    
    NAME=`basename $0`
    PIDFILE=/var/run/${NAME}.pid
    SCRIPTNAME=/etc/init.d/${NAME}
    DESC="the tunnel"
    
    test -x $DAEMON || exit 0
    
    #RNC# export AUTOSSH_PORT=${MPORT}
    export AUTOSSH_PIDFILE=${PIDFILE}
    #RNC# ASOPT=${TUNNEL}" -f -N "${RUSER}"@"${RSERVER}
    ASOPT="-M "${MPORT}" -N -f -i "${KEYFILE}" "${TUNNEL}" "${RUSER}"@"${RSERVER}
    
    #	Function that starts the daemon/service.
    d_start() {
    	start-stop-daemon --start --quiet --pidfile $PIDFILE \
    		--exec $DAEMON -- $ASOPT
    	if [ $? -gt 0 ]; then
    	    echo -n " not started (or already running)"
    	else
    	    sleep 1
    	    start-stop-daemon --stop --quiet --pidfile $PIDFILE \
    		--test --exec $DAEMON > /dev/null || echo -n " not started"
    	fi
    }
    
    #	Function that stops the daemon/service.
    d_stop() {
    	start-stop-daemon --stop --quiet --pidfile $PIDFILE \
    		--exec $DAEMON \
    		|| echo -n " not running"
    }
    
    case "$1" in
      start)
    	echo -n "Starting $DESC: $NAME"
    	d_start
    	echo "."
    	;;
      stop)
    	echo -n "Stopping $DESC: $NAME"
    	d_stop
    	echo "."
    	;;
      restart)
    	echo -n "Restarting $DESC: $NAME"
    	d_stop
    	sleep 1
    	d_start
    	echo "."
    	;;
      *)
    	echo "Usage: $SCRIPTNAME {start|stop|restart}" >&2
    	exit 3
    	;;
    esac
    
    exit 0
    
    
  3. Then from publichost, you can execute ssh localhost -p 5024 (assuming you kept the main port as 5024 in your copy of the script).

SSH authorization

To generate a public/private SSH keypair, and authorize it:

# ON LOCAL:
ssh-keygen -t rsa -f /home/thisuser/.ssh/localhost-to-remotehost-rsync-key
# ... enter a blank passphrase if you want to use unattended operation
# ... makes localhost-to-remotehost-rsync-key (PRIVATE = LOCAL) and localhost-to-remotehost-rsync-key.pub (PUBLIC = REMOTE)
# ... now ensure the private key (without the .pub extension) is only readable by the user
# ... then move the public key to the remote host:
scp /home/thisuser/.ssh/localhost-to-remotehost-rsync-key.pub remoteuser@remotehost:/home/remoteuser/

# ON REMOTE: log in by hand (ssh remoteuser@remotehost), then:
cd ~
if [ ! -d .ssh ]; then mkdir .ssh ; chmod 700 .ssh ; fi
mv localhost-to-remotehost-rsync-key.pub .ssh/
cd .ssh/
if [ ! -f authorized_keys ]; then touch authorized_keys ; chmod 600 authorized_keys ; fi
cat localhost-to-remotehost-rsync-key.pub >> authorized_keys

# ON LOCAL: DO THIS MANUALLY ONCE (SO THAT THE REMOTE HOST GETS ADDED TO THE known_hosts DATABASE):
ssh remoteuser@remotehost
# ... should now authenticate with the keyfile

# Now you're ready for automated operation. But...

Can now restrict operation further using .ssh/authorized_keys on the remote. For details see this helpful CU Engineering page. You can restrict the valid hosts that this key will work for. Additionally, you can restrict the commands it can run. If you intend to run it as root (which you should avoid if it is not absolutely necessary), then you should restrict as much as possible, including the IP addresses allowed and the functions allowed through SSH. For example, (1) in /etc/ssh/sshd_config, specify PermitRootLogin forced-commands-only (as opposed to the better default of PermitRootLogin no); (2) in your /root/.ssh/authorized_keys file, have lines like e.g.

from="myvalidhost.myvaliddomain",command="/usr/local/bin/validate_ssh_command",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa the_key_itself root@myservername

... then have a /usr/local/bin/validate_ssh_command script (downloadable as validate_ssh_command or see below) that only allows certain things through, e.g. this for rsync and unison:

#!/bin/sh
case "$SSH_ORIGINAL_COMMAND" in
	*\&*)
		echo "Rejected"
		;;
	*\(*)
		echo "Rejected"
		;;
	*\{*)
		echo "Rejected"
		;;
	*\;*)
		echo "Rejected"
		;;
	*\<*)
		echo "Rejected"
		;;
	*\`*)
		echo "Rejected"
		;;
	rsync\ --server*)
		$SSH_ORIGINAL_COMMAND
		;;
	unison\ *)
		$SSH_ORIGINAL_COMMAND
		;;
	*)
		echo "Rejected"
 		;;
esac

When things go wrong, open a temporary port on the server and use /usr/sbin/sshd -d -p PORTNUM on the server to watch what's happening, and ssh HOST -p PORTUM -v -v on the client.

Watch a web page

Create the following script as /usr/local/bin/watch_web_page. In /etc/crontab, run it regularly, passing it (1) a URL, (2) a temporary file where it can keep a copy of the web page, and (3) a program to run if the page changes (e.g. to e-mail you about it). Download as watch_web_page or see below:

#!/bin/sh

# $1 = URL to watch.
# $2 = File to use for temporary storage of a copy.
# $3 = Program to run (with URL as parameter) when watched page changes.

# RNC, 18 June 2010.

URL=$1
FILEONE=$2
FILETWO=$FILEONE.new
PROGRAM=$3

if [ -f $FILEONE ];
then
	echo "Page previously stored."
	wget -O $FILETWO $URL >/dev/null 2>&1 && ( echo "Web page fetched using wget." ) || ( echo "Failed to fetch web page."; exit 1 )
	cmp $FILEONE $FILETWO && ( echo "No change to page." ) || ( echo "Page changed."; $PROGRAM $URL )
	rm $FILEONE
	mv $FILETWO $FILEONE
else
	echo "Page not previously stored."
	wget -O $FILEONE $URL >/dev/null 2>&1 && ( echo "Web page fetched using wget." ) || ( echo "Failed to fetch web page."; exit 1 )
fi
exit 0

Local backup and synchronizing files across hosts

Where possible, synchronize user-to-user. Where the backup system has to be able to cope with files owned by any user/group with any permission, use root.

Mount a filesystem using SSH

# mount:
sshfs hostname:/directory/ temp/

# unmount:
fusermount -u temp/

VNC to a dynamic-IP-address Windows machine from a fixed-IP-address Linux host

The prototypical problem: someone needs help with their home Windows computer but has Internet access via a conventional broadband ISP; their computer is therefore not directly accessible to the wider Internet. You have access to a decent machine (viz., a UNIX-based computer with a static IP address). We'll call the computers winbox and linuxbox. Prerequisites:

Method 1: VNC Server to Listening VNC Viewer (directly)

The simplest of the three methods for the Windows user (no PuTTY required). The VNC server has to be on the Windows machine (because that's the one we want to look at). However, the VNC server can initiate the connection to the viewer (which is helpful if the VNC server is on a machine with a dynamic and inaccessible IP address, while the viewer is on a proper static IP address).

Method 2: VNC Server to Listening VNC Viewer (via SSH)

In this method, we connect the same way round (server on the Windows box initiates the connection to the viewer on the Linux host), but we tunnel it through SSH, so only port 22 (SSH) needs to be exposed to the outside world by the Linux host.

Method 3: VNC Viewer to VNC Server (via SSH)

In this method, the VNC connection is initiated by the viewer. To make the server visible to the viewer, it needs to be tunnelled via an SSH connection (which is initiated by the Windows box in our hypothetical situation).

NX to an XFCE system

Desktop: Unix / custom. Settings: Run...: /usr/bin/xfce4-session. Options: New virtual desktop.

Printing from a Windows guest OS in VirtualBox to an underlying CUPS printer

See http://funwithlinux.wordpress.com/2009/05/22/sharing-ubuntu-host-printer-with-windows-xp-running-in-virtualbox/ and http://machine-cycle.blogspot.com/2008/11/sharing-cups-pdf-printer-over-ipp.html. There's no need to open port 631 on your firewall, as the Linux host will see requests from the VirtualBox guest OS as being local.

Valid HTML 4.01 Transitional
Valid CSS