Saturday, November 21, 2009

Proxy environment variables and sudo in Ubuntu

At work we use a proxy server to access the internet from our test network. We also have an internal Ubuntu mirror on the test network to install updates and packages, this server must be accessed by the test machines directly without the proxy.

Setting up the proxy and the exception for the mirror can be done in a terminal using the environment variables http_proxy, https_proxy, ftp_proxy, and no_proxy.

In Ubuntu these variables are all set in the /etc/environment file. On my test system this file contains the following:

trastle@trastle-test:~$ cat /etc/environment
...
http_proxy="http://webproxy:3128/"
ftp_proxy="ftp://webproxy:3128/"
https_proxy="https://webproxy:3128/"
no_proxy="office-mirror"

Testing that these settings have been applied to the current terminal can be done by calling printenv:

trastle@trastle-test:~$ printenv | grep proxy
http_proxy=http://webproxy:3128/
ftp_proxy=ftp://webproxy:3128/
https_proxy=https://webproxy:3128/
no_proxy=office-mirror

This output shows that all of the required environment variables are set. However when I try to perform an update from the office mirror it fails! The http_proxy is being used to access hosts clearly specified in the no_proxy variable.

trastle@trastle-test:~$ sudo apt-get update
Ign http://office-mirror jaunty Release.gpg
...
Err http://office-mirror jaunty/main Packages
503 Service Unavailable
...
W: Failed to fetch http://office-mirror/ubuntu/dists/jaunty/main/binary-i386/Packages
503 Service Unavailable
...
E: Some index files failed to download, they have been ignored, or old ones used instead.

This all makes more sense when you check the environment variables that sudo is running with:

trastle@trastle-test:~$ sudo printenv | grep proxy
http_proxy=http://webproxy:3128/

For safety sudo runs with an minimal environment. This default list is hard coded into sudo (initial_keepenv_table in env.c if you want to go read the source). Ubuntu patches this list to include http_proxy but not no_proxy. This breaks the ability to get updates from the local mirror. Sudo can be queried to determine which environment variables it preserves by default:

trastle@trastle-test:~$ sudo sudo -V
Sudo version 1.6.9p17
...
Sudoers path: /etc/sudoers
...
Environment variables to preserve:
http_proxy
XAUTHORIZATION
XAUTHORITY
TZ
PS2
PS1
PATH
MAIL
LS_COLORS
KRB5CCNAME
HOSTNAME
HOME
DISPLAY
COLORS
...

The solution to this problem is to modify the list of environment variables that sudo will preserve by editing the /etc/sudoers file and providing an explicit list of which environment variables to be preserved, overwriting the default list. The configuration that must be added to /etc/sudoers is as follows:

Defaults env_keep="no_proxy http_proxy https_proxy ftp_proxy XAUTHORIZATION \
XAUTHORITY TZ PS2 PS1 PATH MAIL LS_COLORS KRB5CCNAME HOSTNAME HOME DISPLAY COLORS"

This will preserve all of the default environment variables as well as no_proxy, https_proxy and ftp_proxy. You can test this setting to prove that sudo is now preserving these extra environment variables by running:

trastle@trastle-test:~$
sudo printenv | grep proxy
http_proxy=http://webproxy:3128/
ftp_proxy=ftp://webproxy:3128/
https_proxy=https://webproxy:3128/
no_proxy=office-mirror

With these additional environment variables preserved updates and installs from the command line using your local mirror and ignoring your proxy server will work like a charm.