#!/usr/local/bin/perl # # FreeBSD VPN setup script # Created by Michael Vince / mike@thebeastie.org # # Available from http://thebeastie.org/projects/vpnsetup/ # # This script expects: #· You have installed FreeBSD 5.x / 6.x, this script is hardware platform independent (Tested on a Sparc64 FreeBSD ) #· Have perl installed. ( pkg_add -r perl.tbz ) # # You have already configured basic IP network (Internal and External IPs in rc.conf) configuration for FreeBSD as a router/gateway, # with net.inet.ip.forwarding=1 # # Disclaimer: In no way/event shall I be liable for any damages done by this script in any way conceivably possible. # If in doubt run it as a low priviledged user. $ver = "Version 1.201"; ############################################################# # Place theses settings in a file called vpn.conf for external configuration file settings loading. $local_ext_gatewayip = "10.0.0.254"; $remote_ext_gatewayip = "10.0.1.254"; $local_int_gatewayip = "192.1.1.254"; $local_ip_net = "192.1.1.0/24"; $remote_ip_net = "192.1.2.0/24"; $preshared_key = "pr3shar3ds3cr3tk3y"; ############################################################# $vpn_conf = "./vpn.conf"; if ( -e "$vpn_conf") { print "Configuration 'vpn.conf' file found! Loading its configuration :)\n"; eval `/bin/cat $vpn_conf`; sleep(2); } use Time::Local; use POSIX qw(strftime); $hashbar = '#' x 70 . "\n"; $clear = `clear`; print "$clear"; print "$hashbar"; print "FreeBSD VPN setup script $ver Sets up a common VPN configuration using kernel IPSEC and IPSec-tools (Racoon) with preshared secret key tested on FreeBSD 5.x FreeBSD 6.x Its possible to hit \"Enter\" on all questions for defaults. You will most certainly need to enter your public IPs instead of the 10.0.x.x networks. If you use this script more then once, you may have repetitive entries in the modified configuration files. \n"; print "$hashbar"; diagram(); print "$diagram "; $answer =; print "$hashbar"; $questionreg = "[1-2]"; $rcq = questionexr("After you have made your configuration choices do you want to write configuration changes to files in /tmp/vpn or to the real file system. If you select /tmp/vpn, this script will create the configuration files in the temporary directory. Once you've reviewed them for correctness, you can install them manually into the normal configuration location Note that all files are backed or renamed up before being written to, also kernel building and racoon/ipsec-tools installing are always written to the live filesystem. Choose your configuration: \n 1: Real filesystem 2: /tmp/vpn/ Choice 1 or 2 (default: 2): ","2","$questionreg"); if ($rcq eq "2") { tmpfiles(); } elsif ($rcq eq "1") { realfiles(); } print " FYI these are the chosen configuration files paths: $ipsec_file $rc_conf $racoon_conf $psk_file \n "; print "$hashbar"; print "### Network Configuration\n"; print "$hashbar"; $questionreg = "[YyNn]"; $askquestions = questionexr("Do you want to do set network IP settings, these affect the network IP configuration files: $ipsec_file $rc_conf $racoon_conf\n y/n (default: y): ","y","$questionreg"); if ($askquestions =~ /[Yy]/) { net_questions(); } $questionreg = "[YyNn]"; $askquestions = questionexr("Do you want to setup IPSec-tools and configuration? This will require root access: y/n (default: y): ","y","$questionreg"); if ($askquestions =~ /[Yy]/) { racoon_check(); racoon_questions(); } kernel_check(); diagram(); print "$diagram"; print "VPN configuration complete \n"; ################################################################ sub questionexr { print "$_[0]"; $answer =; chomp($answer); while (1) { if ($answer =~ /$_[2]/) { last; } elsif ("$answer" eq "") { $answer = $_[1]; last; } else { print "$_[0]"; $answer =; chomp($answer); } } return $answer; } sub kernel_check() { print "Checking to see if IPSEC is compiled into your kernel...\n"; $ipsec_test = `/sbin/sysctl -a | grep -q ipsec && echo ipsec`; `sleep 2`; $platform = `uname -m`; chomp($platform); $genconf = "/usr/src/sys/"."$platform"."/conf/GENERIC"; $genconf_ipsec = "$genconf"."_IPSEC"; if ($ipsec_test eq "") { $questionreg = "[YyNn]"; $compilekernel = questionexr("The IPSec options has not been compiled into your kernel, but is required to implement a VPN. Do you wish to compile it into your kernel? If you answer 'y' a GENERIC kernel with IPSec options ( $genconf_ipsec ) will be compiled and installed. If you answer 'no,' this script will skip this part. y/n (default: y): ","y","$questionreg"); } else { #print "Kernel looks fine.\nHit any key to continue"; $answer =; return; $questionreg = "[YyNn]"; $compilekernel = questionexr("The IPSec options appear to have been compiled into your kernel. If you wish to compile in a different type of IPSec such as FAST_IPSEC over IPSEC options answer 'y' and a GENERIC kernel with IPSec options ( $genconf_ipsec ) will be compiled and installed. If you answer 'no,' this script will skip the rest of the kernel recompile section. y/n (default: n): ","n","$questionreg"); } if ($compilekernel !~ /[Yy]/) { return; } $questionreg = "[12]"; $kernel_ipsec = questionexr("Which IPSec do you want to compile into the kernel? \n1: FAST_IPSEC\n2: IPSEC\nChoice 1 or 2 (default: 1): ","1","$questionreg"); if ($kernel_ipsec eq "1") { $kernel_ipsec_options = " options FAST_IPSEC device crypto device cryptodev "; } else { $kernel_ipsec_options = " options IPSEC options IPSEC_ESP options IPSEC_DEBUG "; } $questionreg = "[YyNn]"; $compilekernel2 = questionexr("This script is now about to compile and install the generic ( $genconf ) kernel configuration copied to $genconf_ipsec with these IPSec options inserted which are required for IPSec\" $kernel_ipsec_options do you want to continue? y/n (default: y): ","y","$questionreg"); if ($compilekernel2 !~ /[Yy]/) { return; } if ( -e $genconf) { `cp $genconf $genconf_ipsec`; } else { print "Kernel sources not installed! Install them\n"; } open(GFI,">>$genconf_ipsec") || die "Sorry, I couldn't create $genconf_ipsec\n"; print GFI "$kernel_ipsec_options\n"; close(GFI); system ("cd /usr/src ; make buildkernel KERNCONF=GENERIC_IPSEC && make installkernel KERNCONF=GENERIC_IPSEC"); print "Kernel compile and install completed\n"; } sub racoon_check() { my $racoon_pkginfo = `ls /var/db/pkg/ | grep ipsec-tools`; my $portupgrade_pkginfo = `ls /var/db/pkg/ | grep portupgrade`; chomp($racoon_pkginfo); if ( $racoon_pkginfo !~ /^ipsec-tools.*/) { $questionreg = "[YyNn]"; $installracoon = questionexr("\nIpsec-tools is not installed, Ipsec-tools is a Internet Key Exchange (IKE) daemon for automatically keying IPsec connections. do you want to install it now?\n Install Ipsec-tools now? y/n (default: y): ","y","$questionreg"); } if ($installracoon =~ /[Nn]/) { return; } #print "$portupgrade_pkginfo \n $racoon_pkginfo\n"; while (1) { if ($racoon_pkginfo =~ /^ipsec-tools.*/ && $portupgrade_pkginfo =~ /^portupgrade.*/) { last; } else { racoon(); $portupgrade_pkginfo = `ls /var/db/pkg/ | grep portupgrade`; $racoon_pkginfo = `ls /var/db/pkg/ | grep ipsec-tools`; } } return; } sub racoon() { $installracoon; if ($installracoon =~ /[Yy]/) { portupgrade_check(); print "Installing ipsec-tools via \"portupgrade -N /usr/ports/security/ipsec-tools\"\n"; system("portupgrade -N /usr/ports/security/ipsec-tools"); } } sub portupgrade_check { my $portupgrade_pkginfo = `ls /var/db/pkg/ | grep portupgrade`; if ( $portupgrade_pkginfo !~ /^portupgrade.*/) { $questionreg = "[YyNn]"; $installportupgrade = questionexr("portupgrade is not installed, to install racoon you need portupgrade, do you want to install it now? y/n (default: y): ","y","$questionreg"); } else { return; } if ($installportupgrade =~ /[Yy]/) { if ( ! -e "/usr/ports/sysutils/portupgrade" ) { print "You don't have your ports tree installed, consult the FreeBSD handbook to setup\n"; } system("cd /usr/ports/sysutils/portupgrade ; make install"); } } sub net_questions() { print "$hashbar Starting network questions.. "; $regexpip = '^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'; $local_ext_gatewayip = questionexr("What is your Local External gateway IP of your local router, (default: $local_ext_gatewayip): ","$local_ext_gatewayip","$regexpip"); print "Set to: $local_ext_gatewayip \n\n"; $remote_ext_gatewayip = questionexr("What is your Remote External gateway IP of your remote router, (default: $remote_ext_gatewayip): ","$remote_ext_gatewayip","$regexpip"); print "Set to: $remote_ext_gatewayip\n\n"; $local_int_gatewayip = questionexr("What is your Local Internal gateway IP of your local router, (default: $local_int_gatewayip): ","$local_int_gatewayip","$regexpip"); print "Set to: $local_int_gatewayip\n\n"; #$remote_int_gatewayip = questionexr("What is your Remote Internal gateway IP of your remote router, (default: $remote_int_gatewayip): ","$remote_int_gatewayip","$regexpip"); #print "Set to: $remote_int_gatewayip\n\n"; $regexpnet = '^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/\d+'; $local_ip_net = questionexr("What is the Local IP/network that you want to have all data encrypted, (default: $local_ip_net or single host 192.1.1.1/32): ","$local_ip_net","$regexpnet"); print "Set to: $local_ip_net\n\n"; $remote_ip_net = questionexr("What is the Remote IP/network that you want to have all data encrypted, (default: $remote_ip_net or single host 192.1.2.1/32): ","$remote_ip_net","$regexpnet"); print "Set to: $remote_ip_net\n\n"; $questionreg = "[YyNn]"; $writefiles = questionexr("Going to backup and write this to these files \n$rc_conf \n \n$ipsec_file\n Continue? y/n (default: y): ","y","$questionreg"); if ($writefiles =~ /[Yy]/) { print "Writing data... \n"; writefiles(); return; } elsif ($writefiles =~ /[Nn]/) { return; } } sub racoon_questions() { print "$hashbar"; print "### Ipsec-tools Configuration\n"; print "$hashbar"; print " The defaults in these questions should be the best choice when trying to maximize interoperability to other VPN equipment such as CISCO \n\n"; $questionreg = '\w+'; $preshared_key = questionexr("What enter the string that is your preshared secret key (default: $preshared_key): ","$preshared_key","$questionreg"); chomp($preshared_key); if (preshared_key eq "") { $preshared_key = "pr3shar3ds3cr3tk3y"; } print "Preshared key = \"$preshared_key\"\n"; $questionreg = "[0-9]+"; $rcq = questionexr("Choose a IKE phase 1 proposal encryption algorithm ?(default 1): \n 1: 3des 2: des 3: des_iv64 4: des_iv32 5: idea 6: 3idea 7: cast128 8: blowfish 9: null_enc 10: twofish 11: rijndael Choice 1 to 11 (default: 1): ","1","$questionreg"); if ($rcq eq "1") { $ph1_encryption_algorithm = "3des"; } elsif ($rcq eq "2") { $ph1_encryption_algorithm = "des"; } elsif ($rcq eq "3") { $ph1_encryption_algorithm = "des_iv64"; } elsif ($rcq eq "4") { $ph1_encryption_algorithm = "des_iv32"; } elsif ($rcq eq "5") { $ph1_encryption_algorithm = "idea"; } elsif ($rcq eq "6") { $ph1_encryption_algorithm = "3idea"; } elsif ($rcq eq "7") { $ph1_encryption_algorithm = "cast128"; } elsif ($rcq eq "8") { $ph1_encryption_algorithm = "blowfix"; } elsif ($rcq eq "9") { $ph1_encryption_algorithm = "null_enc"; } elsif ($rcq eq "10") { $ph1_encryption_algorithm = "twofish"; } elsif ($rcq eq "11") { $ph1_encryption_algorithm = "rijndael"; } print "encryption_algorithm = $ph1_encryption_algorithm \n\n"; ### $questionreg = "[1-2]"; $rcq = questionexr("Choose a IKE phase 1 hash algorithm ?(default 1): \n 1: MD5 2: SHA1 Choice 1 or 2 (default: 1): ","1","$questionreg"); if ($rcq eq "1") { $ph1_hash_algorithm = "md5"; } elsif ($rcq eq "2") { $ph1_hash_algorithm = "sha1"; } print "hash_algorithm = $ph1_hash_algorithm \n\n"; #################### $questionreg = "[0-9]+"; $rcq = questionexr("Choose a IKE phase 2 proposal encryption algorithm ?(default 1): \n 1: 3des 2: des 3: des_iv64 4: des_iv32 5: idea 6: 3idea 7: cast128 8: blowfish 9: null_enc 10: twofish 11: rijndael Choice 1 to 11 (default: 1): ","1","$questionreg"); if ($rcq eq "1") { $ph2_encryption_algorithm = "3des"; } elsif ($rcq eq "2") { $ph2_encryption_algorithm = "des"; } elsif ($rcq eq "3") { $ph2_encryption_algorithm = "des_iv64"; } elsif ($rcq eq "4") { $ph2_encryption_algorithm = "des_iv32"; } elsif ($rcq eq "5") { $ph2_encryption_algorithm = "idea"; } elsif ($rcq eq "6") { $ph2_encryption_algorithm = "3idea"; } elsif ($rcq eq "7") { $ph2_encryption_algorithm = "cast128"; } elsif ($rcq eq "8") { $ph2_encryption_algorithm = "blowfish"; } elsif ($rcq eq "9") { $ph2_encryption_algorithm = "null_enc"; } elsif ($rcq eq "10") { $ph2_encryption_algorithm = "twofish"; } elsif ($rcq eq "11") { $ph2_encryption_algorithm = "rijndael"; } print "Phase 2 encryption_algorithm = $ph2_encryption_algorithm \n\n"; ############ $questionreg = "[1-7]"; $rcq = questionexr("Choose a IKE phase 2 authentication algorithm ?(default 5): \n 1: 3des 2: des 3: des_iv64 4: des_iv32 5: hmac_md5 6: hmac_sha1 7: non_auth Choice 1 or 2 (default: 5): ","5","$questionreg"); if ($rcq eq "1") { $ph2_authentication_algorithm = "3des"; } elsif ($rcq eq "2") { $ph2_authentication_algorithm = "des"; } elsif ($rcq eq "3") { $ph2_authentication_algorithm = "des_iv64"; } elsif ($rcq eq "4") { $ph2_authentication_algorithm = "des_iv32"; } elsif ($rcq eq "5") { $ph2_authentication_algorithm = "hmac_md5"; } elsif ($rcq eq "6") { $ph2_authentication_algorithm = "hmac_sha1"; } elsif ($rcq eq "7") { $ph2_authentication_algorithm = "non_auth"; } print "Phase 2 authentication_algorithm = $ph2_authentication_algorithm \n\n"; #################### $questionreg = "[YyNn]"; $writefiles = questionexr("Going to backup and write this to these files \n$rc_conf \n$racoon_conf \n$psk_file\n Continue? y/n (default: y): ","y","$questionreg"); if ($writefiles =~ /[Yy]/) { print "Writing data... \n"; } elsif ($writefiles =~ /[Nn]/) { return; } renamefile("$racoon_conf"); load_racoon_conf(); if ($racoon_conf =~ /\/usr\/local\/etc\/racoon\//) { unless (-e '/usr/local/etc/racoon/') { `mkdir -p /usr/local/etc/racoon/`; } } open(RC_FILE,">>$racoon_conf") || die "Sorry, I couldn't open $racoon_conf\n"; print RC_FILE "$racoon_conf_data\n"; close(RC_FILE); backupfile($psk_file); open(PSK_FILE,">>$psk_file") || die "Sorry, I couldn't open $psk_file\n"; print PSK_FILE ""; close(PSK_FILE); $cnt = chmod 0600, $psk_file; open(PSK_FILE,">>$psk_file") || die "Sorry, I couldn't open $psk_file\n"; $psk_file = "/usr/local/etc/racoon/psk.txt"; print PSK_FILE "$remote_ext_gatewayip $preshared_key\n"; close(PSK_FILE); print "Writing $rc_conf configuration.\n"; open(RCFILE,">>$rc_conf") || die "Sorry, I couldn't open/create $rc_conf\n"; print RCFILE "racoon_enable=\"YES\" # VPN Setup script insertions - $cdate \n"; close(RCFILE); print "Configuration write complete\n"; print "$hashbar\n"; return; } sub writefiles() { my $cdate = strftime "%Y-%m-%d %H:%M:%S", localtime(time); backupfile("$ipsec_file"); print "Writing $ipsec_file configuration.\n"; open(IPSEC,">>$ipsec_file") || die "Sorry, I couldn't create $ipsec_rules\n"; $spaddrules = "spdadd $local_ip_net"."[any] $remote_ip_net"."[any] any -P out ipsec esp/tunnel/"."$local_ext_gatewayip"."-"."$remote_ext_gatewayip"."/require ; spdadd "."$remote_ip_net"."[any] "."$local_ip_net"."[any] any -P in ipsec esp/tunnel/"."$remote_ext_gatewayip"."-"."$local_ext_gatewayip"."/require ;"; print IPSEC "### VPN Setup script insertions - $cdate $spaddrules\n"; close(IPSEC); backupfile($rc_conf); print "Writing $rc_conf configuration.\n"; open(RCFILE,">>$rc_conf") || die "Sorry, I couldn't create $rc_conf\n"; print RCFILE "ipsec_enable=\"YES\" # VPN Setup script insertions - $cdate\n"; print "$hashbar\n"; close(RCFILE); } sub backupfile() { my $file = "$_[0]"; my $file_backup; my $cdate = strftime "%Y-%m-%d-%H:%M:%S", localtime(time); if ( -e $file ) { $file_backup = "$file"."_"."$cdate"; print "Backing up $file To $file_backup \n"; system("cp $file $file_backup"); } else { return; } } sub renamefile() { my $file = "$_[0]"; my $file_backup; my $cdate = strftime "%Y-%m-%d-%H:%M:%S", localtime(time); if ( -e $file ) { $file_backup = "$file"."_"."$cdate"; print "Renaming $file To $file_backup \n"; rename $file, $file_backup ; } else { return; } } sub realfiles() { $ipsec_file = "/etc/ipsec.conf"; $rc_conf = "/etc/rc.conf"; $racoon_conf = "/usr/local/etc/racoon/racoon.conf"; $psk_file = "/usr/local/etc/racoon/psk.txt"; } sub tmpfiles() { `mkdir -p /tmp/vpn`; $ipsec_file = "/tmp/vpn/ipsec.conf"; $rc_conf = "/tmp/vpn/rc.conf"; $racoon_conf = "/tmp/vpn/racoon.conf"; $psk_file = "/tmp/vpn/psk.txt"; } sub load_racoon_conf() { $racoon_conf_data = " path pre_shared_key \"/usr/local/etc/racoon/psk.txt\" ; #log debug2; log debug; listen { isakmp $local_ext_gatewayip [500]; } remote $remote_ext_gatewayip [500] { exchange_mode main,aggressive,base; lifetime time 86400 sec;# sec,min,hour proposal_check obey; proposal { encryption_algorithm $ph1_encryption_algorithm; # phase 1 encryption algorithm hash_algorithm $ph1_hash_algorithm; # phase 1 hash algorithm authentication_method pre_shared_key ; dh_group 2 ; } } #sainfo address $local_ip_net any address $remote_ip_net any #{ # pfs_group 2; # lifetime time 86400 sec; # encryption_algorithm $ph2_encryption_algorithm ; # phase 2 encryption algorithm # authentication_algorithm $ph2_authentication_algorithm ; # phase 2 authentication algorithm # compression_algorithm deflate ; #} ################################# remote anonymous { exchange_mode main,aggressive,base; lifetime time 86400 sec;# sec,min,hour proposal { encryption_algorithm $ph1_encryption_algorithm; # phase 1 encryption algorithm hash_algorithm $ph1_hash_algorithm; # phase 1 hash algorithm authentication_method pre_shared_key ; dh_group 2 ; } proposal_check obey; } sainfo anonymous { pfs_group 2; lifetime time 86400 sec; encryption_algorithm $ph2_encryption_algorithm ; # phase 2 encryption algorithm authentication_algorithm $ph2_authentication_algorithm ; # phase 2 authentication algorithm compression_algorithm deflate ; } "; } sub diagram() { $diagram =" Basic network diagram $local_ip_net $local_int_gatewayip ( Local Int Gateway IP ) +--------+ +--------+ | LOCAL | | LOCAL |$local_ext_gatewayip ( Local Ext Gateway IP ) | NET |--------------| GATEWAY| <------------+ +--------+ +--------+ | | { INTERNET } | +--------+ +---------+ | | REMOTE | | REMOTE |<------------+ | NET |--------------| GATEWAY |$remote_ext_gatewayip ( Remote Ext Gateway IP ) +--------+ +---------+ $remote_ip_net "; }