Virtual Servers

FreeRADIUS 2.0 supports virtual servers. This change is not backwards compatible with version 1.x.

The virtual servers do not have to be set up with the "sites-available" and "sites-enabled" directories. It is still acceptable to have one "radiusd.conf" file and to put the server configuration therein:

	...
	server {
		authorize {
			...
		}
		authenticate {
			...
		}
		...
	}
	...

The power of virtual servers lies in their ability to separate policies. A policy that is placed into a virtual server is guaranteed to affect only the requests that are passed through that virtual server. In version 1.x, the policies were global, and it sometimes took much effort to write a policy so that it only applied in certain limited situations.

What is meant by "virtual server"?

A virtual server is a (nearly complete) RADIUS server, just like a configuration for FreeRADIUS 1.x. However, FreeRADIUS can now run multiple virtual servers at the same time. The virtual servers can even proxy requests to each other!

The simplest way to create a virtual server is to take all of the request processing sections from radius.conf ("authorize" , "authenticate", etc.) and wrap them in a "server {}" block, as above.

Another virtual server can be created by:

  1. Defining a new "server foo {…​}" section in radiusd.conf

  2. Putting the normal ("authorize", etc.) sections inside of the above section

  3. Adding a "listen" section inside of the "server" section.

For example:

	...
	server foo {
		listen {
			ipaddr = 127.0.0.1
			port = 2000
			type = auth
		}

		authorize {
			update control {
				Cleartext-Password := "bob"
			}
			pap
		}

		authenticate {
			pap
		}
	}
	...

With the above text added to "radiusd.conf", run the server in debugging mode (radiusd -X) and, in another terminal window, type:

$ radtest bob bob localhost:2000 0 testing123

The server should return an Access-Accept.

Capabilities and limitations

The only sub-sections that can appear in a virtual server section are as follows:

  • listen

  • client

  • authorize

  • authenticate

  • post-auth

  • pre-proxy

  • post-proxy

  • preacct

  • accounting

  • session

All other configuration parameters (modules, etc.) are global.

Inside of a virtual server, the (authorize, etc.) sections have their normal meaning and can contain anything that an authorize section could contain in version 1.x.

When a "listen" section is inside of a virtual server definition, it means that all requests sent to that IP/port will be processed through the virtual server. There cannot be two "listen" sections with the same IP address and port number.

When a "client" section is inside of a virtual server definition, it means that that client is known only to the "listen" sections that are also inside of that virtual server. Not only is this client definition available only to this virtual server, but the details of the client configuration is also available only to this virtual server, i.e.: two virtual servers can listen on different IP addresses and ports, but both can also have the same client with IP address 127.0.0.1. The shared secret for that client can be different for each virtual server.

More complex "listen" capabilities

The "listen" sections have a few additional configuration items that were not available in version 1.x and were also not mentioned above. These configuration items enable almost any mapping of IP/port to clients to virtual servers.

The configuration items are as follows:

  1. virtual_server = <name>

    • If set, all requests sent to this IP / port are processed through the named virtual server.

    • This directive can be used only for "listen" sections that are global; i.e., it cannot be used if the "listen" section is inside of a virtual server.

  2. clients = <name>

    • If set, the "listen" section looks for a "clients" section:

      clients  {
      	...
      }

      It looks inside of that named "clients" section for "client" subsections, at least one of which must exist. Each client in that section is added to the list of known clients for this IP/port. No other clients are known.

      If it is set, it over-rides the list of clients (if any) in the same virtual server. Note that the clients are not additive!

      If it is not set, then the clients from the current virtual server (if any) are used. If there are no clients in this virtual server, then the global clients are used.

      i.e., the most specific directive is used:

      • configuration in this "listen" section

      • clients in the same virtual server

      • global clients

    • The directives are also exclusive, not additive.

      If there is one client in a virtual server and another client referenced from a "listen" section, then that "listen" section will only use the second client. It will not use both clients.

More complex "client" capabilities

The "client" sections have a few additional configuration items that were not available in version 1.x and were not mentioned above. These configuration items enable almost any mapping of IP/port to clients to virtual servers.

The configuration items are:

  1. virtual_server = <name>

    • If set, all requests from this client are processed through the named virtual server.

    • This directive can be used only for "client" sections that are global; i.e., it cannot be used if the "client" section is inside of a virtual server.

    • If the "listen" section has a "server" entry and a matching client is found also with a "server" entry, then the client’s server is used for that request.

Worked examples

  1. Listening on one socket and mapping requests from two clients to two different servers:

	listen {
		...
	}
	client one {
		...
		virtual_server = server_one
	}
	client two {
		...
		virtual_server = server_two
	}
	server server_one {
		authorize {
			...
		}
		...
	}
	server server_two {
		authorize {
			...
		}
		...
	}

The same result can also be accomplished as follows:

	listen {
		...
		virtual_server = server_one
	}
	client one {
		...
	}
	client two {
		...
		virtual_server = server_two
	}
	server server_one {
		authorize {
			...
		}
		...
	}
	server server_two {
		authorize {
			...
		}
		...
	}

In this case, the default server for the socket is "server_one", so there is no need to set virtual_server in the client "one" configuration. The "server_two" configuration for client "two" overrides the default setting for the socket.

Note that the following configuration will not work:

	listen {
		...
		virtual_server = server_one
	}
	client one {
		...
	}
	server server_one {
		authorize {
			...
		}
		...
	}
	server server_two {
		client two {
			...
		}
		authorize {
			...
		}
		...
	}

In the above example, client "two" is hidden inside of the virtual server where the "listen" section cannot find it.

Outlined examples

This section outlines a number of examples, with alternatives.

  • One server, multiple sockets

    • multiple "listen" sections in a "server" section

  • One server per client

    • define multiple servers

    • have a global "listen" section

    • have multiple global "clients", each with "virtual_server = X"

  • Two servers, each with their own sockets

    Each server can list the same client IP; the secret can be different

    • define multiple servers

    • put "client" sections into each "server"

    • put a "listen" section into each "server"

  • Two sockets, sharing a list of clients but pointing to different servers

    This example allows for the possibility of a third socket, one in which none of these clients are shared.

    • define global "listen" sections

    • in each, set "virtual_server = X"

    • in each, set "clients = Y"

    • define "clients Y" section, containing multiple clients.

How to decide what to do

For completely separate policies for a socket or a client, a separate virtual server can be created. The request can then be mapped to that server by setting configuration entries in a "listen" section or in a "client" section.

Start off with the common cases first. If a particular policy should be applied to most of the clients and/or sockets, then make that policy the default. Configure the first server without paying attention to the sockets or clients to be added later and without adding a second virtual server. Once that server works, then add the second virtual server.

To re-use the previously defined sockets with the second virtual server, then one or more global "client" sections will be needed. Those clients will contain a "virtual_server = …​" entry that will direct requests from those clients to the appropriate virtual server.