What will you miss when this site closes?

198 votes ~ 6 comments

This site will turn read-only at the end of September 2017.

Creating a radius based VPN with support for Windows clients

Posted by jim-barber on Thu 15 Sep 2005 at 11:45


This article discusses setting up up an integrated IPSec/L2TP VPN using Radius and integrating it with Microsoft Windows clients.

Introduction and Planning

The software installed is going to be based on Debian packages as far as it is possible.

If we need to rely on a product not within Debian, then it's source code will be retrieved and rebuilt.

Also sometimes the Debian packages are available, but have a compilation feature switched off (usually SSL support).

Where necessary that Debian package will need to be recompiled too.

Overall Setup

The goal of this article is to set up a Linux based VPN server compatible with MS-Windows IPSec/L2TP clients, where users are authenticated against a RADIUS server.

Each main service in this document should have it's own IP address assigned to it. That way services can be moved to different hosts in the future.

While the VPN server uses the ppp daemon as part of it's solution, a separate IP needs to be allocated to that.

It is the point where the VPN tunnels terminate and route into the network.

For this document, the IP addresses assigned to the services follows.

Note that these will need to change based on your real-world rollout.

  Base Operating System:
  MySQL Database Server:
  FreeRADIUS Server:  
  IPSec VPN Server:   
  ppp Device:         

For redundancy, these services will be mirrored on the 10.10.1.* address range.

IMPORTANT NOTE: For MS windows clients that are to have the VPNs set up, make sure that the following registry key does not exist, or is set to 0.

It should be like this out of the box, but if the user ever setup a non-MS IPSec connection (such as a link to Fortigate VPNs) then this may be set to 1.

I wasted a full working day because I had the key set to 1:


In creating this documentation, I read a LOT of online documentation.

What I found was that there was a lot of conflicting settings across the range of documentation out there.

There was a lot of things that were incorrect, and just a lot of mis-information in general. After spending nearly two weeks of pulling my hair out, I finally found the solution to the problems and this should work.

Operating System

Start with base install of Debian 'testing' using Linux kernel 2.6 The 'testing' distribution is used since it is more up to date than the 'stable' distribution while still protecting from the constant change of the 'unstable' distribution.

NOTE: I actually deviated from this and used the 'unstable' distribution since it had the l2tpd daemon that I required since I could not get l2tpns working.

For a production environment you may want to recompile a newer version of the 2.6 Linux kernel with just the features needed (ppp, IPSec stuff).

It isn't necessary in order to set things up, so this can be left as a last optimisation step.

If you want MPPE ppp encryption (which isn't necessary) then you will have to patch and recompile the kernel.

Create required device nodes - The IPSec VPN server requires a /dev/net/tun device entry created that isn't made in the default Debian install:

# cd /dev

Edit the /etc/hosts file and add entries for all of the services except the ppp device:     mysql1.example.org      mysql1     radius1.example.org     radius1     vpn1.example.org        vpn1

Also define these new addresses in the /etc/network/interfaces file like so:

        auto eth0:0
        iface eth0:0 inet static

        auto eth0:1
        iface eth0:1 inet static

        auto eth0:2
        iface eth0:2 inet static

(This gives the single machine the virtual addresses so that all the services can have their own IP address despite being upon the same physical host.


You can bring the interfaces up with the following:

# ifup eth0:0
# ifup eth0:1
# ifup eth0:2
SSL encryption support

A lot of the products will be relying on X.509 certificates for authenticating and encrypting. In order to generate these certicates we'll need to install OpenSSL.

Install the openssl package:

# apt-get install openssl

Edit the /etc/ssl/openssl.cnf file. In the [req_distinguished_name] section, set the following variables to suit your site. For my testing I used the following values:

countryName_default             = AU
stateOrProvinceName_default     = Western Australia
localityName_default            = Perth
0.organizationName_default      = My Company
Certificates and Keys

You'll need all your keys signed by a Certification Authority.

In our case, rather than using Verisign, or some company like that, we wish to be our own Certification Authority. (It could be that we already have a certificate made up, if so it should be copied to the /etc/ssl/certs directory of the server and then the appropriate symlink made (see below).)

If we don't have a proper certificate yet then we'll need to generate one. The steps to do this are as follows:

To create your own Certification Authority Certificate (valid for 10 years or 3650 days):

# cd /etc/ssl/certs
# openssl req -x509 -new -days 3650 -newkey rsa -nodes -keyout \
  /etc/ssl/private/example-key.pem -out example-cert.pem

Generating a 1024 bit RSA private key
writing new private key to '/etc/ssl/private/example-key.pem'

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.

Country Name (2 letter code) [AU]:
State or Province Name (full name) [Western Australia]:
Locality Name (eg, city) [Perth]:
Organization Name (eg, company) [Example Company]:
Organizational Unit Name (eg, section) []:
Common Name (eg, YOUR name) []:Example Certification Authority
Email Address []:hostmaster@example.org

# ln -s example-cert.pem $(openssl x509 -noout -hash < example-cert.pem).0

After the above steps you should see something like the following:

# ls -l /etc/ssl/certs /etc/ssl/private

total 4
-rw-r--r--  1 root root 1391 Sep  1 09:06 example-cert.pem
lrwxrwxrwx  1 root root   18 Sep  1 09:09 d5160794.0 -> example-cert.pem

total 4
-rw-r--r--  1 root root 887 Sep  1 09:06 example-key.pem

Note: the symlink won't have the same hex digits because it will be unique every time you generate a new certificate.

Server Keys and Certificates

For each service that requires keys, you'll need to generate a public/private key pair for each, and then sign them with the Certification Authority certificate we generated in the above section.

The server keys are created in the configuration areas of the software that is going to be using them.

An example of how to create a server certificate:

# openssl req -new -newkey rsa -nodes -keyout \
  server-key.pem -out server-csr.pem

Enter a suitable Common Name such as "MySQL Server", "VPN Server", "Web Server", etc.

Enter an email address suitable for the administrator responsible for the service.

Leave the challenge phrase blank.

Remove the passphrase/challenge from the server key:

# openssl rsa -in server-key.pem -out server-key.pem

Sign it with the Certification Authority created in the section above:

# openssl x509 -req -days 3650 -CA /etc/ssl/certs/example-cert.pem \
  -CAkey /etc/ssl/private/example-key.pem \
  -CAcreateserial -CAserial ca-srl.txt -in server-csr.pem \
  -out server-cert.pem
Client Keys and Certificates

Client certificates are to be given to each client.

Create the client keys in a dedicated directory somewhere (to be defined later). Perhaps in the future they'll be put into some sort of PKI.

You'll want to issue each client with their own keys and certificates.

These keys need to be imported into their windows machines.

An example of how to create a client certificate:

openssl req -new -newkey rsa -nodes -keyout client-key.pem -out client-csr.pem

Enter a suitable Common Name such as "Dr Nick", "Anonymous Client", etc. Enter an email address suitable for the client using the key. eg. "dr_nick@example.com.au"

Leave the challenge phrase blank.

Remove the passphrase/challenge from the key:

# openssl rsa -in client-key.pem -out client-key.pem

Sign it by the Certification Authority:

# openssl x509 -req -days 3650 -CA /etc/ssl/certs/example-cert.pem \
  -CAkey /etc/ssl/private/example-key.pem \
  -CAcreateserial -CAserial ca-srl.txt -in client-csr.pem \
  -out client-cert.pem
Importing client keys into MS Windows

For Windows Clients you need to convert the PEM key format into pkcs#12 exchange format:

# openssl pkcs12 -export -inkey client-key.pem -in client-cert.pem \
  -certfile /etc/ssl/certs/example-cert.pem \
  -out export.p12 -name "Windows Cert"

Remember the password you enter since this will need to be supplied to the client when for their use to import the key.

(That is, unless we automate a way to install the keys for them - there are certainl ytools out there for this.)

Install the client certificates. First copy the client's export.p12 file to the Windows host, then import it like so:

  • Click "Start" and then "Run"
  • Enter "mmc" and click "OK"
  • Select "File" and then "Add/Remove Snap-in..."
  • Click "Add"
  • Select "Certifictes" snap-in and click "Add".
  • Select "Computer account" radio button and then click "Next".
  • Select "Local Computer" radio button (probably already selected) and then click "Finish".
  • Select "Close" and then "OK".
  • Expand the "Certificates (Local Computer)".
  • Right click on "Personal" branch and choose "All tasks" and then "Import".
  • Click "Next"
  • Click "Browse..."
  • Change "Files of type" to "Personal Information Exchange (*.pfx, *.p12)"
  • Choose the export.p12 file and then click "Open"
  • Click "Next"
  • Enter the password you entered when exporting the key into p12 format then click "Next".
  • Select "Automatically select the certificate store based on the type of certificate" radio button and click "Next".
  • Click "Finish" then hopefully you see a window saying the certificate was successfully imported... click "OK".
  • Click "Action" and then "Refresh" and the new certificate should show up.
  • Select "File" and then "Exit".
  • Saving the settings is optional, but allows you to skip the first few steps of this next time if you want to import another certificate.
MySQL Database Server

First install the appropriate MySQL server package. For this documentation, I'll install the mysql-server-5.0 package.

Select a new password for mysql root (not the same as the Linux root user):

# /usr/bin/mysqladmin -u root password 'mysql_password'

Now that the password is changed, for the MySQL cronjobs to continue working correctly do the following:

# touch /root/.my.cnf
# chmod 600 /root/.my.cnf

Edit the /root/.my.cnf file and add the following:

user     = root
password = mysql_password

Check if the MySQL binary is SSL enabled by running the following query:

# mysql -u root -p -e 'SHOW VARIABLES LIKE "%ssl%"'
Enter password:
| Variable_name | Value |
| have_openssl  | NO    |

If the binary isn't SSL enabled and you want it to support encrypted connections to it, then you'll have to build it yourself using the following section.

Note, that all the mysql-client libraries that connect to it will need SSL support too and will also need to be rebuilt.

Reconfigure MySQL to bind to the correct IP address. Edit the file /etc/mysqy/my.cnf, and change the bind-address setting appropriately:

bind-address            =

Then restart MySQL:

# /etc/init.d/mysql restart

You should still be able to connect via localhost, but you won't be authorised to connect via yet.

MySQL Database Server with SSL support (Optional)

The MySQL binary package for Debian does not support SSL out of the box (as of the time of writing).

So we have to rebuild it. We actually need to rebuild all the various versions of the server offered, since different debian packages seem to use different MySQL client library versions. The alternative is to rebuild all these packages to use the appropriate version of the MySQL client you want to use.

This is beyond the scope of this document at the moment, however once we actually roll out the network, we'll probably need to maintain our own customised versions of some Debian packages that enable extra compilation options.

I think it would be wise to have a dedicated machine for building packages on. These re-built packages could then by copied over to the server that they will be installed on.

We can use the Debian tools to rebuild the MySQL server with OpenSSL support like so. Install the tools required to build the server.

# apt-get build-dep mysql-server-5.0
# apt-get install libssl-dev

Make a directory somewhere and get the server's source code.

# mkdir -p /usr/local/src/debian-mysql
# cd /usr/local/src/debian-mysql
# apt-get source mysql-server-5.0

Amend the default Debian build options:

# vi /usr/local/src/debian-mysql/mysql-dfsg-5.0-5.0.7beta/debian/rules

(Change --without-openssl to --with-openssl)

Build the new MySQL binary packages:

# cd mysql-dfsg-5.0-5.0.7beta
# ./debian/rules binary

Install the newly built packages in place of the non-SSL debian ones:

# dpkg -i mysql-server-5.0_5.0.7beta-1_i386.deb \
        mysql-common_5.0.7beta-1_all.deb \
        mysql-client-5.0_5.0.7beta-1_i386.deb \

Go to the /etc/mysql directory and create a Certificate Authority and Signed Server Certificates as per the SSL section.

Set up the certificates and keys in the /etc/mysql directory.

You'll need to create a server key and certificate and sign them with the certificate authority.

You should create a symlink to the certificate authority like so:

# cd /etc/mysql
# ln -s /etc/ssl/certs/example-cert.pem cacert.pem

Edit the /etc/mysql/my.cnf file and uncomment the following lines in the [mysqld] section:


Restart MySQL and test that you can connect to it:

# /etc/init.d/mysql restart
RADIUS server

Install the freeradius and freeradius-mysql package:

apt-get install freeradius freeradius-mysql

Create the radius database:

# gunzip /usr/share/doc/freeradius/examples/db_mysql.sql.gz
# cp /usr/share/doc/freeradius/examples/db_mysql.sql /tmp/db_mysql.sql

Due to a bug you will need to edit the /tmp/db_mysql.sql file and in the creation of the 'nas' table at the end of the script:

        DEFAULT '0'
        id int(10) DEFAULT '0' NOT NULL auto_increment
  So it looks like:
        id int(10) NOT NULL auto_increment

Create the radius database and populate it:

# mysqladmin create radius
# cat /tmp/db_mysql.sql | mysql -p radius

Create a new radius user in the database and then grant it full rights to the newly created radius database.

For a non-SSL setup create a 'radius' user with a password.

For an SSL setup create a 'radius' user with no password but with references to the appropriately signed keys.

There is much more involved for the SSL setup as all the clients that talk to the RADIUS server will need to support it as well.

I have played a bit with the SSL and radius enough to prove that the following SSL steps do work:


# mysql -p
GRANT ALL ON radius.* TO 'radius'@'localhost' IDENTIFIED BY 'radius_password';
GRANT ALL ON radius.* TO 'radius'@'%' IDENTIFIED BY 'radius_password';


First to get the SUBJECT and ISSUER strings using the following commands on the appropriate certificate files:

# openssl verify client-cert.pem
# openssl verify server-cert.pem

Then use these when generating the following commands.

# mysql -p
GRANT ALL ON radius.* TO 'radius'@'localhost'
  SUBJECT "/C=AU/ST=Western Australia/L=Perth/O=Example/CN=Radius Client/emailAddress=hostmaster@example.org"
  AND ISSUER "/C=AU/ST=Western Australia/L=Perth/O=My Company/CN=MySQL Server/emailAddress=hostmaster@example.org";

GRANT ALL ON radius.* TO 'radius'@'%'
   SUBJECT "/C=AU/ST=Western Australia/L=Perth/O=My Company/CN=Radius Client/emailAddress=hostmaster@example.org"
   AND ISSUER "/C=AU/ST=Western Australia/L=Perth/O=My Company/CN=MySQL Server/emailAddress=hostmaster@example.org";

It is possible that we will still want to have a password associated with the user. That way to connect to RADIUS you'd have to know the correct password as well as possess the correct keys. To add the password, just use the "IDENTIFIED BY 'radius_password'" the same was as done in the non-SSL section.

Edit the /etc/freeradius/sql.conf file and set the server, login, and password variables to the correct values:

server = "mysql1.example.org"
login = "radius"
password = "radius_password"

Edit the /etc/freeradius/clients.conf file:

Set a new secret password (radius_secret) in the client {} section.

Create the new following section under the client{} section:

        client {
                secret          = radius_secret
                shortname       = radius1
                nastype         = other

You can also specify network masks such as "client {" for example.

Edit the /etc/freeradius/radiusd.conf file:

  • Uncomment 'sql' in the authorise{}, accounting{}, and session{} sections.
  • Also comment out radutmp in the accounting{} and session{} sections since it is a performance hit, and unnecessary now that we are using the SQL backend.

Restart FreeRADIUS:

# /etc/init.d/freeradius restart

Populate the MySQL tables with your users. The bare minimum for testing is:

INSERT INTO radcheck (UserName, Attribute, op, Value) VALUES ('user1', 'User-Password', '==', 'password1');

A lot of documentation talks about adding Auth-Type := Local in the radgroupcheck table. This doesn't work if using MS-CHAPv2, so don't do it. In theory, the authentication modules should detect the correct Auth-Type and go from there.

Adding an Auth-Type tends to lock only one method as being valid to the exclusion of all others which is probably not whatwe want.

The use of some of the tables are as follows:

  • The radcheck table contains the username and password pairs.
    • An entry for each VPN user must be in this table.
  • The radreply table contains custom user-specific radius reply attributes.
  • The usergroup table contains a username entry mapping to a group name.
    • The group names are used to determine any custom radius reply attributes for the group.
  • The radgroupreply table contains the custom attributes to be returned for particular groups.

The following is some examples of how to populate these tables. You'll need to customise for a real world roll-out.

        # Define the users and their passwords.
        INSERT INTO radcheck
                (UserName, Attribute, op, Value)
                ('user1', 'User-Password', '==', 'password1'),
                ('user2', 'User-Password', '==', 'password2')

        # Define the users and the group they are in.
        INSERT INTO usergroup
                (UserName, GroupName)

                ('user1', 'dynamic'),
                ('user2', 'static')

        # Return attributes for particular users.
        INSERT INTO radreply
                (UserName, Attribute, op, Value)
                ('user2', 'Cisco-Avpair',      ':=', 'throttle=yes');
                ('user2', 'Framed-IP-Address', ':=', '');

        # Return attributes for particular groups.
        # This is suboptimal as it contains a lot of DEFAULT values.
        # Defaults are supposed to be left out of the database
        # and put into the config file for performance reasons.
        INSERT INTO radgroupreply
                (GroupName, Attribute, op, Value)
                ('static', 'Cisco-Avpair',        ':=', 'throttle=no');
                ('dynamic', 'Cisco-Avpair',       ':=', 'throttle=yes');
                ('dynamic', 'Framed-Compression', ':=', 'Van-Jacobsen-TCP-IP'),
                ('dynamic', 'Framed-IP-Address',  ':=', ''),
                ('dynamic', 'Framed-MTU',         ':=', '1500'),
                ('dynamic', 'Framed-Protocol',    ':=', 'PPP'),
                ('dynamic', 'Framed-Route',       ':=', ''),
                ('dynamic', 'Service-Type',       ':=', 'Framed-User')

Again note that a lot of the entries that I put into the radgroupreply shouldn't be there since they should be default values which the doco recommends is kept out of the database and stored in the freeradius config files.

You should be able to test that the RADIUS server is working using the radtest program:

# radtest user1 password1 localhost 1812 radius_secret
# radtest user1 password1 1812 radius_secret
VPN Server

After a lot of research I've settled on an IPSec based solution that can handle NAT-T.

An easier solution to implement is PPTP, however all sources I refer to say that the protocol itself is inherently insecure. The only reason it is used is because it is easy. IPSec is regarded as a well understood and secure protocol.

The IPSec packets will be handled by the inbuilt IPSec layer in the Linux 2.6 kernel.

Racoon will be used as the Internet Key Exchange (IKE) daemon for automatically keying IPSec connections. Racoon supports NAT-T but perhaps only in Tunnel mode. The Window clients use Transport mode.

It is possible that Racoon may not support NAT-T in transport mode.

Install the ipsec-tools and racoon packages:

apt-get install ipsec-tools racoon

Choose the 'direct' mode for racoon configuration.

If using the modutils package instead of module-init-tools (unlikely if you're using a 2.6 kernel) allow the kernel to autoload the correct IPSec modules create the /etc/modutils/ipsec file with the following lines:

alias xfrm-type-2-50    esp4
alias xfrm-type-2-51    ah4
alias xfrm-type-2-108   ipcomp
alias xfrm-type-10-50   esp6
alias xfrm-type-10-51   ah6
alias xfrm-type-10-108  ipcomp6

Then rebuild the /etc/modules.conf file to include these entries:

# update-modules

These entries were already in the /etc/modprob.d/aliases file as supplied by the modules-init-tools Debian package.

Edit the /etc/racoon/racoon.conf and comment out the 'path pre_shared_key' variable and then add the following lines:

listen {
        isakmp                [500];
        isakmp_natt           [4500];

padding {
        randomize                       off;
        exclusive_tail                  off;

remote anonymous {
        exchange_mode                   main;
        my_identifier                   asn1dn;         # Extract id from public key
        peers_identifier                asn1dn;         # Extract id from public key
        verify_identifier               on;
        certificate_type                x509 "server-cert.pem" "server-key.pem";
        verify_cert                     off;
        passive                         on;             # Racoon should not start the connection itself.
        generate_policy                 on;             # Create policy when connection is initiated.
        nat_traversal                   on;             # NAT-T is used when a NAT gateway is detected between the peers.

        proposal {
                encryption_algorithm    3des;
                hash_algorithm          sha1;
                authentication_method   rsasig;         # Use X.509 RSA public/private key
                dh_group                modp1024;

sainfo anonymous {
        lifetime                        time 28800 sec;
        encryption_algorithm            3des;
        authentication_algorithm        hmac_md5;
        compression_algorithm           deflate;

Make a directory for the racoon certificates and generate and sign the required server certificates and keys:

# mkdir /etc/racoon/certs
# chmod 700 /etc/racoon/certs

Create a server key and certificate using a Common Name of "IPSec Server" or "VPN server" or something like that.

Create symlinks to Certificate Authorisation keys (copy them to the host if necessary).

# ln -s /etc/ssl/certs/example-cert.pem $(cat  /etc/ssl/certs/example-cert.pem | openssl x509 -noout -hash ).0
# ln -s /etc/ssl/certs/example-cert.pem cacert.pem

Restart racoon:

# /etc/init.d/racoon restart

Because of Microsoft's IPSec implementation, I also have to support L2TP.

This has PPP packets that are encapsulated within L2TP packets which are further encapsulated within IPSec (and with NAT-T turned on, further encapsulated within UDP packets). That's a lot of overhead.

The L2TP layer is unnecessary, and my cynical view is this it is probably a ploy by Microsoft to lock customers into buying more, licenses by having to use Microsoft's VPN server solutions.

It is possible to hack the windows registry to allow it to just use TCP/IP within IPSec, removing the need for L2TP. However hacking the registry is undesirable on the client computers, so I'll just have to live with the more complicated server setup, and the extra overhead of the network packets.


No matter what I tried, the l2tpns package just would not work from either 'testing' or 'unstable' :(

It is supposed to be better than lt2pd. But it isn't, if it is impossible to get working. The problem is that it didn't seem to try and contact the RADIUS server. No connection attempt at all that I could see.

I have no idea what it was waiting for and the debugging output gave me no clues. Nor was the web useful for finding the solution... Trust me, I spent HOURS on this.

l2tpd and ppp

Because of my failure to get l2tpns working I used the l2tpd and pppd option.

This is the setup that is probably used by more users on the Internet, so there is more documentation about it.

NOTE: l2tpd is no longer under active development like the l2tpns product is. Bug and security fixes only.

Unfortunately the l2tpd program is only available in the 'unstable' distribution so requires mucking around to get into 'testing' (which I won't go into here).

Install the l2tpd and ppp packages.

Edit /etc/l2tpd/l2tpd.conf and add the following:

listen-addr =
port = 1701

[lns default]
ip range = -
local ip =
hostname = vpn1
ppp debug = yes
pppoptfile = /etc/ppp/options.l2tpd
length bit = yes

NOTE: the 'local ip' is not the address bound to any interfaces already on the host. This is a new IP address that will be assigned to the ppp device that gets created when a connection is established.

All VPN traffic will route via this ppp device.

ALSO NOTE: A lot of doco on the net tells you to put in the refuse and require entries for pap, chap, and authentication. DONT. These override the authentication settings in the ppp daemon, and they don't actually work anyway.

(i.e. if you refuse PAP here, the client can still connect with PAP if so configured. Also all the more appropriate access controls in the ppp options file will be ignored, but they are more flexible, so we want them to work.)

Create /etc/ppp/options.l2tpd with the following contents:

# Specify which DNS Servers the incoming Win95 or WinNT Connection should use.
# Two Servers can be remotely configured.

# Specify which WINS Servers the incoming connection Win95 or WinNT should use.

# Require the peer to authenticate itself before allowing network packets to be sent or received.

# Use hardware flow control.

# Specifies that pppd should use a UUCP-style locks to ensure exclusive access to the device.

# Set the MRU [Maximum Receive Unit] value to  for negotiation.
# Set the MTU [Maximum Transmit Unit] value to .
mru 1400
mtu 1400

# Don't fork to become a background process.

# Turn on debugging. This can be turned off once everything is working.

# Add an entry to this system's ARP table with the IP address of the peer and the Ethernet address of this system.

# pppd will accept the peer's idea of our local IP address, 
# even if the local IP was specified in an option.
# pppd will accept the peer's idea of its (remote) IP address, 
# even if the remote IP was specified in an option.

# Specifies that pppd should disconnect if the link is idle for  seconds.
idle 1800

# Wait for up n milliseconds after the connect script finishes for a valid PPP packet from the peer.
connect-delay 5000

# Tell the windows client to to get a default route from the connection.

# Force MS-CHAP-v2 authentication since it is more secure than the other options.
# Though it probably doesn't matter too much since it all happens inside an IPSec tunnel anyway.
# If you have an MPPE enabled kernel you can turn on support for MPPE, but it isn't necessary.

# Do not sent output from plugins to the pty or the stream from clients may be released.

# Turn on the RADIUS plugin.
#plugin radius.so

Add an entry to the /etc/ppp/chap-secrets file like so:

user1           *       "password1"   
*               user1   "password1"   

Restart the l2tpd daemon:

# /etc/init.d/l2tpd restart

Generate and import client certificates into a Windows client and then connect to the VPN server. (Instructions for configuring the VPN client software on Windows should be written but is straight forward with no special options requiring to be ticked/unticked, etc).

If all is good, then we can move on to integrating PPP with the RADIUS server.

Integrating pppd and RADIUS

This is the trickiest part since there is so much bad information out there. It looks simple when it's written down like this though doesn't it :) Hopefully these are the steps that will bring you success.

Delete the 'user1' entries out of the /etc/ppp/chap-secrets file again.

Install the radiusclient1 package.

Edit /etc/radiusclient/servers file and add the following:

radius1.example.org             radius_secret

Edit /etc/radiusclient/radiusclient.conf and change the following lines from localhost:

authserver      radius1.example.org
acctserver      radius1.example.org

Edit the /etc/ppp/option.l2tp file and uncomment the 'plugin radius.so' entry at the bottom.

Radiusclient Dictionaries

For MS-CHAP protocols to work with the radius server it is critical that the radiusclient setup has the microsoft dictionaries.

Unfortunately these aren't installed with the radiusclient1 package.

Even more unfortunate is that the format of these dictionary files have evolved over time and the radius.so plugin that is part of the ppp package only understands an very old format.

Also the radius plugin is very fussy about the INCLUDE statements used in the dictionary files.

The comments in the files provided with the radiusclient1 package suggest that you'd add an entry like:

$INCLUDE dictionary.microsoft

When in fact, the radius.so plugin requires it to look like:

INCLUDE /etc/radiusclient/dictionary.microsoft

Note the lack of a leading $ sign, and the full path used in the file.

The steps to getting a working dictionary.microsoft file are as follows:

Copy the freeradius dictionary.microsoft file to the radiusclient1 area:

# cp /usr/share/freeradius/dictionary.microsoft /etc/radiusclient/

Edit the file to change it's format back to an older format understood by the radius.so plugin.

# vi /etc/radiusclient/dictionary.microsoft

Change all occurances of the word "octects" to "string".

Delete the following lines:

BEGIN-VENDOR    Microsoft
END-VENDOR      Microsoft

Delete all "encrypt=1", and "encrypt=2" entries from the end of the ATTRIBUTES that have them.

At the end of each ATTRIBUTE line add a new field to the end that says "Microsoft" without the quotes.

Watch out for the lines that have comments at the end. The new field must be before them so it isn't part of the comment.

Now edit the /etc/radiusclient/dictionary file and add the following to the end of it:

INCLUDE /etc/radiusclient/dictionary.microsoft

You should now be able to connect with the VPN client and this time it will be authenticated against the radius server.

If it fails for any reason, shutdown the freeradius server, and run it manually with debugging turned on to see what is happening like so:

# /etc/init.d/freeradius stop
# freeradius -X

On a new connection from the VPN client you should see a whole heap of logs starting with something like:

  rad_recv: Access-Request packet from host, id=244, length=133
        Service-Type = Framed-User
        Framed-Protocol = PPP
        User-Name = "user1"
        MS-CHAP-Challenge = 0xa6f98c2e3c2157432ae1622d9bad2e64
        MS-CHAP2-Response = 0xe500f87302def6dbfc274ac1cb1b5ee6bcf00000000000000000497a4622ec34eff1375a80058ad318233f21aa1ec25ea2d0
        NAS-IP-Address =
        NAS-Port = 0

If the MS-CHAP-Challenge, or MS-CHAP2-Response entries are not there, then there is something wrong with the dictionary setup described above, so double check it all.

The dictionaries are critical to getting this to work.



Re: Creating a radius based VPN with support for Windows clients
Posted by Anonymous (221.124.xx.xx) on Thu 15 Sep 2005 at 13:10
Thanks for the article.

The l2tpns does not work just because it use the wrong port to communicate iwht the RADIUS server. It takes me one whole day to find it out. Adding the following line to /etc/l2tpns/startup-config to make it use the right port.

set primary_radius_port 1812


[ Parent | Reply to this comment ]

Re: Creating a radius based VPN with support for Windows clients
Posted by jim-barber (203.59.xx.xx) on Thu 15 Sep 2005 at 13:33
Thanks Edmond.

When I get time I'll try l2tpns again.
Maybe it will simplify a lot of the above.


[ Parent | Reply to this comment ]

Re: Creating a radius based VPN with support for Windows clients
Posted by jim-barber (203.188.xx.xx) on Thu 22 Sep 2005 at 03:09
I restored the virtual machine that I was using at the point where I gave up on the l2tpns daemon.

I looked at my /etc/l2tpns/startup-config file and it already contained the line:
set primary_radius_port 1812

So that wasn't my problem with getting it to talk to the RADIUS server...
What a shame, I would have liked to have got it working.

[ Parent | Reply to this comment ]

Re: Creating a radius based VPN with support for Windows clients
Posted by Anonymous (64.40.xx.xx) on Fri 31 Mar 2006 at 01:55
The l2tpns daemon only supports PAP and CHAP MD5 authentication protocols, and not MS-CHAP or MS-CHAPv2. If you attempt to connect using MS-CHAP (v1 or v2), it won't even bother trying to contact the radius server. PAP authenticaion works just fine, though, as does CHAP if you happen to have the plaintext passwords handy. The only problem is that you have to manually configure the clients to use PAP instead of MS-CHAP.

[ Parent | Reply to this comment ]

Re: Creating a radius based VPN with support for Windows clients
Posted by jim-barber (203.59.xx.xx) on Thu 15 Sep 2005 at 13:36
To everyone reading this.
Apologies for the choppiness of the language used.

I wrote this as some notes for internal use at my company, hence the talk about splitting services over multiple IP addresses, etc.
It isn't a requirement as stated in this document, but for our needs it is, since if the load ever gets too high on the server we want to be able to shift services to another host with minimal work and disruption.

Thanks to Steve for nicely formatting this all up.
I passed him a nasty great big plain txt file written in 'vi' :)

[ Parent | Reply to this comment ]

Re: Creating a radius based VPN with support for Windows clients
Posted by suspended user ido50 (85.64.xx.xx) on Thu 15 Sep 2005 at 14:50
[ View Weblogs ]
Thanks for the article, but I have no idea what is an "integrated LDAP + IPSec/L2TP VPN using Radius"...

I mean, I've heard of LDAP, IPSec and L2TP, I know what VPN is, but I don't know what Radius is and what exactly is the nature of such a virtual private network...

Can I please get some kind of an introduction?


[ Parent | Reply to this comment ]

Re: Creating a radius based VPN with support for Windows clients
Posted by jim-barber (203.188.xx.xx) on Fri 16 Sep 2005 at 00:46
Actually LDAP doesn't come into it...

Steve, if you read this, can you remove the reference to LDAP in the introduction please?

These are instructions for creating a VPN server for IPSec/L2TP VPN connections where the users are authenticated against a RADIUS server.

RADIUS stands for Remote Authentication Dial-In User Service.
A RADIUS server is an Authentication, Authorisation, and Access (AAA) server. It contains user definitions and passwords, for authenticating users, and is usually used for when users connect to your network. An example of a common use for a RADIUS server is to communicate with a Network Access Server at an Telco where all your user's ADSL connection terminate.
The RADIUS server is able to send back paramaters that define the connection, such as supplying an IP address, specify whether the connection is throttled, etc.
I'm still just a beginner when it comes to RADIUS myself.

[ Parent | Reply to this comment ]

Re: Creating a radius based VPN with support for Windows clients
Posted by suspended user ido50 (85.64.xx.xx) on Fri 16 Sep 2005 at 13:03
[ View Weblogs ]
Understood, thanks a lot.

[ Parent | Reply to this comment ]

Re: Creating a radius based VPN with support for Windows clients
Posted by Anonymous (198.54.xx.xx) on Fri 16 Sep 2005 at 13:37
Unless yo want to give this a once-over:


Shouldn't be too difficult to integrate the lot.

[ Parent | Reply to this comment ]

Re: Creating a radius based VPN with support for Windows clients
Posted by philcore (216.54.xx.xx) on Thu 15 Sep 2005 at 16:24
[ View Weblogs ]
If you don't want to edit the freeradius dictionary.microsoft file, you can get a correctly formatted (or at least correct as far as pppd cares) from the ppp 2.4.3 source tarball.

[ Parent | Reply to this comment ]

Re: Creating a radius based VPN with support for Windows clients
Posted by Anonymous (81.64.xx.xx) on Tue 4 Oct 2005 at 23:55
Hi !

I'd like to implement this in my control panel for web hosting (http://www.gplhost.com/?rub=softwares&sousrub=dtc). I have already support for radius-mysql (it's setup automaticaly by my install scripts), but I have very bad knowledge about VPN, L2ptns or ppp.

1. Do you think it would be a good idea to do what I just said?
2. Would you have some time to help me doing it?

By the way thanks a lot for this very nice article.


[ Parent | Reply to this comment ]

Give l2tpns another shot
Posted by walpoledan (207.190.xx.xx) on Thu 3 Nov 2005 at 21:09
I also spent fruitless time trying to get l2tpns working several months ago. I never got anywhere, gave up and stuck with l2tpd (and cursed l2tpns...)

However, today I was playing around with changing my l2tpd authentication from local chap-secrets to radius (using your article for help with that). After I got the radius config working with l2tpd I tried the new l2tpns package and it just worked. I may have simply had my radius config wrong previously but I'm guessing that it's the new 2.1.x l2tpns code that was the trick. So, if you haven't tried the 2.1.x series (2.1.8 is in 'testing' and 2.1.3 first hit 'unstable' 8-24-05) you might want to try it.

The only thing that I needed to do after firing it up (besides the obvious config file edits for dns and radius servers) was to set eth0 to do proxy arp manually. Evidently there's no setting in l2tpns to do that as there is in pppd.

Good luck and thanks for the helpful article.

[ Parent | Reply to this comment ]

Re: Give l2tpns another shot
Posted by Anonymous (86.68.xx.xx) on Sat 26 Aug 2006 at 17:15
I confirm this, I'm currently loosing all my hairs trying to setup a IPsec/L2TP VPN using openswan & l2tpns, and l2tpns 2.0 from debian sarge doesn't try to contact RADIUS.

So, I made some backports (easy, you just need to get libcli sources, and l2tpns sources from testing), and now it tries :)

[ Parent | Reply to this comment ]

Re: Creating a radius based VPN with support for Windows clients
Posted by Anonymous (217.159.xx.xx) on Mon 16 Jan 2006 at 14:59
Thank you for such a great howto!
Managed to make all the things work together!

[ Parent | Reply to this comment ]

Re: Creating a radius based VPN with support for Windows clients
Posted by Anonymous (195.228.xx.xx) on Thu 2 Feb 2006 at 21:14

I tried to use the dictionary.microsoft from freeradius with the instructions, but it does not worked. A read somewhere on the net: use the one from the ppp source package.
And it worked. I don't now what I made wrong.

A great article, was helpful!

[ Parent | Reply to this comment ]

Re: Creating a radius based VPN with support for Windows clients
Posted by Anonymous (24.144.xx.xx) on Sun 19 Mar 2006 at 05:51
l2tpns would have attempted to contact the RADIUS server on port 1645 (the old RADIUS port number) by default. If you set primary_radius_port to 1812 (the new RADIUS port number) it works. See the thread starting at http://lists.cistron.nl/pipermail/freeradius-users/2001-March/000 227.html

[ Parent | Reply to this comment ]

Re: Creating a radius based VPN with support for Windows clients
Posted by johnlongland (196.11.xx.xx) on Fri 19 May 2006 at 09:38
Thanks for the article, Jim !

Currently I am battling to pass DNS info to my Windows
clients. You don't perhaps have any info in this regard ?
From debugging info, it seems that my radius is in fact
sending the DNS info as a vendor specific attribute.
( I am using the MS-Primary-DNS-Server attrib ).
On the client side I am merely using the windows
dialer. Do I need to config the client to accept
this info ?

Thanks !
JOhn Longland

[ Parent | Reply to this comment ]

Re: Creating a radius based VPN with support for Windows clients
Posted by mary733369 (143.107.xx.xx) on Tue 9 Jan 2007 at 22:44
Dear Jim,

I can't find any source to install the mysql 5.0.

apt-get build-dep mysql-server-5.0
Reading Package Lists... Done
Building Dependency Tree... Done
E: Unable to find a source package for mysql-server-5.0

How I can intall mysql for stable version of Debian?


[ Parent | Reply to this comment ]

Re: Creating a radius based VPN with support for Windows clients
Posted by Anonymous (218.186.xx.xx) on Fri 22 Jun 2007 at 01:27
Can I use this CHAP method with OpenVPN?

[ Parent | Reply to this comment ]

Re: Creating a radius based VPN with support for Windows clients
Posted by Anonymous (78.130.xx.xx) on Tue 24 Feb 2009 at 14:53
I think you can use it with open vpn

[ Parent | Reply to this comment ]

Re: Creating a radius based VPN with support for Windows clients
Posted by Anonymous (132.234.xx.xx) on Tue 2 Jun 2009 at 06:32
I tried this on Ubuntu Jaunty
You need to remove the line from the dictionary.microsoft file that refers to IPv6. It then worked.

[ Parent | Reply to this comment ]

Re: Creating a radius based VPN with support for Windows clients
Posted by Anonymous (197.128.xx.xx) on Wed 1 Jun 2011 at 08:56
hi,i'm working whith ubunto 10.10.
i wanna know if i can use this tuto!!!

[ Parent | Reply to this comment ]

Re: Creating a radius based VPN with support for Windows clients
Posted by marwa (197.128.xx.xx) on Wed 1 Jun 2011 at 09:41
hi,i follow ur tuto but for
#ifup eth0:0
#ifup eth1:1
#ifup eth2:2
i got this message
ignoring unknown interface eth0:=eth0:.
ignoring unknown interface 2=2.

so i can't understand what is the problem !!!

[ Parent | Reply to this comment ]