If you have any problems implementing your VPN based on this example or you have any other technical problem then I recommend you post on Experts Exchange as it is an excellent resource. My username is 'grblades' so I might even be the one answering your question :)


This document describes how to configure a Cisco PIX firewall and a Radius authentication server so that clients can establish a VPN connection. In addition the Radius server will be used to issue an access control list (ACL) name to be used to control what resources the individual VPN client is able to access. This is a very powerful feature and in a lot of cases avoids the need to purchase an additional VPN Concentrator.

The requirements for this solution are as follows

Radius Server Configuration

For the Radius server I chose to use FreeRADIUS version 0.9.3-1.1 which came with RedHat Fedora Core 1.

Edit radiusd.conf and manually define the port to listen on by adding the following line in the corresponding place in the configuration file. Port 1645 is the old Radius port that the Cisco PIX still used by default. Alternatively you can leave the Radius server set to use the new port and change the Cisco configuration to use an alternate port.

# port: Allows you to bind FreeRADIUS to a specific port.
# The default port that most NAS boxes use is 1645, which is historical.
# RFC 2138 defines 1812 to be the new port. Many new servers and
# NAS boxes use 1812, which can create interoperability problems.
# The port is defined here to be 0 so that the server will pick up
# the machine's local configuration for the radius port, as defined
# in /etc/services.
# If you want to use the default RADIUS port as defined on your server,
# (usually through 'grep radius /etc/services') set this to 0 (zero).
# A port given on the command-line via '-p' over-rides this one.
port = 1645

Edit clients.conf and add the following entries. The IP address is the IP address of the internal interface of the PIX. The 'secret' is the shared secret between the PIX and the Radius server and must be the same as defined on the PIX. The 'shortname' does not matter but I normally set it to the hostname of the PIX.

client {
secret = secretpassword
shortname = pixhostname

Freeradius supports various authentication schemes. For this example a simple local authentication method is used with the usernames and passwords being listed in the 'users' file. This file can be modified to authenticate against a LDAP or other database for example which would be better when a large number of users are involved.

#       Please read the documentation file ../doc/processing_users_file,
#       or 'man 5 users' (after installing the server) for more information.
#       This file contains authentication security and configuration
#       information for each user.  Accounting requests are NOT processed
#       through this file.  Instead, see 'acct_users', in this directory.
#       The first field is the user's name and can be up to
#       253 characters in length.  This is followed (on the same line) with
#       the list of authentication requirements for that user.  This can
#       include password, comm server name, comm server port number, protocol
#       type (perhaps set by the "hints" file), and huntgroup name (set by
#       the "huntgroups" file).
#       If you are not sure why a particular reply is being sent by the
#       server, then run the server in debugging mode (radiusd -X), and
#       you will see which entries in this file are matched.
#       When an authentication request is received from the comm server,
#       these values are tested. Only the first match is used unless the
#       "Fall-Through" variable is set to "Yes".
#       A special user named "DEFAULT" matches on all usernames.
#       You can have several DEFAULT entries. All entries are processed
#       in the order they appear in this file. The first entry that
#       matches the login-request will stop processing unless you use
#       the Fall-Through variable.
#       If you use the database support to turn this file into a .db or .dbm
#       file, the DEFAULT entries _have_ to be at the end of this file and
#       you can't have multiple entries for one username.
#       You don't need to specify a password if you set Auth-Type += System
#       on the list of authentication requirements. The RADIUS server
#       will then check the system password file.
#       Indented (with the tab character) lines following the first
#       line indicate the configuration values to be passed back to
#       the comm server to allow the initiation of a user session.
#       This can include things like the PPP configuration values
#       or the host to log the user onto.
#       You can include another `users' file with `$INCLUDE users.other'

#       For a list of RADIUS attributes, and links to their definitions,
#       see:
#       http://www.freeradius.org/rfc/attributes.html

# Deny access for a specific user.  Note that this entry MUST
# be before any other 'Auth-Type' attribute which results in the user
# being authenticated.
# Note that there is NO 'Fall-Through' attribute, so the user will not
# be given any additional resources.
#lameuser       Auth-Type := Reject
#               Reply-Message = "Your account has been disabled."

# Deny access for a group of users.
# Note that there is NO 'Fall-Through' attribute, so the user will not
# be given any additional resources.
#DEFAULT        Group == "disabled", Auth-Type := Reject
#               Reply-Message = "Your account has been disabled."

# This is a complete entry for "steve". Note that there is no Fall-Through
# entry so that no DEFAULT entry will be used, and the user will NOT
# get any attributes in addition to the ones listed here.
#steve  Auth-Type := Local, User-Password == "testing"
#       Service-Type = Framed-User,
#       Framed-Protocol = PPP,
#       Framed-IP-Address =,
#       Framed-IP-Netmask =,
#       Framed-Routing = Broadcast-Listen,
#       Framed-Filter-Id = "std.ppp",
#       Framed-MTU = 1500,
#       Framed-Compression = Van-Jacobsen-TCP-IP

# This is an entry for a user with a space in their name.
# Note the double quotes surrounding the name.
#"John Doe"     Auth-Type := Local, User-Password == "hello"
#               Reply-Message = "Hello, %u"

# Dial user back and telnet to the default host for that port
#Deg    Auth-Type := Local, User-Password == "ge55ged"
#       Service-Type = Callback-Login-User,
#       Login-IP-Host =,
#       Callback-Number = "9,5551212",
#       Login-Service = Telnet,
#       Login-TCP-Port = Telnet

# Another complete entry. After the user "dialbk" has logged in, the
# connection will be broken and the user will be dialed back after which
# he will get a connection to the host "timeshare1".
#dialbk Auth-Type := Local, User-Password == "callme"
#       Service-Type = Callback-Login-User,
#       Login-IP-Host = timeshare1,
#       Login-Service = PortMaster,
#       Callback-Number = "9,1-800-555-1212"

# user "swilson" will only get a static IP number if he logs in with
# a framed protocol on a terminal server in Alphen (see the huntgroups file).
# Note that by setting "Fall-Through", other attributes will be added from
# the following DEFAULT entries
#swilson        Service-Type == Framed-User, Huntgroup-Name == "alphen"
#               Framed-IP-Address =,
#               Fall-Through = Yes

# If the user logs in as 'username.shell', then authenticate them
# against the system database, give them shell access, and stop processing
# the rest of the file.
#DEFAULT        Suffix == ".shell", Auth-Type := System
#               Service-Type = Login-User,
#               Login-Service = Telnet,
#               Login-IP-Host = your.shell.machine

# The rest of this file contains the several DEFAULT entries.
# DEFAULT entries match with all login names.
# Note that DEFAULT entries can also Fall-Through (see first entry).
# A name-value pair from a DEFAULT entry will _NEVER_ override
# an already existing name-value pair.

# First setup all accounts to be checked against the UNIX /etc/passwd.
# (Unless a password was already given earlier in this file).
#DEFAULT        Auth-Type = System
#       Fall-Through = 1

# Set up different IP address pools for the terminal servers.
# Note that the "+" behind the IP address means that this is the "base"
# IP address. The Port-Id (S0, S1 etc) will be added to it.
#DEFAULT        Service-Type == Framed-User, Huntgroup-Name == "alphen"
#               Framed-IP-Address =,
#               Fall-Through = Yes

#DEFAULT        Service-Type == Framed-User, Huntgroup-Name == "delft"
#               Framed-IP-Address =,
#               Fall-Through = Yes

# Defaults for all framed connections.
#DEFAULT        Service-Type == Framed-User
#       Framed-IP-Address =,
#       Framed-MTU = 576,
#       Service-Type = Framed-User,
#       Fall-Through = Yes

# Default for PPP: dynamic IP address, PPP mode, VJ-compression.
# NOTE: we do not use Hint = "PPP", since PPP might also be auto-detected
#       by the terminal server in which case there may not be a "P" suffix.
#       The terminal server sends "Framed-Protocol = PPP" for auto PPP.
#DEFAULT        Framed-Protocol == PPP
#       Framed-Protocol = PPP,
#       Framed-Compression = Van-Jacobson-TCP-IP

# Default for CSLIP: dynamic IP address, SLIP mode, VJ-compression.
#DEFAULT        Hint == "CSLIP"
#       Framed-Protocol = SLIP,
#       Framed-Compression = Van-Jacobson-TCP-IP

# Default for SLIP: dynamic IP address, SLIP mode.
#DEFAULT        Hint == "SLIP"
#       Framed-Protocol = SLIP

# Last default: rlogin to our main server.
#       Service-Type = Login-User,
#       Login-Service = Rlogin,
#       Login-IP-Host = shellbox.ispdomain.com

# #
# # Last default: shell on the local terminal server.
# #
#       Service-Type = Shell-User

# On no match, the user is denied access.

user1           Auth-Type := Local, User-Password == "password1"
                Filter-Id = "vpnstaffin"

user2           Auth-Type := Local, User-Password == "password2"
                Filter-Id = "vpnitdeptin"

user3           Auth-Type := Local, User-Password == "password3"
                Filter-Id = "vpnotherin"

Basic configuration

interface ethernet0 auto
interface ethernet1 auto
interface ethernet2 auto
nameif ethernet0 outside security0
nameif ethernet1 inside security100
nameif ethernet2 dmz security50
enable password ********** encrypted
passwd ********** encrypted
hostname pixhostname
domain-name mydomain.com
clock timezone GMT 0
fixup protocol dns maximum-length 512
fixup protocol ftp 21
fixup protocol h323 h225 1720
fixup protocol h323 ras 1718-1719
fixup protocol http 80
fixup protocol ils 389
fixup protocol rsh 514
fixup protocol rtsp 554
fixup protocol sip 5060
fixup protocol sip udp 5060
fixup protocol skinny 2000
fixup protocol smtp 25
fixup protocol sqlnet 1521
fixup protocol tftp 69
pager lines 24
logging on
logging console critical
logging monitor errors
logging buffered debugging
logging trap debugging
logging facility 21
logging host inside
mtu outside 1500
mtu inside 1500
mtu dmz 1500
ip address outside
ip address inside
ip address dmz
ip verify reverse-path interface outside
ip verify reverse-path interface inside
ip verify reverse-path interface dmz
ip audit info action alarm
ip audit attack action alarm
pdm location inside
pdm logging informational 100
pdm history enable
arp timeout 14400
route outside 1
timeout xlate 3:00:00
timeout conn 1:00:00 half-closed 0:10:00 udp 0:02:00 rpc 0:10:00 h225 1:00:00
timeout h323 0:05:00 mgcp 0:05:00 sip 0:30:00 sip_media 0:02:00
timeout uauth 0:05:00 absolute
aaa-server TACACS+ protocol tacacs+ 
aaa-server RADIUS protocol radius 
http server enable
http inside
snmp-server location location
snmp-server contact your name
snmp-server community public
no snmp-server enable traps
tftp-server inside /pixhostname-config
floodguard enable
telnet timeout 60
ssh inside
ssh timeout 60
management-access inside
console timeout 0
terminal width 80
!--- internal machines get NAT'd to address when accessing the Internet
global (outside) 1
nat (inside) 1 0 0

Restrict what internal users are permitted to access

object-group service Allowed_tcp tcp
  description Ports all users are allowed to access
  port-object eq ssh
  port-object eq pop3
  port-object eq imap4
  port-object eq 6667
access-list inside_access_in remark Allow main server to access everything
access-list inside_access_in permit tcp host any 
access-list inside_access_in remark Allow main server to access DNS and NTP
access-list inside_access_in permit udp host any eq ntp
access-list inside_access_in permit udp host any eq domain
access-list inside_access_in remark Allow everyone to access POP3, IMAP, IRC, SSH
access-list inside_access_in permit tcp any any object-group Allowed_tcp 
access-list inside_access_in deny ip any any 
access-group inside_access_in in interface inside

Permit access to the mail server in the dmz

object-group service Inbound_Mail tcp
  description Ports permitted to mail server from Internet
  port-object eq www
  port-object eq https
  port-object eq smtp
  port-object eq imap4
access-list smtp remark Mail server accepts imap, smtp, and http(s) connections from Internet
access-list smtp permit tcp any host object-group Inbound_Mail 
access-list smtp deny ip any any
static (dmz,outside) netmask 0 0 
access-group smtp in interface outside
!--- do not use NAT between the internal network and the DMZ
static (inside,dmz) netmask 0 0 

Radius configuration

aaa-server partnerauth protocol radius 
aaa-server partnerauth (inside) host secretpassword timeout 5
aaa-server local protocol radius 

IPSEC/ISAKMP configuration

!--- Implisically permit VPN users to access all internal machines.
!--- This command must be present.
sysopt connection permit-ipsec
!--- Define a transform set using AES encryption and sha
crypto ipsec transform-set ESP-AES-256-SHA esp-aes-256 esp-sha-hmac 
crypto dynamic-map outside_dyn_map 20 set transform-set ESP-AES-256-SHA
crypto map outside_map 65535 ipsec-isakmp dynamic outside_dyn_map
!--- IPSEC applies to outside interface
crypto map outside_map interface outside
!--- Use profile 'partnerauth' to authenticate clients
crypto map outside_map client authentication partnerauth
isakmp enable outside
isakmp policy 20 authentication pre-share
isakmp policy 20 encryption aes-256
isakmp policy 20 hash sha
!--- note group 5 is recomended when using 256 bit aes encryption
!--- but this is not supported by the VPN client so have to use group 2
isakmp policy 20 group 2
isakmp policy 20 lifetime 86400

VPN group configuration

!--- Define a split-tunnel ACL so that all traffic to these addresses are sent across the VPN.
!--- All other traffic is sent across the Internet normally.
access-list splitTunnelAcl permit ip any
!--- Define address pools for the vpn user groups
ip local pool staffpool
ip local pool otherpool
!--- Don't perform NAT between internal machines and VPN users
access-list inside_outbound_nat0_acl permit ip 
access-list inside_outbound_nat0_acl permit ip 
nat (inside) 0 access-list inside_outbound_nat0_acl
!--- Staff group used by staff members to connect to LAN and transfer files.
vpngroup groupstaff address-pool staffpool
!--- We want staff to be able to access our intenal DNS and WINS server to resolve machine names
vpngroup groupstaff dns-server
vpngroup groupstaff wins-server
vpngroup groupstaff default-domain mydomain.com
vpngroup groupstaff split-tunnel splitTunnelAcl
!--- Use our internal DNS server for looking up our machines but let the client use its normal
!--- DNS server for other sites.
vpngroup groupstaff split-dns mydomain.com
vpngroup groupstaff idle-time 1800
vpngroup groupstaff password grouppassword
!--- Non staff group. These users don't get access to WINS or internal DNS
vpngroup groupother address-pool siteintpool
vpngroup groupother split-tunnel splitTunnelAcl
vpngroup groupother idle-time 1800
vpngroup groupother password grouppassword

ACL's to be assigned to users by the Radius server

access-list vpnstaffin permit ip host 
access-list vpnstaffin permit udp eq netbios-ns host 
access-list vpnstaffin deny ip any any log
access-list vpnitdeptin permit ip any
access-list vpnitdeptin deny ip any any log
access-list vpnotherin permit ip host
access-list vpnotherin permit ip host eq ssh
access-list vpnotherin deny ip any any log 

Back to homepage