The configuration of the various Components is tightly coupled and must be kept consistent with regard to each other. The the Celery worker, the DBus deputy, the FreeRADIUS server, and the Flask app for example must talk to the PostgreSQL database, the nginx web server must talk to the uWSGI server etc.
All of these components have their own custom configuration file format.
To relieve the administrators from learning all the different configuration file
formats, all the necessary options of the various components and making sure
that the configuration is consistent, Hades defines its own configuration file
and performs the configuration of the other components automatically for the
administrator and does basic error and consistency checking.
The default location of the central configuration file is
/etc/hades/config.py
.
Hades can't fully abstract away the components, so some basic knowledge about the components of Hades, or at least willingness to acquire it if necessary, is required, especially if errors occur. Monitoring the systemd journal is usually sufficient for debugging problems.
The configuration file is a Python file, that should contain a series of
variable assignments for the various options listed below.
Most of the configuration options expect primitive data types as values such as
strings, integers or booleans, while some require more complex types such as
list
and dict
or special data types such as
datetime.timedelta
or netaddr.IPNetwork
.
You are free to use all the features of the Python programming, but complex
control flow or functions and classes should probably be avoided in a
configuration file.
For detailed information about Python syntax, please see the
The Python Tutorial and The Python Language Reference.
Importing Python modules other than the special data types from the standard library or direct dependencies of Hades should also be avoided. One can however use relative imports to spread options among multiple files.
HADES_VRRP_PASSWORD = 'hunter2'
from .secrets import HADES_VRRP_PASSWORD
Because you're using Python's built-in import system, you have to adhere to its
rules.
In particular, if you're trying to import files from other directories, those
directories must contain a file named __init__.py
to denote that this
directory is a Python package.
Warning
You can't import from parent or sibling directories with respect to your
root configuration file. Doing from ..parent import file
from your
root configuration file won't work as you intended, if it all.
There are various ways to specify the configuration file for Hades.
By default Hades uses the file /etc/hades/config.py
.
The Hades command-line tools support the -c/--config
switch to specify a
different file.
In addition, Hades supports the environment variable HADES_CONFIG
.
The environment variable is especially useful for the various Hades systemd
services.
All Hades systemd service units source the EnvironmentFile
/etc/default/hades
.
The command-line switch -c/--config
takes precedence over the environment
variable HADES_CONFIG
, which takes precedence over the default value.
The various third-party components of Hades, like nginx, are obviously not able to read Python configuration files. The Hades components, that are written in Python, don't require this template mechanism. This covers hades-agent, hades-deputy, hades-unauth-portal, and the various helper scripts.
The Hades systemd services generate the appropriate configuration for each third-party component before the actual service daemon is executed. The configuration files are also regenerated, if the services are restarted. Reloading a service, if it supports it, does not regenerate the configuration file. You have to manually regenerate the configuration, if you don't want to restart service. For services that support it, the generated configuration is checked before the service is started or reloaded. These are the services based on dnsmasq, FreeRADIUS, and unbound.
The generated configuration files are stored in the hades
subdirectory of
the system's runtime directory, /run/hades
by default.
The configuration files are generated from Jinja2 templates. For information about the syntax of Jinja2 templates, see the Jinja2 documentation.
You might be in a situation, where you want to manually generate the third-party config files. Doing this is very easy:
hades-generate-config nginx/nginx.conf.j2 /tmp/my-nginx.conf
This will compile the template nginx/nginx.conf.j2
and output its result to
/tmp/my-nginx.conf
.
The configuration of nginx
however requires more than a file to work.
If the first argument passed to hades-generate-config
refers to a directory,
the command will recursively compile all files ending with .j2
into files
with the same name without the .j2
extension and copy all other files and
directories as-is.
hades-generic-config nginx /tmp/my-nginx-config
The templates and other files and directories are looked up in a set of
directories on the template search path.
By default, the template search path is comprised of the two directories
/etc/hades/templates
and /usr/share/hades/templates
in this order.
The directory /usr/share/hades/templates
contains the default templates
shipped with Hades, the directory /etc/hades/templates
is intended for use
by the administrator to override the default templates, if deemed necessary.
The lookup algorithm is analogous to how systemd
looks for its
unit files
or how the shell finds
executables
on the PATH
.
Instead of overriding the contents of a file, it may be necessary or convenient
to omit files or directories from the generated configuration files.
The mechanism is again analogous to how systemd allows you to mask
with symbolic links to /dev/null
.
The following list of available options is automatically generated from the Python classes that represent the options internally.
Name of the site
This option is required.
Must match regular expression: '\\A[a-z][a-z0-9-]*\\Z'
ID of the site node
This option is required.
Must match regular expression: '\\A[a-z][a-z0-9-]*\\Z'
Automatic notification mails will be send to this address.
Must satisfy all of the following:
Must not be empty
All elements must satisfy: Type must be str
RADIUS periodic reauthentication interval
datetime.timedelta(seconds=300)
Must be greater than datetime.timedelta(0)
RADIUS postauth and accounting data retention interval
datetime.timedelta(days=1)
Must be greater than datetime.timedelta(0)
Contact addresses displayed on the captive portal page
This option is required.
Public networks of authenticated users.
Dictionary of networks. Keys are unique identifiers of the network,
values are netaddr.IPNetwork
objects
This option is required.
Must satisfy all of the following:
Must not be empty
All values must satisfy: Must not be network or broadcast address (except if /31)
Additional iptables rules for INPUT
chain.
A list of valid iptables-restore
rule lines with leading -A INPUT
.
[]
IPNetwork('172.18.0.0/31')
Must not be network or broadcast address (except if /31)
IP address must be configured
IPNetwork('172.18.0.1/31')
Must not be network or broadcast address (except if /31)
IP address must be configured
IPNetwork('172.18.0.2/31')
Must not be network or broadcast address (except if /31)
IP address must be configured
IPNetwork('172.18.0.3/31')
Must not be network or broadcast address (except if /31)
IP address must be configured
Port and socket name of the PostgresSQL database
5432
Must be between 1
and 65535
inclusively
A list of addresses PostgreSQL should listen on.
(IPNetwork('127.0.0.1/8'),)
All elements must satisfy: Must not be network or broadcast address (except if /31)
All elements must satisfy: IP address must be configured
Name of the foreign data wrapper extensions that should be used.
If HADES_LOCAL_MASTER_DATABASE
is set, this option is
ignored.
'postgres_fdw'
Foreign data wrapper specific server options
If HADES_LOCAL_MASTER_DATABASE
is set, this option is
ignored.
{}
Foreign data wrapper specific server type
If HADES_LOCAL_MASTER_DATABASE is set, this option is ignored.
Foreign data wrapper specific server version
If HADES_LOCAL_MASTER_DATABASE
is set, this option is
ignored.
Foreign data wrapper options that are set on each foreign table. The options can be overridden with table specific options.
If HADES_LOCAL_MASTER_DATABASE
is set, this option is
ignored.
{}
Whether the IPAddress
column of the foreign alternative_dns
table
has a string type
False
Foreign data wrapper options for the alternative_dns
table
If HADES_LOCAL_MASTER_DATABASE
is set, this option is
ignored.
{'table_name': 'alternative_dns'}
Whether the IPAddress
column of the foreign auth_dhcp_host
table
has a string type
False
Whether the MAC
column of the foreign auth_dhcp_host
table has a
string type
False
Foreign data wrapper options for the auth_dhcp_host
table
If HADES_LOCAL_MASTER_DATABASE
is set, this option is
ignored.
{'table_name': 'auth_dhcp_host'}
Foreign data wrapper options for the nas
table
If HADES_LOCAL_MASTER_DATABASE
is set, this option is
ignored.
{'table_name': 'nas'}
Whether the NASIPAddress
column of the foreign radcheck
table has
a string type.
False
Foreign data wrapper options for the radcheck
table
If HADES_LOCAL_MASTER_DATABASE
is set, this option is
ignored.
{'table_name': 'radcheck'}
Foreign data wrapper options for the radgroupcheck
table
If HADES_LOCAL_MASTER_DATABASE
is set, this option is
ignored.
{'table_name': 'radgroupcheck'}
Foreign data wrapper options for the radgroupreply
table
If HADES_LOCAL_MASTER_DATABASE
is set, this option is
ignored.
{'table_name': 'radgroupreply'}
Whether the NASIPAddress
column of the foreign radgroupcheck
table has a string type
False
Foreign data wrapper options for the radreply
table
If HADES_LOCAL_MASTER_DATABASE
is set, this option is
ignored.
{'table_name': 'radreply'}
Whether the NASIPAddress
column of the foreign radgroupcheck
table has a string type
False
Foreign data wrapper options for the radusergroup
table
If HADES_LOCAL_MASTER_DATABASE
is set, this option is
ignored.
{'table_name': 'radusergroup'}
User mappings from local database users to users on the foreign database server
If HADES_LOCAL_MASTER_DATABASE
is set, this option is
ignored.
Must have contain a mapping for hades-database or PUBLIC
Fully qualified domain name of the captive portal
'captive-portal.agdsn.de'
URL of the landing page of the captive portal
Will be computed from the format string 'http://{}/'
, with HADES_PORTAL_DOMAIN
as positional argument.
Number of nginx worker processes
4
Must be greater than 0
Path to the SSL certificate of the captive portal
'/etc/ssl/certs/ssl-cert-snakeoil.pem'
Must be an existing file
Path to the SSL certificate key of the captive portal
'/etc/ssl/private/ssl-cert-snakeoil.key'
Must be an existing file
Number of uWSGI worker processes
4
Must be greater than 0
DHCP lease lifetime for authenticated users
datetime.timedelta(days=1)
Must be greater than datetime.timedelta(0)
DHCP lease renew timer for authenticated users
Half of HADES_AUTH_DHCP_LEASE_LIFETIME
Must be greater than datetime.timedelta(0)
DHCP lease rebind timer for authenticated users
0.875 of HADES_AUTH_DHCP_LEASE_LIFETIME
Must be greater than datetime.timedelta(0)
Sequence of IPs and networks to listen on for requests from authenticated users.
The first IP in the sequence will be the main IP, e.g. it will be advertised as IP of DNS server in DHCP responses.
(IPNetwork('10.66.67.10/24'),)
Must satisfy all of the following:
Must not be empty
All elements must satisfy: Must not be network or broadcast address (except if /31)
All elements must satisfy: IP address must be configured
Interface where requests of authenticated users arrive.
This interface will be moved into the auth namespace and IP addresses on this interface are managed by the keepalived hades-auth VRRP instance.
This interface should therefore be managed completely by Hades. Aside from
its creation other tools, e.g. ifupdown
, systemd-networkd
, should
not interfere. No other daemons should listen on or bind to this interface.
This option is required.
Network interface must exists
Name of the auth bridge interface
'br-auth'
Must match regular expression: '\\A[A-Za-z0-9_-]{1,15}\\Z'
The next hop, where packets to user networks (e.g. DHCP replies, DNS replies) should be forwarded to.
IPNetwork('10.66.67.1/24')
netaddr.ip.IPNetwork
Must not be network or broadcast address (except if /31)
Allowed TCP destination ports for unauthenticated users
(53, 80, 443, 9053)
Allowed UDP destination ports for unauthenticated users
(53, 67, 9053)
Name of ipset for alternative DNS resolving.
'hades_alternative_dns'
DNS zones that are transparently spoofed if alternative DNS is enabled.
{}
DHCP lease time for unauth users
This lease time should be set rather short, so that unauthenticated will quickly obtain a new address if they become authenticated.
datetime.timedelta(seconds=120)
Must be greater than datetime.timedelta(0)
Interface attached to the unauth VLAN
This option is required.
Network interface must exists
Name of the unauth bridge interface
'br-unauth'
Must match regular expression: '\\A[A-Za-z0-9_-]{1,15}\\Z'
Sequence of IPs and networks to listen for unauthenticated users.
The first IP in the sequence will be the main IP, e.g. it will be advertised as IP of DNS server in DHCP responses.
(IPNetwork('10.66.0.1/19'),)
Must satisfy all of the following:
Must not be empty
All elements must satisfy: Must not be network or broadcast address (except if /31)
All elements must satisfy: IP address must be configured
Allowed TCP destination ports for unauthenticated users
(53, 80, 443)
Allowed UDP destination ports for unauthenticated users
(53, 67)
All traffic destined to these TCP ports is transparently redirected (captured) to the unauth listen address of the site node
(53, 80, 443)
All traffic destined to these UDP ports is transparently redirected (captured) to the unauth listen address of the site node
(53,)
DHCP range for the unauth VLAN. Must be contained within the
HADES_UNAUTH_LISTEN
network.
IPRange('10.66.0.10', '10.66.31.254')
netaddr.ip.IPRange
Must be contained in the networks configured with HADES_UNAUTH_LISTEN
List of DNS names which are whitelisted for unauthenticated users.
()
Name of ipset for whitelisted IPs.
'hades_unauth_whitelist'
Sequence of IPs and networks the RADIUS server is listening on.
(IPNetwork('10.66.68.10/24'),)
Must satisfy all of the following:
Must not be empty
All elements must satisfy: Must not be network or broadcast address (except if /31)
All elements must satisfy: IP address must be configured
Interface the RADIUS server is listening on
This option is required.
Network interface must exists
Send Access-Accept
packets if the RADIUS sql
module fails
True
Reply attributes that will be set in Access-Accept
packets if the RADIUS
sql
module fails.
The attribute value must be specified in proper FreeRADIUS syntax. That means that string replies should be enclosed in single quotes.
{'Reply-Message': "'database_down'"}
The User-Name
, that is used as fallback if the MAC address was not
found in the database.
'unknown'
Period in which gratuitous ARP requests are broadcasted to notify
clients of the MAC address of current master site node instance
clients switching from the auth to the unauth VLAN of the new gateway MAC
datetime.timedelta(seconds=1)
Must be greater than datetime.timedelta(0)
Priority of the site node instance.
The available instance with the highest priority becomes master.
100
Must be between 1
and 254
inclusively
Flag that indicates if the site node instance starts in master state
False
Interface for VRRP communication
Network interface must exists
Interface name for VRRP bridge (created if necessary)
'br-vrrp'
Must not be empty
IP and network for VRRP communication (auth instance)
netaddr.ip.IPNetwork
Must not be network or broadcast address (except if /31)
IP address must be configured
IP and network for VRRP communication (root instance)
netaddr.ip.IPNetwork
Must not be network or broadcast address (except if /31)
IP address must be configured
IP and network for VRRP communication (unauth instance)
netaddr.ip.IPNetwork
Must not be network or broadcast address (except if /31)
IP address must be configured
Shared secret to authenticate VRRP messages between site node instances.
This option is required.
Virtual router ID used by Hades (auth instance)
66
Must be between 0
and 255
inclusively
Virtual router ID used by Hades (root instance)
67
Must be between 0
and 255
inclusively
Virtual router ID used by Hades (unauth instance)
68
Must be between 0
and 255
inclusively
Interval between VRRP advertisements
datetime.timedelta(seconds=5)
Must be greater than datetime.timedelta(0)
Delay before a MASTER transitions to BACKUP when a node with a higher priority comes online
datetime.timedelta(seconds=30)
Must be between datetime.timedelta(0)
and datetime.timedelta(seconds=1000)
inclusively
Create dummy interfaces if interfaces do not exist
False
Link the service interface of the auth and unauth network namespaces through bridges and veth interfaces rather than moving the interface directly into the network namespace.
This allows to attach other interfaces to the bridge to e.g. test DHCP.
False
Default timezone of the portal application
'Europe/Berlin'
A URI targeting the default postgresql socket in the pkgrunstatedir.
The port is set to HADES_POSTGRESQL_PORT
and the user is the default database user.
Hostname of the hades-agent Celery worker.
Will be computed from the format string '{}.{}'
, with HADES_SITE_NAME
, HADES_SITE_NODE_ID
as positional arguments.
Will be computed from the format string '{}rpc-call'
, with HADES_CELERY_PREFIX
as positional argument.
Will be computed from the format string '{}notify'
, with HADES_CELERY_PREFIX
as positional argument.
Will be computed from the format string '{}{}.{}'
, with HADES_CELERY_PREFIX
, HADES_SITE_NAME
, HADES_SITE_NODE_ID
as positional arguments.
Maximum length (in messages) of the node's queue
1000
Will be computed from the format string 'nodes.{}'
, with HADES_SITE_NAME
as positional argument.
Will be computed from the format string 'nodes.{}.{}'
, with HADES_SITE_NAME
, HADES_SITE_NODE_ID
as positional arguments.
Will be computed from the format string 'masters'
, with HADES_SITE_NAME
as positional argument.
Will be computed from the format string 'masters.all.{}'
, with HADES_SITE_NAME
as positional argument.
Will be computed from the format string 'masters.auth.{}'
, with HADES_SITE_NAME
as positional argument.
Will be computed from the format string 'masters.root.{}'
, with HADES_SITE_NAME
as positional argument.
Will be computed from the format string 'masters.unauth.{}'
, with HADES_SITE_NAME
as positional argument.
Path of Celery node state database
'/usr/local/var/run/hades/agent/state.db'
Maximum number of retries before giving up re-establishing the connection to the broker.
Set to zero to retry forever in case of longer partitions between sites and the main database.
0
Declare two exchanges, one for RPCs and one for notifications.
RPCs return results and should therefore only be answered by a single agent. Notifications have no results and are processed by potentially multiple agents.
Each agent/site node has a single queue specific to this node. This queue is bound to the RPC exchange with a node-specific routing key and to the notify exchange with the site-specific, node-specific, and empty routing key. The agent on a site node, where the root VRRP instance has become MASTER, will also bind its queue to the RPC exchange with the site-specific routing key and remove this binding as soon as the sites leaves the MASTER state.
This setup ensures that RPC messages can be sent to a specific agent/node, by using the node-specific routing key and to the agent on the master by using the site-specific routing key. Notifications can be sent to all agents/nodes by using the empty routing key, to all agents/nodes of a site by using the site-specific routing key, and to a specific node by using the node-specific routing key.
['json']
Will be computed from the format string '{}rpc-result'
, with HADES_CELERY_PREFIX
as positional argument.
()
datetime.timedelta(seconds=300)