Last active
October 31, 2025 02:47
-
-
Save basamoahjnr/d2b426a3460d58e016590fea3aa6eaeb to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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 "$@" |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.