use constant RACOON_TMPL => <<'EOF';
log debug2;
path pre_shared_key "@@KEY@@";
padding
{
randomize off;
maximum_length 20;
exclusive_tail off;
strict_check off;
}
timer
{
counter 5;
interval 20 seconds;
persend 1;
phase1 30 seconds;
phase2 15 seconds;
}
@@remote@@
@@sainfo@@
listen
{
isakmp @@client_ip@@;
}
EOF
use constant SAINFO_TMPL => <<'EOF';
sainfo address @@client_ip@@/32 any address @@network@@ any
{
pfs_group @@PFS_GROUP@@;
lifetime time 1 hours;
encryption_algorithm @@ENCRYPT@@;
authentication_algorithm @@IDENT@@;
compression_algorithm deflate;
}
EOF
use constant REMOTE_SERVER_TMPL => <<'EOF';
remote @@SERVER_IP@@
{
exchange_mode aggressive, main;
initial_contact on;
proposal_check obey;
support_mip6 on;
generate_policy off;
nonce_size 16;
doi ipsec_doi;
situation identity_only;
passive off;
my_identifier user_fqdn "@@USER_FQDN@@";
proposal {
encryption_algorithm @@ENCRYPT@@;
hash_algorithm @@HASH@@;
authentication_method pre_shared_key;
dh_group @@DH_GROUP@@;
lifetime time 8 minutes;
}
}
EOF
use constant SCRIPT_TMPL => <<'EOS';
#!/bin/sh
setkey -FP
setkey -F
setkey -c << EOF
@@tunnels@@
EOF
EOS
use constant TUNNEL_TMPL => <<'EOF';
spdadd @@client_ip@@/32 @@network@@ any -P out ipsec
esp/tunnel/@@client_ip@@-@@server_ip@@/require;
spdadd @@network@@ @@client_ip@@/32 any -P in ipsec
esp/tunnel/@@server_ip@@-@@client_ip@@/require;
EOF
1;
####
# The interface on which to
# run racoon. Typically, en0
# is the Ethernet NIC and
# en1 is for WiFi/Airport
INTERFACE => 'en0';
# One anonymous hash for each
# VPN appliance to which to connect.
# Note the notation for the networks
# behind the VPN -- you cannot connect
# to the same subnet on two different
# VPNs.
PROFILES => [
{
NAME => 'Foo',
SERVER_IP => '1.2.3.4',
USER_FQDN => 'user@domain.com',
NETWORKS => ['192.168.1.0/24', '192.168.0.0/24'],
},
{
NAME => 'Bar',
SERVER_IP => '5.6.7.8',
USER_FQDN => 'user2@domain2.com',
NETWORKS => ['192.168.50.0/24'],
},
];
# Set to your IP address if
# the script is having trouble
# determining it via /sbin/ifconfig
CLIENT_IP => '169.254.0.1';
# Where to find the Private Shared
# Key file. There is one already in
# set up for you in /etc/racoon, but
# you'll need to set up the keys for
# each VPN appliance before connecting
KEY => '/etc/racoon/psk.txt';
# These are connection parameters.
# You should work these out with
# your sysadmin. The defaults below
# work with a properly-configured
# NetScreen VPN appliance.
PFS_GROUP => 'modp1024';
DH_GROUP => 'modp1024';
ENCRYPT => '3des';
HASH => 'sha1';
IDENT => 'hmac_sha1';
##
##
#!/usr/bin/perl
#####################################
# This script is distributed on the understanding
# that if you make improvements you will return
# them to the community of Perl and Mac users. This
# is pint-ware -- if you find it particularly useful
# a thank you pint would be appreciated but is not
# required.
#
# For more information visit http://www.reades.com/
#
# Please note that I *cannot* offer support on VPN
# configuration from the systems side -- not only
# is each and every VPN different but, frankly, I do
# not know enough about how they work to be of any
# use.
#
# Enjoy,
# jon reades
####################################
BEGIN {
# Normally, you will want to
# keep your script and files
# here.
push @INC, "/etc/racoon";
}
use Cwd;
use strict;
use Templates;
# The default profile
my $profile = "./Profiles.pm";
# Assume that you should use
# the current working directory
# unless one is specified in the
# command-line args
my $dir = getcwd;
my $to_do;
unless (@ARGV) {
print STDOUT "Usage:\n";
print STDOUT "\tperl setup.pl [dir=/path/to/output/files] [profile=/path/to/profile.txt] config|restart|all\n";
print STDOUT "\tDefault profile is in ./Profile.pm\n";
print STDOUT "\tDefault output directory is cwd ($dir)\n";
exit 0;
}
foreach(@ARGV) {
my ($key, $val) = split /=/;
# Override the profile
$profile = $val if ($key eq 'profile');
# Override the output directory
$dir = $val if ($key eq 'dir');
# What to do?
$to_do = "configure" if ($key eq 'config');
$to_do = "restart" if ($key eq 'restart');
$to_do = "all" if ($key eq 'all');
}
# Are we generating a config file?
if ($to_do eq 'all' || $to_do eq 'configure') {
print STDOUT "\nFiles will be output to $dir\n";
generateConfig($profile, $dir);
}
# Are we restarting racoon and setting the tunnels?
if ($to_do eq 'all' || $to_do eq 'restart') {
print STDOUT "\nLooking for configuration files in $dir\n";
restartRacoon($dir);
}
exit 0;
# Reads in the profile file and splits
# it based on a key => val syntax
sub parseProfile {
my $profile = shift;
my %lookup;
# We slurp it in so that we can
# properly handle arrays and hashes
# spread across multiple lines of
# the profile file
my $delimiter = $/;
$/ = undef;
open (PROFILE, "<$profile") or die ("Couldn't open profile ($profile) to read: $!");
my $slurp = ;
close PROFILE;
my (@params) = split /\;/, $slurp;
foreach my $param (@params) {
# Remove any comments
$param =~ s|^\#.*?$||gm;
# Parse out the parameter
my ($key, $val) = parseParam($param);
# And assign it to the lookup
# hash for interpolation
$lookup{$key} = $val if ($key);
}
$/ = $delimiter;
return \%lookup;
}
sub parseParam {
my $param = shift;
# We're assuming that everything
# after the '=>' is part of the value
my ($key, $val) = $param =~ m/(\S+)\s+=>\s+(.*)$/s;
# This 'vivifies' the
# hash or array
my $ref = eval ($val);
return $key, $ref;
}
sub interpolate {
my $string = shift;
my $lookup = shift;
$string =~ s/@@([^@]+)@@/$lookup->{$1}/g;
return $string;
}
sub generateConfig {
my $profile = shift;
my $dir = shift;
my %lookup = %{parseProfile($profile)};
# Look for a recognisable IP address
# on the interface specified in
# the profile file
my $match;
open (IP, "/sbin/ifconfig |") or die ("Couldn't open pipe (/sbin/ifconfig) to read: $!");
while () {
$match = 'found' if ($_ =~ m|$lookup{INTERFACE}|);
next if ($match ne 'found');
if ($_ =~ m|inet (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|) {
$lookup{client_ip} = $1;
last;
}
}
$lookup{client_ip} = $lookup{CLIENT_IP} unless ($lookup{client_ip});
close IP;
print STDOUT "\nSetting up racoon on " . $lookup{INTERFACE} . " : " . $lookup{client_ip} . "\n\n";
if ($lookup{client_ip} eq '169.254.0.1') {
print STDOUT "Warning: you don't appear to have properly configured the interface\n";
print STDOUT "It is higly unlikely that you really want to run racoon on " . $lookup{client_ip} .
"\n";
print STDOUT "Try checking the INTERFACE parameter in $profile\n";
print STDOUT "or setting the CLIENT_IP parameter to your machine's IP.\n";
exit 0;
}
############################
# Set up the tunnels to be
# run via a shell script
############################
my $tunnels = "";
print STDOUT "Setting up tunnels on " . $lookup{INTERFACE} . "\n";
foreach my $profile (@{$lookup{PROFILES}}) {
print STDOUT "\tSetting up profile: " . $profile->{NAME} . "\n";
$lookup{server_ip} = $profile->{SERVER_IP};
foreach my $network (@{$profile->{NETWORKS}}) {
$lookup{network} = $network;
$tunnels .= interpolate(TUNNEL_TMPL, \%lookup);
print STDOUT "\t\tSetting up subnet: " . $network . "\n";
}
}
$lookup{tunnels} = $tunnels;
open (OUT, ">$dir/interface.sh") or die ("Couldn't open $dir/interface.sh to write: $!");
print OUT interpolate(SCRIPT_TMPL, \%lookup);
close OUT;
print STDOUT "\nTunnel script (interface.sh) complete.\n";
print STDOUT "Next time you can just run 'sudo ${dir}interface.sh'.\n\n";
############################
# Set up the SAInfo section
# of the racoon.conf file
############################
my $sainfo = "";
print STDOUT "Setting up SAInfo section for racoon.conf\n";
foreach my $profile (@{$lookup{PROFILES}}) {
print STDOUT "\tSetting up profile: " . $profile->{NAME} . "\n";
foreach my $network (@{$profile->{NETWORKS}}) {
$lookup{network} = $network;
$sainfo .= interpolate(SAINFO_TMPL, \%lookup);
}
}
$lookup{sainfo} = $sainfo;
############################
# Set up the remote servers section
# of the racoon.conf file
############################
my $remote = "";
print STDOUT "Setting up Remote Server section for racoon.conf\n";
foreach my $profile (@{$lookup{PROFILES}}) {
print STDOUT "\tSetting up profile: " . $profile->{NAME} . "\n";
foreach (keys %$profile) {
$lookup{$_} = $profile->{$_};
}
$remote .= interpolate(REMOTE_SERVER_TMPL, \%lookup);
}
$lookup{remote} = $remote;
############################
# Set up the racoon.conf file
############################
print STDOUT "Setting up racoon.conf\n";
open (OUT, ">$dir/racoon.conf") or die ("Couldn't open $dir/racoon.conf to write: $!");
print OUT interpolate(RACOON_TMPL, \%lookup);
close OUT;
print STDOUT "\nRacoon setup (racoon.conf) complete.\n";
print STDOUT "Next time you can just run: 'sudo racoon -f ${dir}racoon.conf'.\n\n";
print STDOUT "Setup complete\n";
return;
}
sub restartRacoon {
my $dir = shift;
print STDOUT "\nRestarting\n";
my $retval1 = system('sudo', 'killall', 'racoon');
if ($retval1) {
print STDOUT "Couldn't kill any running racoon processes: $retval1\n";
}
my $retval2 = system ('sudo', '/bin/sh', $dir . '/interface.sh');
if ($retval2) {
print STDOUT "Couldn't run interface.sh: $retval2\n";
}
my $retval3 = system ('sudo', '/usr/sbin/racoon', '-f', $dir . '/racoon.conf');
if ($retval3) {
print "Couldn't start racoon: $retval3\n";
}
if (!($retval1 || $retval2 || $retval3)) {
print STDOUT "Restart complete\n";
} else {
print STDOUT "May not have restarted cleanly\n";
}
return;
}