Issue with dynamic home server and duplicate server ipaddr on 3.2.8
James Wood
james.wood at purplewifi.com
Mon Mar 16 20:29:38 UTC 2026
I've come across an issue with dynamic home_servers for supporting
OpenRoaming connections.
In `authorize`, we use the following code to check if it's our home domain,
if the 'home_server' already exists, or if we need to dynamically query
which radsec server to proxy to:
--------------------------------
openroaming_lookup {
if (&control:Proxy-To-Realm || &control:Home-Server-Name) {
return
}
if (&User-Name && &User-Name =~ /home.domain/) {
return
}
elsif (&User-Name && &User-Name =~ /@(.*)$/) {
switch "%{home_server_dynamic:%{1}}" {
case "1" {
# Proxy to this one particular home server
update control {
&Home-Server-Name := "%{1}"
}
}
case "0" {
# Proxy with home server pool, failover,
etc.
update control {
&Proxy-To-Realm := "%{1}"
}
}
case {
# no home server exists, ask DNS
update control {
&Temp-Home-Server-String :=
`%{config:confdir}/mods-config/realm/freeradius-naptr-to-home-server.sh -d
%{config:confdir} %{1} aaa+auth:radius>
}
if (&control:Temp-Home-Server-String == ""
) {
reject
} else {
update control {
&Home-Server-Name := "%{1}"
}
}
}
}
}
}
--------------------------------
This generally works well. For a new (unknown home server), we call the
below mods-config/realm/freeradius-naptr-to-home-server.sh script to
perform the dynamic discovery:
--------------------------------
#!/bin/sh
usage() {
echo "Usage: ${0} [OPTIONS] <realm> <optional NAPTR tag>"
echo " -d RADIUS_DIR Set radius directory"
echo " -t test (skip running radmin)"
exit 1
}
test -n "${1}" || usage
RADDB=/usr/local/etc/raddb
RADMIN=y
while [ `echo "$1" | cut -c 1` = "-" ]
do
case "$1" in
-d)
RADDB=$2
shift;shift
;;
-t)
RADMIN=
shift
;;
*)
usage
;;
esac
done
test -n "${2}" && NAPTRTAG="${2}" || NAPTRTAG="x-eduroam:radius.tls"
DIGCMD=$(command -v dig)
HOSTCMD=$(command -v host)
PRINTCMD=$(command -v printf)
validate_host() {
echo ${@} | tr -d '\n\t\r' | grep -E '^[_0-9a-zA-Z][-._0-9a-zA-Z]*$'
}
validate_port() {
echo ${@} | tr -d '\n\t\r' | grep -E '^[0-9]+$'
}
dig_it_srv() {
${DIGCMD} +short srv $SRV_HOST | sort -n -k1 |
while read line; do
set $line
PORT=$(validate_port $3)
HOST=$(validate_host $4)
if [ -n "${HOST}" ] && [ -n "${PORT}" ]; then
$PRINTCMD "\tipaddr = ${HOST%.}\n\tport = ${PORT}\n"
fi
done
}
dig_it_naptr() {
${DIGCMD} +short naptr "${REALM}" | grep $NAPTRTAG | sort -n -k1 |
while read line; do
set $line
TYPE=$3
HOST=$(validate_host $6)
if ( [ "$TYPE" = "\"s\"" ] || [ "$TYPE" = "\"S\"" ] ) && [
-n "${HOST}" ]; then
SRV_HOST=${HOST%.}
dig_it_srv
fi
done
}
host_it_srv() {
${HOSTCMD} -t srv $SRV_HOST | sort -n -k5 |
while read line; do
set $line
PORT=$(validate_port $7)
HOST=$(validate_host $8)
if [ -n "${HOST}" ] && [ -n "${PORT}" ]; then
$PRINTCMD "\tipaddr ${HOST%.}:${PORT}\n"
fi
done
}
host_it_naptr() {
${HOSTCMD} -t naptr "${REALM}" | grep $NAPTRTAG | sort -n -k5 |
while read line; do
set $line
TYPE=$7
HOST=$(validate_host ${10})
if ( [ "$TYPE" = "\"s\"" ] || [ "$TYPE" = "\"S\"" ] ) && [
-n "${HOST}" ]; then
SRV_HOST=${HOST%.}
host_it_srv
fi
done
}
REALM=$(validate_host ${1})
if [ -z "${REALM}" ]; then
echo "realm \"${1}\" failed validation" >&2
usage
fi
if [ -x "${DIGCMD}" ]; then
SERVERS=$(dig_it_naptr)
elif [ -x "${HOSTCMD}" ]; then
SERVERS=$(host_it_naptr)
else
echo "${0} requires either \"dig\" or \"host\" command." >&2
exit 1
fi
if [ ! -n "${SERVERS}" ]; then
echo "No servers found" >&2
exit 1
fi
if [ -z "${RADMIN}" ]; then
$PRINTCMD "home_server ${REALM} {\n${SERVERS}\n\t\$INCLUDE
tls.conf\n}\n"
exit 0
fi
HOME_SERVER_FILE="$RADDB/home_servers/$1"
if [ -f "$HOME_SERVER_FILE" ]; then
echo "Home server file $HOME_SERVER_FILE already exists. Skipping
creation and radmin command." >&2
else
echo "Creating new home server file $HOME_SERVER_FILE." >&2
$PRINTCMD "home_server ${REALM} {\n${SERVERS}\n\t\$INCLUDE
tls.conf\n}\n" > "$HOME_SERVER_FILE"
/usr/local/sbin/radmin -e "add home_server file $HOME_SERVER_FILE"
fi
echo $1
--------------------------------
Again, this works well. It creates a new file in the home_servers folder,
and radmin adds it to the server to use immediately.
The problem arises if we have an authentication request from a new realm
but the "server" is one that we already have a reference to, from a
previously added dynamic home server definition.
For example, an authentication request comes in with a realm of
davidlloyd.openroaming.net and the script generates the following home
server config:
# cat home_servers/davidlloyd.openroaming.net
home_server davidlloyd.openroaming.net {
ipaddr = idpeu.openroaming.net
port = 2083
$INCLUDE tls.conf
}
This new home server was then added via radmin, and everything was ok. The
request is proxied to that new server. So far, so good.
Later, another authentication request comes in with a realm of
eu-sdk.openroaming.net, and the script generates the following home server
config:
# cat home_servers/eu-sdk.openroaming.net
home_server eu-sdk.openroaming.net {
ipaddr = idpeu.openroaming.net
port = 2083
$INCLUDE tls.conf
}
However, this failed to add via radmin, because the "ipaddr" is the same as
a previously defined dynamic home server, and the authentication fails as
it doesn't proxy the request.
ipaddr = idpeu.openroaming.net <<<< this
The error thrown by radmin is:
# radmin -e "add home_server file /usr/local/etc/raddb/home_servers/
davidlloyd.openroaming.net"
ERROR: Unable to add home server - Failed adding home_server to the
internal data structures
This doesn't just happen for "openroaming" hosted realms either. Take
cityroam in Japan for example. They have multiple realms too, but the same
servers:
# cat home_servers/*city*
home_servers/jwa.bemap.cityroam.jp:
home_server jwa.bemap.cityroam.jp {
ipaddr = jpgw4.cityroam.jp
port = 2083
ipaddr = jpgw.cityroam.jp
port = 2083
$INCLUDE tls.conf
}
home_servers/w-jp1.wi2.cityroam.jp:
home_server w-jp1.wi2.cityroam.jp {
ipaddr = jpgw4.cityroam.jp
port = 2083
ipaddr = jpgw.cityroam.jp
port = 2083
$INCLUDE tls.conf
}
It's like we need a way to set servers and then map multiple realms to the
same server (as traditionally done in proxy.conf)?
How can we best solve this issue? Is there another method for doing this
besides the one above?
We can't statically define these servers (as the whole point of openroaming
is to dynamic proxy to the resolved servers, which may change), and we'll
never know what user realm may hit us next will be, and they will probably
share servers.
Thanks,
James
--
Visit purple.ai <https://purple.ai/>
Purple on LinkedIn
<https://uk.linkedin.com/company/purple-wifi>
Email disclaimer
<http://www.purple.ai/email-disclaimer/>
More information about the Freeradius-Users
mailing list