Skip to content

RHEL/Alma/Rocky/Amazon/Fedora Server Administration Best Practices

This comprehensive guide provides practical examples and best practices for administering RHEL-based servers (Red Hat Enterprise Linux, AlmaLinux, Rocky Linux, Amazon Linux, and Fedora) hosting NOW Learning Management System.

Table of Contents

  1. Initial Server Setup
  2. User & Access Management
  3. System Updates & Software Management
  4. Security Hardening with SELinux
  5. Network Configuration & Firewalld
  6. Remote Access & Monitoring
  7. Backup & Recovery
  8. Performance & Reliability
  9. Database Management
  10. Web Server Configuration
  11. Virtualization & Containers
  12. Compliance & Documentation
  13. Troubleshooting

Initial Server Setup

Update the System

Always start with a fully updated system:

# For RHEL/Alma/Rocky/CentOS
sudo dnf update -y

# For Amazon Linux 2
sudo yum update -y

# For Fedora
sudo dnf update -y

# Reboot if kernel was updated
sudo reboot

# Install essential packages
sudo dnf install -y curl wget git vim htop tree unzip epel-release

Enable Additional Repositories

# Enable EPEL repository (Essential for additional packages)
sudo dnf install -y epel-release

# For Rocky/Alma Linux - enable PowerTools/CRB
sudo dnf config-manager --set-enabled powertools  # Rocky 8
sudo dnf config-manager --set-enabled crb         # Rocky 9/Alma 9

# For RHEL with subscription
sudo subscription-manager repos --enable=rhel-*-optional-rpms
sudo subscription-manager repos --enable=rhel-*-extras-rpms

# Install development tools
sudo dnf groupinstall -y "Development Tools"

Set the Hostname and Timezone

# Set a meaningful hostname
sudo hostnamectl set-hostname your-lms-server.example.com

# Update /etc/hosts
echo "127.0.0.1 your-lms-server.example.com your-lms-server" | sudo tee -a /etc/hosts

# Set timezone
sudo timedatectl set-timezone America/New_York

# Verify settings
hostnamectl status
timedatectl status

User & Access Management

Create a Non-Root Administrative User

Never use root directly for daily operations:

# Create a new user with home directory
sudo adduser lmsadmin

# Add user to wheel group (sudo equivalent in RHEL)
sudo usermod -aG wheel lmsadmin

# Verify sudo access
sudo -l -U lmsadmin

Configure SSH Key-Based Authentication

# Generate SSH key pair (on your local machine)
ssh-keygen -t ed25519 -C "your-email@example.com"

# Copy public key to server (on your local machine)
ssh-copy-id lmsadmin@your-server-ip

# Or manually add the key on the server
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "your-public-key-here" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

Secure SSH Configuration

# Backup original SSH configuration
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup

# Edit SSH configuration
sudo nano /etc/ssh/sshd_config

Apply these secure SSH settings:

# /etc/ssh/sshd_config
Port 2222                          # Change default port
PermitRootLogin no                  # Disable root login
PasswordAuthentication no           # Force key-based auth
PubkeyAuthentication yes            # Enable key authentication
AuthorizedKeysFile .ssh/authorized_keys
MaxAuthTries 3                      # Limit login attempts
ClientAliveInterval 300             # Keep connections alive
ClientAliveCountMax 2               # Disconnect idle clients
AllowUsers lmsadmin                 # Restrict allowed users
Protocol 2                          # Use SSH protocol 2
X11Forwarding no                    # Disable X11 forwarding
UsePAM yes                          # Use PAM for authentication

Restart SSH service:

sudo systemctl restart sshd
sudo systemctl status sshd

# Allow new SSH port through firewall
sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --reload

Configure Fail2Ban for Brute Force Protection

# Install fail2ban
sudo dnf install -y fail2ban

# Create custom configuration
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local
# /etc/fail2ban/jail.local
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 3
backend = systemd

[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/secure
maxretry = 3
bantime = 3600

[nginx-http-auth]
enabled = true
filter = nginx-http-auth
port = http,https
logpath = /var/log/nginx/error.log

[nginx-limit-req]
enabled = true
filter = nginx-limit-req
port = http,https
logpath = /var/log/nginx/error.log
# Enable and start fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban

# Check status
sudo fail2ban-client status
sudo fail2ban-client status sshd

System Updates & Software Management

Configure Automatic Security Updates

# Install dnf-automatic
sudo dnf install -y dnf-automatic

# Configure automatic updates
sudo nano /etc/dnf/automatic.conf
# /etc/dnf/automatic.conf
[commands]
upgrade_type = security
random_sleep = 3600
network_online_timeout = 60
download_updates = yes
apply_updates = yes

[emitters]
emit_via = email
system_name = your-lms-server

[email]
email_from = admin@your-domain.com
email_to = admin@your-domain.com
email_host = localhost
# Enable and start automatic updates
sudo systemctl enable dnf-automatic.timer
sudo systemctl start dnf-automatic.timer
sudo systemctl status dnf-automatic.timer

Package Management Best Practices

# Clean package cache regularly
sudo dnf autoremove -y
sudo dnf clean all

# List installed packages
sudo dnf list installed

# Remove unnecessary packages
sudo dnf remove package-name

# Lock package versions (prevent updates)
sudo dnf versionlock add package-name

# Check for security updates
sudo dnf updateinfo list security
sudo dnf update --security

YUM/DNF Repository Management

# List enabled repositories
sudo dnf repolist

# Add custom repository
sudo nano /etc/yum.repos.d/custom.repo
# /etc/yum.repos.d/custom.repo
[custom-repo]
name=Custom Repository
baseurl=https://repo.example.com/el$releasever/
enabled=1
gpgcheck=1
gpgkey=https://repo.example.com/RPM-GPG-KEY

Security Hardening with SELinux

SELinux Configuration and Management

SELinux is the cornerstone of RHEL security:

# Check SELinux status
sudo sestatus
sudo getenforce

# Ensure SELinux is in enforcing mode
sudo setenforce 1
sudo sed -i 's/SELINUX=.*/SELINUX=enforcing/' /etc/selinux/config

# Install SELinux management tools
sudo dnf install -y policycoreutils-python-utils setools-console setroubleshoot-server

SELinux Context Management

# Check file contexts
ls -Z /var/www/html/
ps auxZ | grep nginx

# Set proper context for web files
sudo setsebool -P httpd_can_network_connect 1
sudo setsebool -P httpd_can_network_relay 1

# Set context for NOW-LMS directory
sudo semanage fcontext -a -t httpd_exec_t "/opt/nowlms/venv/bin/lmsctl"
sudo restorecon -Rv /opt/nowlms/

# Create custom SELinux policy for NOW-LMS
sudo audit2allow -a -M nowlms_policy
sudo semodule -i nowlms_policy.pp

SELinux Troubleshooting

# Monitor SELinux denials
sudo tail -f /var/log/audit/audit.log | grep AVC

# Use sealert for detailed analysis
sudo sealert -a /var/log/audit/audit.log

# Troubleshoot specific denials
sudo audit2why < /var/log/audit/audit.log

# Temporarily allow specific access (for testing only)
sudo audit2allow -a --enable

Custom SELinux Policy for NOW-LMS

# Create NOW-LMS SELinux policy
sudo nano /tmp/nowlms.te
# /tmp/nowlms.te
module nowlms 1.0;

require {
    type httpd_t;
    type httpd_exec_t;
    type httpd_tmp_t;
    type var_lib_t;
    type admin_home_t;
    class file { read write create open getattr execute };
    class dir { search read write add_name remove_name };
}

# Allow httpd to execute NOW-LMS
allow httpd_t httpd_exec_t:file execute;

# Allow NOW-LMS to access its data directory
allow httpd_t var_lib_t:dir { read write search add_name remove_name };
allow httpd_t var_lib_t:file { read write create open getattr };

# Allow access to temporary files
allow httpd_t httpd_tmp_t:file { read write create open getattr };
# Compile and install the policy
sudo checkmodule -M -m -o /tmp/nowlms.mod /tmp/nowlms.te
sudo semodule_package -o /tmp/nowlms.pp -m /tmp/nowlms.mod
sudo semodule -i /tmp/nowlms.pp

Kernel Security Settings

# Configure kernel security parameters
sudo nano /etc/sysctl.d/99-security.conf
# /etc/sysctl.d/99-security.conf
# IP Spoofing protection
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.rp_filter = 1

# Ignore ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0

# Ignore send redirects
net.ipv4.conf.all.send_redirects = 0

# Disable source packet routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0

# Log Martians
net.ipv4.conf.all.log_martians = 1

# Ignore ping requests
net.ipv4.icmp_echo_ignore_all = 1

# Ignore Directed pings
net.ipv4.icmp_echo_ignore_broadcasts = 1

# Disable IPv6 if not needed
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1

# Hide kernel pointers
kernel.kptr_restrict = 1

# Restrict dmesg access
kernel.dmesg_restrict = 1

# Randomize memory layout
kernel.randomize_va_space = 2

Apply settings:

sudo sysctl -p /etc/sysctl.d/99-security.conf

Network Configuration & Firewalld

Firewalld Configuration

Firewalld is the default firewall management tool in RHEL:

# Enable and start firewalld
sudo systemctl enable firewalld
sudo systemctl start firewalld

# Check firewall status
sudo firewall-cmd --state
sudo firewall-cmd --list-all

# Set default zone
sudo firewall-cmd --set-default-zone=public

# Allow essential services
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https

# Custom SSH port
sudo firewall-cmd --permanent --add-port=2222/tcp

# Remove default SSH service if using custom port
sudo firewall-cmd --permanent --remove-service=ssh

# Reload firewall
sudo firewall-cmd --reload

Advanced Firewalld Configuration

# Create custom service definition
sudo nano /etc/firewalld/services/nowlms.xml
<!-- /etc/firewalld/services/nowlms.xml -->
<?xml version="1.0" encoding="utf-8"?>
<service>
  <short>NOW-LMS</short>
  <description>NOW Learning Management System</description>
  <port protocol="tcp" port="8080"/>
</service>
# Reload and use custom service
sudo firewall-cmd --reload
sudo firewall-cmd --permanent --add-service=nowlms

# Rich rules for advanced access control
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" service name="ssh" accept'
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="0.0.0.0/0" service name="http" accept'

# Rate limiting
sudo firewall-cmd --permanent --add-rich-rule='rule service name="ssh" accept limit value="3/m"'

# Block specific IPs
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="1.2.3.4" drop'

# Apply changes
sudo firewall-cmd --reload

Network Security

# Disable unused network protocols
echo "install dccp /bin/true" | sudo tee -a /etc/modprobe.d/blacklist-rare-network.conf
echo "install sctp /bin/true" | sudo tee -a /etc/modprobe.d/blacklist-rare-network.conf
echo "install rds /bin/true" | sudo tee -a /etc/modprobe.d/blacklist-rare-network.conf
echo "install tipc /bin/true" | sudo tee -a /etc/modprobe.d/blacklist-rare-network.conf

# Configure TCP wrappers (if installed)
echo "sshd: ALL" | sudo tee -a /etc/hosts.deny
echo "sshd: 192.168.1.0/255.255.255.0" | sudo tee -a /etc/hosts.allow

Remote Access & Monitoring

Install and Configure Cockpit

Cockpit is the default web console for RHEL systems:

# Install Cockpit (usually pre-installed)
sudo dnf install -y cockpit cockpit-system cockpit-selinux

# Enable and start Cockpit
sudo systemctl enable --now cockpit.socket

# Allow Cockpit through firewall
sudo firewall-cmd --permanent --add-service=cockpit
sudo firewall-cmd --reload

# Access via https://your-server:9090

Configure Cockpit Security

# Configure Cockpit for secure access
sudo nano /etc/cockpit/cockpit.conf
# /etc/cockpit/cockpit.conf
[WebService]
LoginTitle = NOW-LMS Server Management
LoginTo = false
RequireHost = your-lms-server.example.com
MaxStartups = 3
AllowUnencrypted = false

[Session]
IdleTimeout = 15
Banner = /etc/cockpit/banner.txt
# Create login banner
sudo nano /etc/cockpit/banner.txt
WARNING: Authorized access only. All activities are monitored and logged.

System Monitoring Tools

# Install monitoring tools
sudo dnf install -y htop iotop nethogs glances

# Install and configure Netdata
bash <(curl -Ss https://my-netdata.io/kickstart.sh)

# Configure Netdata
sudo nano /etc/netdata/netdata.conf
# /etc/netdata/netdata.conf
[global]
    run as user = netdata
    default port = 19999
    bind to = 127.0.0.1

[web]
    allow connections from = localhost 192.168.1.*
    allow dashboard from = localhost 192.168.1.*
    allow badges from = *

Centralized Logging with rsyslog

# Configure rsyslog for centralized logging
sudo nano /etc/rsyslog.d/01-nowlms.conf
# /etc/rsyslog.d/01-nowlms.conf
# NOW-LMS application logs
if $programname == 'nowlms' then /var/log/nowlms/application.log
& stop

# Security logs
auth,authpriv.*                  /var/log/secure
mail.*                          /var/log/maillog
cron.*                          /var/log/cron

# Remote logging (optional)
# *.* @@remote-log-server:514
# Restart rsyslog
sudo systemctl restart rsyslog

# Configure logrotate for NOW-LMS logs
sudo nano /etc/logrotate.d/nowlms
# /etc/logrotate.d/nowlms
/var/log/nowlms/*.log {
    daily
    missingok
    rotate 52
    compress
    delaycompress
    notifempty
    create 0644 nowlms nowlms
    postrotate
        systemctl reload nowlms
    endscript
}

Set Up Email Alerting

# Install and configure postfix
sudo dnf install -y postfix mailx

# Configure postfix for outbound mail
sudo nano /etc/postfix/main.cf
# /etc/postfix/main.cf - Key settings
myhostname = your-lms-server.example.com
mydomain = example.com
myorigin = $mydomain
inet_interfaces = localhost
mydestination = $myhostname, localhost.$mydomain, localhost
relayhost = [smtp.example.com]:587

# SMTP authentication (if required)
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_tls_security_level = encrypt
# Start postfix
sudo systemctl enable --now postfix

# Create monitoring scripts
sudo mkdir -p /opt/monitoring
sudo nano /opt/monitoring/system-monitor.sh
#!/bin/bash
# /opt/monitoring/system-monitor.sh

HOSTNAME=$(hostname)
EMAIL="admin@example.com"

# Check disk space
DISK_USAGE=$(df -h | awk '$5 > 80 {print $0}')
if [ ! -z "$DISK_USAGE" ]; then
    echo "Disk space warning on $HOSTNAME: $DISK_USAGE" | mail -s "Disk Alert: $HOSTNAME" $EMAIL
fi

# Check memory usage
MEM_USAGE=$(free | awk 'NR==2{printf "%.2f", $3*100/$2}')
if (( $(echo "$MEM_USAGE > 80" | bc -l) )); then
    echo "Memory usage is ${MEM_USAGE}% on $HOSTNAME" | mail -s "Memory Alert: $HOSTNAME" $EMAIL
fi

# Check service status
for service in nowlms nginx postgresql; do
    if ! systemctl is-active --quiet $service; then
        echo "Service $service is not running on $HOSTNAME" | mail -s "Service Alert: $HOSTNAME" $EMAIL
    fi
done

# Check SELinux denials
DENIALS=$(ausearch -m avc -ts recent 2>/dev/null | wc -l)
if [ $DENIALS -gt 0 ]; then
    echo "SELinux denials detected: $DENIALS" | mail -s "SELinux Alert: $HOSTNAME" $EMAIL
fi
# Make script executable and schedule
sudo chmod +x /opt/monitoring/system-monitor.sh
sudo crontab -e
# System monitoring cron jobs
*/15 * * * * /opt/monitoring/system-monitor.sh
0 6 * * * /bin/df -h | mail -s "Daily disk report $(hostname)" admin@example.com
0 7 * * * /bin/free -h | mail -s "Daily memory report $(hostname)" admin@example.com

Backup & Recovery

Automated Backup Strategy

# Install backup tools
sudo dnf install -y rsync borgbackup tar gzip

# Create backup directories
sudo mkdir -p /backup/{daily,weekly,monthly}
sudo mkdir -p /backup/scripts
sudo mkdir -p /var/lib/nowlms-backup

# Database backup script
sudo nano /backup/scripts/db-backup.sh
#!/bin/bash
# /backup/scripts/db-backup.sh

BACKUP_DIR="/backup/daily"
DATE=$(date +%Y%m%d_%H%M%S)
DB_NAME="nowlms"
DB_USER="nowlms_user"
DB_PASS="your_password"

# Create backup directory if it doesn't exist
mkdir -p $BACKUP_DIR

# PostgreSQL backup
export PGPASSWORD=$DB_PASS
pg_dump -U $DB_USER -h localhost $DB_NAME | gzip > $BACKUP_DIR/nowlms_db_$DATE.sql.gz

# MySQL backup (alternative)
# mysqldump -u $DB_USER -p$DB_PASS $DB_NAME | gzip > $BACKUP_DIR/nowlms_db_$DATE.sql.gz

# SQLite backup (if using SQLite)
# cp /var/lib/nowlms/nowlms.db $BACKUP_DIR/nowlms_db_$DATE.db

# Remove backups older than 7 days
find $BACKUP_DIR -name "nowlms_db_*.sql.gz" -mtime +7 -delete

# Log backup completion
logger "Database backup completed: $DATE"
echo "Database backup completed: $DATE"

System and Application Backup

# System backup script
sudo nano /backup/scripts/system-backup.sh
#!/bin/bash
# /backup/scripts/system-backup.sh

BACKUP_DIR="/backup/daily"
DATE=$(date +%Y%m%d_%H%M%S)
SOURCE_DIRS="/etc /home /var/lib/nowlms /opt/nowlms"
EXCLUDE_FILE="/backup/scripts/exclude.txt"

# Create exclude file
cat > $EXCLUDE_FILE << EOF
/proc/*
/sys/*
/dev/*
/tmp/*
/var/tmp/*
/var/cache/*
/var/log/journal/*
/home/*/.cache/*
/root/.cache/*
*.pyc
__pycache__
EOF

# Create system backup
tar -czf $BACKUP_DIR/system_backup_$DATE.tar.gz \
    --exclude-from=$EXCLUDE_FILE \
    $SOURCE_DIRS

# NOW-LMS specific backup
tar -czf $BACKUP_DIR/nowlms_files_$DATE.tar.gz \
    /var/lib/nowlms \
    /etc/systemd/system/nowlms.service \
    /opt/nowlms

# Configuration backup
tar -czf $BACKUP_DIR/config_backup_$DATE.tar.gz \
    /etc/nginx \
    /etc/httpd \
    /etc/postgresql \
    /etc/systemd/system

# Remove old backups
find $BACKUP_DIR -name "system_backup_*.tar.gz" -mtime +7 -delete
find $BACKUP_DIR -name "nowlms_files_*.tar.gz" -mtime +7 -delete
find $BACKUP_DIR -name "config_backup_*.tar.gz" -mtime +7 -delete

logger "System backup completed: $DATE"
echo "System backup completed: $DATE"

BorgBackup Configuration

# Initialize borg repository
sudo borg init --encryption=repokey /backup/borg-repo

# Create borg backup script
sudo nano /backup/scripts/borg-backup.sh
#!/bin/bash
# /backup/scripts/borg-backup.sh

export BORG_REPO='/backup/borg-repo'
export BORG_PASSPHRASE='your-secure-passphrase'

# Backup directories
borg create \
    --verbose \
    --filter AME \
    --list \
    --stats \
    --show-rc \
    --compression lz4 \
    --exclude-caches \
    --exclude '/home/*/.cache' \
    --exclude '/var/cache' \
    --exclude '/var/tmp' \
    --exclude '/tmp' \
    ::'{hostname}-{now}' \
    /etc \
    /home \
    /var/lib/nowlms \
    /opt/nowlms \
    /var/lib/postgresql \
    /root

backup_exit=$?

# Prune old archives
borg prune \
    --list \
    --prefix '{hostname}-' \
    --show-rc \
    --keep-daily 7 \
    --keep-weekly 4 \
    --keep-monthly 6 \
    --keep-yearly 1

prune_exit=$?

# Use highest exit code as global exit code
global_exit=$(( backup_exit > prune_exit ? backup_exit : prune_exit ))

if [ ${global_exit} -eq 0 ]; then
    logger "Borg backup and prune finished successfully"
elif [ ${global_exit} -eq 1 ]; then
    logger "Borg backup and/or prune finished with warnings"
else
    logger "Borg backup and/or prune finished with errors"
fi

exit ${global_exit}

Schedule Backups with systemd timers

# Create backup service
sudo nano /etc/systemd/system/nowlms-backup.service
# /etc/systemd/system/nowlms-backup.service
[Unit]
Description=NOW-LMS Backup Service
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
ExecStart=/backup/scripts/db-backup.sh
ExecStart=/backup/scripts/system-backup.sh
User=root
Group=root
# Create backup timer
sudo nano /etc/systemd/system/nowlms-backup.timer
# /etc/systemd/system/nowlms-backup.timer
[Unit]
Description=Run NOW-LMS backup daily
Requires=nowlms-backup.service

[Timer]
OnCalendar=daily
Persistent=true
RandomizedDelaySec=30m

[Install]
WantedBy=timers.target
# Enable and start backup timer
sudo systemctl daemon-reload
sudo systemctl enable nowlms-backup.timer
sudo systemctl start nowlms-backup.timer
sudo systemctl status nowlms-backup.timer

# Check timer status
sudo systemctl list-timers

Performance & Reliability

Optimize System Performance

# Configure swap
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

# Make swap permanent
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

# Optimize swap usage
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf
echo 'vm.vfs_cache_pressure=50' | sudo tee -a /etc/sysctl.conf

Configure systemd Services for NOW-LMS

# Create NOW-LMS systemd service
sudo nano /etc/systemd/system/nowlms.service
# /etc/systemd/system/nowlms.service
[Unit]
Description=NOW Learning Management System
After=network.target postgresql.service
Wants=postgresql.service

[Service]
Type=notify
User=nowlms
Group=nowlms
WorkingDirectory=/opt/nowlms
Environment=FLASK_APP=now_lms
Environment=SECRET_KEY=your-very-secure-secret-key
Environment=DATABASE_URL=postgresql://user:pass@localhost/nowlms
Environment=LOG_LEVEL=INFO
ExecStart=/opt/nowlms/venv/bin/lmsctl serve
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
RestartSec=3
KillMode=mixed
TimeoutStopSec=30

# Security settings
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/nowlms /tmp

[Install]
WantedBy=multi-user.target
# Create nowlms user
sudo useradd --system --home-dir /opt/nowlms --shell /sbin/nologin nowlms

# Set permissions
sudo chown -R nowlms:nowlms /opt/nowlms
sudo chown -R nowlms:nowlms /var/lib/nowlms

# Enable and start service
sudo systemctl daemon-reload
sudo systemctl enable nowlms
sudo systemctl start nowlms
sudo systemctl status nowlms

Kernel Live Patching (RHEL)

# For RHEL with subscription
sudo subscription-manager repos --enable=rhel-*-kpatch-repo
sudo dnf install -y kpatch

# Enable kpatch service
sudo systemctl enable kpatch.service

# Apply available kernel patches
sudo kpatch install

# Check installed patches
sudo kpatch list

Disk Health Monitoring

# Install smartmontools
sudo dnf install -y smartmontools

# Check disk health
sudo smartctl -a /dev/sda

# Enable SMART monitoring
sudo systemctl enable smartd
sudo systemctl start smartd

# Configure SMART monitoring
sudo nano /etc/smartmontools/smartd.conf
# /etc/smartmontools/smartd.conf
# Monitor all disks
DEVICESCAN -a -o on -S on -n standby,q -s (S/../.././02|L/../../6/03) -W 4,35,40 -m admin@example.com
# Schedule SMART tests
sudo nano /etc/cron.d/smartmon
# /etc/cron.d/smartmon
# Short test every Sunday at 2 AM
0 2 * * 0 root /usr/sbin/smartctl -t short /dev/sda

# Long test first Sunday of every month at 3 AM
0 3 1-7 * 0 root /usr/sbin/smartctl -t long /dev/sda

Database Management

PostgreSQL Setup and Hardening

# Install PostgreSQL
sudo dnf install -y postgresql-server postgresql-contrib

# Initialize database
sudo postgresql-setup --initdb --unit postgresql

# Start and enable PostgreSQL
sudo systemctl enable --now postgresql

# Create database and user for NOW-LMS
sudo -u postgres psql
-- PostgreSQL commands
CREATE USER nowlms_user WITH PASSWORD 'secure_password_here';
CREATE DATABASE nowlms_db OWNER nowlms_user;
GRANT ALL PRIVILEGES ON DATABASE nowlms_db TO nowlms_user;
\q
# Configure PostgreSQL
sudo nano /var/lib/pgsql/data/postgresql.conf
# /var/lib/pgsql/data/postgresql.conf
listen_addresses = 'localhost'
port = 5432
max_connections = 100
shared_buffers = 256MB
effective_cache_size = 1GB
maintenance_work_mem = 64MB
checkpoint_completion_target = 0.9
wal_buffers = 16MB
default_statistics_target = 100
random_page_cost = 1.1
effective_io_concurrency = 200
work_mem = 4MB

# Enable logging
log_destination = 'stderr'
logging_collector = on
log_directory = 'log'
log_filename = 'postgresql-%a.log'
log_truncate_on_rotation = on
log_rotation_age = 1d
log_min_duration_statement = 1000
log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h '
# Configure authentication
sudo nano /var/lib/pgsql/data/pg_hba.conf
# /var/lib/pgsql/data/pg_hba.conf
local   all             postgres                                peer
local   all             all                                     md5
host    all             all             127.0.0.1/32            md5
host    all             all             ::1/128                 md5
# Set SELinux context for PostgreSQL
sudo setsebool -P postgresql_can_rsync on
sudo semanage port -a -t postgresql_port_t -p tcp 5432

# Restart PostgreSQL
sudo systemctl restart postgresql

MySQL/MariaDB Setup (Alternative)

# Install MariaDB
sudo dnf install -y mariadb-server mariadb

# Start and enable MariaDB
sudo systemctl enable --now mariadb

# Secure MariaDB installation
sudo mysql_secure_installation

# Create database and user
sudo mysql -u root -p
-- MariaDB commands
CREATE DATABASE nowlms_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'nowlms_user'@'localhost' IDENTIFIED BY 'secure_password_here';
GRANT ALL PRIVILEGES ON nowlms_db.* TO 'nowlms_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;

Web Server Configuration

Nginx Setup and Hardening

# Install Nginx
sudo dnf install -y nginx

# Create NOW-LMS site configuration
sudo nano /etc/nginx/conf.d/nowlms.conf
# /etc/nginx/conf.d/nowlms.conf
server {
    listen 80;
    server_name your-domain.com www.your-domain.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name your-domain.com www.your-domain.com;

    # SSL Configuration
    ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
    ssl_session_cache shared:SSL:1m;
    ssl_session_timeout 5m;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers on;

    # Security Headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "no-referrer-when-downgrade" always;
    add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    # Root directory
    root /var/www/html;
    index index.html index.htm;

    # NOW-LMS proxy
    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_redirect off;
        proxy_connect_timeout 30;
        proxy_send_timeout 30;
        proxy_read_timeout 30;
    }

    # Static files
    location /static/ {
        alias /opt/nowlms/now_lms/static/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    # Security
    location ~ /\. {
        deny all;
    }

    location ~ /\.ht {
        deny all;
    }

    # Logging
    access_log /var/log/nginx/nowlms_access.log;
    error_log /var/log/nginx/nowlms_error.log;
}
# Test and restart Nginx
sudo nginx -t
sudo systemctl enable --now nginx

# Configure SELinux for Nginx
sudo setsebool -P httpd_can_network_connect 1
sudo setsebool -P httpd_can_network_relay 1

# Allow Nginx through firewall
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload

Apache (httpd) Setup (Alternative)

# Install Apache
sudo dnf install -y httpd mod_ssl

# Create NOW-LMS virtual host
sudo nano /etc/httpd/conf.d/nowlms.conf
# /etc/httpd/conf.d/nowlms.conf
<VirtualHost *:80>
    ServerName your-domain.com
    ServerAlias www.your-domain.com
    Redirect permanent / https://your-domain.com/
</VirtualHost>

<VirtualHost *:443>
    ServerName your-domain.com
    ServerAlias www.your-domain.com

    # SSL Configuration
    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/your-domain.com/cert.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/your-domain.com/privkey.pem
    SSLCertificateChainFile /etc/letsencrypt/live/your-domain.com/chain.pem

    # Security Headers
    Header always set X-Frame-Options "SAMEORIGIN"
    Header always set X-XSS-Protection "1; mode=block"
    Header always set X-Content-Type-Options "nosniff"
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"

    # NOW-LMS proxy
    ProxyPass / http://127.0.0.1:8080/
    ProxyPassReverse / http://127.0.0.1:8080/
    ProxyPreserveHost On

    # Static files
    Alias /static/ /opt/nowlms/now_lms/static/
    <Directory "/opt/nowlms/now_lms/static/">
        Require all granted
        ExpiresActive On
        ExpiresDefault "access plus 30 days"
    </Directory>

    # Logging
    CustomLog /var/log/httpd/nowlms_access.log combined
    ErrorLog /var/log/httpd/nowlms_error.log
</VirtualHost>
# Enable required modules
sudo systemctl enable --now httpd

# Configure SELinux for Apache
sudo setsebool -P httpd_can_network_connect 1
sudo setsebool -P httpd_can_network_relay 1

SSL/TLS with Let's Encrypt

# Install Certbot
sudo dnf install -y certbot python3-certbot-nginx

# Obtain SSL certificate
sudo certbot --nginx -d your-domain.com -d www.your-domain.com

# Set up automatic renewal
sudo crontab -e
# Auto-renew SSL certificates
0 12 * * * /usr/bin/certbot renew --quiet

Virtualization & Containers

Podman Setup (RHEL/Fedora Default)

Podman is the default container runtime in RHEL/Fedora:

# Install Podman
sudo dnf install -y podman podman-compose

# Create rootless container for NOW-LMS
podman run -d \
  --name nowlms-app \
  --restart=unless-stopped \
  -p 127.0.0.1:8080:8080 \
  -e SECRET_KEY=your-very-secure-secret-key \
  -e DATABASE_URL=postgresql://nowlms:password@host.containers.internal:5432/nowlms \
  -v nowlms-data:/app/data:Z \
  -v nowlms-themes:/app/themes:Z \
  quay.io/bmosoluciones/now_lms:latest

# Create systemd service for rootless container
mkdir -p ~/.config/systemd/user
podman generate systemd --new --name nowlms-app > ~/.config/systemd/user/nowlms-app.service

# Enable user service
systemctl --user daemon-reload
systemctl --user enable nowlms-app.service
systemctl --user start nowlms-app.service

# Enable lingering for user services
sudo loginctl enable-linger $USER

Docker Setup (Alternative)

# Install Docker
sudo dnf install -y dnf-plugins-core
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

# Start and enable Docker
sudo systemctl enable --now docker

# Add user to docker group
sudo usermod -aG docker $USER

# Configure Docker daemon
sudo nano /etc/docker/daemon.json
{
    "log-driver": "journald",
    "log-opts": {
        "tag": "{{.Name}}/{{.ID}}"
    },
    "storage-driver": "overlay2",
    "selinux-enabled": true
}

NOW-LMS Container Compose

# Create docker-compose configuration
mkdir -p /opt/nowlms-docker
cd /opt/nowlms-docker
nano docker-compose.yml
# docker-compose.yml
version: "3.8"

services:
    nowlms:
        image: quay.io/bmosoluciones/now_lms:latest
        container_name: nowlms-app
        restart: unless-stopped
        ports:
            - "127.0.0.1:8080:8080"
        environment:
            - SECRET_KEY=your-very-secure-secret-key
            - DATABASE_URL=postgresql://nowlms:password@postgres:5432/nowlms
            - REDIS_URL=redis://redis:6379/0
            - LOG_LEVEL=INFO
        volumes:
            - nowlms-data:/app/data:Z
            - nowlms-themes:/app/themes:Z
        depends_on:
            - postgres
            - redis
        networks:
            - nowlms-network

    postgres:
        image: postgres:15
        container_name: nowlms-postgres
        restart: unless-stopped
        environment:
            - POSTGRES_DB=nowlms
            - POSTGRES_USER=nowlms
            - POSTGRES_PASSWORD=secure_password_here
        volumes:
            - postgres-data:/var/lib/postgresql/data:Z
        networks:
            - nowlms-network

    redis:
        image: redis:7-alpine
        container_name: nowlms-redis
        restart: unless-stopped
        volumes:
            - redis-data:/data:Z
        networks:
            - nowlms-network

volumes:
    nowlms-data:
    nowlms-themes:
    postgres-data:
    redis-data:

networks:
    nowlms-network:
        driver: bridge
# Set SELinux context for container volumes
sudo setsebool -P container_manage_cgroup 1

# Start services
docker-compose up -d

# Check status
docker-compose ps
docker-compose logs nowlms

Compliance & Documentation

OpenSCAP Security Compliance

# Install OpenSCAP
sudo dnf install -y openscap-scanner scap-security-guide

# List available profiles
oscap info /usr/share/xml/scap/ssg/content/ssg-rhel*.xml

# Run security scan
sudo oscap xccdf eval \
    --profile xccdf_org.ssgproject.content_profile_cis \
    --results-arf /tmp/arf.xml \
    --report /tmp/compliance-report.html \
    /usr/share/xml/scap/ssg/content/ssg-rhel*.xml

# Generate remediation script
sudo oscap xccdf generate fix \
    --profile xccdf_org.ssgproject.content_profile_cis \
    --template bash \
    /usr/share/xml/scap/ssg/content/ssg-rhel*.xml > /tmp/remediation.sh

Lynis Security Audit

# Install Lynis
sudo dnf install -y lynis

# Run comprehensive security audit
sudo lynis audit system

# Generate custom report
sudo lynis audit system --report-file /tmp/lynis-report.log

# Review recommendations
sudo lynis show suggestions

Infrastructure as Code with Ansible

# Install Ansible
sudo dnf install -y ansible

# Create Ansible playbook for NOW-LMS
mkdir -p /opt/ansible/{playbooks,inventories,roles}
nano /opt/ansible/playbooks/nowlms-deployment.yml
# /opt/ansible/playbooks/nowlms-deployment.yml
---
- name: Deploy NOW-LMS on RHEL
  hosts: localhost
  become: yes
  vars:
      nowlms_user: nowlms
      nowlms_dir: /opt/nowlms
      db_name: nowlms
      db_user: nowlms_user

  tasks:
      - name: Install EPEL repository
        dnf:
            name: epel-release
            state: present

      - name: Install dependencies
        dnf:
            name:
                - python3
                - python3-pip
                - nginx
                - postgresql-server
                - postgresql-contrib
                - git
            state: present

      - name: Create NOW-LMS user
        user:
            name: "{{ nowlms_user }}"
            system: yes
            shell: /sbin/nologin
            home: "{{ nowlms_dir }}"
            create_home: yes

      - name: Install NOW-LMS
        pip:
            name: now-lms
            virtualenv: "{{ nowlms_dir }}/venv"
            virtualenv_python: python3
        become_user: "{{ nowlms_user }}"

      - name: Configure systemd service
        template:
            src: nowlms.service.j2
            dest: /etc/systemd/system/nowlms.service
        notify:
            - reload systemd
            - restart nowlms

      - name: Configure Nginx
        template:
            src: nginx-nowlms.conf.j2
            dest: /etc/nginx/conf.d/nowlms.conf
        notify: restart nginx

      - name: Configure firewall
        firewalld:
            service: "{{ item }}"
            permanent: yes
            state: enabled
            immediate: yes
        loop:
            - http
            - https

      - name: Enable and start services
        systemd:
            name: "{{ item }}"
            enabled: yes
            state: started
        loop:
            - nowlms
            - nginx
            - postgresql

  handlers:
      - name: reload systemd
        systemd:
            daemon_reload: yes

      - name: restart nowlms
        systemd:
            name: nowlms
            state: restarted

      - name: restart nginx
        systemd:
            name: nginx
            state: restarted

Configuration Management with Git

# Set up configuration repository
mkdir -p /opt/config-management
cd /opt/config-management
git init

# Track important configuration files
mkdir -p {nginx,httpd,postgresql,systemd,firewalld,selinux}
cp /etc/nginx/conf.d/nowlms.conf nginx/ 2>/dev/null || true
cp /etc/httpd/conf.d/nowlms.conf httpd/ 2>/dev/null || true
cp /var/lib/pgsql/data/postgresql.conf postgresql/ 2>/dev/null || true
cp /etc/systemd/system/nowlms.service systemd/ 2>/dev/null || true
firewall-cmd --list-all > firewalld/current-config.txt
semodule -l > selinux/modules.txt

# Create configuration tracking script
nano track-config.sh
#!/bin/bash
# /opt/config-management/track-config.sh

cd /opt/config-management

# Copy current configurations
cp /etc/nginx/conf.d/nowlms.conf nginx/ 2>/dev/null || true
cp /etc/httpd/conf.d/nowlms.conf httpd/ 2>/dev/null || true
cp /var/lib/pgsql/data/postgresql.conf postgresql/ 2>/dev/null || true
cp /etc/systemd/system/nowlms.service systemd/ 2>/dev/null || true

# Update firewall config
firewall-cmd --list-all > firewalld/current-config.txt

# Update SELinux modules
semodule -l > selinux/modules.txt

# Check for changes
if [[ -n $(git status --porcelain) ]]; then
    git add .
    git commit -m "Configuration update - $(date +%Y-%m-%d_%H:%M:%S)"
    echo "Configuration changes committed"
else
    echo "No configuration changes detected"
fi
# Make executable and schedule
chmod +x track-config.sh

# Schedule daily configuration tracking
echo "0 2 * * * /opt/config-management/track-config.sh" | sudo crontab -e

Troubleshooting

Common Issues and Solutions

Service Issues

# Check service status
sudo systemctl status nowlms
sudo systemctl status nginx
sudo systemctl status postgresql

# View service logs
sudo journalctl -u nowlms -f
sudo journalctl -u nginx -f
sudo journalctl -u postgresql -f

# Check service dependencies
sudo systemctl list-dependencies nowlms

# Restart services in correct order
sudo systemctl restart postgresql
sudo systemctl restart nowlms
sudo systemctl reload nginx

SELinux Issues

# Check SELinux denials
sudo ausearch -m avc -ts recent
sudo grep AVC /var/log/audit/audit.log

# Use sealert for detailed analysis
sudo sealert -a /var/log/audit/audit.log

# Temporarily set SELinux to permissive (for testing only)
sudo setenforce 0

# Check current SELinux mode
getenforce

# Re-enable enforcing mode
sudo setenforce 1

Database Connection Issues

# Check PostgreSQL status
sudo systemctl status postgresql
sudo -u postgres psql -l

# Test database connectivity
sudo -u postgres psql -d nowlms -c "SELECT version();"

# Check PostgreSQL logs
sudo tail -f /var/lib/pgsql/data/log/postgresql-*.log

# Verify authentication configuration
sudo cat /var/lib/pgsql/data/pg_hba.conf

Firewall Issues

# Check firewall status
sudo firewall-cmd --state
sudo firewall-cmd --list-all

# Test port connectivity
sudo ss -tulpn | grep :8080
sudo netstat -tulpn | grep :8080

# Temporary firewall rule for testing
sudo firewall-cmd --add-port=8080/tcp

# Make permanent
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --reload

Network and DNS Issues

# Check network connectivity
curl -I http://localhost:8080
curl -I https://your-domain.com

# Check DNS resolution
nslookup your-domain.com
dig your-domain.com

# Check routing
ip route show
ss -rn

Performance Troubleshooting

# Monitor system resources
htop
top -u nowlms

# Check I/O performance
iotop
iostat -x 1

# Monitor network traffic
nethogs
ss -tuln

# Check disk usage
df -h
du -sh /var/* | sort -hr

# Monitor memory usage
free -h
vmstat 1

# Check for high CPU processes
ps aux --sort=-%cpu | head -20

Log Analysis Commands

# Search for errors in system logs
sudo journalctl -p err -n 50
sudo grep -i error /var/log/messages

# Analyze Nginx logs
sudo tail -f /var/log/nginx/access.log
sudo grep "5xx" /var/log/nginx/access.log

# Monitor authentication attempts
sudo grep "Failed password" /var/log/secure
sudo grep "authentication failure" /var/log/secure

# Analyze PostgreSQL logs
sudo grep ERROR /var/lib/pgsql/data/log/postgresql-*.log

Emergency Recovery Procedures

# Emergency system information
sudo lsof -i :80
sudo lsof -i :443
sudo lsof -i :8080

# Kill problematic processes
sudo pkill -f python
sudo systemctl stop nowlms

# Emergency disk cleanup
sudo dnf autoremove -y
sudo dnf clean all
sudo find /var/log -name "*.log" -mtime +30 -delete
sudo find /tmp -mtime +7 -delete
sudo journalctl --vacuum-time=7d

# Reset SELinux contexts
sudo restorecon -Rv /opt/nowlms/
sudo restorecon -Rv /var/lib/nowlms/

# Emergency firewall reset
sudo firewall-cmd --reload
sudo firewall-cmd --panic-off

Security Checklist

Use this checklist to ensure your RHEL-based server follows security best practices:

  • [ ] System fully updated with automatic security updates enabled
  • [ ] SELinux in enforcing mode with proper contexts configured
  • [ ] Non-root administrative user created with sudo privileges
  • [ ] SSH hardened with key-based authentication and non-default port
  • [ ] Root login disabled
  • [ ] Fail2ban configured and active
  • [ ] Firewalld enabled with minimal required services
  • [ ] Unnecessary services disabled
  • [ ] Kernel security parameters configured
  • [ ] Automatic security updates configured
  • [ ] Regular backups scheduled and tested
  • [ ] SSL/TLS certificates configured and auto-renewing
  • [ ] System monitoring and alerting configured
  • [ ] Log rotation and retention configured
  • [ ] Database properly secured and backed up
  • [ ] Web server hardened with security headers
  • [ ] Regular security audits with OpenSCAP
  • [ ] Configuration changes tracked in version control
  • [ ] Documentation kept up to date
  • [ ] Compliance reports generated regularly

Additional RHEL-Specific Considerations

Red Hat Subscription Management

# Register system with Red Hat
sudo subscription-manager register --username your-username

# Attach subscription
sudo subscription-manager attach --auto

# Enable required repositories
sudo subscription-manager repos --enable=rhel-*-server-optional-rpms
sudo subscription-manager repos --enable=rhel-*-server-extras-rpms

Security Scanning and Compliance

# Use Red Hat Insights for security analysis
sudo insights-client --register
sudo insights-client --check-results

# Install security scanner
sudo dnf install -y aide

# Initialize AIDE database
sudo aide --init
sudo mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz

# Schedule regular integrity checks
echo "0 3 * * * /usr/sbin/aide --check" | sudo crontab -e

This comprehensive guide provides a solid foundation for securely administering RHEL-based servers running NOW Learning Management System. The emphasis on SELinux, firewalld, and RHEL-specific tools ensures optimal security and performance for enterprise environments.