cron
jobs on a Juniper RouterUpdate (March 2009): Importing binaries compiled elsewhere may no longer work on current JunOS versions. I no longer have access to a Juniper router to verify this, so please take the information below with a grain of salt.
Among other things, this document is a ringing endorsement of Juniper's choice to build their routers on top of a UNIX platform. Viewed from this angle, a Juniper router (such as the M10) is really a Pentium-class PC running FreeBSD v4, with a really, really beefy network card (i.e., the forwarding engine).
One of the great things about the router being a UNIX box is that one can write cron
jobs on it. The router updates itself automatically from within, avoiding the requirement to have an expect
script drive the update from a remote host. Why does this matter? For an expect
script to work, the cleartext password to the router (or an ssh key allowing access to it) must be stored on the machine running expect
, which weakens security.
This document describes how to set up a cron
job that automatically updates two prefix lists:
cli
commandJust like any other UNIX box, the Juniper router has user accounts, stored in the /etc/passwd
file. The root
user's entry looks something like this:
root:*:0:0:Root User:/root:/bin/cshwhich means that
root
gets /bin/csh
as the default login shell. So far, so good. On the other hand, regular user accounts look something like this:somlo:*:1005:20:Gabriel Somlo:/var/home/somlo:/usr/sbin/cliNotice that
somlo
's default shell is /usr/sbin/cli
. This means that, upon login to the Juniper router, somlo
gets the familiar Juniper command line interface instead of a shell prompt.
The really nice thing about the cli
program is that it accepts commands on stdin
(and hence allows scripting), and doesn't restrict its use to interactive mode. In other words, logging into the router and executing the following commands at the cli
prompt:
somlo@gw1> configure Entering configuration mode [edit] somlo@gw1# load replace foo load complete [edit] somlo@gw1# commit check configuration check succeeds [edit] somlo@gw1# commit commit complete [edit] somlo@gw1# exit Exiting configuration mode somlo@gw1> exit Connection to gw1 closed.is equivalent to running the following shell script:
{ echo "configure" echo "load replace foo" echo "commit check" echo "commit" echo "exit" } | /usr/sbin/cli
The Juniper runs a stripped-down version of FreeBSD v.4, which means that some required binaries had to be compiled and copied over from elsewhere. This list includes:
iprange.c
, a program which manipulates network blocks and contiguous ranges of IP addresses;
wget
, which allows one to automate the download of HTTP and FTP files from the Internet;
mini_sendmail
, an SMTP mail transport client.
Since the Juniper doesn't come with a C compiler (or any other development tools, for that matter), a separate FreeBSD 4.11 machine was built to allow these programs to be compiled. Because some of these programs require the existence of certain libraries which may or may not be included with the Juniper's version of BSD, each binary was built with the -static
flag. For instance:
gcc -o iprange iprange.c -O2 -Wall -staticIf the build process was controlled by a
Makefile
, the last command executed by make
, which built the binary, was rerun manually with the -static
option added. Aditionally, each binary was stripped to minimize its size:strip iprangebefore being copied over to the Juniper.
Firewall terms were written which match addresses based on a source-
or destination-prefix-list
. These firewall terms can remain static, and changes occur by re-loading the prefix lists themselves from a shell script that drives cli
and is driven in turn by cron
. First, the shell script builds a prefix of I2 addresses, by dumping out routes received via BGP that contain the Abilene AS number in their AS Path and aggregating them with iprange.c
. Next, the SPAMHAUS DROP list is downloaded using wget
and aggregated with iprange.c
. Then, both lists are loaded and committed into the router config using the cli
command. A log of the transaction (consisting of the stdout
output of the scripted commands) is then emailed out to the router's administrator(s). The script is shown below:
#!/bin/sh # # Automatically update portions of the Juniper config file # # Gabriel L. Somlo, Mar. 2005 TIMESTAMP=$(date +%Y-%m-%d) ############################################################################### # Build prefix list of I2 addresses, by aggregating routes # received via the Abilene AS number (11537) ############################################################################### BGP_PEER="AAA.BBB.CCC.DDD" { echo "policy-options {" echo "replace:" echo " prefix-list I2_prefixes {" { echo "set cli screen-length 0" echo "show route receive-protocol bgp ${BGP_PEER} aspath-regex .*11537.* table inet.0" } | cli \ | grep '^*' \ | awk '{print $2}' \ | ./iprange -J \ | sed -e 's/^\(.*\)$/ \1;/' echo " }" echo "}" } > pfx_i2_${TIMESTAMP} ############################################################################### # Build prefix list for firewall term blocking traffic from networks listed by # the Spamhaus DROP list ############################################################################### { echo "policy-options {" echo "replace:" echo " prefix-list SPAMHAUS_prefixes {" ./wget -q 'http://www.spamhaus.org/DROP/drop.lasso' -O - \ | grep -v ^\; \ | grep -v ^$ \ | awk '{print $1}' \ | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n \ | ./iprange -J \ | sed -e 's/^\(.*\)$/ \1;/' echo " }" echo "}" } > pfx_spamhaus_${TIMESTAMP} ############################################################################### # load/replace the new prefix lists and commit changes; # email report ############################################################################### REPORT_RCPT="noc@acns.colostate.edu" MAIL_GW="TTT.XXX.YYY.ZZZ" { echo "Subject: gw1 AutoUpdate report: ${TIMESTAMP}" echo echo "I2 Prefix list line count:" echo wc -l pfx_i2_${TIMESTAMP} echo echo "Spamhaus DROP Prefix list line count:" echo wc -l pfx_spamhaus_${TIMESTAMP} echo echo "Loading and committing changes:" echo { echo "configure" echo "load replace pfx_i2_${TIMESTAMP}" echo "load replace pfx_spamhaus_${TIMESTAMP}" echo "commit check" echo "commit" echo "exit" } | cli echo echo "Done." } | ./mini_sendmail -s${MAIL_GW} ${REPORT_RCPT} ############################################################################### # delete log files older than two weeks (14 days) ############################################################################### for F in $(find ./ -name pfx\* -ctime +14); do rm -f "${F}" done
For this script to work, it must be called from one of the admin users' crontab
, like so:
# Run the AutoUpdate script daily at 1:05am 5 1 * * * (cd /var/home/somlo/AutoUpdate; ./AutoUpdate.sh)It is assumed that established TCP responses from the mail gateway and SPAMHAUS's Web server are allowed by the route-engine filter, and so are DNS packets from the name servers configured in
system / name-server
.