Wednesday, June 25, 2014

Netbooting my iMac G3

I recently inherited an iMac G3 from a friend moving away and wanted to get Debian Wheezy on it via network boot. Here's how. My original resource was http://blog.jardinmagique.info/2009/05/net-install-and-setup-debian-ppc-on-new.html. This post distills those instructions to the bare minimum.

Preparing a Netboot Server

Fully automatic netboot requires a TFTP server to provide a boot image and a BOOTP server to advertise the image's availability. The Open Firmware boot command allows us to directly specify a TFTP server address and image, so we can skip the BOOTP part. So we'll just need a TFTP server with the right Debian netboot image.

I'm using a Debian wheezy installation in a virtual machine for my tftpd server.


Setting up tftpd

Most guides suggest using the "tftpd-hpa" package. The version in Debian wheezy seems to be broken, so I'm going for "tftpd". Run:
sudo apt-get install tftpd tftp
sudo mkdir /srv/tftpd

Next edit /etc/inetd.conf. Find the line specifying the command to start the tftpd server as "/usr/sbin/in.tftpd /srv/tftp" and add an argument "-s" before the directory name; that section of the line should read "/usr/sbin/in.tftpd -s /srv/tftp". This tells tftpd that when it receives a request for an absolute path like "/vmlinuz", it should serve the file "/srv/tftp/vmlinuz" instead of indicating that access is denied for the real absolute path, which is important to make the standard Debian netboot image work without modification.

Finally restart inetd:
sudo service openbsd-inetd restart


Preparing the Debian netboot image

Next we need to download the contents of the powerpc netboot image folder to /srv/tftpd. For wheezy, here's the command:
lftp -c mirror ftp://http.us.debian.org/debian/dists/wheezy/main/installer-powerpc/current/images/powerpc/netboot /srv/tftp

Let's test and see if we can download something:
tftp 127.0.0.1
tftp> get boot.msg
tftp> exit

If the "get boot.msg" command terminates, you've set tftp up correctly.

Netbooting the Mac

Boot the Mac, holding Command-Option-O-F. At the Open Firmware prompt, enter:
boot enet:<your tftpd server ip address>, yaboot

Once you get to the yaboot prompt, hit enter. From there you should be in the standard Debian installer.

Monday, February 10, 2014

Yet another Mac SSH SOCKS proxy script

Comcast hasn't been handling video streaming very well lately, so I thought I'd try through a VPN. I've already got a private server with SSH access and unlimited bandwidth. SSH has a flag to present a SOCKS proxy to the client machine and forward through the connection and a SOCKS client is built in to the OS X networking stack, so we'll give that a try. I found one or two scripts to establish a the proxy with SSH and configure OS X to use it, but they don't handle error conditions very nicely and require me to enter an admin password twice - once to establish the connection, and once to break it down. Here's my entry in the genre, followed by a description of how it works.

Launching SSH

We can launch the proxy with:

ssh -ND 1080 $host
'-N' keeps the connection open without running a command; '-D 1080' configures the SOCKS proxy on port 1080. Ideally we want to try to start the SSH tunnel first so that if it can't connect we don't bother to prompt for an admin password and enable, then disable, the proxy configuration. We'll need to keep the SSH connection open while we complete the subsequent configuration steps, so we want to launch it as a background subprocess. That's pretty easy to do with python's subprocess module:
import subprocess, sys
host = sys.argv[1]
ssh = subprocess.Popen(['ssh', '-ND', '1080', host])
# configure OS X to use the proxy here...
However, that doesn't give us any way to know when it has successfully connected and we can move on. We need ssh to print something out upon a successful connection, and we need our script to look for that output. Let's use the ssh LocalCommand option, which runs a command in your shell once the connection has been established. We'll just echo a string we can look for from the script.
import subprocess, sys
host = sys.argv[1]
ssh = subprocess.Popen([
    'ssh', '-ND', '1080',

    # Have ssh print 'connected' after successful connection
    '-o', 'PermitLocalCommand=yes',
    '-o', "LocalCommand=echo connected",

    host],

    stdout=subprocess.PIPE
    )

if ssh.stdout.readline() == "connected\n":
    # configure OS X to use the proxy here...

Configuring OS X to use the proxy

It's easy enough to configure use of a SOCKS proxy from Network Preferences, but it takes a few clicks. Let's use the networksetup tool on the command line instead. Initially we need to point it to localhost at the right port:

sudo networksetup -setsocksfirewallproxy Wi-Fi localhost 1080
Subsequently, we can turn it on:
sudo networksetup -setsocksfirewallproxystate Wi-Fi on
and off:
sudo networksetup -setsocksfirewallproxystate Wi-Fi off
For our implementation in python we could just call each of those commands with Popen as above. But if there's a long time between turning the proxy configuration on and turning it off, sudo will ask for our password both times. We can do better. Let's launch a root privilege shell with with "sudo -s" and send it commands:
proxy_command_base = ['networksetup', '-setsocksfirewallproxystate', 'Wi-Fi']
admin_shell = subprocess.Popen(
        ['sudo', '-s'],
        # Line buffered so we're sure the shell gets our commands immediately.
        bufsize=1,
        stdin=subprocess.PIPE)

admin_shell.stdin.write(' '.join(proxy_command_base + ['on']) + \
        " && echo 'SOCKS proxy via %s on. CTRL-C to turn off.'\n" % host)

ssh.wait()

admin_shell.stdin.write(' '.join(proxy_command_base + ['off']) + \
        ' && echo "SOCKS proxy off."\n')

admin_shell.stdin.close()
admin_shell.wait()
No matter how long the SSH connection stays open, when it terminates we'll still be able to turn off the proxy configuration without authenticating again.

We still need to handle clean termination of the connection, however. Let's allow the user to disable the proxy by sending our script SIGINT, eg. pressing CTRL-C. When we press CTRL-C, the shell sends SIGINT to each process in the current process group. That'll include our python interpreter, the ssh process, and the privileged shell. We want ssh to respond by terminating, but we want the other two processes to stay alive so we can turn the proxy configuration off. We can keep our script alive by catching the KeyboardInterrupt exception:

try:
    # configure proxy here
    ssh.wait()

# SIGINT will have killed ssh; carry on to clean up SOCKS config.
except KeyboardInterrupt:
    print

# disable proxy configuration here
Finally, we need to stop the privileged shell from exiting. We'll use the preexec_fn option of the Popen object to remap SIGINT so that it's ignored in the subprocess:
admin_shell = subprocess.Popen(
        ['sudo', '-s'],

        # Line buffered so we're sure the shell gets our commands immediately.
        bufsize=1,

        stdin=subprocess.PIPE,

        # Don't let SIGINT (ctrl-c) kill the shell - we need to use it to clean up.
        preexec_fn = lambda: signal.signal(signal.SIGINT, signal.SIG_IGN)
        )
That's about it. Comments welcome.