Shell proxy autoconfiguration

The following article presents how to deploy a Unix shell script using Rudder.


The script is put in /etc/profile.d/ and thus loaded on user login. It will be processed and configure the PROXY shell variables based on values determined by Rudder policy and the CFEngine agent.

The end result is that without manual intervention a proxy is automatically set up on systems supporting the /etc/profile.d/ structure. More steps could be added to support systems that only use /etc/profile.

The tools used are CFEngine variables defined using a Rudder rule and CFEngine methods created using the NCF addon to rudder.

Rudder Variables

 

 

The resulting variable will be called: generic_variable_definition.proxy

It'll be avaible to nodes in groups that are assigned to the following rules.

 

 

The script

I found a very good starter in the following blog post: http://blog.acsystem.sk/linux/redhat-rhel-6-system-wide-proxy-settings

It just happened that I wanted to run it directly from rudder and not manually adapt it. While adding that I got drifting and added the other parts like proxy testing.

 

The means is a templated script that CFEngine will first download as a file and then locally consider as a template to process.

Two variables are looked at:

${generic_variable_definition.proxy}
${sys.domain}

The first one is assigned via one or more rules in Rudder, and the second one is a class that is automatically defined on the agent during runtime. If you were to use it in a plain shellscript, you could just get the same thing from the domain field in /etc/resolv.conf.

 

# profile env script for configuring proxy in shell use
# make sure proxy var was initialized
load_proxy()
{
    proxy='${generic_variable_definition.proxy}'
    if echo "$proxy" | grep -q http ; then
        export proxy
    else
       return 1
    fi
}

test_proxy()
{
case $SHELL in
    # shells supporting /dev/tcp
    *bash|*ksh*)
        h=`echo $proxy | cut -f3 -d\/ | cut -f1 -d\:`
        p=`echo $proxy | cut -f3 -d\/ | cut -f2 -d\:`
        [ echo >/dev/tcp/$h/$p ]
        return $?
    ;;
    *)
    # no testing for other shells.
        return 0
    ;;
esac
}

setup_proxy()
{
    export http_proxy=$proxy
    export  ftp_proxy=$proxy
    export HTTP_PROXY=$proxy
    export  FTP_PROXY=$proxy
    export no_proxy="127.0.0.1,localhost,.localdomain.com,.${sys.domain}"
}

load_proxy  &&
test_proxy  &&
setup_proxy

 

As you see from the variable format ${name.name} these are not variables readable to a Unix shell.

 

CFEngine 3.6 knows two template types. Both can be really powerful and let you do a lot more than just fill variables :)

 

 

NCF

 

 

The NCF technique starts with a simple check - determine if the OS in question does in fact have a profile.d directory.

Raw Unix, or FreeBSD as an example, don't have this directory.

 

This method will defined a _kept class if found. We'll make use of that in later steps!

 

Next, let's download the script:

Note the server folder /var/rudder/configuration-repository/shared-files is a default. The scripts subfolder was created by me, as is the local folder /var/rudder/templates. One of my policies is creating that on all agents.

After the script has been downloaded to /var/rudder/templates, let's process it as a template:

First, please notice here I added a condition. It's called a 'class context', which means, a certain class needs to be defined in CFEngine. That's the one generated by the Directory exists method!

The meaning: CFEngine will only run this segment if the /etc/profile.d directory was found to exist.

The template file will be read from the 'templates' directory, it's variables will be procedded and - if possible - filled.

Unknown ones will be kept in place as they were. You'll see that in a few moments. After processing the variables, the resulting file is compared to what is already in place in /etc/profile.d.

If there was a difference, the generated file is copied there.

 

Finally, we just need to set the permissions to be world readable. By default, CFEngine will have a pretty tight umask.

 

 

This is the end of the NCF technique!

After saving, it can be used in Rudder:

It's assigned to only one rule, named Default Settings, which is mostly a catchall.

(There's one preconfigured like this, called Global configuration for all nodes.)

 

Lets test

 

If we look at a system that has the variable correctly assigned in Rudder, you'll see the following:

noproblem:~ $ echo $HTTP_PROXY
http://192.168.66.6:3128
noproblem:~ $ echo $no_proxy
127.0.0.1,localhost,.localdomain.com,.lab.wartungsfenster.de

a valid proxy was defined - and, even better the system's domain name is automatically added to the proxy exceptions.

 

 

The below error occurs if the variable is not assigned for a certain group of systems. The load_proxy wrapper in the script above ensures you'll still be able to log in. Normally, a broken /etc/profile is something every sysadmin dreads.

Last login: Sun Jun 21 13:01:09 2015 from xxx
-bash: ${generic_variable_definition.proxy}: bad substitution

By wrapping the variable in single quotes, this is also avoided!

 

After login you can see the variable is empty / not declared. So no application would break. 

problem:~ $ echo $HTTP_PROXY

 

Next would be to add another proxy variable definition in Rudder, this time with a priority higher than our default / catch all of '1' above.

 

If the proxy should be down, the script detects it pretty reliably, yet it takes around 2 seconds to do so. This might be too long for some purposes. For me it's just fine. ;-)