Redundant SQL connection. Starting radiusd and Respond with Access-Accept if all databases do not work
Никита Борисенков
bna at sunlink.ru
Thu Nov 28 16:22:07 CET 2019
The task is to allow or prohibit calls to subscribers. If the subscriber
has valid status, then we send Access-Accept. If the subscriber calls
the emergency service, then we send Access-Accept. In other cases, we
send Access-Reject.
I configured two radius server, and two sql server. Replication
configured between sql servers.
I write accounting and authentication to the database.
I want that when one database is unavailable, data is written to another
database. But if both databases are not available, then we send
Access-Accept
Some questions:
* Is there a way to not duplicate config for modules sql1 and sql2
because they differ only in ip-address?
* Is there a way to start freeradius if one or all databases are down?
* What is the correct way to verify that sql1 and sql2 modules are fail
without checking "Calling-Station-Id-Is-Exists" for empty string?
* How can I simplify this configuration?
My config:
sql sql1 {
driver = "rlm_sql_mysql"
dialect = "mysql"
server = "localhost"
port = 3306
login = "radius"
password = "test"
query_timeout = 5
radius_db = "radius"
read_clients = no
# Table to keep radius client info
client_table = "nas"
# If you want both stop and start records logged to the
# same SQL table, leave this as is. If you want them in
# different tables, put the start table in acct_table1
# and stop table in acct_table2
acct_table1 = "radacct"
acct_table2 = "radacct"
# Allow for storing data after authentication
postauth_table = "radpostauth"
# Tables containing 'check' items
authcheck_table = "radcheck"
groupcheck_table = "radgroupcheck"
# Tables containing 'reply' items
authreply_table = "radreply"
groupreply_table = "radgroupreply"
# Table to keep group info
usergroup_table = "radusergroup"
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 = 0
idle_timeout = 60
}
# This entry should be used for additional instances (sql foo {})
# of the SQL module.
group_attribute = "${.:instance}-SQL-Group"
# This entry should be used for the default instance (sql {})
# of the SQL module.
#group_attribute = "SQL-Group"
# Read database-specific queries
# $INCLUDE ${modconfdir}/${.:name}/main/${dialect}/queries.conf
$INCLUDE ${modconfdir}/sql/main/${dialect}/queries.conf
}
sql sql2 {
driver = "rlm_sql_mysql"
dialect = "mysql"
server = "172.27.65.243"
port = 3306
login = "radius"
password = "test"
query_timeout = 5
radius_db = "radius"
read_clients = no
# Table to keep radius client info
client_table = "nas"
# If you want both stop and start records logged to the
# same SQL table, leave this as is. If you want them in
# different tables, put the start table in acct_table1
# and stop table in acct_table2
acct_table1 = "radacct"
acct_table2 = "radacct"
# Allow for storing data after authentication
postauth_table = "radpostauth"
# Tables containing 'check' items
authcheck_table = "radcheck"
groupcheck_table = "radgroupcheck"
# Tables containing 'reply' items
authreply_table = "radreply"
groupreply_table = "radgroupreply"
# Table to keep group info
usergroup_table = "radusergroup"
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 = 0
idle_timeout = 60
}
# This entry should be used for additional instances (sql foo {})
# of the SQL module.
group_attribute = "${.:instance}-SQL-Group"
# This entry should be used for the default instance (sql {})
# of the SQL module.
#group_attribute = "SQL-Group"
# Read database-specific queries
#$INCLUDE ${modconfdir}/${.:name}/main/${dialect}/queries.conf
$INCLUDE ${modconfdir}/sql/main/${dialect}/queries.conf
}
# Create virtual module for SELECTs
instantiate {
redundant sql1_or_sql2 {
sql1
sql2
}
}
server ats_local {
authorize {
update control {
Calling-Station-Id-Status = "%{sql1_or_sql2:SELECT
gtdb_users.status FROM gtdb_users WHERE aon='%{Calling-Station-Id}'}"
Called-Station-Id-Status = "%{sql1_or_sql2:SELECT
gtdb_users.status FROM gtdb_users WHERE aon='%{Called-Station-Id}'}"
Calling-Station-Id-Is-Exists = "%{sql1_or_sql2:SELECT
COUNT(gtdb_users.status) FROM gtdb_users WHERE aon='%{Calling-Station-Id}'}"
Called-Station-Id-Is-Exists = "%{sql1_or_sql2:SELECT
COUNT(gtdb_users.status) FROM gtdb_users WHERE aon='%{Called-Station-Id}'}"
Called-Station-Id-Emergency = "%{sql1_or_sql2:SELECT
COUNT(aon) FROM emergency_cid WHERE aon='%{Called-Station-Id}'}"
}
# These attributes in normal condition must contain the number
>= 0 (because we use sql COUNT() )
# If there is an empty string inside, then sql queries failed
for some reason and we return Access-Accept
if (&control:Calling-Station-Id-Is-Exists == '' ||
&control:Called-Station-Id-Is-Exists == '') {
update control {
Auth-Type := Accept
}
}
else {
if (control:Called-Station-Id-Emergency == '1') {
update control {
Auth-Type := Accept
}
}
# ...
# bunch of elsif for Calling-Station-Id-Status and
Called-Station-Id-Status
# ...
else {
update control {
Auth-Type := Reject
}
}
}
}
authenticate {
}
preacct {
acct_unique
}
$INCLUDE ${confdir}/ats_accounting.conf
session {
}
post-auth {
update reply {
cisco-avpair = "h323-credit-time=86400"
}
if (&User-Name) {
# this not work, because I can't put "ok" to virtual module
"sql1_or_sql2"
# sql1_or_sql2
redundant {
sql1
sql2
ok
}
}
Post-Auth-Type REJECT {
if (&User-Name) {
redundant {
sql1
sql2
ok
}
}
}
}
pre-proxy {
}
post-proxy {
}
}
More information about the Freeradius-Users
mailing list