Skip to content

Instantly share code, notes, and snippets.

@basamoahjnr
Last active October 31, 2025 02:47
Show Gist options
  • Select an option

  • Save basamoahjnr/d2b426a3460d58e016590fea3aa6eaeb to your computer and use it in GitHub Desktop.

Select an option

Save basamoahjnr/d2b426a3460d58e016590fea3aa6eaeb to your computer and use it in GitHub Desktop.
#!/bin/bash
# RADIUSdesk + FreeRADIUS Installation Script for Ubuntu 24.04
# Combines RADIUSdesk LEMP stack installation with FreeRADIUS configuration
# Based on official documentation from https://radiusdesk.com/wiki24/install_24_4
set -euo pipefail # Exit on any error
# Color codes for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color (used to reset text color)
DEFAULT_FILE="$HOME/.rd_defaults"
DEFAULT_PASSWORD=$(openssl rand -base64 12 | tr -dc 'A-Za-z0-9' | head -c 16)
TEST_PASSWORD=$(openssl rand -base64 12 | tr -dc 'A-Za-z0-9' | head -c 16)
USERNAME='system'
# Logging functions
log() {
echo -e "${GREEN}[LOG:$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
}
warning() {
echo -e "${YELLOW}[WARNING:$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
}
error() {
echo -e "${RED}[ERROR:$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
exit 1
}
info() {
echo -e "${BLUE}[INFO:$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
}
# Check if running as root
((EUID != 0)) && error "Run as root."
# Check if command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
create_system_user (){
if id "$USERNAME" &>/dev/null; then
warning "User '$USERNAME' already exists."
else
info "Creating system user '$USERNAME'..."
sudo adduser --system --no-create-home --group "$USERNAME"
info "System user '$USERNAME' created."
fi
}
# Check Ubuntu version compatibility
check_ubuntu_version() {
info "Checking Ubuntu version..."
if ! grep -q "Ubuntu 24.04" /etc/os-release; then
warning "This script is designed for Ubuntu 24.04. Continue anyway? (Y/n): "
read -r response
response=${response:-Y}
if [[ ! "$response" =~ ^[Yy]$ ]]; then
exit 0
fi
fi
}
# Database configuration
get_credentials() {
echo
info "Database Configuration"
echo "Default database name: rd"
echo "Default database user: rd"
echo "Default database password can be found in $DEFAULT_FILE"
echo -e "database default username: rd\ndefault database name: rd\ndefault password: $DEFAULT_PASSWORD\ntest password: $TEST_PASSWORD\n" > "$DEFAULT_FILE"
chmod 600 "$DEFAULT_FILE"
echo
read -p "Do you want to use default database credentials? (Y/n): " use_defaults
use_defaults=${use_defaults:-Y}
if [[ "$use_defaults" =~ ^[Yy]$ ]]; then
DB_NAME="rd"
DB_USER="rd"
DB_PASS="$DEFAULT_PASSWORD"
else
while true; do
read -p "Database name [rd]: " DB_NAME
read -p "Database user [rd]: " DB_USER
read -s -p "Database password: " DB_PASS; echo
read -s -p "Confirm Database password: " DB_PASS_CONFIRM; echo
if [ "$DB_PASS" = "$DB_PASS_CONFIRM" ]; then
break
else
error "Passwords don't match. Please try again."
fi
done
fi
DB_NAME=${DB_NAME:-rd}
DB_USER=${DB_USER:-rd}
DB_PASS=${DB_PASS:-$DEFAULT_PASSWORD}
}
# FreeRADIUS client secret configuration
get_radius_secret() {
echo
info "FreeRADIUS Client Configuration"
echo "This secret will be used by network devices to authenticate with FreeRADIUS"
echo "Default secret can be found in $DEFAULT_FILE after installation"
read -p "Do you want to use the default FreeRADIUS client secret? (Y/n): " use_default_secret
use_default_secret=${use_default_secret:-Y}
if [[ "$use_default_secret" =~ ^[Yy]$ ]]; then
RADIUS_SECRET="$DEFAULT_PASSWORD"
else
while true; do
read -s -p "Enter FreeRADIUS client secret: " RADIUS_SECRET; echo
read -s -p "Confirm FreeRADIUS client secret: " RADIUS_SECRET_CONFIRM; echo
if [ "$RADIUS_SECRET" = "$RADIUS_SECRET_CONFIRM" ]; then
break
else
error "Secrets don't match. Please try again."
fi
done
fi
if [ -z "$RADIUS_SECRET" ]; then
RADIUS_SECRET="$DEFAULT_PASSWORD"
fi
echo -e "radius secret: $RADIUS_SECRET\n" >> "$DEFAULT_FILE"
}
# System preparation
prepare_system() {
log "Updating system packages..."
apt-get update
apt-get upgrade -y
log "Installing required language pack..."
# apt-get install -y language-pack-en-base dnsutils
apt-get install -y nginx php8.3-fpm php-cli php-mysql php-gd php-curl \
php-xml php-mbstring php-intl php-sqlite3 mariadb-server php8.3-mysql \
git wget freeradius freeradius-mysql dnsutils \
libdatetime-perl libdbd-mysql-perl libdigest-hmac-perl \
libdatetime-format-rfc3339-perl eapoltest \
ufw
# Remove Apache if installed
if command_exists apache2; then
log "Removing Apache2..."
systemctl stop apache2.service || true
apt-get remove -y apache2 || true
fi
}
# Get public IP address
PUBLIC_IP=$(dig +short myip.opendns.com @resolver1.opendns.com)
# Install and configure Nginx
install_nginx() {
log "Installing Nginx..."
# apt-get install -y nginx
log "Starting and enabling Nginx..."
systemctl stop nginx.service || true
systemctl start nginx.service
systemctl enable nginx.service
info "Nginx installed successfully. You can test it by visiting http://$PUBLIC_IP"
}
# Install and configure PHP-FPM
install_php() {
log "Installing PHP-FPM and required extensions..."
log "Enabling and starting PHP 8.3 FPM..."
systemctl enable php8.3-fpm
systemctl start php8.3-fpm
PHP_SOCKET="/var/run/php/php8.3-fpm.sock"
if [ ! -S "$PHP_SOCKET" ]; then
echo "Warning: PHP-FPM socket $PHP_SOCKET not found (yet). PHP-FPM may not be ready."
echo "If nginx -t later fails with fastcgi errors, ensure php8.3-fpm is running and the socket exists."
fi
NGX_SITE="/etc/nginx/sites-enabled/default"
NGX_BACKUP="/etc/nginx/sites-available/default.backup.$(date +%s)"
log "Configuring Nginx site - backing up existing file if present..."
if [ -f "$NGX_SITE" ]; then
cp -a "$NGX_SITE" "$NGX_BACKUP"
log "Backup saved to $NGX_BACKUP"
else
log "No existing $NGX_SITE found; proceeding to create a new one."
fi
log "Writing new server block to $NGX_SITE..."
tee "$NGX_SITE" > /dev/null <<'EOF'
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.php index.html index.htm index.nginx-debian.html;
server_name _;
location /cake4/rd_cake/node-reports/submit_report.json {
try_files $uri $uri/ /reporting/reporting.php;
}
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
}
location /cake4/rd_cake {
rewrite ^/cake4/rd_cake(.+)$ /cake4/rd_cake/webroot$1 break;
try_files $uri $uri/ /cake4/rd_cake/index.php$is_args$args;
}
location ~ ^/cake4/.+\.(jpg|jpeg|gif|png|ico|js|css)$ {
rewrite ^/cake4/rd_cake/webroot/(.*)$ /cake4/rd_cake/webroot/$1 break;
rewrite ^/cake4/rd_cake/(.*)$ /cake4/rd_cake/webroot/$1 break;
access_log off;
expires max;
add_header Cache-Control public;
}
location ~ /\.ht {
deny all;
}
error_page 404 /404.html;
location = /404.html { root /var/www/html; }
error_page 500 502 503 504 /50x.html;
location = /50x.html { root /var/www/html; }
}
EOF
log "Testing Nginx configuration (nginx -t)..."
# Capture output and exit code
NGX_TEST_OUTPUT="$(nginx -t 2>&1)"
NGX_TEST_EXIT=$?
if [ $NGX_TEST_EXIT -ne 0 ]; then
error "Nginx configuration test failed:"
echo "---- nginx -t output ----"
echo "$NGX_TEST_OUTPUT"
echo "-------------------------"
# attempt to restore backup if available
if [ -f "$NGX_BACKUP" ]; then
echo "Restoring previous config from $NGX_BACKUP..."
cp -a "$NGX_BACKUP" "$NGX_SITE"
# try reloading the previous config to bring nginx back to known state
systemctl reload nginx.service >/dev/null 2>&1 || true
echo "Previous config restored and nginx reloaded (best-effort)."
else
info "No backup available to restore. Please inspect $NGX_SITE and /var/log/nginx/error.log"
fi
return 1
fi
info "Nginx configuration OK:"
echo "$NGX_TEST_OUTPUT"
log "Reloading Nginx to apply new configuration..."
systemctl reload nginx.service
log "Creating PHP test file..."
echo '<?php phpinfo(); ?>' | tee /var/www/html/test.php > /dev/null
info "PHP installed successfully. Test it by visiting http://$PUBLIC_IP/test.php"
return 0
}
# Install and configure MariaDB
install_mariadb() {
log "Installing MariaDB..."
# apt-get install -y mariadb-server php8.3-mysql
log "Enabling and starting MariaDB..."
systemctl enable mariadb
systemctl restart mariadb
log "Configuring MariaDB..."
# Disable strict mode
tee /etc/mysql/conf.d/disable_strict_mode.cnf > /dev/null << 'EOF'
[mysqld]
sql_mode=IGNORE_SPACE,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
EOF
# Enable event scheduler
tee /etc/mysql/conf.d/enable_event_scheduler.cnf > /dev/null << 'EOF'
[mysqld]
event_scheduler=on
EOF
systemctl restart mariadb
log "Populating timezone information in MariaDB..."
mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root mysql || warning "Some timezone errors may be normal"
info "MariaDB installed and configured successfully"
}
# Setup database for RADIUSdesk
setup_database() {
log "Creating RADIUSdesk database and user..."
mysql -u root << EOF
CREATE DATABASE IF NOT EXISTS ${DB_NAME};
GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'127.0.0.1' IDENTIFIED BY '${DB_PASS}';
GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'localhost' IDENTIFIED BY '${DB_PASS}';
FLUSH PRIVILEGES;
EOF
info "Database '${DB_NAME}' created with user '${DB_USER}'"
}
# Install RADIUSdesk
install_radiusdesk() {
log "Installing additional required packages..."
# apt-get install -y git wget
systemctl restart php8.3-fpm
log "Cloning RADIUSdesk repositories..."
cd /var/www
if [ ! -d "rdcore" ]; then
git clone https://github.com/RADIUSdesk/rdcore.git
else
cd rdcore && git pull && cd ..
fi
# Clone mobile UI repository
if [ ! -d "rd_mobile" ]; then
git clone https://github.com/RADIUSdesk/rd_mobile.git
else
warning "rd_mobile directory already exists, skipping clone"
fi
log "Creating symbolic links..."
cd /var/www/html
rm -f rd cake4 login conf_dev reporting rd_mobile 2>/dev/null || true
ln -s ../rdcore/rd ./rd
ln -s ../rdcore/cake4 ./cake4
ln -s ../rdcore/login ./login
ln -s ../rdcore/AmpConf/build/production/AmpConf ./conf_dev
ln -s ../rdcore/cake4/rd_cake/setup/scripts/reporting ./reporting
ln -s ../rd_mobile/build/production/RdMobile ./rd_mobile
ln -s ../rd_connect/build/production/RdConnect ./rd_connect
log "Setting up directories and permissions..."
mkdir -p /var/www/html/cake4/rd_cake/logs
mkdir -p /var/www/html/cake4/rd_cake/webroot/files/imagecache
mkdir -p /var/www/html/cake4/rd_cake/tmp
log "Set proper ownership for web server..."
chown -R www-data:www-data /var/www/html/cake4/rd_cake/tmp
chown -R www-data:www-data /var/www/html/cake4/rd_cake/logs
chown -R www-data:www-data /var/www/html/cake4/rd_cake/webroot/img/realms
chown -R www-data:www-data /var/www/html/cake4/rd_cake/webroot/img/dynamic_details
chown -R www-data:www-data /var/www/html/cake4/rd_cake/webroot/img/dynamic_photos
chown -R www-data:www-data /var/www/html/cake4/rd_cake/webroot/img/access_providers
chown -R www-data:www-data /var/www/html/cake4/rd_cake/webroot/img/hardwares
chown -R www-data:www-data /var/www/html/cake4/rd_cake/webroot/files/imagecache
}
# Populate database with RADIUSdesk schema
populate_database() {
log "Populating database with RADIUSdesk schema..."
if [ -f /var/www/html/cake4/rd_cake/setup/db/rd.sql ]; then
mysql -u root "${DB_NAME}" < /var/www/html/cake4/rd_cake/setup/db/rd.sql
info "Database populated successfully"
else
error "RADIUSdesk database schema file not found"
fi
}
# fix stuck on loading screen
change_default_radius_database_information(){
local file="/var/www/html/cake4/rd_cake/config/app_local.php"
local current_pass=$(grep -Po "'password'\s*=>\s*'\K[^']+" "$file")
# local current_db=$(grep -Po "'database'\s*=>\s*'\K[^']+" "$file")
# Update username if different
# if [[ "$current_user" != "$user" ]]; then
# sed -i "s/'username' *=> *'[^']*'/'username' => '$user'/g" "$file"
# echo "Username updated to '$user'"
# else
# echo "Username already set to '$user', skipping"
# fi
# Update password if different
info "Updating RADIUSdesk database login information ..."
if [[ "$current_pass" != "$DEFAULT_PASSWORD" ]]; then
sed -i "s/'password' *=> *'[^']*'/'password' => '$DEFAULT_PASSWORD'/g" "$file"
info "login information updated successfully"
else
warning "Password already set, skipping"
fi
# Update database if different
# if [[ "$current_db" != "$db" ]]; then
# sed -i "s/'database' *=> *'[^']*'/'database' => '$db'/g" "$file"
# echo "Database updated to '$db'"
# else
# echo "Database already set to '$db', skipping"
# fi
}
# Setup cron jobs
setup_cron() {
log "Setting up cron jobs for RADIUSdesk maintenance..."
if [ -f /var/www/html/cake4/rd_cake/setup/cron/cron4 ]; then
cp /var/www/html/cake4/rd_cake/setup/cron/cron4 /etc/cron.d/
info "Cron jobs installed successfully"
else
warning "Cron configuration file not found, skipping cron setup"
fi
}
#move production data to root /var/www/html
move_production_to_webroot() {
log "Moving production data directly to webroot..."
sudo cp -R /var/www/html/rd/build/production/Rd/* /var/www/html/
log "Production data moved to webroot successfully"
log "Restarting nginx"
systemctl restart nginx
info "Nginx started successfully"
}
# Install FreeRADIUS and dependencies
initialize_freeradius() {
log "Enabling FreeRADIUS service..."
sudo systemctl enable freeradius
log "Starting FreeRADIUS service..."
if sudo systemctl start freeradius; then
log "FreeRADIUS started successfully."
else
log "Failed to start FreeRADIUS service. Stopping script."
exit 1
fi
log "Checking FreeRADIUS service status..."
systemctl status freeradius --no-pager
systemctl stop freeradius
}
# Configure FreeRADIUS with RADIUSdesk settings
configure_freeradius() {
log "Configuring FreeRADIUS for RADIUSdesk..."
# Stop FreeRADIUS service if running
if systemctl stop freeradius; then
log "FreeRADIUS stopped successfully"
else
log "Failed to stop FreeRADIUS service. Stopping script"
exit 1;
fi
# Backup original configuration
if [ -d "/etc/freeradius" ] && [ ! -d "/etc/freeradius.orig" ]; then
log "Backing up original FreeRADIUS configuration..."
mv /etc/freeradius /etc/freeradius.orig
fi
# Extract RADIUSdesk modified FreeRADIUS configuration
log "Installing RADIUSdesk FreeRADIUS configuration..."
if [ -f "/var/www/html/cake4/rd_cake/setup/radius/freeradius-radiusdesk.tar.gz" ]; then
tar xzf /var/www/html/cake4/rd_cake/setup/radius/freeradius-radiusdesk.tar.gz --directory /etc
# Set proper ownership
chown -R freerad: /etc/freeradius/3.0/
chown freerad:www-data /etc/freeradius
chown freerad:www-data /etc/freeradius/3.0
chown freerad:www-data /etc/freeradius/3.0/dictionary
# Create run directory
mkdir -p /var/run/freeradius
chown freerad: /var/run/freeradius
else
error "RADIUSdesk FreeRADIUS configuration file not found"
fi
}
# Configure dynamic clients with user-specified secret
configure_dynamic_clients() {
SRC="/etc/freeradius/3.0/sites-available/dynamic-clients"
DEST="/etc/freeradius/3.0/sites-enabled/dynamic-clients"
log "Configuring FreeRADIUS dynamic clients..."
if [ -L "$DEST" ]; then
log "Symlink already exists."
else
ln -s "$SRC" "$DEST" || { error "Linking failed"; return 1; }
log "Symlink created in sites-enabled."
fi
sed -i "s/testing123/$RADIUS_SECRET/g" "$SRC" \
&& info "Secret updated using $HOME/$DEFAULT_FILE" \
|| { error "Failed to update secret in $SRC"; return 1; }
}
# Make sure DEFAULT_PASSWORD is defined globally for testing purposes
update_freeradius_shared_password() {
local file="/etc/freeradius/3.0/clients.conf"
if [[ -z "$TEST_PASSWORD" ]]; then
error "Error: test passowrd for testing freeradius is not set"
return 1
fi
# Update the secret for the client named 'test' only
sed -i "s/^\s*secret\s*=.*/\tsecret = $TEST_PASSWORD/" "$file"
info "FreeRADIUS test client password updated for testing purposes on 127.0.0.1"
}
# Fix FreeRADIUS service configuration safely
fix_freeradius_service() {
log "Applying FreeRADIUS systemd service fixes..."
tee /lib/systemd/system/freeradius.service > /dev/null << 'EOF'
[Unit]
Description=FreeRADIUS multi-protocol policy server
After=syslog.target network.target
Documentation=man:radiusd(8) man:radiusd.conf(5) http://wiki.freeradius.org/ http://networkradius.com/doc/
[Service]
Type=forking
PIDFile=/run/freeradius/freeradius.pid
#EnvironmentFile=-/etc/default/freeradius
#ExecStartPre=/usr/sbin/freeradius $FREERADIUS_OPTIONS -Cxm -lstdout
ExecStart=/usr/sbin/freeradius $FREERADIUS_OPTIONS
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
# Fix tmpfiles configuration to prevent startup issues
log "Creating tmpfiles configuration for FreeRADIUS..."
mkdir -p /usr/lib/tmpfiles.d/
tee /usr/lib/tmpfiles.d/freeradius.conf > /dev/null << 'EOF'
d /run/freeradius 750 freerad freerad -
EOF
}
configure_freeradius_database() {
log "Configuring FreeRADIUS database connection..."
local sql_conf="/etc/freeradius/3.0/mods-enabled/sql"
if [ -f "$sql_conf" ]; then
# Replace existing parameters in the sql config
sudo sed -i "s|^\s*server\s*=.*| server = \"localhost\"|" "$sql_conf"
sudo sed -i "s|^\s*login\s*=.*| login = \"${DB_USER}\"|" "$sql_conf"
sudo sed -i "s|^\s*password\s*=.*| password = \"${DB_PASS}\"|" "$sql_conf"
# If radius_db is defined, update it; otherwise append it after 'password'
if grep -q "radius_db" "$sql_conf"; then
sudo sed -i "s|^\s*radius_db\s*=.*| radius_db = \"${DB_NAME}\"|" "$sql_conf"
else
sudo sed -i "/password =/a\ radius_db = \"${DB_NAME}\"" "$sql_conf"
fi
else
warning "FreeRADIUS SQL configuration file not found at $sql_conf"
return
fi
# Test database connectivity
log "Testing database connection..."
if mysql -u "${DB_USER}" -p"${DB_PASS}" -h localhost "${DB_NAME}" -e "SELECT COUNT(*) FROM radcheck;" >/dev/null 2>&1; then
info "Database connection test successful"
else
warning "Database connection test failed — please verify credentials"
fi
# --- Verification Step ---
log "Verifying FreeRADIUS SQL configuration values..."
if [ -f "$sql_conf" ]; then
echo " FreeRADIUS SQL Configuration Summary:"
echo " Database Connectivity Test Result:"
if mysql -u "${DB_USER}" -p"${DB_PASS}" -h localhost "${DB_NAME}" -e "SELECT COUNT(*) FROM radcheck;" >/dev/null 2>&1; then
echo "[OK] Successfully connected to database '${DB_NAME}' with user '${DB_USER}'"
else
echo "[ERROR] Failed to connect to database '${DB_NAME}' with user '${DB_USER}'"
fi
else
warning "Verification skipped: $sql_conf not found."
fi
}
# -----------------------------------------------------------------------------
# Updates /etc/freeradius/3.0/mods-config/perl/auto_add_dynamic_client.pl
# so its DBI connection uses the same DB credentials as FreeRADIUS SQL.
# The Perl script doesn’t read mods-enabled/sql automatically.
# Fixes: "DBI connect('database=rd;host=127.0.0.1;port=3306','rd',...) failed:
# Access denied for user 'rd'@'localhost' (using password: YES)" the password
is hard codded in the file and needs to be changed
# -----------------------------------------------------------------------------
update_freeradius_perl_db_dynamic_clients_credentials() {
local perl_script="/etc/freeradius/3.0/mods-config/perl/auto_add_dynamic_client.pl"
if [ ! -f "$perl_script" ]; then
echo "Script not found: $perl_script"
return 1
fi
local changed=0
# Update db_name
if [ -n "$DB_NAME" ]; then
current=$(sed -n "s/^\s*\$conf{'db_name'}\s*=\s*\"\([^\"]*\)\";/\1/p" "$perl_script")
if [ "$current" != "$DB_NAME" ]; then
sudo sed -i "s|\(\$conf{'db_name'}\s*=\s*\"\)[^\"]*\"|\1$DB_NAME\"|" "$perl_script"
echo "Updated db_name from '$current' to '$DB_NAME'"
changed=1
else
echo "db_name is already set to '$DB_NAME', no change needed"
fi
fi
# Update db_host
# if [ -n "$DB_HOST" ]; then
# current=$(sed -n "s/^\s*\$conf{'db_host'}\s*=\s*\"\([^\"]*\)\";/\1/p" "$perl_script")
# if [ "$current" != "$DB_HOST" ]; then
# sudo sed -i "s|\(\$conf{'db_host'}\s*=\s*\"\)[^\"]*\"|\1$DB_HOST\"|" "$perl_script"
# echo "Updated db_host from '$current' to '$DB_HOST'"
# changed=1
# else
# echo "db_host is already set to '$DB_HOST', no change needed"
# fi
# fi
# Update db_user
if [ -n "$DB_USER" ]; then
current=$(sed -n "s/^\s*\$conf{'db_user'}\s*=\s*\"\([^\"]*\)\";/\1/p" "$perl_script")
if [ "$current" != "$DB_USER" ]; then
sudo sed -i "s|\(\$conf{'db_user'}\s*=\s*\"\)[^\"]*\"|\1$DB_USER\"|" "$perl_script"
echo "Updated db_user from '$current' to '$DB_USER'"
changed=1
else
echo "db_user is already set to '$DB_USER', no change needed"
fi
fi
# Update db_passwd
if [ -n "$DEFAULT_PASSWORD" ]; then
current=$(sed -n "s/^\s*\$conf{'db_passwd'}\s*=\s*\"\([^\"]*\)\";/\1/p" "$perl_script")
if [ "$current" != "$DEFAULT_PASSWORD" ]; then
sudo sed -i "s|\(\$conf{'db_passwd'}\s*=\s*\"\)[^\"]*\"|\1$DEFAULT_PASSWORD\"|" "$perl_script"
info "Updated db_passwd to default password"
changed=1
else
echo "db_passwd is already set to default password, no change needed"
fi
fi
if [ $changed -eq 1 ]; then
echo "Database credentials updated successfully in $perl_script"
else
echo "No changes were necessary; credentials are already up to date"
fi
}
# Configure firewall for FreeRADIUS
configure_firewall() {
log "Configuring firewall for FreeRADIUS..."
# Check if ufw is available and active
if command_exists ufw && ufw status | grep -q "Status: active"; then
log "Opening FreeRADIUS ports in UFW..."
ufw allow 1812/udp comment "FreeRADIUS Authentication"
ufw allow 1813/udp comment "FreeRADIUS Accounting"
info "UFW firewall rules added for FreeRADIUS"
else
info "UFW not active. Manual firewall configuration may be required:"
info "Enabling UFW Firwall"
systemctl enable ufw
systemctl start ufw
ufw allow 1812/udp comment "FreeRADIUS Authentication"
ufw allow 1813/udp comment "FreeRADIUS Accounting"
ufw allow "OpenSSH"
ufw allow "Nginx Full"
ufw enable
info "UFW firewall rules added for FreeRADIUS"
echo " - Port 1812/UDP: RADIUS Authentication"
echo " - Port 1813/UDP: RADIUS Accounting"
fi
}
# Start and test FreeRADIUS
start_freeradius() {
log "Starting FreeRADIUS service..."
systemctl start freeradius
# Wait a moment for service to start
sleep 3
# Check service status
if systemctl is-active --quiet freeradius; then
log "FreeRADIUS started successfully!"
# Display service status
echo
info "=== FreeRADIUS Service Status ==="
systemctl status freeradius --no-pager -l
else
error "FreeRADIUS failed to start. Checking logs..."
echo
warning "=== FreeRADIUS Error Logs ==="
journalctl -u freeradius --no-pager -l --since "5 minutes ago"
echo
warning "You may need to manually debug the FreeRADIUS configuration"
fi
}
# Test FreeRADIUS functionality
test_freeradius() {
log "Testing FreeRADIUS functionality..."
# Test basic RADIUS functionality
if echo "User-Name = \"test\", User-Password = \"test\"" | radclient -x localhost:1812 auth "${TEST_PASSWORD}" >/dev/null 2>&1; then
info "FreeRADIUS basic test passed"
else
warning "FreeRADIUS basic test failed (this may be normal if no test user exists)"
fi
}
#Patch pernament_user table because of error
#SQLSTATE[42S22]: Column not found: 1054 Unknown column \u0027PermanentUsers.admin_state\u0027 in \u0027WHERE\u0027"
check_and_add_admin_state_column() {
local DB_USER="rd"
local DB_PASS="$DEFAULT_PASSWORD"
local DB_NAME="rd"
local TABLE_NAME="permanent_users"
local COLUMN_NAME="admin_state"
# Check if the column exists
local COLUMN_EXISTS
COLUMN_EXISTS=$(mysql -u"$DB_USER" -p"$DB_PASS" -D"$DB_NAME" -sse \
"SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA='$DB_NAME'
AND TABLE_NAME='$TABLE_NAME'
AND COLUMN_NAME='$COLUMN_NAME';")
if [ "$COLUMN_EXISTS" -eq 0 ]; then
info "Column '$COLUMN_NAME' does not exist in '$TABLE_NAME'. Adding it..."
mysql -u"$DB_USER" -p"$DB_PASS" -D"$DB_NAME" -e \
"ALTER TABLE $TABLE_NAME ADD COLUMN $COLUMN_NAME VARCHAR(50) DEFAULT 'active';"
if [ $? -eq 0 ]; then
info "Column '$COLUMN_NAME' added successfully."
else
warning "Failed to add column '$COLUMN_NAME'."
fi
else
info "Column '$COLUMN_NAME' already exists in '$TABLE_NAME'."
fi
}
#Patch to fix load of setting page
# needs wireguard to be loaded
#Solution to this "SQLSTATE[42S02]: Base table or view not found: 1146 Table rd.wireguard_servers doesnt exist" message
import_wireguard_table() {
local DB_USER="root"
local DB_NAME="rd"
local SQL_FILE="/var/www/html/cake4/rd_cake/setup/db/8.106_add_wireguard.sql"
local TABLE_NAME="wireguard_servers"
# Check if table exists
local TABLE_EXISTS
TABLE_EXISTS=$(mysql -u "$DB_USER" -Nse "SHOW TABLES LIKE '$TABLE_NAME';" "$DB_NAME")
if [ "$TABLE_EXISTS" == "$TABLE_NAME" ]; then
warning "Table '$TABLE_NAME' already exists in database '$DB_NAME'. Skipping import."
else
log "Table '$TABLE_NAME' not found. Importing SQL file..."
mysql -u "$DB_USER" "$DB_NAME" < "$SQL_FILE"
if [ $? -eq 0 ]; then
info "SQL file imported successfully."
else
error "Error importing SQL file."
return 1
fi
fi
}
# delete the nginx default file
delete_default_nginx_html() {
local default_file="/var/www/html/index.nginx-debian.html"
log "Deleteting default nginx html file..."
if [ -f "$default_file" ]; then
rm "$default_file" && echo "Deleted $default_file successfully." || echo "Failed to delete $default_file."
info "Default nginx html file deleted successfully"
else
error "File $default_file does not exist."
fi
}
# Final configuration and cleanup
finalize_installation() {
log "Finalizing installation..."
# Remove test PHP file
rm -f /var/www/html/test.php
# Restart services
systemctl reload nginx
systemctl restart php8.3-fpm
systemctl restart mariadb
log "Installation completed successfully!"
echo
info "=== RADIUSdesk + FreeRADIUS Installation Summary ==="
echo "Web Server: Nginx"
echo "PHP Version: 8.3 (FPM)"
echo "Database: MariaDB"
echo "Database Name: ${DB_NAME}"
echo "Database User: ${DB_USER}"
echo "FreeRADIUS Version: 3.x"
echo "Authentication Port: 1812/UDP"
echo "Accounting Port: 1813/UDP"
echo "Client Secret: ${RADIUS_SECRET}"
echo
info "=== Access URLs ==="
echo "Main UI (Optimized): http://$(hostname -I | awk '{print $1}')/rd/build/production/Rd/"
echo "Mobile UI: http://$(hostname -I | awk '{print $1}')/rd_mobile"
echo "Admin UI: http://$(hostname -I | awk '{print $1}')/cake4/rd_cake"
echo
info "=== Default Login Credentials ==="
echo "RADIUSdesk Username: root"
echo "RADIUSdesk Password: admin"
echo
info "=== Network Device Configuration ==="
echo "Configure your network devices (routers, switches, APs) with:"
echo "RADIUS Server IP: $(hostname -I | awk '{print $1}')"
echo "Authentication Port: 1812"
echo "Accounting Port: 1813"
echo "Shared Secret: ${RADIUS_SECRET}"
echo
warning "SECURITY NOTES:"
echo "Change all default passwords immediately!"
echo "Consider implementing SSL/TLS certificates"
echo "Monitor logs regularly: /var/log/freeradius/"
echo
info "Next steps:"
echo "1. Log in to RADIUSdesk and change the admin password"
echo "2. Configure your network equipment to use RADIUS authentication"
echo "3. Set up SSL certificates (Let's Encrypt recommended)"
echo "4. Create users and configure access policies"
echo
}
# Main installation function
main() {
echo
info "=== RADIUSdesk + FreeRADIUS Installation Script for Ubuntu 24.04 ==="
echo "This script will install:"
echo "RADIUSdesk with LEMP stack (Linux, Nginx, MariaDB, PHP)"
echo "FreeRADIUS server with MySQL integration"
echo
read -p "Do you want to continue with the installation? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
info "Installation cancelled."
exit 0
fi
check_ubuntu_version
get_credentials
get_radius_secret
log "Starting installation process..."
# Install RADIUSdesk components
create_system_user
prepare_system
install_nginx
install_php
install_mariadb
setup_database
install_radiusdesk
populate_database
change_default_radius_database_information
setup_cron
# Install and configure FreeRADIUS
initialize_freeradius
configure_freeradius
configure_dynamic_clients
fix_freeradius_service
configure_freeradius_database
update_freeradius_perl_db_dynamic_clients_credentials
update_freeradius_shared_password
configure_firewall
start_freeradius
test_freeradius
# Finalize
check_and_add_admin_state_column
import_wireguard_table
delete_default_nginx_html
finalize_installation
}
# Run main function
main "$@"
@basamoahjnr
Copy link
Author

basamoahjnr commented Oct 31, 2025

script that installs RadiusDesk, for any questions visit radius desk documentation. I needed a script to help em bootstrap RadiusDesk and this was created. not perfect but it does the job.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment