[SBC] —- [Cisco CUBE (Radius)] —- [RHEL - Radius Server + Radius REST Module)] —- [RHE Linux - Web Server (Custom API)] — [SQL Server Database (Single Table)]
Note - by default CUBE would only failover to the second Radius server, if and when the requessts to the first one timed out. However this command radius-server dead-criteria time 5 tries 3 means if that we have 3 failed attempts in a row and we have NOT had a valid reply in the last 5 seconds, then mark the server as DEAD!
We then have a deadtimer at the server level, in the below case of 60 mins (max if 1440 mins (24 hours)) - so when tehre is a failover - it will stay witj the backup server for an hour and THEN failback. If the server is still down, it will get marked as Dead again until the next hour etc.
! radius-server dead-criteria time 5 tries 3 ! aaa new-model aaa group server radius FREERADIUS deadtime 60 ! 60 minutes server name RAD1 server name RAD2 ! aaa accounting update newinfo ! *** the config enables 'Interim-Update' accounting records - such as when call is connected, instead of just start and end records. *** ! aaa accounting connection CUBE-CDR start-stop group FREERADIUS ! radius server RAD1 address ipv4 10.123.123.101 key MySuperSecret ! *** This key (and the below one) - is used in the Radius client setup *** timeout 2 retransmit 1 ! radius server RAD2 address ipv4 10.123.123.102 key MySuperSecret timeout 2 retransmit 1 ! gw-accounting aaa method CUBE-CDR ! ! This will be applied to dial-peers which are NOT to send accounting records voice class aaa 1000 accounting suppress ! ip radius source-interface Loopback0 ! dial-peer 100 voip description This is the dial-peer used to send and receive calls to the PSTN Provider ! *** NOTE - we do NOT configure voice-class aaa 1000 on this dial-peer - otherwise we would NOT get sent a Radius accounting record session transport udp incoming called e164-pattern-map 100 destination dpg 100 voice-class codec 100 offer-all dtmf-relay rtp-nte no vad ! dial-peer 200 voip description This is an example dial-peer to send calls to CUCM ! *** NOTE - we DO configure voice-class aaa 1000 on this dial-peer - as we do NOT want a Radius Record for calls active on this dial-peer (otherwise we will get duplicate records and not know if the call is a "incoming" or "outgoing" call) incoming called e164-pattern-map 200 destination dpg 200 voice-class aaa 1000 ! *** This line supresses Radius records for this dialpeer *** incoming called 3164-pattern-map 131 voice-class codec 100 offer-all dtmf-relay rtp-nte no vad !
Checking Repo list
dnf repolist
Check config (before restarting)
sudo radiusd -XC
Status, Start, Stop, Restart FreeRadius
systemctl status radiusd systemctl start radiusd systemctl stop radiusd systemctl restart radiusd
Which Services are set to auto startup
systemctl list-unit-files --type=service --state=enabled
Which Services are set running
systemctl list-units --type=service --state=running
Check Firewall ports
sudo firewall-cmd --list-all
List all the Repositorities, their IDs, their names and if they are enabled
sudo dnf repolist --all
Just lists the Enabled Repositorities Names
sudo dnf repolist --enabled
sudo yum install freeradius
sudo systemctl enable radiusd
sudo rm /etc/raddb/sites-enabled/inner-tunnel
sudo rm /etc/raddb/sites-enabled/default
sudo vi /etc/raddb/sites-available/cube-accounting
server cube-accounting { listen { type = acct ipaddr = * port = 1813 proto = udp } recv Accounting-Request { accounting } accounting { detail } }
sudo chown root:radiusd /etc/raddb/sites-available/cube-accounting
sudo ln -s /etc/raddb/sites-available/cube-accounting /etc/raddb/sites-enabled/cube-accounting
sudo chown -h root:radiusd /etc/raddb/sites-enabled/cube-accounting
sudo rm /etc/raddb/mods-enabled/eap
sudo radiusd -XC
sudo systemctl start radiusd
sudo systemctl status radiusd
sudo firewall-cmd --permanent --add-port=1813/udp
sudo firewall-cmd --reload
sudo vi /etc/raddb/clients.conf
client cisco-cube-test { ipaddr = 10.123.123.0/24 secret = MySuperSecret! }
Accounting files will be stored here:
sudo ls -al /var/log/radius/radacct
Install the Rest Module
sudo dnf install freeradius-rest
Lets update the simple site config we have above to now call the rest module for start and stop events:
sudo vi /etc/raddb/sites-available/cube-accounting
server cube-accounting { listen { type = acct ipaddr = * port = 1813 proto = udp } recv Accounting-Request { accounting } accounting { # Log Records to detail text file - useful for troubleshooting and contains other info - taht we are not sening to teh database (GOR) detail # # Voice START records only # # Voice Interim-Update records only - which also have the 'h323-connect-time' attribute. if (&Acct-Status-Type == Interim-Update) { if (&h323-connect-time){ if (&h323-setup-time && &h323-setup-time =~ /^h323-setup-time=(.*)$/) { update request { &Tmp-String-0 := "%{1}" } } if (&h323-gw-id && &h323-gw-id =~ /^h323-gw-id=(.*)$/) { update request { &Tmp-String-1 := "%{1}" } } if (&h323-conf-id && &h323-conf-id =~ /^h323-conf-id=(.*)$/) { update request { &Tmp-String-2 := "%{1}" } } if (&h323-call-origin && &h323-call-origin =~ /^h323-call-origin=(.*)$/) { update request { &Tmp-String-3 := "%{1}" } } if (&h323-connect-time =~ /^h323-connect-time=(.*)$/) { update request { &Tmp-String-4 := "%{1}" } } foreach &Cisco-AVPair { if ("%{Foreach-Variable-0}" =~ /^gw-rxd-cdn=ton:0,npi:0,#:(.+)$/) { update request { Tmp-String-5 := "%{1}" } break } } rest_voice_connect } } # # Voice STOP records only # elsif (&Acct-Status-Type == Stop) { if (&h323-conf-id && &h323-conf-id =~ /^h323-conf-id=(.*)$/) { update request { &Tmp-String-0 := "%{1}" } } if (&h323-connect-time && &h323-connect-time =~ /^h323-connect-time=(.*)$/) { update request { &Tmp-String-1 := "%{1}" } } if (&h323-disconnect-time && &h323-disconnect-time =~ /^h323-disconnect-time=(.*)$/) { update request { &Tmp-String-2 := "%{1}" } } if (&h323-disconnect-cause && &h323-disconnect-cause =~ /^h323-disconnect-cause=(.*)$/) { update request { &Tmp-String-3 := "%{1}" } } if (&h323-remote-address && &h323-remote-address =~ /^h323-remote-address=(.*)$/) { update request { &Tmp-String-4 := "%{1}" } } rest_voice_stop } # # Interim-Update and other accounting types: # no REST request - GOR reminder - check out the following Cisco Command: aaa accounting update newinfo # } }
We will send a Rest request on start and end events. Lets create and configure two rest config files.
sudo vi /etc/raddb/mods-available/rest_voice_connect
rest rest_voice_connect { connect_uri = "http://127.0.0.1" accounting { uri = "${..connect_uri}/api/v1/radius-voice-calls.php" method = "put" body = "json" data = '{"Acct-Session-Id": "%{jsonquote:%{Acct-Session-Id}}","h323-setup-time": "%{jsonquote:%{Tmp-String-0}}","h323-gw-id": "%{jsonquote:%{Tmp-String-1}}","h323-conf-id": "%{jsonquote:%{Tmp-String-2}}","h323-call-origin": "%{jsonquote:%{Tmp-String-3}}","h323-connect-time": "%{jsonquote:%{Tmp-String-4}}","gw-cdn": "%{jsonquote:%{Tmp-String-5}}","Called-Station-Id": "%{jsonquote:%{Called-Station-Id}}","User-Name": "%{jsonquote:%{User-Name}}","NAS-IP-Address": "%{jsonquote:%{NAS-IP-Address}}","Acct-Status-Type": "Interim-Update"}' } }
sudo vi /etc/raddb/mods-available/rest_voice_stop
rest rest_voice_stop { connect_uri = "http://127.0.0.1" accounting { uri = "${..connect_uri}/api/v1/radius-voice-calls.php" method = "put" body = "json" data = '{"Acct-Session-Id": "%{jsonquote:%{Acct-Session-Id}}","h323-conf-id": "%{jsonquote:%{Tmp-String-0}}","h323-connect-time": "%{jsonquote:%{Tmp-String-1}}","h323-disconnect-time": "%{jsonquote:%{Tmp-String-2}}","h323-disconnect-cause": "%{jsonquote:%{Tmp-String-3}}","h323-remote-address": "%{jsonquote:%{Tmp-String-4}}","Acct-Session-Time": "%{Acct-Session-Time}","User-Name": "%{jsonquote:%{User-Name}}" ,"Acct-Status-Type": "Stop"}' } }
Create Symlinks and update the config files and links ownership.
sudo ln -s ../mods-available/rest_voice_connect /etc/raddb/mods-enabled/rest_voice_connect
sudo ln -s ../mods-available/rest_voice_stop /etc/raddb/mods-enabled/rest_voice_stop
sudo chown root:radiusd /etc/raddb/mods-available/rest_voice_connect
sudo chown root:radiusd /etc/raddb/mods-available/rest_voice_stop
sudo chown -h root:radiusd /etc/raddb/mods-enabled/rest_voice_connect
sudo chown -h root:radiusd /etc/raddb/mods-enabled/rest_voice_stop
We will have a Start, Interim-Update and Stop records. Start Records occur at call setup, Interim-Update on connect and Stop when call disconnected.
The hostname of the CUBE
This is the Cisco GUID and is also available for inbound calls to the Finesse Desktop via the ECC variable user.media.id
This lets us know if the radius record is coming from an inbound or outbound dialpeer.
The Called Party Number
The Calling Party Number
the IP address of the Cisco CUBE - and since our Radius config specifies to use the Loopback 0 this will be the IP address of Loopback 0 of the CUBE.
The Time in epoch time (to the second) - not quite as accurate at the h323-setup-time - which is in milliseconds.
Note - only available on 'Stop' Records
This allow us to know if the Calling or Called Party disconnected the call
| Value | Cisco's definition | Interpretation |
|---|---|---|
| 1 | Calling party located in PSTN | Calling party on the PSTN side released the call |
| 2 | Calling party located in VoIP network | Calling party on the VoIP side released the call |
| 3 | Called party located in PSTN | Called party on the PSTN side released the call |
| 4 | Called party located in VoIP network | Called party on the VoIP side released the call |
Since we are using a CUBE - we will only ever have values of '2' or '4'
Note - only available on 'Stop' Records
Note - that the STOP record has a lot more info, including Connect Time, which party disconnected the call and VoIP Quality info.
Wed Oct 14 11:01:22 2026
Acct-Session-Id = "000057D6"
h323-setup-time = "h323-setup-time=11:01:22.259 IRL Wed Oct 14 2026"
h323-gw-id = "h323-gw-id=CUBE-A.example.org"
h323-conf-id = "h323-conf-id=0B31D45A C6ED11F1 8491B15D B7BDE126"
h323-call-origin = "h323-call-origin=answer"
h323-call-type = "h323-call-type=VoIP"
Cisco-AVPair = "h323-incoming-conf-id=0B31D45A C6ED11F1 8491B15D B7BDE126"
Cisco-AVPair = "feature-vsa=fn:TWC,ft:10/14/2026 11:01:22.259,cgn:+35312399138,cdn:+35312612800,frs:0,fid:179819,fcid:B31D45AC6ED11F18491B15DB7BDE126,legID:287C402"
Called-Station-Id = "+35312612800"
Cisco-AVPair = "call-id=SDr82a401-1a877a5a8a67402e82fd3232747012bb-a04bk71030"
Cisco-AVPair = "iphop=count:1"
Cisco-AVPair = "iphop=hop1:10.123.123.36"
Cisco-AVPair = "subscriber=Unknown"
Cisco-AVPair = "session-protocol=sipv2"
Cisco-AVPair = "gw-rxd-cdn=ton:0,npi:0,#:+35312612800"
User-Name = "+35312399138"
Cisco-AVPair = "connect-progress=Call Up"
Acct-Authentic = Local
Acct-Status-Type = Start
Service-Type = Login-User
NAS-IP-Address = 10.123.123.22
Acct-Delay-Time = 0
Timestamp = 1791972082
Wed Oct 14 11:01:45 2026
Acct-Session-Id = "000057D6"
h323-setup-time = "h323-setup-time=11:01:22.259 IRL Wed Oct 14 2026"
h323-gw-id = "h323-gw-id=CUBE-A.example.org"
h323-conf-id = "h323-conf-id=0B31D45A C6ED11F1 8491B15D B7BDE126"
h323-call-origin = "h323-call-origin=answer"
h323-call-type = "h323-call-type=VoIP"
Cisco-AVPair = "h323-incoming-conf-id=0B31D45A C6ED11F1 8491B15D B7BDE126"
Cisco-AVPair = "feature-vsa=fn:TWC,ft:10/14/2026 11:01:22.259,cgn:+35312399138,cdn:+35312612800,frs:0,fid:179819,fcid:B31D45AC6ED11F18491B15DB7BDE126,legID:287C402"
Called-Station-Id = "12800"
Cisco-AVPair = "call-id=SDr82a401-1a877a5a8a67402e82fd3232747012bb-a04bk71030"
Cisco-AVPair = "iphop=count:1"
Cisco-AVPair = "iphop=hop1:10.123.123.36"
Cisco-AVPair = "subscriber=Unknown"
Cisco-AVPair = "session-protocol=sipv2"
Cisco-AVPair = "gw-rxd-cdn=ton:0,npi:0,#:+35312612800"
h323-connect-time = "h323-connect-time=11:01:22.317 IRL Wed Oct 14 2026"
h323-disconnect-time = "h323-disconnect-time=11:01:45.222 IRL Wed Oct 14 2026"
h323-disconnect-cause = "h323-disconnect-cause=10"
h323-remote-address = "h323-remote-address=10.123.123.36"
Cisco-AVPair = "gw-rxd-cgn=ton:0,npi:0,pi:0,si:0,#:+35312399138"
Acct-Input-Octets = 222304
Acct-Output-Octets = 220800
Acct-Input-Packets = 1124
Acct-Output-Packets = 1104
Cisco-AVPair = "MOS-Con=0.0000"
Cisco-AVPair = "round-trip-delay=0 ms"
Cisco-AVPair = "receive-delay=0 ms"
Cisco-AVPair = "voice-quality-total-packet-loss=0.00 %"
Cisco-AVPair = "voice-quality-out-of-order=0.00 %"
Acct-Session-Time = 23
Cisco-AVPair = "release-source=2"
h323-voice-quality = "h323-voice-quality=0"
Cisco-AVPair = "remote-media-address=10.123.123.36"
User-Name = "+35312399138"
Acct-Authentic = Local
Acct-Status-Type = Stop
Service-Type = Login-User
NAS-IP-Address = 10.123.123.22
Acct-Delay-Time = 0
Timestamp = 1791972105
FreeRadius does NOT use logrotate to rotate the logs. It has configured in the Radius config file /etc/raddb/mods-enabled/detail the following line:
filename = ${radacctdir}/%{%{Packet-Src-IP-Address}:-%{Packet-Src-IPv6-Address}}/detail-%Y%m%d
This means that freeRadius itself is rotating the log file every day.
So the simpliest solution to radius detail files files older 30 days is the following:
sudo vi /etc/cron.daily/radiusd-detail-cleanup
#!/bin/bash find /var/log/radius/radacct -type f -name "detail-*" -mtime +30 -delete
sudo chmod 755 /etc/cron.daily/radiusd-detail-cleanup
Counts number of detail files
sudo find /var/log/radius/radacct -type f -name "detail-*" | wc -l
Counts number of detail files older than 30 days
sudo find /var/log/radius/radacct -type f -name "detail-*" -mtime +30 | wc -l
Details file space used in the directory
sudo du -sh /var/log/radius/radacct
The Radius Files are owned by the user:group of radiusd:radiusd But the Group does not natively have any read access (or executable access on the directory). So enable group read and executable access on the directories and read access on the log files and then add the relavant users to the radiusd group.
So that any new detail file will also allow the same access edit the following config file
sudo vi /etc/raddb/mods-enabled/detail
permissions = 0600
permissions = 0640
systemctl restart radiusd
sudo find /var/log/radius -type d -exec chmod g+rx {} +
sudo find /var/log/radius -type f -exec chmod g+r {} +
sudo usermod -aG radiusd <user>
getent group radiusd
Radius will use Rest to send requests to a webserver running PHP - which will insert the data into a SQL ddatabase.
Install Apache
sudo yum install httpd
Enable for Auto Startup
sudo systemctl enable httpd
Start Apache
sudo systemctl start httpd
Check Apache Status
sudo systemctl status httpd
Note by default Apache will be configured to run on port 80 - but we will change this to listen only on the loopback address on port 80 so only internal connections to apache can be made.
Use the below command to edit the httpd config file and change “Listen 80” to “Listen 127.0.0.1:80” and then restart the httpd service
sudo vi /etc/httpd/conf/httpd.conf
you can confirm this will no longer work by using netcat (nc) on any address other than 127.0.0.1 by simple testing 127.0.0.2 (this would have worked before this change) - example output below - where the first attempt fails:
[user]$ nc -zv 127.0.0.2 80 Ncat: Version 7.92 ( https://nmap.org/ncat ) Ncat: Connection refused. [user]$ nc -zv 127.0.0.1 80 Ncat: Version 7.92 ( https://nmap.org/ncat ) Ncat: Connected to 127.0.0.1:80. Ncat: 0 bytes sent, 0 bytes received in 0.01 seconds. [user]$ nc -zv localhost 80 Ncat: Version 7.92 ( https://nmap.org/ncat ) Ncat: Connected to 127.0.0.1:80. Ncat: 0 bytes sent, 0 bytes received in 0.01 seconds.
Reference1: https://www.redhat.com/sysadmin/webserver-use-https
Reference2: https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/5/html/deployment_guide/s1-httpd-secure-server
Redhat 8 Security guide - https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/security_hardening/securing-services_security-hardening#securing-apache-http-servers_securing-http-servers
//The mod_ssl configuration file is located at /etc/httpd/conf.d/ssl.conf. For this file to be loaded, and hence for mod_ssl to work, you must have the statement Include conf.d/*.conf in the /etc/httpd/conf/httpd.conf file. This statement is included by default in the default Apache HTTP Server configuration file. //
sudo yum install httpd mod_ssl
sudo systemctl restart httpd
The SSL module install will have created it own SSL Cert with just the hostname and not the FQDN.
So you will need to create your CSR (and if you want a temp Self Signed Cert).
The SSL Conf - ssl.conf file is located in /etc/httpd/conf.d/ directory
You must also open the Firewall on port 443 and then reload the Firewall - as per below commands:
open Firewall on port 443
sudo firewall-cmd --permanent --add-port=443/tcp
Reload the Firewall
sudo firewall-cmd --reload
A respository with PHP8.5 must be download and enabled below is an example of the repo TEMP_PHP8_5_PHP8_5_10
sudo dnf --enablerepo="TEMP_PHP8_5_PHP8_5_10" install php85
Install PHP system pasth (e.g. so you can run php from command line)
sudo dnf --enablerepo="TEMP_PHP8_5_PHP8_5_10" install php85-syspaths
Reminder
/var/www/html/
/etc/opt/remi/php85/php-fpm.conf
/var/opt/remi/php85/log/php-fpm/error.log
/var/opt/remi/php85/log/php-fpm/www-error.log
To check Updates are available:
sudo dnf check-update 'php*'
To install update
sudo dnf update 'php*'
sudo ACCEPT_EULA=Y dnf install -y msodbcsql18 unixODBC-devel gcc gcc-c++ make autoconf
libtool-ltdl is needed to be able to make pdo_sqlsrv successfully.
sudo mv libtool-ltdl-devel-2.4.6-46.el9.x86_64.rpm /tmp
sudo dnf install /tmp/libtool-ltdl-devel-2.4.6-46.el9.x86_64.rpm
https://github.com/microsoft/msphpsql/releases/download/v5.13.3/pdo_sqlsrv-5.13.3.tgz
tar -xzf pdo_sqlsrv-5.13.3.tgz
cd pdo_sqlsrv-5.13.3
sudo dnf list --installed 'php*'
sudo dnf --enablerepo="TEMP_PHP8_5_PHP8_5_10" install php85-php-devel
/opt/remi/php85/root/usr/bin/phpize
./configure --with-php-config=/opt/remi/php85/root/usr/bin/php-config
make
sudo make install
sudo bash -c 'echo "extension=pdo_sqlsrv.so" > /etc/opt/remi/php85/php.d/40-pdo_sqlsrv.ini'
sudo systemctl restart php85-php-fpm
sudo chmod o+xr /var/opt/remi/php85/log/php-fpm
sudo vi /etc/logrotate.d/php85-php-fpm
/var/opt/remi/php85/log/php-fpm/*log {
missingok
notifempty
sharedscripts
delaycompress
postrotate
/bin/kill -SIGUSR1 `cat /var/opt/remi/php85/run/php-fpm/php-fpm.pid 2>/dev/null` 2>/dev/null || true
endscript
}
/var/opt/remi/php85/log/php-fpm/*log {
daily
rotate 30
missingok
notifempty
sharedscripts
nocompress
dateext
create
postrotate
/bin/kill -SIGUSR1 `cat /var/opt/remi/php85/run/php-fpm/php-fpm.pid 2>/dev/null` 2>/dev/null || true
endscript
}
sudo logrotate -d /etc/logrotate.conf
sudo logrotate -f /etc/logrotate.conf
this webpage just logs the request to log file and always gives back a 200 OK.
<?php http_response_code(200); header('Content-Type: application/json'); $ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown'; // Collect request headers $headers = function_exists('getallheaders') ? getallheaders() : []; if (!is_array($headers)) { $headers = []; } // Read the raw request body $body = file_get_contents('php://input'); // Build a single-line JSON log entry $log = [ 'time' => date('c'), 'ip' => $ip, 'method' => $_SERVER['REQUEST_METHOD'] ?? 'unknown', 'uri' => $_SERVER['REQUEST_URI'] ?? '/', 'headers' => $headers, 'body' => $body, ]; // Log everything as one line error_log(json_encode( $log, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE )); // Return a Successful response JSON object // If we send below JSON - radius parses this and sends this back to the Gateway CUBE - this is probably a nice thing? You can see this Reply-Message in the *debug radius* output - but I have commented it out here. // Note you **cannot** reply back with any values - this is Reply-Message is a standard RADIUS attribute (Attribute 18) defined by the protocol. i.e. if you do - you will get an error parising the response in radius - but it still shouldn't cause an issue. // The "Reply-Message" response is a human-readable text message that the RADIUS server can send back to the NAS/client. //echo '{"Reply-Message": "OK"}'; // // in thie example we will just send back an empty 200 OK - this also means no parsing is done by Radius so no error occurs. The CUBE always get a positive response for the accounting record whatever happens. But if was set - we would ALSO send back to CUBE a Reply-Message OK. // Send back a 200 OK (empty content) http_response_code(200); ?>
Below command saves the CSR file to the /home/user1/ directory. the Below also adds two SAN records - update as required (making one the same as the CN which you will fill in when prompted).
sudo openssl req -newkey rsa:2048 -addext "subjectAltName = DNS:myhomename.dmz.example.com, DNS:webchat.example.com" -nodes -keyout /etc/pki/tls/private/httpd.key -out /home/user1/httpd.csr
Just use
sudo more /home/user1/httpd.csr
to read the the CSR file and copy to a text file and request for it to be signed. Or use
sudo chmod o+r /home/user1/httpd.csr
so that all (the other group) can read it and copy via WinSCP
Copy the imported signed cert as /etc/pki/tls/certs/httpd.crt
sudo vi /etc/pki/tls/certs/httpd.crt
Update the ssl.conf configuration file to utilise the certificate key and cert as per below.
sudo vi /etc/httpd/conf.d/ssl.conf
And edit the SSLCertificateFile and SSLCertificateKeyFile lines with the new relevant names - example below
SSLCertificateFile /etc/pki/tls/certs/httpd.crt SSLCertificateKeyFile /etc/pki/tls/private/httpd.key
Restart the apache server.
sudo systemctl restart httpd
If the Radius server is up - but Apache is down, the records won't be able to get added to the database, as we only try its local apache server.
To resolve this issue we will create a service monitor - which will stop the radius service if and when apache probe is unavailable. i.e. attempt every 10 seconds, and after 3 failed attempts shutdown the service. If we get 2 good attempts, we start it back up.
sudo vi /usr/local/sbin/apache-radius-monitor.sh
#!/bin/bash # # apache-radius-monitor.sh # # Probes Apache every INTERVAL seconds. After FAIL_THRESHOLD consecutive failed # probes it stops radiusd (so the CUBE fails over to the secondary RADIUS server). # After OK_THRESHOLD consecutive good probes it starts radiusd again, but ONLY if # this script was the one that stopped it (a radiusd you stopped by hand stays stopped). # # Install to: /usr/local/sbin/apache-radius-monitor.sh (chmod 755) # ---------------- Settings ---------------- URL="http://localhost/api/probe.php" INTERVAL=10 # seconds between probes CURL_TIMEOUT=3 # seconds before a probe counts as failed FAIL_THRESHOLD=3 # consecutive failures before stopping radiusd (~30s) OK_THRESHOLD=2 # consecutive successes before starting radiusd (~20s) SERVICE="radiusd" LOGFILE="/var/log/apache-radius-monitor.log" STATE_FILE="/run/apache-radius-monitor.stopped" # exists only if WE stopped radiusd MAIL_TO="" # e.g. "[email protected]". Leave empty to disable email alerts. # ------------------------------------------ log() { echo "$(date '+%Y-%m-%d %H:%M:%S') $*" >> "$LOGFILE" } alert() { log "$1" if [ -n "$MAIL_TO" ] && command -v mail >/dev/null 2>&1; then echo "$1" | timeout 30 mail -s "$(hostname -s): $1" "$MAIL_TO" || log "WARNING: email send failed" fi } probe() { code=$(curl -s -o /dev/null -w '%{http_code}' --max-time "$CURL_TIMEOUT" "$URL") [ "$code" = "200" ] } fails=0 oks=0 log "Monitor started: url=$URL interval=${INTERVAL}s stop_after=${FAIL_THRESHOLD} start_after=${OK_THRESHOLD}" while true; do cycle_start=$SECONDS if probe; then fails=0 if [ -f "$STATE_FILE" ]; then oks=$((oks + 1)) log "Probe OK ($oks/$OK_THRESHOLD) while $SERVICE is stopped by monitor" if [ "$oks" -ge "$OK_THRESHOLD" ]; then if systemctl start "$SERVICE"; then rm -f "$STATE_FILE" oks=0 alert "Apache recovered - $SERVICE started" else alert "Apache recovered but FAILED to start $SERVICE (will retry)" fi fi else oks=0 fi else oks=0 fails=$((fails + 1)) log "Probe FAILED (HTTP ${code:-000}) $fails/$FAIL_THRESHOLD" if [ "$fails" -ge "$FAIL_THRESHOLD" ] && [ ! -f "$STATE_FILE" ]; then if systemctl is-active --quiet "$SERVICE"; then if systemctl stop "$SERVICE"; then touch "$STATE_FILE" alert "Apache down - $SERVICE stopped" else alert "Apache down but FAILED to stop $SERVICE (will retry)" fi fi # If $SERVICE is not active (e.g. stopped by an admin) leave it alone. fi fi elapsed=$((SECONDS - cycle_start)) remaining=$((INTERVAL - elapsed)) if [ "$remaining" -gt 0 ]; then sleep "$remaining" fi done
sudo chmod 755 /usr/local/sbin/apache-radius-monitor.sh
sudo vi /etc/systemd/system/apache-radius-monitor.service
[Unit] Description=Apache probe monitor - stops/starts radiusd for RADIUS failover After=network.target httpd.service radiusd.service [Service] Type=simple ExecStart=/usr/local/sbin/apache-radius-monitor.sh Restart=always RestartSec=5 User=root [Install] WantedBy=multi-user.target
sudo chmod 644 /etc/systemd/system/apache-radius-monitor.service
sudo vi /etc/logrotate.d/apache-radius-monitor
/var/log/apache-radius-monitor.log {
weekly
rotate 8
compress
missingok
notifempty
copytruncate
}
sudo chmod 644 /etc/logrotate.d/apache-radius-monitor
Then run the following commands to install this service and monitor the log file
sudo systemctl daemon-reload
sudo systemctl enable --now apache-radius-monitor
tail -f /var/log/apache-radius-monitor.log
Check PHP error logs
sudo more /var/opt/remi/php85/log/php-fpm/www-error.log
Find which radius files have a certain string - example
sudo grep -rn "0034A582" /var/log/radius/radacct/
Check Radius Logs
sudo more /var/log/radius/radius.log
sudo ls -al /var/log/radius/
First stop the service
sudo systemctl stop radiusd
then start it from command line with the “-X” argument
sudo radiusd -X
Linux Socket statistics command - you can find what process are used for:
sudo ss -lunp | grep -E ':1812|:1813'
| Part | Meaning |
|---|---|
| sudo | Runs the command with administrator privileges, needed to see process details for some sockets. |
| ss | Stands for socket statistics. It displays network sockets and their state. |
| -l | Shows listening sockets only. |
| -u | Shows UDP sockets. RADIUS commonly uses UDP. |
| -n | Shows numeric addresses and port numbers instead of resolving names. |
| -p | Shows the process using each socket, when permitted. |
| grep -E | Filters the output using an extended regular expression. |
| ':1812' | Matches lines containing port 1812 |
$ sudo ss -lunp | grep -E ":1813"
UNCONN 0 0 0.0.0.0:1813 0.0.0.0:* users:(("radiusd",pid=848159,fd=37))
You could kill this process using the kill command, but you would only do this it was NOT possible to stop via systemctl
sudo kill -TTERM <PID>
Allow Apache access to the network.
We first confirm that SELinux is enforced:
getenforce
If it is set to “Enforcing” we then confirm that httpd is NOT allowed access to the network:
getsebool httpd_can_network_connect
We then enable it
sudo setsebool -P httpd_can_network_connect on
<?php require_once "./include/sql_db.php"; header('Content-Type: application/json'); http_response_code(200); $response = [ "Host" => getDbHost(), "InService" => false ]; try { $pdo = getDbConnection(); // Lightweight health check $stmt = $pdo->query("SELECT 1 AS Alive"); $result = $stmt->fetch(); if (!empty($result['Alive'])) { $response['InService'] = true; } } catch (Throwable $e) { $response['sqlError'] = $e->getMessage(); } echo json_encode($response, JSON_PRETTY_PRINT); ?>
And put the below file into a subfolder 'include'
<?php $host = "mysqldb.mydomain.com"; function getDbConnection(): PDO { global $host; $database = "myDatabase"; $username = "myUsername"; $password = "myPa$$w0rd!"; // Note we add TrustServerCertificate=true to the below - so we trust the SQL Server certificate, even if self signed etc. $dsn = "sqlsrv:Server={$host};Database={$database};TrustServerCertificate=true;LoginTimeout=5"; return new PDO( $dsn, $username, $password, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ] ); } function getDbHost(): string { global $host; return "$host"; } ?>
A PFX (Personal Information Exchange) file is a password-protected binary container file based on the PKCS#12 standard that bundles a digital certificate (public key), its corresponding private key, and any intermediate or root certificates together in a single archive
more /etc/httpd/conf.d/ssl.conf
sudo openssl pkcs12 -export -out ~/radius-server-a.pfx -inkey /etc/pki/tls/private/httpd_20261029.key -in /etc/pki/tls/certs/httpd_20261029.crt
cd ~
sudo chmod 644 radius-server-a.pfx
sudo cd /var/www/html
sudo tar -cvf ~/web.tar ./
sudo chomd 644 ~/web.tar
sudo tar -xvf web.tar -C /var/www/html/