Monday, November 24, 2008

Enable HTTP proxy in Gnome automatically

I have a laptop with Linux (currently Ubuntu) which I use both at home and at work. The corporate security policy requires everyone to use the HTTP proxy server with authentication for web access, so when I come to work I had to manually enable it, and then disable again at home - not very convenient.

As a side note, Firefox 3+ is great in respecting the global or system-wide proxy configuration (System->Preferences->Network proxy or gnome-network-preferences) as well as gnome-terminal is very nice to set the http_proxy environment variable automatically when proxy is configured, making most command-line tools respect the global proxy setting as well, which is very cool.

So, before network profiles have arrived to Gnome or NetworkManager (I have seen some related commits in Gnome SVN), I still want to enable the proxy automatically depending on my location. Thankfully, NetworkManager supports execution of scripts when it brings interfaces up or down, so this is not difficult at all.

At least on Ubuntu, NetworkManager executes the scripts that are located in /etc/NetworkManager/dispatcher.d/ when it brings interfaces up. Inside of the script I can detect whether I am at work by checking the domain name in /etc/resolv.conf provided by the corporate DHCP server, or the beginning of the assigned IP address if domain can't be used for any reason.

OK, here is the working script for Ubuntu Karmic, Jaunty and Intrepid (Gnome 2.24+), see notes below for older versions. I have this script in /etc/NetworkManager/dispatcher.d/02proxy, because 01ifupdown already exists there.

It is an updated version, attempting to make the script suitable for more general use, eg in our company we now provide it in a .deb package for all Ubuntu-based laptops.

#!/bin/bash
# The script for automatically setting the proxy server depending on location.
# Put it under /etc/NetworkManager/dispatcher.d/02proxy
# Create also the /etc/NetworkManager/proxy_domains.conf, specifying the mapping of
# DHCP domains to proxy server addresses, eg "example.com proxy.example.com:3128"
# Written by Anton Keks

PROXY_DOMAINS="/etc/NetworkManager/proxy_domains.conf"

# provided by NetworkManager
INTERFACE=$1
COMMAND=$2

function gconf() {
sudo -E -u $USER gconftool-2 "$@"
}

function saveUserConfFile() {
echo "DOMAIN_USER=$DOMAIN_USER" > $CONF_FILE;
echo "DOMAIN_PWD_BASE64="`echo $DOMAIN_PWD | base64` >> $CONF_FILE;
echo "PROXY_HOST=$PROXY_HOST" >> $CONF_FILE;
echo "PROXY_PORT=$PROXY_PORT" >> $CONF_FILE;
}

function enableProxy() {
PROXY_HOST=`cat $PROXY_DOMAINS | grep $DOMAIN | sed 's/.* \+//' | sed 's/:.*//'`
PROXY_PORT=`cat $PROXY_DOMAINS | grep $DOMAIN | sed 's/.*://'`

# check if authentication is required
http_proxy=http://$PROXY_HOST:$PROXY_PORT/ wget com 2>&1 | grep "ERROR 407"
if [ $? -eq 0 ]; then
AUTH_REQUIRED="true"
CONF_FILE=$HOME/.proxy:$DOMAIN

if [ ! -e $CONF_FILE ]; then
DOMAIN_USER=`sudo -E -u $USER zenity --entry --text "Login name for domain $DOMAIN"`
DOMAIN_PWD=`sudo -E -u $USER zenity --entry --text "Password for domain $DOMAIN" --hide-text`
saveUserConfFile
fi

# load user proxy settings
. $CONF_FILE
# decode password
DOMAIN_PWD=`echo $DOMAIN_PWD_BASE64 | base64 -d`

# get Kerberos ticket (if it's configured)
if echo $DOMAIN_PWD | sudo -E -u $USER kinit $DOMAIN_USER; then
KLIST_INFO=`sudo -E -u $USER klist | fgrep Default`
sudo -E -u $USER notify-send -i gtk-info "Domain login" "Kerberos ticket retrieved successfully: $KLIST_INFO"
fi
else
AUTH_REQUIRED="false"
fi

# setup proxy
gconf --type string --set /system/proxy/mode "manual"
gconf --type bool --set /system/http_proxy/use_http_proxy "true"
gconf --type string --set /system/http_proxy/host $PROXY_HOST
gconf --type int --set /system/http_proxy/port $PROXY_PORT
gconf --type bool --set /system/http_proxy/use_same_proxy "true"
gconf --type bool --set /system/http_proxy/use_authentication $AUTH_REQUIRED
gconf --type string --set /system/http_proxy/authentication_user $DOMAIN_USER
gconf --type string --set /system/http_proxy/authentication_password $DOMAIN_PWD

# notify
sudo -E -u $USER notify-send -i gtk-info "Proxy configuration" "Your proxy settings have been set to: $DOMAIN_USER@$PROXY_HOST:$PROXY_PORT"
}

function disableProxy() {
gconf --type string --set /system/proxy/mode "none"
gconf --type bool --set /system/http_proxy/use_http_proxy "false"
gconf --type string --set /system/http_proxy/host ""
gconf --type bool --set /system/http_proxy/use_authentication "false"
gconf --type string --set /system/http_proxy/authentication_user ""
gconf --type string --set /system/http_proxy/authentication_password ""
}

# wait for gnome-settings-daemon to appear, ie until user logs in
for i in {1..100}; do
if [ ! `pidof gnome-settings-daemon` ]; then
sleep 5;
echo "Waiting for gnome-settings-daemon to appear..."
else
break
fi
done
if [ ! `pidof gnome-settings-daemon` ]; then
echo "gnome-settings-daemon is not running. exiting."
exit 1
fi

# steal environment from the current non-root user
XENV=`xargs -n 1 -0 echo </proc/$(pidof gnome-settings-daemon)/environ`
# init DBUS connection string in order to reach gconfd
eval export `echo "$XENV" | fgrep DBUS_SESSION_BUS_ADDRESS=`
eval export `echo "$XENV" | fgrep USER=`
eval export `echo "$XENV" | fgrep HOME=`
eval export `echo "$XENV" | fgrep DISPLAY=`
eval export `echo "$XENV" | fgrep XAUTHORITY=`

if [ $COMMAND != 'up' ]; then
disableProxy;
exit
fi

DOMAIN=`cat /etc/resolv.conf | grep domain | sed 's/domain \+//'`
# check if we need to set proxy settings for this domain
if [[ -e $PROXY_DOMAINS && ! `cat $PROXY_DOMAINS | grep $DOMAIN` ]]; then
echo "Proxy is not required for domain $DOMAIN"
disableProxy
else
echo "Setting proxy for domain $DOMAIN"
enableProxy
fi

Don't forget to:
  • give this script execute permissions
  • have gconftool-2, zenity and kinit installed (gconf2, zenity, krb5-user packages in Ubuntu). Install gconf-editor as well for a graphical config editor.
  • create /etc/NetworkManager/proxy_domains.conf, specifying the mapping of DHCP domains to proxy server addresses, eg "example.com proxy.example.com:3128". Specify each domain on a new line.
The script doesn't need you to hardcode your username and the proxy password anymore - the script will ask you for these values on first run and then store them in $HOME/.proxy:$DOMAIN file, so the script is now perfectly usable on multiuser machines and doens't bug you in case of 'unknown' domains.

For more functionality, it even tries to retrieve the Kerberos ticket for you, if the kerberos is configured properly in /etc/krb5.conf. You can check if this is the case by running this on the command-line:
kinit your-user-name; klist
This works very well for me and saves several mouse clicks every morning :-)

Note to Gnome 2.22 and older users (Ubuntu Hardy, etc): I had this script initially done in Hardy, but after upgrading to Intrepid (Gnome 2.24) it stopped working. The reason was that starting from Gnome 2.24, the gconf setting of /system/http_proxy/use_http_proxy is not the primary one and has been replaced by /system/proxy/mode, which takes one of three values: 'auto', 'manual' and 'none'. In Intrepid, if you set only /system/http_proxy/use_http_proxy as before - it has no effect, you need to set /system/proxy/mode to manual, and this will set the value of the old setting to 'true' automatically.

Another thing introduced with Intrepid is the need to set the DBUS_SESSION_BUS_ADDRESS environment variable (the script steals it from the x-session-manager process) - this is because gconfd has switched to DBUS from CORBA for a communication protocol. If you have older Gnome, then you may omit these 2 lines involving DBUS.

Enjoy!


200 comments: