diff --git a/radius-server/defaults/main.yml b/radius-server/defaults/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..1c7ce992972762a2654e93c029876833b1cda3e8
--- /dev/null
+++ b/radius-server/defaults/main.yml
@@ -0,0 +1,47 @@
+---
+
+radius_ldap_server:
+  ad.example.com: 'ad.example.com'
+  ad2.example.com: 'ad2.example.com'
+radius_ldap_bind_user: "cn=radius,cn=users,dc=example,dc=com"
+radius_ldap_bind_pass: ""
+radius_ldap_base_dn: "cn=users,dc=example,dc=com"
+
+radius_certs_dir: "{{ inventory_dir }}/files/radius-certs/"
+radius_secret_localclient: testing123
+radius_secret_cert: testing123
+
+radius_mschapv2_identity: ad.example.com
+radius_ntlm_domain: EXAMPLE
+
+radius_clients:
+  - name: example
+    ip: 10.10.10.10
+    secret: testing123
+
+radius_realms:
+  - example.com
+  - ssl.example.com
+radius_default_realm: example.com
+
+radius_vlan_assignments:
+  - key: User-Name
+    condition: =~
+    value: '"^host\/.*\.example\.com$"'
+    vlan: 23
+  - key: User-Name
+    condition: =~
+    value: '"^lxc.*@ssl\.example\.com$"'
+    vlan: 23
+  - key: Ldap-Group
+    condition: ==
+    value: vlan42
+    vlan: 42
+
+radius_tunnel_checks:
+  - station: OtherStationSSID
+    condition: '(Ldap-Group == "CN=vlan42,CN=Users,DC=asta,DC=rwth-aachen,DC=de") || (&User-Name =~ /^host\/.*\.example\.com$/ )'
+    error: 'Not allowed to use this SSID'
+  - condition: '(&User-Name =~ /^host\/.*\.example\.com$/ ) || (Ldap-Group == "CN=foobar,CN=Users,DC=asta,DC=rwth-aachen,DC=de")'
+    error: 'User not allowed'
+
diff --git a/radius-server/handlers/main.yml b/radius-server/handlers/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..454c6c882b090cd2d6a1282132cedfba2e8bda3e
--- /dev/null
+++ b/radius-server/handlers/main.yml
@@ -0,0 +1,4 @@
+---
+
+- name: reload freeradius
+  service: name=freeradius state=restarted
diff --git a/radius-server/tasks/main.yml b/radius-server/tasks/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a8655b6f9289fe8ea1c07222eed2384f2d97bde6
--- /dev/null
+++ b/radius-server/tasks/main.yml
@@ -0,0 +1,105 @@
+---
+
+- name: ensure freeradius server is installed
+  apt:
+    name: "{{ item }}"
+    state: present
+  with_items:
+    - freeradius
+    - freeradius-config
+    - freeradius-ldap
+    - freeradius-mysql
+    - freeradius-postgresql
+    - freeradius-utils
+    - winbind
+  tags:
+    - radius-server
+    - packages
+
+- name: ensure freeradius server SSL configuration
+  copy:
+    src: "{{ radius_certs_dir }}/{{ item }}"
+    dest: /etc/freeradius/3.0/certs/
+    owner: root
+    group: freerad
+    mode: 0640
+  with_items:
+    - dh
+    - cacert.pem
+    - "{{ ansible_fqdn }}.pem"
+    - "{{ ansible_fqdn }}.key"
+  notify:
+    - reload freeradius
+  tags:
+    - freeradius
+    - config
+
+- name: configure available modules
+  template:
+    src: "{{ item }}.j2"
+    dest: "/etc/freeradius/3.0/{{ item }}"
+    owner: root
+    group: root
+    mode: 0644
+  with_items:
+    - mods-available/eap
+    - mods-available/ldap
+  notify:
+    - reload freeradius
+  tags:
+    - freeradius
+    - config
+
+- name: ensure freeradius server sites and policies are configured
+  template:
+    src: "{{ item }}.j2"
+    dest: "/etc/freeradius/3.0/{{ item }}"
+    owner: root
+    group: freerad
+    mode: 0640
+  with_items:
+    - mods-available/mschap
+    - mods-available/realm
+    - mods-available/ntlm_auth
+    - clients.conf
+    - proxy.conf
+    - sites-available/default
+    - sites-available/inner-tunnel
+    - policy.d/rewrite_station_to_ssid
+    - policy.d/ntlm_auth
+    - mods-config/files/authorize
+  notify:
+    - reload freeradius
+  tags:
+    - freeradius
+      
+- name: enable freeradius server modules
+  file:
+    src: "/etc/freeradius/3.0/mods-available/{{ item }}"
+    dest: "/etc/freeradius/3.0/mods-enabled/{{ item }}"
+    state: link
+  with_items:
+    - ldap
+    - eap
+    - mschap
+    - realm
+    - ntlm_auth
+  notify:
+    - reload freeradius
+  tags:
+    - freeradius
+
+- name: enable freeradius server sites
+  file:
+    src: "/etc/freeradius/3.0/sites-available/{{ item }}"
+    dest: "/etc/freeradius/3.0/sites-enabled/{{ item }}"
+    state: link
+  with_items:
+    - default
+    - inner-tunnel
+  notify:
+    - reload freeradius
+  tags:
+    - freeradius
+
+- meta: flush_handlers
diff --git a/radius-server/templates/clients.conf.j2 b/radius-server/templates/clients.conf.j2
new file mode 100644
index 0000000000000000000000000000000000000000..306a69fdb69cea04b233198bd15993662171a52b
--- /dev/null
+++ b/radius-server/templates/clients.conf.j2
@@ -0,0 +1,96 @@
+# -*- text -*-
+##
+## clients.conf -- client configuration directives
+##
+##	$Id: 76b300d3c55f1c5c052289b76bf28ac3a370bbb2 $
+
+#######################################################################
+#
+#  Define RADIUS clients (usually a NAS, Access Point, etc.).
+
+#
+#  Defines a RADIUS client.
+#
+#  '127.0.0.1' is another name for 'localhost'.  It is enabled by default,
+#  to allow testing of the server after an initial installation.  If you
+#  are not going to be permitting RADIUS queries from localhost, we suggest
+#  that you delete, or comment out, this entry.
+#
+#
+
+#
+#  Each client has a "short name" that is used to distinguish it from
+#  other clients.
+#
+#  In version 1.x, the string after the word "client" was the IP
+#  address of the client.  In 2.0, the IP address is configured via
+#  the "ipaddr" or "ipv6addr" fields.  For compatibility, the 1.x
+#  format is still accepted.
+#
+client localhost {
+	#  Only *one* of ipaddr, ipv4addr, ipv6addr may be specified for
+	#  a client.
+	ipaddr = 127.0.0.1
+
+	proto = *
+
+	#
+	#  The shared secret use to "encrypt" and "sign" packets between
+	#  the NAS and FreeRADIUS.  You MUST change this secret from the
+	#  default, otherwise it's not a secret any more!
+	secret = {{ radius_secret_localclient }}
+
+	#
+	#  Old-style clients do not send a Message-Authenticator
+	#  in an Access-Request.  RFC 5080 suggests that all clients
+	#  SHOULD include it in an Access-Request.  The configuration
+	#  item below allows the server to require it.  If a client
+	#  is required to include a Message-Authenticator and it does
+	#  not, then the packet will be silently discarded.
+	#
+	#  allowed values: yes, no
+	require_message_authenticator = no
+
+	shortname = localhost
+
+	#
+	#  As of 2.0, clients can also be tied to a virtual server.
+	#  This is done by setting the "virtual_server" configuration
+	#  item, as in the example below.
+	#
+#	virtual_server = home1
+
+
+	#
+	#  Connection limiting for clients using "proto = tcp".
+	#
+	#  This section is ignored for clients sending UDP traffic
+	#
+	limit {
+		#  Limit the number of simultaneous TCP connections from a client
+		max_connections = 16
+
+		#  The per-socket "max_requests" option does not exist.
+		lifetime = 0
+
+		#
+		#  The idle timeout, in seconds, of a TCP connection.
+		#  If no packets have been received over the connection for
+		#  this time, the connection will be closed.
+		idle_timeout = 30
+	}
+}
+
+# IPv6 Client
+client localhost_ipv6 {
+	ipv6addr	= ::1
+	secret		= testing123
+}
+
+{%- for client in radius_clients %}
+client {{ client.name }} {
+	ipaddr = {{ client.ip }}
+	secret = {{ client.secret }}
+}
+{% endfor -%}
+
diff --git a/radius-server/templates/mods-available/eap.j2 b/radius-server/templates/mods-available/eap.j2
new file mode 100644
index 0000000000000000000000000000000000000000..6160697d17191959f954f4069d85457b51b5b438
--- /dev/null
+++ b/radius-server/templates/mods-available/eap.j2
@@ -0,0 +1,262 @@
+# -*- text -*-
+##
+##  eap.conf -- Configuration for EAP types (PEAP, TTLS, etc.)
+##
+##	$Id: 1b69550d28293a76de7c6aa7389ad318696b8509 $
+
+#######################################################################
+#
+#  Whatever you do, do NOT set 'Auth-Type := EAP'.  The server
+#  is smart enough to figure this out on its own.  The most
+#  common side effect of setting 'Auth-Type := EAP' is that the
+#  users then cannot use ANY other authentication method.
+#
+eap {
+	default_eap_type = peap
+	timer_expire     = 60
+	
+	ignore_unknown_eap_types = no
+
+	cisco_accounting_username_bug = no
+	max_sessions = ${max_requests}
+
+	# Supported EAP-types
+
+	## Common TLS configuration for TLS-based EAP types
+	tls-config tls-common {
+		private_key_password = {{ radius_secret_cert }}
+		private_key_file = ${certdir}/{{ ansible_fqdn }}.key
+		certificate_file = ${certdir}/{{ ansible_fqdn }}.pem
+
+		ca_file = ${certdir}/cacert.pem
+
+		dh_file = ${certdir}/dh
+		random_file = /dev/urandom
+
+		#  Check the Certificate Revocation List
+		#
+		#  1) Copy CA certificates and CRLs to same directory.
+		#  2) Execute 'c_rehash <CA certs&CRLs Directory>'.
+		#    'c_rehash' is OpenSSL's command.
+		#  3) uncomment the lines below.
+		#  5) Restart radiusd
+	#	check_crl = yes
+		# Check if intermediate CAs have been revoked.
+	#	check_all_crl = yes
+
+		ca_path = ${cadir}
+
+
+		#  In 2.1.10 and later, this check can be done
+		#  more generally by checking the value of the
+		#  TLS-Client-Cert-CN attribute.  This check
+		#  can be done via any mechanism you choose.
+		#
+		check_cert_cn = %{User-Name}
+
+		#
+		# Set this option to specify the allowed
+		# TLS cipher suites.  The format is listed
+		# in "man 1 ciphers".
+		cipher_list = "DEFAULT"
+
+		# Work-arounds for OpenSSL nonsense
+		# OpenSSL 1.0.1f and 1.0.1g do not calculate
+		# the EAP keys correctly.  The fix is to upgrade
+		# OpenSSL, or disable TLS 1.2 here. 
+#		disable_tlsv1_2 = no
+
+		#
+
+		#  Elliptical cryptography configuration
+		ecdh_curve = "prime256v1"
+
+		cache {
+			enable = no
+			lifetime = 24 # hours
+			max_entries = 255
+
+			#
+			#  Internal "name" of the session cache. Used to
+			#  distinguish which TLS context sessions belong to.
+			#
+			#  The server will generate a random value if unset.
+			#  This will change across server restart so you MUST
+			#  set the "name" if you want to persist sessions (see
+			#  below).
+			#
+			#name = "EAP module"
+
+			#
+			#  Simple directory-based storage of sessions.
+			#  Two files per session will be written, the SSL
+			#  state and the cached VPs. This will persist session
+			#  across server restarts.
+			#
+			#  The server will need write perms, and the directory
+			#  should be secured from anyone else. You might want
+			#  a script to remove old files from here periodically:
+			#
+			#    find ${logdir}/tlscache -mtime +2 -exec rm -f {} \;
+			#
+			#  This feature REQUIRES "name" option be set above.
+			#
+			#persist_dir = "${logdir}/tlscache"
+		}
+
+		#
+		#  As of version 2.1.10, client certificates can be
+		#  validated via an external command.  This allows
+		#  dynamic CRLs or OCSP to be used.
+		#
+		#  This configuration is commented out in the
+		#  default configuration.  Uncomment it, and configure
+		#  the correct paths below to enable it.
+		#
+		#  If OCSP checking is enabled, and the OCSP checks fail,
+		#  the verify section is skipped.
+		#
+		verify {
+			#  If the OCSP checks succeed, the verify section
+			#  is run to allow additional checks.
+			#
+			#  If you want to skip verify on OCSP success,
+			#  uncomment this configuration item, and set it
+			#  to "yes".
+	#		skip_if_ocsp_ok = no
+
+			#  A temporary directory where the client
+			#  certificates are stored.  This directory
+			#  MUST be owned by the UID of the server,
+			#  and MUST not be accessible by any other
+			#  users.  When the server starts, it will do
+			#  "chmod go-rwx" on the directory, for
+			#  security reasons.  The directory MUST
+			#  exist when the server starts.
+			#
+			#  You should also delete all of the files
+			#  in the directory when the server starts.
+			#tmpdir = /tmp/radiusd
+
+			#  The command used to verify the client cert.
+			#  We recommend using the OpenSSL command-line
+			#  tool.
+			#
+			#  The ${..ca_path} text is a reference to
+			#  the ca_path variable defined above.
+			#
+			#  The %{TLS-Client-Cert-Filename} is the name
+			#  of the temporary file containing the cert
+			#  in PEM format.  This file is automatically
+			#  deleted by the server when the command
+			#  returns.
+	#		client = "/path/to/openssl verify -CApath ${..ca_path} %{TLS-Client-Cert-Filename}"
+		}
+
+		#
+		#  OCSP Configuration
+		#  Certificates can be verified against an OCSP
+		#  Responder. This makes it possible to immediately
+		#  revoke certificates without the distribution of
+		#  new Certificate Revocation Lists (CRLs).
+		#
+		ocsp {
+			#
+			#  Enable it.  The default is "no".
+			#  Deleting the entire "ocsp" subsection
+			#  also disables ocsp checking
+			#
+			enable = no
+
+			#
+			#  The OCSP Responder URL can be automatically
+			#  extracted from the certificate in question.
+			#  To override the OCSP Responder URL set
+			#  "override_cert_url = yes".
+			#
+			override_cert_url = yes
+
+			#
+			#  If the OCSP Responder address is not extracted from
+			#  the certificate, the URL can be defined here.
+			#
+			url = "http://127.0.0.1/ocsp/"
+		}
+	}
+	
+	## EAP-TLS
+	tls {
+		tls = tls-common
+
+		#
+		# As part of checking a client certificate, the EAP-TLS
+		# sets some attributes such as TLS-Client-Cert-CN. This
+		# virtual server has access to these attributes, and can
+		# be used to accept or reject the request.
+		#
+	#	virtual_server = check-eap-tls
+	}
+
+
+	## EAP-TTLS
+	ttls {
+		tls = tls-common
+		default_eap_type = mschapv2
+
+		# This seems to effectively destroy the user.name propagation for session logging --> Disabled
+		# However, if it is disabled the authentication does not work properly
+		copy_request_to_tunnel = yes
+		use_tunneled_reply = yes
+
+		virtual_server = "inner-tunnel"
+
+
+		#
+		# Unlike EAP-TLS, EAP-TTLS does not require a client
+		# certificate. However, you can require one by setting the
+		# following option. You can also override this option by
+		# setting
+		#
+		#	EAP-TLS-Require-Client-Cert = Yes
+		#
+		# in the control items for a request.
+		#
+	#	require_client_cert = yes
+	}
+
+
+	## EAP-PEAP
+ 	peap {
+ 		tls = tls-common
+ 		default_eap_type = tls #mschapv2
+
+		# This seems to effectively destroy the user.name propagation for session logging --> Disabled
+		# However, if it is disabled the authentication does not work properly
+		copy_request_to_tunnel = yes
+ 		use_tunneled_reply = yes
+ 
+ 		virtual_server = "inner-tunnel"
+ 
+ 		# This option enables support for MS-SoH
+ 		# see doc/SoH.txt for more info.
+ 		# It is disabled by default.
+ 		#
+ 	#	soh = yes
+ 	#	soh_virtual_server = "soh-server"
+ 
+ 		#
+ 		# Unlike EAP-TLS, PEAP does not require a client certificate.
+ 		# However, you can require one by setting the following
+ 		# option. You can also override this option by setting
+ 		#
+ 		#	EAP-TLS-Require-Client-Cert = Yes
+ 		#
+ 		# in the control items for a request.
+ 		#
+ 	#	require_client_cert = yes
+ 	}
+
+	mschapv2 {
+		identity = "{{ radius_mschapv2_identity }}"
+	}
+}
diff --git a/radius-server/templates/mods-available/ldap.j2 b/radius-server/templates/mods-available/ldap.j2
new file mode 100644
index 0000000000000000000000000000000000000000..135c21e6972bcabdb051f2217d7a8b8fcc91c7f2
--- /dev/null
+++ b/radius-server/templates/mods-available/ldap.j2
@@ -0,0 +1,362 @@
+# -*- text -*-
+#
+#  $Id: 0a1cf0221a9cbc95001d86d2ba3b16ac99330471 $
+
+#
+#  Lightweight Directory Access Protocol (LDAP)
+#
+ldap {
+	#  Note that this needs to match the name(s) in the LDAP server
+	#  certificate, if you're using ldaps.  See OpenLDAP documentation
+	#  for the behavioral semantics of specifying more than one host.
+	#
+	#  Depending on the libldap in use, server may be an LDAP URI.
+	#  In the case of OpenLDAP this allows additional the following
+	#  additional schemes:
+	#  - ldaps:// (LDAP over SSL)
+	#  - ldapi:// (LDAP over Unix socket)
+	#  - ldapc:// (Connectionless LDAP)
+	server = '{{ radius_ldap_server[ansible_fqdn] }}'
+
+	#  Port to connect on, defaults to 389, will be ignored for LDAP URIs.
+#	port = 389
+
+	identity = '{{ radius_ldap_bind_user }}'
+	password = '{{ radius_ldap_bind_pass }}'
+
+	base_dn = '{{ radius_ldap_base_dn }}'
+
+	sasl {
+	}
+
+
+	#
+	#  Mapping of LDAP directory attributes to RADIUS dictionary attributes.
+	#
+
+	#  WARNING: Although this format is almost identical to the unlang
+	#  update section format, it does *NOT* mean that you can use other
+	#  unlang constructs in module configuration files.
+	#
+	#  Configuration items are in the format:
+	# 	<radius attr> <op> <ldap attr>
+	#
+	#  Where:
+	#  	<radius attr>:	Is the destination RADIUS attribute
+	# 			with any valid list and request qualifiers.
+	#  	<op>: 		Is any assignment attribute (=, :=, +=, -=).
+	#  	<ldap attr>:	Is the attribute associated with user or
+	#			profile objects in the LDAP directory.
+	# 			If the attribute name is wrapped in double
+	# 			quotes it will be xlat expanded.
+	#
+	#  Request and list qualifiers may also be placed after the 'update'
+	#  section name to set defaults destination requests/lists
+	#  for unqualified RADIUS attributes.
+	#
+	#  Note: LDAP attribute names should be single quoted unless you want
+	#  the name value to be derived from an xlat expansion, or an
+	#  attribute ref.
+	update {
+		control:Password-With-Header	+= 'userPassword'
+#		control:NT-Password		:= 'ntPassword'
+#		reply:Reply-Message		:= 'radiusReplyMessage'
+#		reply:Tunnel-Type		:= 'radiusTunnelType'
+#		reply:Tunnel-Medium-Type	:= 'radiusTunnelMediumType'
+#		reply:Tunnel-Private-Group-ID	:= 'radiusTunnelPrivategroupId'
+
+		#  Where only a list is specified as the RADIUS attribute,
+		#  the value of the LDAP attribute is parsed as a valuepair
+		#  in the same format as the 'valuepair_attribute' (above).
+		control:			+= 'radiusControlAttribute'
+		request:			+= 'radiusRequestAttribute'
+		reply:				+= 'radiusReplyAttribute'
+	}
+
+	#  Set to yes if you have eDirectory and want to use the universal
+	#  password mechanism.
+#	edir = no
+
+	#  Set to yes if you want to bind as the user after retrieving the
+	#  Cleartext-Password. This will consume the login grace, and
+	#  verify user authorization.
+#	edir_autz = no
+
+	#  Note: set_auth_type was removed in v3.x.x
+	#  Equivalent functionality can be achieved by adding the following
+	#  stanza to the authorize {} section of your virtual server.
+	#
+	#    ldap
+	#    if ((ok || updated) && User-Password) {
+	#        update {
+	#            control:Auth-Type := ldap
+	#        }
+	#    }
+
+	#
+	#  User object identification.
+	#
+	user {
+		base_dn = "${..base_dn}"
+{% raw %}
+		filter = "(sAMAccountName=%{%{Stripped-User-Name}:-%{User-Name}})"
+{% endraw %}
+		sasl {
+			# SASL mechanism
+#			mech = 'PLAIN'
+
+			# SASL authorisation identity to proxy.
+#			proxy = &User-Name
+
+			# SASL realm. Used for kerberos.
+#			realm = 'example.org'
+		}
+
+#		scope = 'sub'
+	}
+
+	#
+	#  User membership checking.
+	#
+	group {
+		#  Where to start searching in the tree for groups
+		base_dn = "${..base_dn}"
+
+		#  Filter for group objects, should match all available
+		#  group objects a user might be a member of.
+		filter = '(objectClass=group)'
+
+		# Search scope, may be 'base', 'one', sub' or 'children'
+#		scope = 'sub'
+
+		name_attribute = cn
+		
+{% raw %}
+#		membership_filter = "(|(member=%{control:Ldap-UserDn})(memberUid=%{%{Stripped-User-Name}:-%{User-Name}}))"
+		membership_filter = "(&(objectcategory=group)(member:1.2.840.113556.1.4.1941:=%{control:Ldap-UserDn}))"
+{% endraw %}
+		membership_attribute = 'memberOf'
+	}
+
+	#
+	#  User profiles. RADIUS profile objects contain sets of attributes
+	#  to insert into the request. These attributes are mapped using
+	#  the same mapping scheme applied to user objects (the update section above).
+	#
+	profile {
+	}
+
+	#
+	#  Bulk load clients from the directory
+	#
+	client {
+		base_dn = "${..base_dn}"
+		filter = '(objectClass=radiusClient)'
+
+#		scope = 'sub'
+		template {
+#			login				= 'test'
+#			password			= 'test'
+#			proto	 			= tcp
+#			require_message_authenticator	= yes
+
+			# Uncomment to add a home_server with the same
+			# attributes as the client.
+#			coa_server {
+#				response_window = 2.0
+#			}
+		}
+
+		attribute {
+			ipaddr				= 'radiusClientIdentifier'
+			secret				= 'radiusClientSecret'
+#			shortname			= 'radiusClientShortname'
+#			nas_type			= 'radiusClientType'
+#			virtual_server			= 'radiusClientVirtualServer'
+#			require_message_authenticator	= 'radiusClientRequireMa'
+		}
+	}
+
+	#  Load clients on startup
+	read_clients = no
+
+	#
+	#  Modify user object on receiving Accounting-Request
+	#
+
+	accounting {
+	}
+
+	#
+	#  Post-Auth can modify LDAP objects too
+	#
+#	post-auth {
+#		update {
+#			description := "Authenticated at %S"
+#		}
+#	}
+
+	#
+	#  LDAP connection-specific options.
+	#
+	#  These options set timeouts, keep-alives, etc. for the connections.
+	#
+	options {
+		#  Control under which situations aliases are followed.
+		#  May be one of 'never', 'searching', 'finding' or 'always'
+		#  default: libldap's default which is usually 'never'.
+		#
+		#  LDAP_OPT_DEREF is set to this value.
+#		dereference = 'always'
+
+		#
+		#  The following two configuration items control whether the
+		#  server follows references returned by LDAP directory.
+		#  They are  mostly for Active Directory compatibility.
+		#  If you set these to 'no', then searches will likely return
+		#  'operations error', instead of a useful result.
+		#
+		chase_referrals = yes
+		rebind = yes
+
+		#  Seconds to wait for LDAP query to finish. default: 20
+		res_timeout = 10
+
+		#  Seconds LDAP server has to process the query (server-side
+		#  time limit). default: 20
+		#
+		#  LDAP_OPT_TIMELIMIT is set to this value.
+		srv_timelimit = 3
+
+		#  Seconds to wait for response of the server. (network
+		#  failures) default: 10
+		#
+		#  LDAP_OPT_NETWORK_TIMEOUT is set to this value.
+		net_timeout = 1
+
+		#  LDAP_OPT_X_KEEPALIVE_IDLE
+		idle = 60
+
+		#  LDAP_OPT_X_KEEPALIVE_PROBES
+		probes = 3
+
+		#  LDAP_OPT_X_KEEPALIVE_INTERVAL
+		interval = 3
+
+		#  ldap_debug: debug flag for LDAP SDK
+		#  (see OpenLDAP documentation).  Set this to enable
+		#  huge amounts of LDAP debugging on the screen.
+		#  You should only use this if you are an LDAP expert.
+		#
+		#	default: 0x0000 (no debugging messages)
+		#	Example:(LDAP_DEBUG_FILTER+LDAP_DEBUG_CONNS)
+		ldap_debug = 0x0028
+	}
+
+	#
+	#  This subsection configures the tls related items
+	#  that control how FreeRADIUS connects to an LDAP
+	#  server.  It contains all of the 'tls_*' configuration
+	#  entries used in older versions of FreeRADIUS.  Those
+	#  configuration entries can still be used, but we recommend
+	#  using these.
+	#
+	tls {
+		# Set this to 'yes' to use TLS encrypted connections
+		# to the LDAP database by using the StartTLS extended
+		# operation.
+		#
+		# The StartTLS operation is supposed to be
+		# used with normal ldap connections instead of
+		# using ldaps (port 636) connections
+		start_tls = yes
+
+		ca_file	= ${certdir}/cacert.pem
+
+#		ca_path	= ${certdir}
+#		certificate_file = /path/to/radius.crt
+#		private_key_file = /path/to/radius.key
+#		random_file = /dev/urandom
+
+ 		#  Certificate Verification requirements.  Can be:
+		#    'never' (do not even bother trying)
+ 		#    'allow' (try, but don't fail if the certificate
+		#		cannot be verified)
+		#    'demand' (fail if the certificate does not verify)
+		#    'hard'  (similar to 'demand' but fails if TLS
+		#             cannot negotiate)
+ 		#
+		#  The default is libldap's default, which varies based
+		#  on the contents of ldap.conf.
+
+		require_cert	= 'demand'
+	}
+
+	#  As of version 3.0, the 'pool' section has replaced the
+	#  following configuration items:
+	#
+	#  ldap_connections_number
+
+	#  The connection pool is new for 3.0, and will be used in many
+	#  modules, for all kinds of connection-related activity.
+	#
+	#  When the server is not threaded, the connection pool
+	#  limits are ignored, and only one connection is used.
+	pool {
+		#  Connections to create during module instantiation.
+		#  If the server cannot create specified number of
+		#  connections during instantiation it will exit.
+		#  Set to 0 to allow the server to start without the
+		#  directory being available.
+		start = ${thread[pool].start_servers}
+
+		#  Minimum number of connections to keep open
+		min = ${thread[pool].min_spare_servers}
+
+		#  Maximum number of connections
+		#
+		#  If these connections are all in use and a new one
+		#  is requested, the request will NOT get a connection.
+		#
+		#  Setting 'max' to LESS than the number of threads means
+		#  that some threads may starve, and you will see errors
+		#  like 'No connections available and at max connection limit'
+		#
+		#  Setting 'max' to MORE than the number of threads means
+		#  that there are more connections than necessary.
+		max = ${thread[pool].max_servers}
+
+		#  Spare connections to be left idle
+		#
+		#  NOTE: Idle connections WILL be closed if "idle_timeout"
+		#  is set.  This should be less than or equal to "max" above.
+		spare = ${thread[pool].max_spare_servers}
+
+		#  Number of uses before the connection is closed
+		#
+		#  0 means "infinite"
+		uses = 0
+
+		#  The number of seconds to wait after the server tries
+		#  to open a connection, and fails.  During this time,
+		#  no new connections will be opened.
+		retry_delay = 30
+
+		#  The lifetime (in seconds) of the connection
+		lifetime = 0
+
+		#  Idle timeout (in seconds).  A connection which is
+		#  unused for this length of time will be closed.
+		idle_timeout = 60
+
+		#  NOTE: All configuration settings are enforced.  If a
+		#  connection is closed because of 'idle_timeout',
+		#  'uses', or 'lifetime', then the total number of
+		#  connections MAY fall below 'min'.  When that
+		#  happens, it will open a new connection.  It will
+		#  also log a WARNING message.
+		#
+		#  The solution is to either lower the 'min' connections,
+		#  or increase lifetime/idle_timeout.
+	}
+}
diff --git a/radius-server/templates/mods-available/mschap.j2 b/radius-server/templates/mods-available/mschap.j2
new file mode 100644
index 0000000000000000000000000000000000000000..609d3e81820b4e8108afbff4a7dd38c794b14888
--- /dev/null
+++ b/radius-server/templates/mods-available/mschap.j2
@@ -0,0 +1,111 @@
+# -*- text -*-
+#
+#  $Id: 4673fa7f9fd1d9931fcf1e4e1cdd9bb656b1d434 $
+
+# Microsoft CHAP authentication
+#
+#  This module supports MS-CHAP and MS-CHAPv2 authentication.
+#  It also enforces the SMB-Account-Ctrl attribute.
+#
+mschap {
+	with_ntdomain_hack=yes
+
+	# The module can perform authentication itself, OR
+	# use a Windows Domain Controller.  This configuration
+	# directive tells the module to call the ntlm_auth
+	# program, which will do the authentication, and return
+	# the NT-Key.  Note that you MUST have "winbindd" and
+	# "nmbd" running on the local machine for ntlm_auth
+	# to work.  See the ntlm_auth program documentation
+	# for details.
+	#
+	# If ntlm_auth is configured below, then the mschap
+	# module will call ntlm_auth for every MS-CHAP
+	# authentication request.  If there is a cleartext
+	# or NT hashed password available, you can set
+	# "MS-CHAP-Use-NTLM-Auth := No" in the control items,
+	# and the mschap module will do the authentication itself,
+	# without calling ntlm_auth.
+	#
+	# Be VERY careful when editing the following line!
+	#
+	# You can also try setting the user name as:
+	#
+	#	... --username=%{mschap:User-Name} ...
+	#
+	# In that case, the mschap module will look at the User-Name
+	# attribute, and do prefix/suffix checks in order to obtain
+	# the "best" user name for the request.
+	#
+{% raw %}
+	#ntlm_auth = "/usr/bin/ntlm_auth --request-nt-key --username=%{%{Stripped-User-Name}:-%{%{User-Name}:-None}} --challenge=%{%{mschap:Challenge}:-00} --nt-response=%{%{mschap:NT-Response}:-00}"
+	ntlm_auth = "/usr/bin/ntlm_auth --request-nt-key --username=%{mschap:User-Name} --challenge=%{%{mschap:Challenge}:-00} --nt-response=%{%{mschap:NT-Response}:-00}"
+{% endraw %}
+
+	# The default is to wait 10 seconds for ntlm_auth to
+	# complete.  This is a long time, and if it's taking that
+	# long then you likely have other problems in your domain.
+	# The length of time can be decreased with the following
+	# option, which can save clients waiting if your ntlm_auth
+	# usually finishes quicker. Range 1 to 10 seconds.
+	#
+#	ntlm_auth_timeout = 10
+
+	# An alternative to using ntlm_auth is to connect to the
+	# winbind daemon directly for authentication. This option
+	# is likely to be faster and may be useful on busy systems,
+	# but is less well tested.
+	#
+	# Using this option requires libwbclient from Samba 4.2.1
+	# or later to be installed. Make sure that ntlm_auth above is
+	# commented out.
+	#
+#	winbind_username = "%{mschap:User-Name}"
+#	winbind_domain = "%{mschap:NT-Domain}"
+
+	#
+	#  Information for the winbind connection pool.  The configuration
+	#  items below are the same for all modules which use the new
+	#  connection pool.
+	#
+	pool {
+		start = ${thread[pool].start_servers}
+		min = ${thread[pool].min_spare_servers}
+		max = ${thread[pool].max_servers}
+
+		spare = ${thread[pool].max_spare_servers}
+
+		uses = 0
+		retry_delay = 30
+		lifetime = 86400
+		cleanup_interval = 300
+		idle_timeout = 600
+	}
+
+	passchange {
+		# This support MS-CHAPv2 (not v1) password change
+		# requests.  See doc/mschap.rst for more IMPORTANT
+		# information.
+		#
+		# Samba/ntlm_auth - if you are using ntlm_auth to
+		# validate passwords, you will need to use ntlm_auth
+		# to change passwords.  Uncomment the three lines
+		# below, and change the path to ntlm_auth.
+		#
+#		ntlm_auth = "/usr/bin/ntlm_auth --helper-protocol=ntlm-change-password-1"
+#		ntlm_auth_username = "username: %{mschap:User-Name}"
+#		ntlm_auth_domain = "nt-domain: %{mschap:NT-Domain}"
+
+		# To implement a local password change, you need to
+		# supply a string which is then expanded, so that the
+		# password can be placed somewhere.  e.g. passed to a
+		# script (exec), or written to SQL (UPDATE/INSERT).
+		# We give both examples here, but only one will be
+		# used.
+		#
+#		local_cpw = "%{exec:/path/to/script %{mschap:User-Name} %{MS-CHAP-New-Cleartext-Password}}"
+		#
+#		local_cpw = "%{sql:UPDATE radcheck set value='%{MS-CHAP-New-NT-Password}' where username='%{SQL-User-Name}' and attribute='NT-Password'}"
+	}
+
+}
diff --git a/radius-server/templates/mods-available/ntlm_auth.j2 b/radius-server/templates/mods-available/ntlm_auth.j2
new file mode 100644
index 0000000000000000000000000000000000000000..5029a42ecbdc8989c953278d491e4171536b3ed4
--- /dev/null
+++ b/radius-server/templates/mods-available/ntlm_auth.j2
@@ -0,0 +1,12 @@
+#
+#  For testing ntlm_auth authentication with PAP.
+#
+#  If you have problems with authentication failing, even when the
+#  password is good, it may be a bug in Samba:
+#
+#	https://bugzilla.samba.org/show_bug.cgi?id=6563
+#
+exec ntlm_auth {
+	wait = yes
+	program = "/usr/bin/ntlm_auth --request-nt-key --domain={{ radius_ntlm_domain }} --username=%{mschap:User-Name} --password=%{User-Password}"
+}
diff --git a/radius-server/templates/mods-available/realm.j2 b/radius-server/templates/mods-available/realm.j2
new file mode 100644
index 0000000000000000000000000000000000000000..93c65ca3e832f2388e6577ff29733aa99aa5d9ea
--- /dev/null
+++ b/radius-server/templates/mods-available/realm.j2
@@ -0,0 +1,56 @@
+# -*- text -*-
+#
+#  $Id: b4c8ee3d8534ece75f6129d4853e6bc081cf0aa5 $
+
+# Realm module, for proxying.
+#
+#  You can have multiple instances of the realm module to
+#  support multiple realm syntaxes at the same time.  The
+#  search order is defined by the order that the modules are listed
+#  in the authorize and preacct sections.
+#
+#  Four config options:
+#	format	 -  must be "prefix" or "suffix"
+#			  The special cases of "DEFAULT"
+#			  and "NULL" are allowed, too.
+#	delimiter      -  must be a single character
+
+#  'realm/username'
+#
+#  Using this entry, IPASS users have their realm set to "IPASS".
+realm IPASS {
+	format = prefix
+	delimiter = "/"
+}
+
+#  'username@realm'
+#
+realm suffix {
+	format = suffix
+	delimiter = "@"
+
+	ignore_null = yes
+	# The next 3 configuration items are valid ONLY
+	# for a trust-router.  For all other realms,
+	# they are ignored.
+#	trust_router = "localhost"
+#	rp_realm = "painless-security.com"
+#	default_community = "apc.moonshot.ja.net"
+}
+
+#  'username%realm'
+#
+realm realmpercent {
+	format = suffix
+	delimiter = "%"
+}
+
+#
+#  'domain\user'
+#
+realm ntdomain {
+	format = prefix
+	delimiter = "\\"
+
+	ignore_null = yes
+}
diff --git a/radius-server/templates/mods-config/files/authorize.j2 b/radius-server/templates/mods-config/files/authorize.j2
new file mode 100644
index 0000000000000000000000000000000000000000..1b93cecc1be3deb428fa31d356e3c59be311333b
--- /dev/null
+++ b/radius-server/templates/mods-config/files/authorize.j2
@@ -0,0 +1,49 @@
+#
+# 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."
+
+# old MAC auth user types
+
+
+#######################################################
+#                      DEFAULT
+#
+DEFAULT		User-Name =~ "^[Aa][Nn][Oo][Nn][Yy][Mm][Oo][Uu][Ss]$"
+		Auth-Type := Reject
+
+DEFAULT		User-Name =~ "^[Aa][Nn][Oo][Nn][Yy][Mm][Oo][Uu][Ss]@.*$"
+		Auth-Type := EAP
+
+DEFAULT		Realm == NULL, Auth-Type := Reject
+
+DEFAULT		Realm == {{ radius_default_realm }}, FreeRADIUS-Proxied-To == 127.0.0.1
+		User-Name = '%{User-Name}',
+		Fall-Through = yes
+
+{%- for assign in radius_vlan_assignments %}
+DEFAULT   {{ assign.key }} {{ assign.condition }} {{ assign.value }}
+		Tunnel-Type = VLAN,
+		Tunnel-Medium-Type = IEEE-802,
+		Tunnel-Private-Group-Id = {{ assign.vlan }},
+		Fall-Through = yes
+{% endfor -%}
+
+#
+# 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."
+#
+
+### Allow authentication with these certificates
+
diff --git a/radius-server/templates/policy.d/ntlm_auth.j2 b/radius-server/templates/policy.d/ntlm_auth.j2
new file mode 100644
index 0000000000000000000000000000000000000000..ac869e0395c984eb869190f102a887a46fc470cf
--- /dev/null
+++ b/radius-server/templates/policy.d/ntlm_auth.j2
@@ -0,0 +1,10 @@
+# Give the ntlm_auth exec module an "authorize" method that sets Auth-Type
+# to itself but only if it's a valid PAP request, and Auth-Type is not
+# already set to something
+ntlm_auth.authorize {
+    if (!control:Auth-Type && User-Password) {
+        update control {
+            Auth-Type := ntlm_auth
+        }
+    }
+}
diff --git a/radius-server/templates/policy.d/rewrite_station_to_ssid.j2 b/radius-server/templates/policy.d/rewrite_station_to_ssid.j2
new file mode 100644
index 0000000000000000000000000000000000000000..2d6a47833490442da0820d74b35f3a7920f67537
--- /dev/null
+++ b/radius-server/templates/policy.d/rewrite_station_to_ssid.j2
@@ -0,0 +1,13 @@
+# Source: http://wiki.freeradius.org/guide/Mac-Auth
+#
+rewrite_called_station_to_ssid {
+	if(Called-Station-Id =~ /^([0-9a-f]{2})[-:]?([0-9a-f]{2})[-:.]?([0-9a-f]{2})[-:]?([0-9a-f]{2})[-:.]?([0-9a-f]{2})[-:]?([0-9a-f]{2})[-:]?([-a-z0-9_. ]*)?/i){
+                update request {
+                        Called-Station-Id := "%{1}%{2}%{3}%{4}%{5}%{6}"
+                        Called-Station-SSID := "%{7}"
+                }
+        }
+        else {
+                noop
+        }
+}
diff --git a/radius-server/templates/proxy.conf.j2 b/radius-server/templates/proxy.conf.j2
new file mode 100644
index 0000000000000000000000000000000000000000..26857b4f5899155bb512723ab75c6147dd3d9ca5
--- /dev/null
+++ b/radius-server/templates/proxy.conf.j2
@@ -0,0 +1,825 @@
+# -*- text -*-
+##
+## proxy.conf -- proxy radius and realm configuration directives
+##
+##	$Id: a72beebf52d791616a09ebd69dd7ea9349597363 $
+
+#######################################################################
+#
+#  Proxy server configuration
+#
+#  This entry controls the servers behaviour towards ALL other servers
+#  to which it sends proxy requests.
+#
+proxy server {
+	#
+	#  Note that as of 2.0, the "synchronous", "retry_delay",
+	#  "retry_count", and "dead_time" have all been deprecated.
+	#  For backwards compatibility, they are are still accepted
+	#  by the server, but they ONLY apply to the old-style realm
+	#  configuration.  i.e. realms with "authhost" and/or "accthost"
+	#  entries.
+	#
+	#  i.e. "retry_delay" and "retry_count" have been replaced
+	#  with per-home-server configuration.  See the "home_server"
+	#  example below for details.
+	#
+	#  i.e. "dead_time" has been replaced with a per-home-server
+	#  "revive_interval".  We strongly recommend that this not
+	#  be used, however.  The new method is much better.
+
+	#
+	#  In 2.0, the server is always "synchronous", and setting
+	#  "synchronous = no" is impossible.  This simplifies the
+	#  server and increases the stability of the network.
+	#  However, it means that the server (i.e. proxy) NEVER
+	#  originates packets.  It proxies packets ONLY when it receives
+	#  a packet or a re-transmission from the NAS.  If the NAS never
+	#  re-transmits, the proxy never re-transmits, either.  This can
+	#  affect fail-over, where a packet does *not* fail over to a
+	#  second home server.. because the NAS never retransmits the
+	#  packet.
+	#
+	#  If you need to set "synchronous = no", please send a
+	#  message to the list <freeradius-users@lists.freeradius.org>
+	#  explaining why this feature is vital for your network.
+
+	#
+	#  If a realm exists, but there are no live home servers for
+	#  it, we can fall back to using the "DEFAULT" realm.  This is
+	#  most useful for accounting, where the server can proxy
+	#  accounting requests to home servers, but if they're down,
+	#  use a DEFAULT realm that is LOCAL (i.e. accthost = LOCAL),
+	#  and then store the packets in the "detail" file.  That data
+	#  can be later proxied to the home servers by radrelay, when
+	#  those home servers come back up again.
+
+	#  Setting this to "yes" may have issues for authentication.
+	#  i.e. If you are proxying for two different ISP's, and then
+	#  act as a general dial-up for Gric.  If one of the first two
+	#  ISP's has their RADIUS server go down, you do NOT want to
+	#  proxy those requests to GRIC.  Instead, you probably want
+	#  to just drop the requests on the floor.  In that case, set
+	#  this value to 'no'.
+	#
+	#  allowed values: {yes, no}
+	#
+	default_fallback = no
+
+}
+
+#######################################################################
+#
+#  Configuration for the proxy realms.
+#
+#  As of 2.0, the "realm" configuration has changed.  Instead of
+#  specifying "authhost" and "accthost" in a realm section, the home
+#  servers are specified separately in a "home_server" section.  For
+#  backwards compatibility, you can still use the "authhost" and
+#  "accthost" directives.  If you only have one home server for a
+#  realm, it is easier to use the old-style configuration.
+#
+#  However, if you have multiple servers for a realm, we STRONGLY
+#  suggest moving to the new-style configuration.
+#
+#
+#  Load-balancing and failover between home servers is handled via
+#  a "home_server_pool" section.
+#
+#  Finally, The "realm" section defines the realm, some options, and
+#  indicates which server pool should be used for the realm.
+#
+#  This change means that simple configurations now require multiple
+#  sections to define a realm.  However, complex configurations
+#  are much simpler than before, as multiple realms can share the same
+#  server pool.
+#
+#  That is, realms point to server pools, and server pools point to
+#  home servers.  Multiple realms can point to one server pool.  One
+#  server pool can point to multiple home servers.  Each home server
+#  can appear in one or more pools.
+#
+#  See sites-available/tls for an example of configuring home servers,
+#  pools, and realms with TLS.
+#
+
+######################################################################
+#
+#  This section defines a "Home Server" which is another RADIUS
+#  server that gets sent proxied requests.  In earlier versions
+#  of FreeRADIUS, home servers were defined in "realm" sections,
+#  which was awkward.  In 2.0, they have been made independent
+#  from realms, which is better for a number of reasons.
+#
+home_server localhost {
+	#
+	#  Home servers can be sent Access-Request packets
+	#  or Accounting-Request packets.
+	#
+	#  Allowed values are:
+	#	auth	  - Handles Access-Request packets
+	#	acct	  - Handles Accounting-Request packets
+	#	auth+acct - Handles Access-Request packets at "port",
+	#		    and Accounting-Request packets at "port + 1"
+	#	coa	  - Handles CoA-Request and Disconnect-Request packets.
+	#		    See also raddb/sites-available/originate-coa
+	type = auth
+
+	#
+	#  Configure ONE OF the following entries:
+	#
+	#	IPv4 address
+	#
+	ipaddr = 127.0.0.1
+
+	#	OR IPv6 address
+	# ipv6addr = ::1
+
+	#	OR virtual server
+	# virtual_server = foo
+
+	#	Note that while both ipaddr and ipv6addr will accept
+	#	both addresses and host names, we do NOT recommend
+	#	using host names.  When you specify a host name, the
+	#	server has to do a DNS lookup to find the IP address
+	#	of the home server.  If the DNS server is slow or
+	#	unresponsive, it means that FreeRADIUS will NOT be
+	#	able to determine the address, and will therefore NOT
+	#	start.
+	#
+	#	Also, the mapping of host name to address is done ONCE
+	#	when the server starts.  If DNS is later updated to
+	#	change the address, FreeRADIUS will NOT discover that
+	#	until after a re-start, or a HUP.
+	#
+	#	If you specify a virtual_server here, then requests
+	#	will be proxied internally to that virtual server.
+	#	These requests CANNOT be proxied again, however.  The
+	#	intent is to have the local server handle packets
+	#	when all home servers are dead.
+	#
+	#	Requests proxied to a virtual server will be passed
+	#	through the pre-proxy and post-proxy sections, just
+	#	like any other request.  See also the sample "realm"
+	#	configuration, below.
+	#
+	#	None of the rest of the home_server configuration is used
+	#	for the "virtual_server" configuration.
+
+	#
+	#  The port to which packets are sent.
+	#
+	#  Usually 1812 for type "auth", and  1813 for type "acct".
+	#  Older servers may use 1645 and 1646.
+	#  Use 3799 for type "coa"
+	#
+	port = 1812
+
+	#
+	#  The transport protocol.
+	#
+	#  If unspecified, defaults to "udp", which is the traditional
+	#  RADIUS transport.  It may also be "tcp", in which case TCP
+	#  will be used to talk to this home server.
+	#
+	#  When home servers are put into pools, the pool can contain
+	#  home servers with both UDP and TCP transports.
+	#
+	#proto = udp
+
+	#
+	#  The shared secret use to "encrypt" and "sign" packets between
+	#  FreeRADIUS and the home server.
+	#
+	#  The secret can be any string, up to 8k characters in length.
+	#
+	#  Control codes can be entered vi octal encoding,
+	#	e.g. "\101\102" == "AB"
+	#  Quotation marks can be entered by escaping them,
+	#	e.g. "foo\"bar"
+	#  Spaces or other "special" characters can be entered
+	#  by putting quotes around the string.
+	#	e.g. "foo bar"
+	#	     "foo;bar"
+	#
+	secret = testing123
+
+	############################################################
+	#
+	#  The rest of the configuration items listed here are optional,
+	#  and do not have to appear in every home server definition.
+	#
+	############################################################
+
+	#
+	#  You can optionally specify the source IP address used when
+	#  proxying requests to this home server.  When the src_ipaddr
+	#  it set, the server will automatically create a proxy
+	#  listener for that IP address.
+	#
+	#  If you specify this field for one home server, you will
+	#  likely need to specify it for ALL home servers.
+	#
+	#  If you don't care about the source IP address, leave this
+	#  entry commented.
+	#
+#	src_ipaddr = 127.0.0.1
+
+	#
+	#  If the home server does not respond to a request within
+	#  this time, the server marks the request as timed out.
+	#  After "response_timeouts", the home server is marked
+	#  as being "zombie", and "zombie_period" starts.
+	#
+	#  The response window can be a number between 0.001 and 60.000
+	#  Values on the low end are discouraged, as they will likely
+	#  not work due to limitations of operating system timers.
+	#
+	#  The default response window is large because responses may
+	#  be slow, especially when proxying across the Internet.
+	#
+	#  Useful range of values: 5 to 60
+	response_window = 20
+
+	#
+	#  Start "zombie_period" after this many responses have
+	#  timed out.
+	#
+#	response_timeouts = 1
+
+	#
+	#  If you want the old behaviour of the server rejecting
+	#  proxied requests after "response_window" timeout, set
+	#  the following configuration item to "yes".
+	#
+	#  This configuration WILL be removed in a future release
+	#  If you believe you need it, email the freeradius-users
+	#  list, and explain why it should stay in the server.
+	#
+#	no_response_fail = no
+
+	#
+	#  If the home server does not respond to ANY packets during
+	#  the "zombie period", it will be considered to be dead.
+	#
+	#  A home server that is marked "zombie" will be used for
+	#  proxying as a low priority.  If there are live servers,
+	#  they will always be preferred to a zombie.  Requests will
+	#  be proxied to a zombie server ONLY when there are no
+	#  live servers.
+	#
+	#  Any request that is proxied to a home server will continue
+	#  to be sent to that home server until the home server is
+	#  marked dead.  At that point, it will fail over to another
+	#  server, if a live server is available.  If none is available,
+	#  then the "post-proxy-type fail" handler will be called.
+	#
+	#  If "status_check" below is something other than "none", then
+	#  the server will start sending status checks at the start of
+	#  the zombie period.  It will continue sending status checks
+	#  until the home server is marked "alive".
+	#
+	#  Useful range of values: 20 to 120
+	zombie_period = 40
+
+	############################################################
+	#
+	#  As of 2.0, FreeRADIUS supports RADIUS layer "status
+	#  checks".  These are used by a proxy server to see if a home
+	#  server is alive.
+	#
+	#  These status packets are sent ONLY if the proxying server
+	#  believes that the home server is dead.  They are NOT sent
+	#  if the proxying server believes that the home server is
+	#  alive.  They are NOT sent if the proxying server is not
+	#  proxying packets.
+	#
+	#  If the home server responds to the status check packet,
+	#  then it is marked alive again, and is returned to use.
+	#
+	############################################################
+
+	#
+	#  Some home servers do not support status checks via the
+	#  Status-Server packet.  Others may not have a "test" user
+	#  configured that can be used to query the server, to see if
+	#  it is alive.  For those servers, we have NO WAY of knowing
+	#  when it becomes alive again.  Therefore, after the server
+	#  has been marked dead, we wait a period of time, and mark
+	#  it alive again, in the hope that it has come back to
+	#  life.
+	#
+	#  If it has NOT come back to life, then FreeRADIUS will wait
+	#  for "zombie_period" before marking it dead again.  During
+	#  the "zombie_period", ALL AUTHENTICATIONS WILL FAIL, because
+	#  the home server is still dead.  There is NOTHING that can
+	#  be done about this, other than to enable the status checks,
+	#  as documented below.
+	#
+	#  e.g. if "zombie_period" is 40 seconds, and "revive_interval"
+	#  is 300 seconds, the for 40 seconds out of every 340, or about
+	#  10% of the time, all authentications will fail.
+	#
+	#  If the "zombie_period" and "revive_interval" configurations
+	#  are set smaller, than it is possible for up to 50% of
+	#  authentications to fail.
+	#
+	#  As a result, we recommend enabling status checks, and
+	#  we do NOT recommend using "revive_interval".
+	#
+	#  The "revive_interval" is used ONLY if the "status_check"
+	#  entry below is "none".  Otherwise, it will not be used,
+	#  and should be deleted.
+	#
+	#  Useful range of values: 60 to 3600
+	revive_interval = 120
+
+	#
+	#  The proxying server (i.e. this one) can do periodic status
+	#  checks to see if a dead home server has come back alive.
+	#
+	#  If set to "none", then the other configuration items listed
+	#  below are not used, and the "revive_interval" time is used
+	#  instead.
+	#
+	#  If set to "status-server", the Status-Server packets are
+	#  sent.  Many RADIUS servers support Status-Server.  If a
+	#  server does not support it, please contact the server
+	#  vendor and request that they add it. With status-server if
+	#  the home server is marked as a zombie and a status-server
+	#  response is received, it will be immediately marked as live.
+	#
+	#  This prevents spurious failovers in federations such as 
+	#  eduroam, where intermediary proxy servers may be functional
+	#  but the servers of a home institution may not be,
+	#
+	#  If set to "request", then Access-Request, or Accounting-Request
+	#  packets are sent, depending on the "type" entry above (auth/acct).
+	#
+	#  Allowed values: none, status-server, request
+	status_check = status-server
+
+	#
+	#  If the home server does not support Status-Server packets,
+	#  then the server can still send Access-Request or
+	#  Accounting-Request packets, with a pre-defined user name.
+	#
+	#  This practice is NOT recommended, as it may potentially let
+	#  users gain network access by using these "test" accounts!
+	#
+	#  If it is used, we recommend that the home server ALWAYS
+	#  respond to these Access-Request status checks with
+	#  Access-Reject.  The status check just needs an answer, it
+	#  does not need an Access-Accept.
+	#
+	#  For Accounting-Request status checks, only the username
+	#  needs to be set.  The rest of the accounting attribute are
+	#  set to default values.  The home server that receives these
+	#  accounting packets SHOULD NOT treat them like normal user
+	#  accounting packets.  i.e It should probably NOT log them to
+	#  a database.
+	#
+	# username = "test_user_please_reject_me"
+	# password = "this is really secret"
+
+	#
+	#  Configure the interval between sending status check packets.
+	#
+	#  Setting it too low increases the probability of spurious
+	#  fail-over and fallback attempts.
+	#
+	#  Useful range of values: 6 to 120
+	check_interval = 30
+
+	#
+	#  Wait "check_timeout" seconds for a reply to a status check
+	#  packet.
+	#
+	check_timeout = 4
+
+	#
+	#  Configure the number of status checks in a row that the
+	#  home server needs to respond to before it is marked alive.
+	#
+	#  If you want to mark a home server as alive after a short
+	#  time period of being responsive, it is best to use a small
+	#  "check_interval", and a large value for
+	#  "num_answers_to_alive".  Using a long "check_interval" and
+	#  a small number for "num_answers_to_alive" increases the
+	#  probability of spurious fail-over and fallback attempts.
+	#
+	#  Useful range of values: 3 to 10
+	num_answers_to_alive = 3
+
+	#
+	#  Limit the total number of outstanding packets to the home
+	#  server.
+	#
+	#  if ((#request sent) - (#requests received)) > max_outstanding
+	#	then stop sending more packets to the home server
+	#
+	#  This lets us gracefully fall over when the home server
+	#  is overloaded.
+	max_outstanding = 65536
+
+	#
+	#  The configuration items in the next sub-section are used ONLY
+	#  when "type = coa".  It is ignored for all other type of home
+	#  servers.
+	#
+	#  See RFC 5080 for the definitions of the following terms.
+	#  RAND is a function (internal to FreeRADIUS) returning
+	#  random numbers between -0.1 and +0.1
+	#
+	#  First Re-transmit occurs after:
+	#
+	#	 RT = IRT + RAND*IRT
+	#
+	#  Subsequent Re-transmits occur after:
+	#
+	#	RT = 2 * RTprev + RAND * RTprev
+	#
+	#  Re-transmits are capped at:
+	#
+	#	if (MRT && (RT > MRT)) RT = MRT + RAND * MRT
+	#
+	#  For a maximum number of attempts: MRC
+	#
+	#  For a maximum (total) period of time: MRD.
+	#
+	coa {
+		# Initial retransmit interval: 1..5
+		irt = 2
+
+		# Maximum Retransmit Timeout: 1..30 (0 == no maximum)
+		mrt = 16
+
+		# Maximum Retransmit Count: 1..20 (0 == retransmit forever)
+		mrc = 5
+
+		# Maximum Retransmit Duration: 5..60
+		mrd = 30
+	}
+
+	#
+	#  Connection limiting for home servers with "proto = tcp".
+	#
+	#  This section is ignored for other home servers.
+	#
+	limit {
+	      #
+	      #  Limit the number of TCP connections to the home server.
+	      #
+	      #  The default is 16.
+	      #  Setting this to 0 means "no limit"
+	      max_connections = 16
+
+	      #
+	      #  Limit the total number of requests sent over one
+	      #  TCP connection.  After this number of requests, the
+	      #  connection will be closed.  Any new packets that are
+	      #  proxied to the home server will result in a new TCP
+	      #  connection being made.
+	      #
+	      #  Setting this to 0 means "no limit"
+	      max_requests = 0
+
+	      #
+	      #  The lifetime, in seconds, of a TCP connection.  After
+	      #  this lifetime, the connection will be closed.
+	      #
+	      #  Setting this to 0 means "forever".
+	      lifetime = 0
+
+	      #
+	      #  The idle timeout, in seconds, of a TCP connection.
+	      #  If no packets have been sent over the connection for
+	      #  this time, the connection will be closed.
+	      #
+	      #  Setting this to 0 means "no timeout".
+	      idle_timeout = 0
+	}
+
+}
+
+# Sample virtual home server.
+#
+#
+#home_server virtual.example.com {
+#	    virtual_server = virtual.example.com
+#}
+
+######################################################################
+#
+#  This section defines a pool of home servers that is used
+#  for fail-over and load-balancing.  In earlier versions of
+#  FreeRADIUS, fail-over and load-balancing were defined per-realm.
+#  As a result, if a server had 5 home servers, each of which served
+#  the same 10 realms, you would need 50 "realm" entries.
+#
+#  In version 2.0, you would need 5 "home_server" sections,
+#  10 'realm" sections, and one "home_server_pool" section to tie the
+#  two together.
+#
+home_server_pool my_auth_failover {
+	#
+	#  The type of this pool controls how home servers are chosen.
+	#
+	#  fail-over - the request is sent to the first live
+	#  	home server in the list.  i.e. If the first home server
+	#	is marked "dead", the second one is chosen, etc.
+	#
+	#  load-balance - the least busy home server is chosen,
+	#	where "least busy" is counted by taking the number of
+	#	requests sent to that home server, and subtracting the
+	#	number of responses received from that home server.
+	#
+	#	If there are two or more servers with the same low
+	#	load, then one of those servers is chosen at random.
+	#	This configuration is most similar to the old
+	#	"round-robin" method, though it is not exactly the same.
+	#
+	#	Note that load balancing does not work well with EAP,
+	#	as EAP requires packets for an EAP conversation to be
+	#	sent to the same home server.  The load balancing method
+	#	does not keep state in between packets, meaning that
+	#	EAP packets for the same conversation may be sent to
+	#	different home servers.  This will prevent EAP from
+	#	working.
+	#
+	#	For non-EAP authentication methods, and for accounting
+	#	packets, we recommend using "load-balance".  It will
+	#	ensure the highest availability for your network.
+	#
+	#  client-balance - the home server is chosen by hashing the
+	#	source IP address of the packet.  If that home server
+	#	is down, the next one in the list is used, just as
+	#	with "fail-over".
+	#
+	#	There is no way of predicting which source IP will map
+	#	to which home server.
+	#
+	#	This configuration is most useful to do simple load
+	#	balancing for EAP sessions, as the EAP session will
+	#	always be sent to the same home server.
+	#
+	#  client-port-balance - the home server is chosen by hashing
+	#	the source IP address and source port of the packet.
+	#	If that home server is down, the next one in the list
+	#	is used, just as with "fail-over".
+	#
+	#	This method provides slightly better load balancing
+	#	for EAP sessions than "client-balance".  However, it
+	#	also means that authentication and accounting packets
+	#	for the same session MAY go to different home servers.
+	#
+	#  keyed-balance - the home server is chosen by hashing (FNV)
+	#	the contents of the Load-Balance-Key attribute from the
+	#	control items.  The  request is then sent to home server
+	#	chosen by taking:
+	#
+	#		server = (hash % num_servers_in_pool).
+	#
+	#	If there is no Load-Balance-Key in the control items,
+	#	the load balancing method is identical to "load-balance".
+	#
+	#	For most non-EAP authentication methods, The User-Name
+	#	attribute provides a good key.  An "unlang" policy can
+	#	be used to copy the User-Name to the Load-Balance-Key
+	#	attribute.  This method may not work for EAP sessions,
+	#	as the User-Name outside of the TLS tunnel is often
+	#	static, e.g. "anonymous@realm".
+	#
+	#
+	#  The default type is fail-over.
+	type = fail-over
+
+	#
+	#  A virtual_server may be specified here.  If so, the
+	#  "pre-proxy" and "post-proxy" sections are called when
+	#  the request is proxied, and when a response is received.
+	#
+	#  This lets you have one policy for all requests that are proxied
+	#  to a home server.  This policy is completely independent of
+	#  any policies used to receive, or process the request.
+	#
+	#virtual_server = pre_post_proxy_for_pool
+
+	#
+	#  Next, a list of one or more home servers.  The names
+	#  of the home servers are NOT the hostnames, but the names
+	#  of the sections.  (e.g. home_server foo {...} has name "foo".
+	#
+	#  Note that ALL home servers listed here have to be of the same
+	#  type.  i.e. they all have to be "auth", or they all have to
+	#  be "acct", or the all have to be "auth+acct".
+	#
+	home_server = localhost
+
+	#  Additional home servers can be listed.
+	#  There is NO LIMIT to the number of home servers that can
+	#  be listed, though using more than 10 or so will become
+	#  difficult to manage.
+	#
+	# home_server = foo.example.com
+	# home_server = bar.example.com
+	# home_server = baz.example.com
+	# home_server = ...
+
+
+	#
+	#  If ALL home servers are dead, then this "fallback" home server
+	#  is used.  If set, it takes precedence over any realm-based
+	#  fallback, such as the DEFAULT realm.
+	#
+	#  For reasons of stability, this home server SHOULD be a virtual
+	#  server.  Otherwise, the fallback may itself be dead!
+	#
+	#fallback = virtual.example.com
+}
+
+######################################################################
+#
+#
+#  This section defines a new-style "realm".  Note the in version 2.0,
+#  there are many fewer configuration items than in 1.x for a realm.
+#
+#  Automatic proxying is done via the "realms" module (see "man
+#  rlm_realm").  To manually proxy the request put this entry in the
+#  "users" file:
+
+#
+#
+#DEFAULT	Proxy-To-Realm := "realm_name"
+#
+#
+realm example.com {
+	#
+	#  Realms point to pools of home servers.
+#
+	#  For authentication, the "auth_pool" configuration item
+	#  should point to a "home_server_pool" that was previously
+	#  defined.  All of the home servers in the "auth_pool" must
+	#  be of type "auth".
+	#
+	#  For accounting, the "acct_pool" configuration item
+	#  should point to a "home_server_pool" that was previously
+	#  defined.  All of the home servers in the "acct_pool" must
+	#  be of type "acct".
+	#
+	#  If you have a "home_server_pool" where all of the home servers
+	#  are of type "auth+acct", you can just use the "pool"
+	#  configuration item, instead of specifying both "auth_pool"
+	#  and "acct_pool".
+
+	auth_pool = my_auth_failover
+#	acct_pool = acct
+
+	#  As of Version 3.0, the server can proxy CoA packets
+	#  based on the Operator-Name attribute.  This requires
+	#  that the "suffix" module be listed in the "recv-coa"
+	#  section.
+	#
+	#  See raddb/sites-available/coa
+	#
+#	coa_pool = name_of_coa_pool
+
+	#
+	#  Normally, when an incoming User-Name is matched against the
+	#  realm, the realm name is "stripped" off, and the "stripped"
+	#  user name is used to perform matches.
+	#
+	#  e.g. User-Name = "bob@example.com" will result in two new
+	#  attributes being created by the "realms" module:
+	#
+	#	Stripped-User-Name = "bob"
+	#	Realm = "example.com"
+	#
+	#  The Stripped-User-Name is then used as a key in the "users"
+	#  file, for example.
+	#
+	#  If you do not want this to happen, uncomment "nostrip" below.
+	#
+	# nostrip
+
+	#  There are no more configuration entries for a realm.
+}
+
+
+#
+#  This is a sample entry for iPass.
+#  Note that you have to define "ipass_auth_pool" and
+#  "ipass_acct_pool", along with home_servers for them, too.
+#
+#realm IPASS {
+#	nostrip
+#
+#	auth_pool = ipass_auth_pool
+#	acct_pool = ipass_acct_pool
+#}
+
+#
+#  This realm is used mainly to cancel proxying.  You can have
+#  the "realm suffix" module configured to proxy all requests for
+#  a realm, and then later cancel the proxying, based on other
+#  configuration.
+#
+#  For example, you want to terminate PEAP or EAP-TTLS locally,
+#  you can add the following to the "users" file:
+#
+#  DEFAULT EAP-Type == PEAP, Proxy-To-Realm := LOCAL
+#
+realm LOCAL {
+	#  If we do not specify a server pool, the realm is LOCAL, and
+	#  requests are not proxied to it.
+}
+
+{%- for realm in radius_realms %}
+realm {{ realm }} {
+}
+{% endfor -%}
+
+#
+#  This realm is for requests which don't have an explicit realm
+#  prefix or suffix.  User names like "bob" will match this one.
+#
+#realm NULL {
+#	authhost	= radius.company.com:1600
+#	accthost	= radius.company.com:1601
+#	secret		= testing123
+#}
+
+#
+#  This realm is for ALL OTHER requests.
+#
+#realm DEFAULT {
+#	authhost	= radius.company.com:1600
+#	accthost	= radius.company.com:1601
+#	secret		= testing123
+#}
+
+
+#  This realm "proxies" requests internally to a virtual server.
+#  The pre-proxy and post-proxy sections are run just as with any
+#  other kind of home server.  The virtual server then receives
+#  the request, and replies, just as with any other packet.
+#
+#  Once proxied internally like this, the request CANNOT be proxied
+#  internally or externally.
+#
+#realm virtual.example.com {
+#	virtual_server = virtual.example.com
+#}
+#
+
+#
+#  Regular expressions may also be used as realm names.  If these are used,
+#  then the "find matching realm" process is as follows:
+#
+#    1) Look for a non-regex realm with an *exact* match for the name.
+#       If found, it is used in preference to any regex matching realm.
+#
+#    2) Look for a regex realm, in the order that they are listed
+#       in the configuration files.  Any regex match is performed in
+#	a case-insensitive fashion.
+#
+#    3) If no realm is found, return the DEFAULT realm, if any.
+#
+#  The order of the realms matters in step (2).  For example, defining
+#  two realms ".*\.example.net$" and ".*\.test\.example\.net$" will result in
+#  the second realm NEVER matching.  This is because all of the realms
+#  which match the second regex also match the first one.  Since the
+#  first regex matches, it is returned.
+#
+#  The solution is to list the realms in the opposite order,. e.g.
+#  ".*\.test\.example.net$", followed by ".*\.example\.net$".
+#
+#
+#  Some helpful rules:
+#
+#   - always place a '~' character at the start of the realm name.
+#     This signifies that it is a regex match, and not an exact match
+#     for the realm.
+#
+#   - place the regex in double quotes.  This helps the configuration
+#     file parser ignore any "special" characters in the regex.
+#     Yes, this rule is different than the normal "unlang" rules for
+#     regular expressions.  That may be fixed in a future release.
+#
+#   - for version 3.0.4 and following, with "correct_escapes = true",
+#     use normal regex backslash rules.  Just one.  Not two.
+#
+#   - If you are matching domain names, put a '$' at the end of the regex
+#     that matches the domain name.  This tells the regex matching code
+#     that the realm ENDS with the domain name, so it does not match
+#     realms with the domain name in the middle.  e.g. "~.*\.example\.net"
+#     will match "test.example.netFOO", which is likely not what you want.
+#     Using "~(.*\.)example\.net$" is better.
+#
+#  The more regex realms that are defined, the more time it takes to
+#  process them.  You should define as few regex realms as possible
+#  in order to maximize server performance.
+#
+#realm "~(.*\.)*example\.net$" {
+#      auth_pool = my_auth_failover
+#}
diff --git a/radius-server/templates/sites-available/default.j2 b/radius-server/templates/sites-available/default.j2
new file mode 100644
index 0000000000000000000000000000000000000000..7b5a2db81f299912ee59752db2a8cb5bbf320e89
--- /dev/null
+++ b/radius-server/templates/sites-available/default.j2
@@ -0,0 +1,590 @@
+server default {
+listen {
+	#  Type of packets to listen for.
+	#  Allowed values are:
+	#	auth	listen for authentication packets
+	#	acct	listen for accounting packets
+	#	proxy   IP to use for sending proxied packets
+	#	detail  Read from the detail file.  For examples, see
+	#               raddb/sites-available/copy-acct-to-home-server
+	#	status  listen for Status-Server packets.  For examples,
+	#		see raddb/sites-available/status
+	#	coa     listen for CoA-Request and Disconnect-Request
+	#		packets.  For examples, see the file
+	#		raddb/sites-available/coa
+	#
+	type = auth
+
+
+	ipaddr = *
+	port = 0
+
+	#
+	#  Connection limiting for sockets with "proto = tcp".
+	#
+	limit {
+	      max_connections = 16
+	      lifetime = 0
+	      idle_timeout = 30
+	}
+}
+
+listen {
+	ipaddr = *
+	port = 0
+	type = acct
+
+	limit {
+	}
+}
+
+# IPv6 versions of the above - read their full config to understand options
+listen {
+	type = auth
+	ipv6addr = ::	# any.  ::1 == localhost
+	port = 0
+	limit {
+	      max_connections = 16
+	      lifetime = 0
+	      idle_timeout = 30
+	}
+}
+
+listen {
+	ipv6addr = ::
+	port = 0
+	type = acct
+
+	limit {
+	}
+}
+
+#  Authorization. First preprocess (hints and huntgroups files),
+#  then realms, and finally look in the "users" file.
+#
+#  Any changes made here should also be made to the "inner-tunnel"
+#  virtual server.
+#
+#  The order of the realm modules will determine the order that
+#  we try to find a matching realm.
+#
+#  Make *sure* that 'preprocess' comes before any realm if you
+#  need to setup hints for the remote radius server
+authorize {
+	#
+	#  Take a User-Name, and perform some checks on it, for spaces and other
+	#  invalid characters.  If the User-Name appears invalid, reject the
+	#  request.
+	#
+	#  See policy.d/filter for the definition of the filter_username policy.
+	#
+	filter_username
+
+	#
+	#  Some broken equipment sends passwords with embedded zeros.
+	#  i.e. the debug output will show
+	#
+	#	User-Password = "password\000\000"
+	#
+	#  This policy will fix it to just be "password".
+	#
+	filter_password
+
+	#
+	#  The preprocess module takes care of sanitizing some bizarre
+	#  attributes in the request, and turning them into attributes
+	#  which are more standard.
+	#
+	#  It takes care of processing the 'raddb/hints' and the
+	#  'raddb/huntgroups' files.
+	preprocess
+
+	# seperate AP MAC and SSID
+	rewrite_called_station_id
+
+	#
+	#  If you want to have a log of authentication requests,
+	#  un-comment the following line.
+	auth_log
+
+	#
+	#  If the users are logging in with an MS-CHAP-Challenge
+	#  attribute for authentication, the mschap module will find
+	#  the MS-CHAP-Challenge attribute, and add 'Auth-Type := MS-CHAP'
+	#  to the request, which will cause the server to then use
+	#  the mschap module for authentication.
+	mschap
+
+	pap
+
+	# extract realm
+	suffix
+	ntdomain
+
+	ntlm_auth
+
+	#
+	#  This module takes care of EAP-MD5, EAP-TLS, and EAP-LEAP
+	#  authentication.
+	#
+	#  It also sets the EAP-Type attribute in the request
+	#  attribute list to the EAP type from the packet.
+	#
+	#  The EAP module returns "ok" if it is not yet ready to
+	#  authenticate the user.  The configuration below checks for
+	#  that code, and stops processing the "authorize" section if
+	#  so.
+	#
+	#  Any LDAP and/or SQL servers will not be queried for the
+	#  initial set of packets that go back and forth to set up
+	#  TTLS or PEAP.
+	#
+	eap {
+		ok = return
+	}
+
+	#
+	#  Read the 'users' file.  In v3, this is located in
+	#  raddb/mods-config/files/authorize
+	files
+
+	#
+	#  Look in an SQL database.  The schema of the database
+	#  is meant to mirror the "users" file.
+	#
+	#  See "Authorization Queries" in mods-available/sql
+	-sql
+
+	#
+	#  The ldap module reads passwords from the LDAP database.
+	-ldap
+
+
+	#
+	expiration
+	logintime
+}
+
+
+#  Authentication.
+#
+#
+#  This section lists which modules are available for authentication.
+#  Note that it does NOT mean 'try each module in order'.  It means
+#  that a module from the 'authorize' section adds a configuration
+#  attribute 'Auth-Type := FOO'.  That authentication type is then
+#  used to pick the appropriate module from the list below.
+#
+
+#  In general, you SHOULD NOT set the Auth-Type attribute.  The server
+#  will figure it out on its own, and will do the right thing.  The
+#  most common side effect of erroneously setting the Auth-Type
+#  attribute is that one authentication method will work, but the
+#  others will not.
+#
+#  The common reasons to set the Auth-Type attribute by hand
+#  is to either forcibly reject the user (Auth-Type := Reject),
+#  or to or forcibly accept the user (Auth-Type := Accept).
+#
+#  Note that Auth-Type := Accept will NOT work with EAP.
+#
+#  Please do not put "unlang" configurations into the "authenticate"
+#  section.  Put them in the "post-auth" section instead.  That's what
+#  the post-auth section is for.
+#
+authenticate {
+	#
+	#  MSCHAP authentication.
+	Auth-Type MS-CHAP {
+		mschap
+	}
+
+	Auth-Type ntlm_auth {
+		ntlm_auth
+	}
+
+	#
+	#  Allow EAP authentication.
+	eap
+
+	#
+	#  The older configurations sent a number of attributes in
+	#  Access-Challenge packets, which wasn't strictly correct.
+	#  If you want to filter out these attributes, uncomment
+	#  the following lines.
+	#
+#	Auth-Type eap {
+#		eap {
+#			handled = 1
+#		}
+#		if (handled && (Response-Packet-Type == Access-Challenge)) {
+#			attr_filter.access_challenge.post-auth
+#			handled  # override the "updated" code from attr_filter
+#		}
+#	}
+}
+
+
+#
+#  Pre-accounting.  Decide which accounting type to use.
+#
+preacct {
+	preprocess
+
+	#
+	#  Merge Acct-[Input|Output]-Gigawords and Acct-[Input-Output]-Octets
+	#  into a single 64bit counter Acct-[Input|Output]-Octets64.
+	#
+#	acct_counters64
+
+	#
+	#  Session start times are *implied* in RADIUS.
+	#  The NAS never sends a "start time".  Instead, it sends
+	#  a start packet, *possibly* with an Acct-Delay-Time.
+	#  The server is supposed to conclude that the start time
+	#  was "Acct-Delay-Time" seconds in the past.
+	#
+	#  The code below creates an explicit start time, which can
+	#  then be used in other modules.  It will be *mostly* correct.
+	#  Any errors are due to the 1-second resolution of RADIUS,
+	#  and the possibility that the time on the NAS may be off.
+	#
+	#  The start time is: NOW - delay - session_length
+	#
+
+#	update request {
+#	  	FreeRADIUS-Acct-Session-Start-Time = "%{expr: %l - %{%{Acct-Session-Time}:-0} - %{%{Acct-Delay-Time}:-0}}"
+#	}
+
+
+	#
+	#  Ensure that we have a semi-unique identifier for every
+	#  request, and many NAS boxes are broken.
+	acct_unique
+
+	#
+	#  Look for IPASS-style 'realm/', and if not found, look for
+	#  '@realm', and decide whether or not to proxy, based on
+	#  that.
+	#
+	#  Accounting requests are generally proxied to the same
+	#  home server as authentication requests.
+#	IPASS
+	suffix
+
+	#
+	#  Read the 'acct_users' file
+	files
+}
+
+#
+#  Accounting.  Log the accounting data.
+#
+accounting {
+	#  Update accounting packet by adding the CUI attribute
+	#  recorded from the corresponding Access-Accept
+	#  use it only if your NAS boxes do not support CUI themselves
+#	cui
+	#
+	#  Create a 'detail'ed log of the packets.
+	#  Note that accounting requests which are proxied
+	#  are also logged in the detail file.
+	detail
+#	daily
+
+	#  Update the wtmp file
+	#
+	#  If you don't use "radlast", you can delete this line.
+	unix
+
+	#
+	#  For Simultaneous-Use tracking.
+	#
+	#  Due to packet losses in the network, the data here
+	#  may be incorrect.  There is little we can do about it.
+#	radutmp
+#	sradutmp
+
+	#  Return an address to the IP Pool when we see a stop record.
+#	main_pool
+
+	#
+	#  Log traffic to an SQL database.
+	#
+	#  See "Accounting queries" in mods-available/sql
+	-sql
+
+	#
+	#  If you receive stop packets with zero session length,
+	#  they will NOT be logged in the database.  The SQL module
+	#  will print a message (only in debugging mode), and will
+	#  return "noop".
+	#
+	#  You can ignore these packets by uncommenting the following
+	#  three lines.  Otherwise, the server will not respond to the
+	#  accounting request, and the NAS will retransmit.
+	#
+#	if (noop) {
+#		ok
+#	}
+
+	#
+	#  Instead of sending the query to the SQL server,
+	#  write it into a log file.
+	#
+#	sql_log
+
+	#  Cisco VoIP specific bulk accounting
+#	pgsql-voip
+
+	# For Exec-Program and Exec-Program-Wait
+	exec
+
+	#  Filter attributes from the accounting response.
+	attr_filter.accounting_response
+
+	#
+	#  See "Autz-Type Status-Server" for how this works.
+	#
+#	Acct-Type Status-Server {
+#
+#	}
+}
+
+
+#  Session database, used for checking Simultaneous-Use. Either the radutmp
+#  or rlm_sql module can handle this.
+#  The rlm_sql module is *much* faster
+session {
+#	radutmp
+
+	#
+	#  See "Simultaneous Use Checking Queries" in mods-available/sql
+#	sql
+}
+
+
+#  Post-Authentication
+#  Once we KNOW that the user has been authenticated, there are
+#  additional steps we can take.
+post-auth {
+	#
+	#  If you need to have a State attribute, you can
+	#  add it here.  e.g. for later CoA-Request with
+	#  State, and Service-Type = Authorize-Only.
+	#
+#	if (!&reply:State) {
+#		update reply {
+#			State := "0x%{randstr:16h}"
+#		}
+#	}
+
+	#
+	#  For EAP-TTLS and PEAP, add the cached attributes to the reply.
+	#  The "session-state" attributes are automatically cached when
+	#  an Access-Challenge is sent, and automatically retrieved
+	#  when an Access-Request is received.
+	#
+	#  The session-state attributes are automatically deleted after
+	#  an Access-Reject or Access-Accept is sent.
+	#
+	update {
+		&reply: += &session-state:
+	}
+
+	#  Get an address from the IP Pool.
+#	main_pool
+
+
+	#  Create the CUI value and add the attribute to Access-Accept.
+	#  Uncomment the line below if *returning* the CUI.
+#	cui
+
+	#
+	#  If you want to have a log of authentication replies,
+	#  un-comment the following line, and enable the
+	#  'detail reply_log' module.
+	reply_log
+
+	#
+	#  After authenticating the user, do another SQL query.
+	#
+	#  See "Authentication Logging Queries" in mods-available/sql
+	-sql
+
+	#
+	#  Instead of sending the query to the SQL server,
+	#  write it into a log file.
+	#
+#	sql_log
+
+	#
+	#  Un-comment the following if you want to modify the user's object
+	#  in LDAP after a successful login.
+	#
+#	ldap
+
+	# For Exec-Program and Exec-Program-Wait
+	exec
+
+	#  If there is a client certificate (EAP-TLS, sometimes PEAP
+	#  and TTLS), then some attributes are filled out after the
+	#  certificate verification has been performed.  These fields
+	#  MAY be available during the authentication, or they may be
+	#  available only in the "post-auth" section.
+	#
+	#  The first set of attributes contains information about the
+	#  issuing certificate which is being used.  The second
+	#  contains information about the client certificate (if
+	#  available).
+#
+#	update reply {
+#	       Reply-Message += "%{TLS-Cert-Serial}"
+#	       Reply-Message += "%{TLS-Cert-Expiration}"
+#	       Reply-Message += "%{TLS-Cert-Subject}"
+#	       Reply-Message += "%{TLS-Cert-Issuer}"
+#	       Reply-Message += "%{TLS-Cert-Common-Name}"
+#	       Reply-Message += "%{TLS-Cert-Subject-Alt-Name-Email}"
+#
+#	       Reply-Message += "%{TLS-Client-Cert-Serial}"
+#	       Reply-Message += "%{TLS-Client-Cert-Expiration}"
+#	       Reply-Message += "%{TLS-Client-Cert-Subject}"
+#	       Reply-Message += "%{TLS-Client-Cert-Issuer}"
+#	       Reply-Message += "%{TLS-Client-Cert-Common-Name}"
+#	       Reply-Message += "%{TLS-Client-Cert-Subject-Alt-Name-Email}"
+#	}
+
+	#  Insert class attribute (with unique value) into response,
+	#  aids matching auth and acct records, and protects against duplicate
+	#  Acct-Session-Id. Note: Only works if the NAS has implemented
+	#  RFC 2865 behaviour for the class attribute, AND if the NAS
+	#  supports long Class attributes.  Many older or cheap NASes
+	#  only support 16-octet Class attributes.
+#	insert_acct_class
+
+	#  MacSEC requires the use of EAP-Key-Name.  However, we don't
+	#  want to send it for all EAP sessions.  Therefore, the EAP
+	#  modules put required data into the EAP-Session-Id attribute.
+	#  This attribute is never put into a request or reply packet.
+	#
+	#  Uncomment the next few lines to copy the required data into
+	#  the EAP-Key-Name attribute
+#	if (&reply:EAP-Session-Id) {
+#		update reply {
+#			EAP-Key-Name := &reply:EAP-Session-Id
+#		}
+#	}
+
+	#  Remove reply message if the response contains an EAP-Message
+	remove_reply_message_if_eap
+
+	#
+	#  Access-Reject packets are sent through the REJECT sub-section of the
+	#  post-auth section.
+	#
+	#  Add the ldap module name (or instance) if you have set
+	#  'edir_account_policy_check = yes' in the ldap module configuration
+	#
+	#  The "session-state" attributes are not available here.
+	#
+	Post-Auth-Type REJECT {
+		# log failed authentications in SQL, too.
+		-sql
+		attr_filter.access_reject
+
+		# Insert EAP-Failure message if the request was
+		# rejected by policy instead of because of an
+		# authentication failure
+		eap
+
+		#  Remove reply message if the response contains an EAP-Message
+		remove_reply_message_if_eap
+	}
+}
+
+#
+#  When the server decides to proxy a request to a home server,
+#  the proxied request is first passed through the pre-proxy
+#  stage.  This stage can re-write the request, or decide to
+#  cancel the proxy.
+#
+#  Only a few modules currently have this method.
+#
+pre-proxy {
+	# Before proxing the request add an Operator-Name attribute identifying
+	# if the operator-name is found for this client.
+	# No need to uncomment this if you have already enabled this in
+	# the authorize section.
+#	operator-name
+
+	#  The client requests the CUI by sending a CUI attribute
+	#  containing one zero byte.
+	#  Uncomment the line below if *requesting* the CUI.
+#	cui
+
+	#  Uncomment the following line if you want to change attributes
+	#  as defined in the preproxy_users file.
+#	files
+
+	#  Uncomment the following line if you want to filter requests
+	#  sent to remote servers based on the rules defined in the
+	#  'attrs.pre-proxy' file.
+#	attr_filter.pre-proxy
+
+	#  If you want to have a log of packets proxied to a home
+	#  server, un-comment the following line, and the
+	#  'detail pre_proxy_log' section, above.
+#	pre_proxy_log
+}
+
+#
+#  When the server receives a reply to a request it proxied
+#  to a home server, the request may be massaged here, in the
+#  post-proxy stage.
+#
+post-proxy {
+
+	#  If you want to have a log of replies from a home server,
+	#  un-comment the following line, and the 'detail post_proxy_log'
+	#  section, above.
+#	post_proxy_log
+
+	#  Uncomment the following line if you want to filter replies from
+	#  remote proxies based on the rules defined in the 'attrs' file.
+#	attr_filter.post-proxy
+
+	#
+	#  If you are proxying LEAP, you MUST configure the EAP
+	#  module, and you MUST list it here, in the post-proxy
+	#  stage.
+	#
+	#  You MUST also use the 'nostrip' option in the 'realm'
+	#  configuration.  Otherwise, the User-Name attribute
+	#  in the proxied request will not match the user name
+	#  hidden inside of the EAP packet, and the end server will
+	#  reject the EAP request.
+	#
+	eap
+
+	#
+	#  If the server tries to proxy a request and fails, then the
+	#  request is processed through the modules in this section.
+	#
+	#  The main use of this section is to permit robust proxying
+	#  of accounting packets.  The server can be configured to
+	#  proxy accounting packets as part of normal processing.
+	#  Then, if the home server goes down, accounting packets can
+	#  be logged to a local "detail" file, for processing with
+	#  radrelay.  When the home server comes back up, radrelay
+	#  will read the detail file, and send the packets to the
+	#  home server.
+	#
+	#  With this configuration, the server always responds to
+	#  Accounting-Requests from the NAS, but only writes
+	#  accounting packets to disk if the home server is down.
+	#
+#	Post-Proxy-Type Fail-Accounting {
+#			detail
+#	}
+}
+}
diff --git a/radius-server/templates/sites-available/inner-tunnel.j2 b/radius-server/templates/sites-available/inner-tunnel.j2
new file mode 100644
index 0000000000000000000000000000000000000000..904009fc87f35d2cadf7e78e54f05f99e4e6a6ac
--- /dev/null
+++ b/radius-server/templates/sites-available/inner-tunnel.j2
@@ -0,0 +1,321 @@
+# -*- text -*-
+######################################################################
+#
+#	This is a virtual server that handles *only* inner tunnel
+#	requests for EAP-TTLS and PEAP types.
+#
+#	$Id: c250afa30a78fe9ff7a97b6c9b8a7c3a419a6946 $
+#
+######################################################################
+
+server inner-tunnel {
+
+#
+#  This next section is here to allow testing of the "inner-tunnel"
+#  authentication methods, independently from the "default" server.
+#  It is listening on "localhost", so that it can only be used from
+#  the same machine.
+#
+#	$ radtest USER PASSWORD 127.0.0.1:18120 0 testing123
+#
+#  If it works, you have configured the inner tunnel correctly.  To check
+#  if PEAP will work, use:
+#
+#	$ radtest -t mschap USER PASSWORD 127.0.0.1:18120 0 testing123
+#
+#  If that works, PEAP should work.  If that command doesn't work, then
+#
+#	FIX THE INNER TUNNEL CONFIGURATION SO THAT IT WORKS.
+#
+#  Do NOT do any PEAP tests.  It won't help.  Instead, concentrate
+#  on fixing the inner tunnel configuration.  DO NOTHING ELSE.
+#
+listen {
+       ipaddr = 127.0.0.1
+       port = 18120
+       type = auth
+}
+
+
+#  Authorization. First preprocess (hints and huntgroups files),
+#  then realms, and finally look in the "users" file.
+authorize {
+	filter_username
+
+	#
+	#  Do checks on outer / inner User-Name, so that users
+	#  can't spoof us by using incompatible identities
+	#
+	filter_inner_identity
+
+	chap
+	mschap
+	
+	suffix
+	ntdomain
+
+	#
+	#  The "suffix" module takes care of stripping the domain
+	#  (e.g. "@example.com") from the User-Name attribute, and the
+	#  next few lines ensure that the request is not proxied.
+	#
+	#  If you want the inner tunnel request to be proxied, delete
+	#  the next few lines.
+	#
+#	update control {
+#		&Proxy-To-Realm := LOCAL
+#	}
+
+	#
+	#  This module takes care of EAP-MSCHAPv2 authentication.
+	#
+	#  It also sets the EAP-Type attribute in the request
+	#  attribute list to the EAP type from the packet.
+	#
+	#  The example below uses module failover to avoid querying all
+	#  of the following modules if the EAP module returns "ok".
+	#  Therefore, your LDAP and/or SQL servers will not be queried
+	#  for the many packets that go back and forth to set up TTLS
+	#  or PEAP.  The load on those servers will therefore be reduced.
+	#
+	eap {
+		ok = return
+	}
+
+	#
+	#  Read the 'users' file
+	files
+
+	#
+	#  Look in an SQL database.  The schema of the database
+	#  is meant to mirror the "users" file.
+	#
+	#  See "Authorization Queries" in sql.conf
+	-sql
+	ldap
+
+	{%- for check in radius_tunnel_checks %}
+	{%- if check.station %}
+	if ( &Called-Station-SSID == "{{ check.station }}" ) {
+	{% endif -%}
+	if {{ check.condition }} {
+	}
+	else {
+		update reply {
+			Reply-Message = "{{ check_error }}"
+		}
+		reject
+	}
+	{%- if check.station %}
+	}
+	{% endif -%}
+	{% endfor -%}
+
+	expiration
+	logintime
+}
+
+
+#  Authentication.
+#
+#
+#  This section lists which modules are available for authentication.
+#  Note that it does NOT mean 'try each module in order'.  It means
+#  that a module from the 'authorize' section adds a configuration
+#  attribute 'Auth-Type := FOO'.  That authentication type is then
+#  used to pick the appropriate module from the list below.
+#
+
+#  In general, you SHOULD NOT set the Auth-Type attribute.  The server
+#  will figure it out on its own, and will do the right thing.  The
+#  most common side effect of erroneously setting the Auth-Type
+#  attribute is that one authentication method will work, but the
+#  others will not.
+#
+#  The common reasons to set the Auth-Type attribute by hand
+#  is to either forcibly reject the user, or forcibly accept him.
+#
+authenticate {
+
+	#
+	#  MSCHAP authentication.
+	Auth-Type MS-CHAP {
+		mschap
+	}
+
+	#
+	#  Allow EAP authentication.
+	eap
+}
+
+######################################################################
+#
+#	There are no accounting requests inside of EAP-TTLS or PEAP
+#	tunnels.
+#
+######################################################################
+
+
+#  Session database, used for checking Simultaneous-Use. Either the radutmp
+#  or rlm_sql module can handle this.
+#  The rlm_sql module is *much* faster
+session {
+	radutmp
+
+	#
+	#  See "Simultaneous Use Checking Queries" in sql.conf
+#	sql
+}
+
+
+#  Post-Authentication
+#  Once we KNOW that the user has been authenticated, there are
+#  additional steps we can take.
+#
+#  Note that the last packet of the inner-tunnel authentication
+#  MAY NOT BE the last packet of the outer session.  So updating
+#  the outer reply MIGHT work, and sometimes MIGHT NOT.  The
+#  exact functionality depends on both the inner and outer
+#  authentication methods.
+#
+#  If you need to send a reply attribute in the outer session,
+#  the ONLY safe way is to set "use_tunneled_reply = yes", and
+#  then update the inner-tunnel reply.
+post-auth {
+	#  If you want privacy to remain, see the
+	#  Chargeable-User-Identity attribute from RFC 4372.
+	#  If you want to use it just uncomment the line below.
+#       cui-inner
+
+	#
+	#  If you want to have a log of authentication replies,
+	#  un-comment the following line, and enable the
+	#  'detail reply_log' module.
+	reply_log
+
+	#
+	#  After authenticating the user, do another SQL query.
+	#
+	#  See "Authentication Logging Queries" in sql.conf
+	-sql
+
+	#
+	#  Instead of sending the query to the SQL server,
+	#  write it into a log file.
+	#
+#	sql_log
+
+	#
+	#  Un-comment the following if you have set
+	#  'edir_account_policy_check = yes' in the ldap module sub-section of
+	#  the 'modules' section.
+	#
+	ldap
+
+
+	#
+	#  Instead of "use_tunneled_reply", uncomment the
+	#  next two "update" blocks.
+	#
+#	update {
+#		&outer.session-state: += &reply:
+#	}
+
+	#
+	#  These attributes are for the inner session only.
+	#  They MUST NOT be sent in the outer reply.
+	#
+	#  If you uncomment the previous block and leave
+	#  this one commented out, WiFi WILL NOT WORK,
+	#  because the client will get two MS-MPPE-keys
+	#
+#	update outer.session-state {
+#		MS-MPPE-Encryption-Policy !* ANY
+#		MS-MPPE-Encryption-Types !* ANY
+#		MS-MPPE-Send-Key !* ANY
+#		MS-MPPE-Recv-Key !* ANY
+#		Message-Authenticator !* ANY
+#		EAP-Message !* ANY
+#		Proxy-State !* ANY
+#	}
+
+	#
+	#  Access-Reject packets are sent through the REJECT sub-section of the
+	#  post-auth section.
+	#
+	#  Add the ldap module name (or instance) if you have set
+	#  'edir_account_policy_check = yes' in the ldap module configuration
+	#
+	Post-Auth-Type REJECT {
+		# log failed authentications in SQL, too.
+		-sql
+		attr_filter.access_reject
+
+		#
+		#  Let the outer session know which module failed, and why.
+		#
+		update outer.session-state {
+			&Module-Failure-Message := &request:Module-Failure-Message
+		}
+	}
+
+	update outer.reply {
+		User-Name = "%{request:User-Name}"
+	}
+}
+
+#
+#  When the server decides to proxy a request to a home server,
+#  the proxied request is first passed through the pre-proxy
+#  stage.  This stage can re-write the request, or decide to
+#  cancel the proxy.
+#
+#  Only a few modules currently have this method.
+#
+pre-proxy {
+	#  Uncomment the following line if you want to change attributes
+	#  as defined in the preproxy_users file.
+#	files
+
+	#  Uncomment the following line if you want to filter requests
+	#  sent to remote servers based on the rules defined in the
+	#  'attrs.pre-proxy' file.
+#	attr_filter.pre-proxy
+
+	#  If you want to have a log of packets proxied to a home
+	#  server, un-comment the following line, and the
+	#  'detail pre_proxy_log' section, above.
+#	pre_proxy_log
+}
+
+#
+#  When the server receives a reply to a request it proxied
+#  to a home server, the request may be massaged here, in the
+#  post-proxy stage.
+#
+post-proxy {
+
+	#  If you want to have a log of replies from a home server,
+	#  un-comment the following line, and the 'detail post_proxy_log'
+	#  section, above.
+#	post_proxy_log
+
+	#  Uncomment the following line if you want to filter replies from
+	#  remote proxies based on the rules defined in the 'attrs' file.
+#	attr_filter.post-proxy
+
+	#
+	#  If you are proxying LEAP, you MUST configure the EAP
+	#  module, and you MUST list it here, in the post-proxy
+	#  stage.
+	#
+	#  You MUST also use the 'nostrip' option in the 'realm'
+	#  configuration.  Otherwise, the User-Name attribute
+	#  in the proxied request will not match the user name
+	#  hidden inside of the EAP packet, and the end server will
+	#  reject the EAP request.
+	#
+	eap
+}
+
+} # inner-tunnel server block