463

When I use any command with sudo the environment variables are not there. For example after setting HTTP_PROXY the command wget works fine without sudo. However if I type sudo wget it says it can't bypass the proxy setting.

codeforester
  • 28,846
  • 11
  • 78
  • 104
Ahmed Aswani
  • 7,169
  • 7
  • 31
  • 52

6 Answers6

519

First you need to export HTTP_PROXY. Second, you need to read man sudo carefully, and pay attention to the -E flag. This works:

$ export HTTP_PROXY=foof
$ sudo -E bash -c 'echo $HTTP_PROXY'

Here is the quote from the man page:

-E, --preserve-env
             Indicates to the security policy that the user wishes to preserve their
             existing environment variables.  The security policy may return an error
             if the user does not have permission to preserve the environment.
ZachB
  • 9,402
  • 2
  • 43
  • 76
Employed Russian
  • 164,132
  • 27
  • 242
  • 306
  • 1
    great the only problem that is modify some config files for example pacman for arch to make the -E is passed – Ahmed Aswani Dec 26 '11 at 09:01
  • 7
    To allow -E (preserve environment) for wget, you need to specify the SETENV tag on the sudo rule that allows the running of wget -- Example: ALL=(root) NOPASSWD:SETENV: – John Bowers Sep 16 '14 at 16:13
  • 81
    This "-E" doesn't work if the variable is PATH or PYTHONPATH. – apporc Jun 27 '16 at 06:23
  • Also doesn't work with any `LC_*` variable. So just do `export LOL_FOO=$LC_FOO` and use `LOL_FOO` instead. – luckydonald Nov 04 '16 at 20:39
  • 9
    This did not work with the simpler case of adding one element to the `PATH` in the `.bashrc` file -- say, `export PATH=myPath:$PATH`. If I type `sudo -E bash -c 'echo $PATH'`, then `PATH` does not contain myPath probably because `sudo` has already disabled the local value of `PATH` before calling `bash`. Rather, I found the answer below http://stackoverflow.com/a/33183620/5459638 effective, that is `sudo PATH=$PATH command` – XavierStuvw Jan 05 '17 at 17:42
  • Addition to the comment above. Say that `package` is under myPath, then `sudo PATH=$PATH which package` finds the right answer unlike `sudo which package`. However, `sudo PATH=$PATH package` does not go any further than `sudo package` (file not found). On the other hand, launching a plain `package` from a shell invoked with `sudo bash` preserves the extended path and gives `package` sudo rights (two pigeons with one stone). So the response really depends on which commands you are launching – XavierStuvw Jan 05 '17 at 18:13
  • The (setter) user may not have permission to keep environment variables: `-E, --preserve-env ... . The security policy may return an error if the user does not have permission to preserve the environment.` – Mohsen Abasi Mar 19 '18 at 06:54
  • @XavierStuvw It's an "obvious" security measure not allowing PATH by default, since the user could inadvertently execute some non-standard command with UN-expected results and root privileges. – earizon Jul 31 '18 at 08:00
  • Remember, you can always set environment variables inline as well: `HTTP_PROXY=val sudo -E bash -c 'echo $HTTP_PROXY'`. – palswim Oct 17 '18 at 06:24
  • this won't work if your env. variables are set via ~/.profile – tolache Apr 01 '20 at 14:45
324

The trick is to add environment variables to sudoers file via sudo visudo command and add these lines:

Defaults env_keep += "ftp_proxy http_proxy https_proxy no_proxy"

taken from ArchLinux wiki.

For Ubuntu 14, you need to specify in separate lines as it returns the errors for multi-variable lines:

Defaults  env_keep += "http_proxy"
Defaults  env_keep += "https_proxy"
Defaults  env_keep += "HTTP_PROXY"
Defaults  env_keep += "HTTPS_PROXY"
Eric C
  • 3,666
  • 3
  • 30
  • 26
Ahmed Aswani
  • 7,169
  • 7
  • 31
  • 52
  • 13
    This is arguably the best option, to avoid information leakage and security holes. `sudo -E` is the sure-fire way to adhoc get the same effect for a one-off, though – sehe Dec 26 '11 at 14:55
  • I encountered the problem of a process being the one who call sudo (jhbuild) and i can't tell it to pass the -E flag to sudo, so this is my solution. – jgomo3 May 13 '13 at 12:23
  • 64
    Notice that you should *never* edit the `etc/sudoers` directly. Instead, use the `visudo` command, which syntax-checks your edits before overwriting the `sudoers` file. That way, you don't lock yourself out if you make a mistake while editing. – Henning Nov 15 '13 at 08:37
  • 1
    Consider using uppercase env vars. In my case the use of HTTP_PROXY and HTTPS_PROXY did the trick. – pabo Feb 18 '15 at 10:41
  • The syntax to restrict the Defaults to a command or user is here: http://unix.stackexchange.com/a/13246/66983 – matthid Mar 26 '15 at 17:13
  • 3
    lowercase variant is better from my experience as it works in both wget and curl. – Miroslav Mocek Jun 30 '15 at 22:15
  • Does editing / appending new environment variables this way take effect right away? Or does it require a server restart? I can't seem to preserve my `~/.bash_profile` exported vars even after using `sudo visudo` to add them with `Defaults env_keep += "VARNAME"` – bigp Aug 10 '17 at 04:26
  • (continue.) Nevermind, realized I was placing it too early, before the `Defaults env_reset`, haaa! Glad I figured that one out! – bigp Aug 10 '17 at 04:31
  • this won't work if your env. variables are set via ~/.profile – tolache Apr 01 '20 at 14:46
69

For individual variables you want to make available on a one off basis you can make it part of the command.

sudo http_proxy=$http_proxy wget "http://stackoverflow.com"
buckaroo1177125
  • 1,145
  • 8
  • 17
  • I have tested this answer for a `package` under some myPath added to `PATH` in the `.bashrc` file (with `export` clausule). Then `sudo PATH=$PATH which package` finds the right answer, unlike `sudo which package`. However, `sudo PATH=$PATH package` does not go any further than `sudo package` (file not found). On the other hand, launching a plain `package` from a shell invoked with `sudo bash` preserves the extended path and gives `package` sudo rights (two pigeons with one stone). So the response really depends on which commands you are launching – XavierStuvw Jan 05 '17 at 18:16
  • 2
    PATH resolution for sudo is another matter - should anyone find this post in search of that matter I suggest seeing http://unix.stackexchange.com/questions/83191/how-to-make-sudo-preserve-path – buckaroo1177125 Jan 06 '17 at 09:16
25

You can also combine the two env_keep statements in Ahmed Aswani's answer into a single statement like this:

Defaults env_keep += "http_proxy https_proxy"

You should also consider specifying env_keep for only a single command like this:

Defaults!/bin/[your_command] env_keep += "http_proxy https_proxy"

Stian S
  • 517
  • 6
  • 12
5

A simple wrapper function (or in-line for loop)

I came up with a unique solution because:

  • sudo -E "$@" was leaking variables that was causing problems for my command
  • sudo VAR1="$VAR1" ... VAR42="$VAR42" "$@" was long and ugly in my case

demo.sh

#!/bin/bash

function sudo_exports(){
    eval sudo $(for x in $_EXPORTS; do printf '%q=%q ' "$x" "${!x}"; done;) "$@"
}

# create a test script to call as sudo
echo 'echo Forty-Two is $VAR42' > sudo_test.sh
chmod +x sudo_test.sh

export VAR42="The Answer to the Ultimate Question of Life, The Universe, and Everything."

export _EXPORTS="_EXPORTS VAR1 VAR2 VAR3 VAR4 VAR5 VAR6 VAR7 VAR8 VAR9 VAR10 VAR11 VAR12 VAR13 VAR14 VAR15 VAR16 VAR17 VAR18 VAR19 VAR20 VAR21 VAR22 VAR23 VAR24 VAR25 VAR26 VAR27 VAR28 VAR29 VAR30 VAR31 VAR32 VAR33 VAR34 VAR35 VAR36 VAR37 VAR38 VAR39 VAR40 VAR41 VAR42"

# clean function style
sudo_exports ./sudo_test.sh

# or just use the content of the function
eval sudo $(for x in $_EXPORTS; do printf '%q=%q ' "$x" "${!x}"; done;) ./sudo_test.sh

Result

$ ./demo.sh
Forty-Two is The Answer to the Ultimate Question of Life, The Universe, and Everything.
Forty-Two is The Answer to the Ultimate Question of Life, The Universe, and Everything.

How?

This is made possible by a feature of the bash builtin printf. The %q produces a shell quoted string. Unlike the parameter expansion in bash 4.4, this works in bash versions < 4.0

Bruno Bronosky
  • 54,357
  • 9
  • 132
  • 120
0

If you have the need to keep the environment variables in a script you can put your command in a here document like this. Especially if you have lots of variables to set things look tidy this way.

# prepare a script e.g. for running maven
runmaven=/tmp/runmaven$$
# create the script with a here document 
cat << EOF > $runmaven
#!/bin/bash
# run the maven clean with environment variables set
export ANT_HOME=/usr/share/ant
export MAKEFLAGS=-j4
mvn clean install
EOF
# make the script executable
chmod +x $runmaven
# run it
sudo $runmaven
# remove it or comment out to keep
rm $runmaven
Wolfgang Fahl
  • 12,097
  • 9
  • 75
  • 150