Freeradius, Dynamic VLANs, and G Suite

This tutorial is based on Ubuntu 18.04. Here, we will install FreeRadius and connect it to G Suite Secure LDAP. Then, we will assign VLANs to users based on their groupings in G Suite.

G Suite Secure LDAP Setup

  1. In the Google Admin Console, go to Apps > LDAP. Click on ADD LDAP CLIENT.
  2. Give the client a name and description.
  3. Set the following access permissions:
    • Verify user credentials: Entire domain
    • Read user information: Entire domain
    • Read group information: On
  4. Click on the ADD LDAP CLIENT button.
  5. At this point, you can download the certificate if desired, otherwise leave it for later and click CONTINUE TO CLIENT DETAILS.
  6. The next screen shows the settings for you new LDAP client. Click on the Authentication section.
  7. If you haven't already, download the certificate zip file. Extract the .crt and .key file and save them for later.
  8. Click on GENERATE NEW CREDENTIALS. Make note of the username and password. Be sure to copy the password now because it will no longer be available after closing the credential popup. Create new credentials if necessary.
  9. Keep the .crt and .key files, as well as the access credentials, for the Freeradius setup.

Installing Freeradius

  1. On an existing Ubuntu 18.04 installation, install Freeradius: sudo apt install freeradius freeradius-ldap
  2. Copy the .crt and .key files into the /etc/freeradius/3.0/certs folder. In this example, we'll rename the .crt and .key files to google.crt and google.key for simplicity:
    • sudo cp /path/to/.crt /etc/freeradius/3.0/certs/google.crt
    • sudo cp /path/to/.key /etc/freeradius/3.0/certs/google.key
  3. These are the freeradius files that need to be modified:
    • /etc/freeradius/3.0/mods-available/ldap
    • /etc/freeradius/3.0/mods-available/eap
    • /etc/freeradius/3.0/sites-available/default
    • /etc/freeradius/3.0/sites-available/inner-tunnel
    • /etc/freeradius/3.0/clients.conf
    • Optional: /etc/freeradius/3.0/proxy.conf
    • /etc/freeradius/3.0/mods-config/files/authorize

Modifying Freeradius

Editing /etc/freeradius/3.0/mods-available/ldap

In the ldap section, modify the following lines:

    • server 'ldaps://ldap.google.com:636'
    • identity = username from access credentials
    • password = password from access credentials
    • base_dn = 'dc=domain,dc=com'
    • comment out the line description := "Authenticated as %S" in the section 'ldap -> post-auth -> update' so it appears as follows:
post-auth {
    update {
       #description := "Authenticated at %S"
    }
}

In the tls section, modify the following lines:

    • start_tls = no
    • certificate_file = /etc/freeradius/3.0/certs/google.crt
    • private_key_file = /etc/freeradius/3.0/certs/google.key
    • require_cert = 'allow'

Save and exit the file. Next, symlink the file to mods-enabled:

sudo ln -s /etc/freeradius/3.0/mods-available/ldap /etc/freeradius/3.0/mods-enabled/ldap

Editing /etc/freeradius/3.0/mods-available/eap

  1. First, create a certificate authority, then generate the server.key and server.crt files, as well as the ca.pem. The easiest way to get these files is to have freeradius generate the files. To do this, modify the .cnf files in /etc/freeradius/3.0/certs. Change the info for:
    • default_days
    • input_password
    • output_password
    • all the stuff in the [server] and [certificate_authority] sections of server.cnf, client.cnf and ca.cnf, respectively
    • Then issue the make command inside /etc/freeradius/3.0/certs/
  2. Change ownership of new files to freerad
    • sudo chown freerad:freerad /etc/freeradius/3.0/certs/*
  3. Enter the .key, .crt, and .pem in the tls-config tls-common section.
    • private_key_password = [whatever password you created]
    • private_key_file = /path/to/server.key
    • certificate_file = /path/to/server.crt
    • ca_file = /path/to/ca.pem
  4. In the eap section, modify the following lines:
    • default_eap_type = ttls
    • *ttls -> use_tunneled_reply = yes

*Note: this setting has been deprecated and has been replaced by the "if (1)" modification that is shown later. However, because it's deprecated, modifying this setting couldn't hurt...famous last words.

Editing /etc/freeradius/3.0/sites-available/default

In the authorize section, enable LDAP by removing the "-" sign before it so it looks like:

#
# The ldap module reads passwords from the LDAP database.
ldap

In the authorize section, after the password authentication protocol (PAP) statement, add an if statement so it looks like:

#  This module should be listed last, so that the other modules
#  get a chance to set Auth-Type for themselves.
#
pap

if (User-Password ) {
update control {
Auth-Type := ldap
}
}

In the authenticate section, modify the Auth-Type PAP block as follows:

Auth-Type PAP {
#  pap
   ldap
}

In the authenticate section, modify the Auth-Type LDAP block as follows:

# Auth-Type LDAP {
    ldap
# }

Editing /etc/freeradius/3.0/sites-available/inner-tunnel

  1. Perform the same steps as in /etc/freeradius/3.0/sites-available/default
  2. In the authorize section, uncomment filter_inner_identity. This will provide the ability to match outer/inner User-Name so that users can't offer anonymous names.
  3. In the post-auth section, find the block that starts with if (0) and change it to if (1) as shown below:
 #  Instead of "use_tunneled_reply", change this "if (0)" to an
 #  "if (1)".
 #
 if (1) {
  #
  #  These attributes are for the inner-tunnel only,
  #  and MUST NOT be copied to the outer reply.
  #
  update reply {
   User-Name !* ANY
   Message-Authenticator !* ANY
   EAP-Message !* ANY
   Proxy-State !* ANY
   MS-MPPE-Encryption-Types !* ANY
   MS-MPPE-Encryption-Policy !* ANY
   MS-MPPE-Send-Key !* ANY
   MS-MPPE-Recv-Key !* ANY
  }

  #
  #  Copy the inner reply attributes to the outer
  #  session-state list.  The post-auth policy will take
  #  care of copying the outer session-state list to the
  #  outer reply.
  #
  update {
   &outer.session-state: += &reply:
  }
 }

Editing /etc/freeradius/3.0/clients.conf

Add the following block to have the freeradius server accept requests from your lan. Modify the client name, IP block, and secret.

client mylan {
  ipaddr  = 192.168.0.0/24
  secret  = [your radius secret]
}

Editing /etc/freeradius/3.0/proxy.conf

Optional: This is a useful setting if you want your RADIUS server to serve multiple domains or subdomains. Create a realm, then point it at the specific Auth-Type which controls that realm. Alternatively, this can use used to require users to type in their full email address as their username. An example is shown below.

realm domain.com {
     Auth-Type := domain
}

realm subdomain.domain.com {
     Auth-Type := subdomain
}

At this point, we have made the necessary modifications to use Freeradius with G Suite Secure LDAP. Restart Freeradius:

sudo systemctl restart freeradius

Test a user:

radtest [username]@[domain.com] [userpassword] localhost 0 testing 123

If everything is set up correctly, you should receive the following response from freeradius:

Received Access-Accept Id 68 from 127.0.0.1:1812 to 0.0.0.0:0 length 20

Editing /etc/freeradius/3.0/mods-config/files/authorize

This file is used to attach VLAN information to the user account based on the user's G Suite group. These modifications make the assumption that you have groups created in G Suite and the users are in the group individually, not using shortcut entries such as "All users in the organization". In the following example, there are 3 groups with their respective group email addresses: group1@domain.com (VLAN 10), group2@domain.com (VLAN 30), and group3@domain.com (VLAN 50). Place this block of code at the top of /etc/freeradius/3.0/mods-config/files/authorize.

DEFAULT Ldap-Group == "cn=group1,ou=Groups,dc=domain,dc=com"
        Tunnel-Type = VLAN,
        Tunnel-Medium-Type = IEEE-802,
        Tunnel-Private-Group-Id = "10"

DEFAULT realm == "domain.com", Ldap-Group == "cn=group2,ou=Groups,dc=domain,dc=com"
        Tunnel-Type = VLAN,
        Tunnel-Medium-Type = IEEE-802,
        Tunnel-Private-Group-Id = "30"

DEFAULT realm == "domain.com", Ldap-Group == "cn=group3,ou=Groups,dc=domain,dc=com"
        Tunnel-Type = VLAN,
        Tunnel-Medium-Type = IEEE-802,
        Tunnel-Private-Group-Id = "50"

The realm == "domain.com" portion above is optional and is useful for multiple domains or for required users to type their entire email address as their username. If you modified proxy.conf, you should consider matching the realms here.

Save the file, then restart freeradius:

sudo systemctl restart freeradius

Let's assume we have an account of testuser@domain.com with a password of "sh3k6cy" who is part of group1. Perform a radtest:

radtest testuser@domain.com sh3k6cy localhost 0 testing123

The radtest should produce the following result showing Access-Accept and the Tunnel-Private-Group-ID, which is the VLAN assignment for the user:

Sent Access-Request Id 214 from 0.0.0.0:40001 to 127.0.0.1:1812 length 115
  User-Name = "testuser@domain.com"
  User-Password = "sh3k6cy"
  NAS-IP-Address = 127.0.1.1
  NAS-Port = 0
  Message-Authenticator = 0x00
  Cleartext-Password = "sh3k6cy"
Received Access-Accept Id 214 from 127.0.0.1:1812 to 0.0.0.0:0 length 36
  Tunnel-Type:0 = VLAN
  Tunnel-Medium-Type:0 = IEEE-802
  Tunnel-Private-Group-Id:0 = "10"