Policies: writing a custom policy

Introduction

This document explains in some detail how to write custom policies to be used in FreeNAC v3.0. It is assumes that you have some knowledge of PHP and OOP (object oriented programming).

Please read the chapters Sample Policies and introduction to policies first. The pre-defined examples can be copied and modified: its recommended to read and experiment with those first, before creating your own here.

In this document, we create a new policy from scratch called 'My_Policy' which allows active devices into a default vlan, and denies access to unknown devices.

To see the classes, methods and functions used in the FreeNAC framework, please see the source code phpDocumentor page .

Policy: How it works

When pre-connect is started, it searches the config table for the name of a policy. If this policy (which is a PHP file) is available, it is loaded. The policy contains a preconnect() function, which is called once a request has been received. This function calls specific checks and finishes by calling the ALLOW() or DENY() function to attribute a vlan and health status.

Likewise the postconnect() function decides what do do after a device has been authenticated in the pre-connect phase.

These functions have access to REQUEST data, which contains the original Switch request, with a sub-object HOST containing information / methods relevant to that end-device and a sub-object PORT containing information / methods relevant to that switch/port.

Creating the new Policy

In order to create a policy file, create a PHP file containing a class which extends the Policy class. The Policy class defines two methods that you must override in your new class, preconnect and postconnect. These methods are used by the vmpsd_external and postconnect methods respectively. The reason why you must override those methods in your new class, is because its default behaviour in the Policy class is to deny everything. So, to start then creating our policy class, create the file My_Policy.php in the /opt/nac/etc directory with the following contents:

<?php
class My_Policy extends Policy
{

}
?>

Now, in order to override the preconnect and postconnect methods, add the definition for those two methods as shown below:

<?php
class My_Policy extends Policy
{
public function preconnect($REQUEST)
{

}

public function postconnect($REQUEST)
{

}
}
?>

The parameter $REQUEST is the request you'll be receiving either by the vmpsd_external or the postconnect daemon. With this object you can access properties of the connecting device through the $host object or the port where this device is connecting to, by using the $switch_port object. Also, $REQUEST has access to the configuration settings through the $conf object.

The $host variable is an object of the EndDevice class. The $switch_port object is an object of the Port class. These objects allow information about the host, switch or port to be examined, and used to make a policy decision. They are accessed as follows:

$REQUEST->switch_port->method();
$REQUEST->host->method();

The list of standard methods available in each object is visible in the phpDocumentor page. Methods are ways of asking questions about objects, or taking action.

Each object also has a set of properties, which correspond to fields in the database. For example a port has a name, comment, last used time, up/down status. The list of properties can be retrieve use getAllProps(), e.g. to see all host properties try this:

print_r( $REQUEST->host->getAllProps() );

Preconnection policy

The pre-connection function is called when a device initially connects to the network, requesting access.

The EndDevice class defines the isActive() method. With this method we test if the connecting device is already in the database with an 'active' status. For a list of available methods and how to use them, please have a look at the phpDocumentor page. The isActive() method is the one that we'll use to write this simple policy file.

#Check if the connecting device is in the DB and is active.
if ($REQUEST->host->isActive())
{
#If so, allow it into the global default vlan

}

Once a decision has been reached on wthere to allow or deny access to a host, this decision is communicated by to the network switch by 'throwing exceptions'. The exceptions are abstracted in two functions, which are

ALLOW($vlan_id);
DENY($message);

The ALLOW() function throws an AllowException and the DENY function throws a DenyException.

Now, what we need is to allow the active systems into the global default vlan, so we add this code to the if-block we previously had.

ALLOW($REQUEST->conf->default_vlan);

Make sure you have defined this default vlan in your config table first. You can do this easily through the Windows GUI. When we throw the exception, control returns to vmpsd_external which will return back to the switch the vlan name where this device should be placed.

Now, we need to write the part to deny unknown systems. After the 'if' block, add the following code:

DENY('Denying access to unknown systems');

Postconnection policy

The postconnect function is called after a device has passed the preconnect phase and has been allowed or refused access. Postconnect is used for documentation, and additional policy check that are too slow to occur in real time.

Now, in the postconnect part for this example, we'll be inserting unknown systems into the database. The devices inserted into the database will have an 'unknown' status, so if they reconnect to the network they'll have their access blocked. You need to modify this status in the Windows GUI for the systems you want to allow.

In postconnect, add the following code to insert unknown devices.

$REQUEST->host->insertIfUnknown();

To update device information (time of connection, port this device was connected to, etc), call the update method.

$REQUEST->host->update();

The order here is important. Make sure you always call the insertIfUnknown() method before any update, otherwise you'll get errors trying to update a device which is not yet in the database.

Now, let's update the switch port information (last time this port was used, what vlan was last assigned, etc).

$REQUEST->switch_port->update();

Your final policy file should look like the following.

<?php
class My_Policy extends Policy
{
public function preconnect($REQUEST)
{
#Check if the connecting device is in the DB and is active.
if ($REQUEST->host->isActive())
{
#If so, allow it into the global default vlan
ALLOW($REQUEST->conf->default_vlan);
}
#Deny access to unknown or inactive systems
DENY('Denying access to unknown systems');
}

public function postconnect($REQUEST)
{
#Insert this device in the database if it doesn't exist
$REQUEST->host->insertIfUnknown();
#Update this device's information
$REQUEST->host->update();

#Update switch port information
$REQUEST->switch_port->update();
}
}
?>

Activating the policy

Now, in order to activate this policy, modify the value of the default_policy field in your config table to contain My_Policy. Now go to the /opt/nac/etc directory, and delete the policy.inc.php symlink and create a new one pointing to your newly created policy file.

rm /opt/nac/etc/policy.inc.php
ln -s /opt/nac/etc/My_Policy.php /opt/nac/etc/policy.inc.php

And restart the daemons (vmps and postconnect). Your newly created policy should now be loaded. See syslog to check if your policy was successfully loaded.

See also the sample policies and the policy testing sections.

Advanced administration: If you want to rename the policy class, in the config table you need to register the name of the policy (class name) you want to load.

update config set value='BasicPolicy' where name='default_policy'; 

Please leave your comments at the end of this guide, or if you prefer to discuss, visit the developer forum.