Skip to main content

Switch Over Instructions

Server Setup Script - copy and paste into terminal to execute the script

Script with options
#!/bin/bash

# --- Single, Robust Command to Mount, Copy, Execute, and Unmount ---
#
# This command is designed to be safely copied and pasted into any server terminal.
# It handles all the necessary steps to run the main setup script from your fileserver.
# It will prompt you to select which mode to run the main script in.
#
# Password for the mount command is included. Ensure this is run in a secure environment.

# --- Configuration ---
MOUNT_POINT="/mnt/fileserver"
SERVER_PATH="//172.16.21.16/fileserver2"
SCRIPT_SOURCE_PATH="${MOUNT_POINT}/General/IT FILES/script3.sh"
SCRIPT_DEST_PATH="/tmp/script3.sh"
MOUNT_USER="Cipher.m21"
MOUNT_PASS=")\1y;634'NJ%i+"

# --- Logic ---

# Ensure the mount point is unmounted on script exit (success or failure)
trap "echo 'Unmounting fileserver...'; sudo umount '${MOUNT_POINT}' &>/dev/null || true" EXIT

# Check if already mounted. If not, create directory and mount.
if ! grep -qs "${MOUNT_POINT}" /proc/mounts; then
    echo "Mounting fileserver..."
    sudo mkdir -p "${MOUNT_POINT}"
    sudo mount -t cifs "${SERVER_PATH}" "${MOUNT_POINT}" -o username="${MOUNT_USER}",password="${MOUNT_PASS}"
fi

echo "Copying script (overwriting if exists)..."
sudo cp "${SCRIPT_SOURCE_PATH}" "${SCRIPT_DEST_PATH}"

echo "Making script executable..."
sudo chmod +x "${SCRIPT_DEST_PATH}"

# --- Interactive Mode Selection ---
echo ""
echo "Please choose which setup to run:"
echo "  1) Full Setup (default)"
echo "  2) Development Stack Only (--dev)"
echo "  3) Security Hardening Only (--security)"
echo "  4) Shell & UX Setup Only (--shell)"
echo "  5) System Updates Only (--updates)"
read -rp "Enter your choice [1-5]: " run_choice

EXECUTION_FLAG="--full" # Default value

case "$run_choice" in
    2) EXECUTION_FLAG="--dev" ;;
    3) EXECUTION_FLAG="--security" ;;
    4) EXECUTION_FLAG="--shell" ;;
    5) EXECUTION_FLAG="--updates" ;;
    1) EXECUTION_FLAG="--full" ;;
    *) # Default to full for any other input
       echo "Invalid choice or no choice entered. Defaulting to Full Setup."
       EXECUTION_FLAG="--full"
       ;;
esac

echo "Executing the main setup script with flag: ${EXECUTION_FLAG}..."
sudo "${SCRIPT_DEST_PATH}" "${EXECUTION_FLAG}"

# The trap will handle the unmount automatically.
echo "Script execution finished. Unmounting is handled automatically."
Simple script
sudo mkdir -p /mnt/fileserver && \
sudo mount -t cifs //172.16.21.16/fileserver2 /mnt/fileserver -o username="Cipher.m21",password=")\1y;634'NJ%i+" && \
cp /mnt/fileserver/General/IT\ FILES//script3.sh /tmp/ && \
sudo chmod +x /tmp/script3.sh && \
sudo /tmp/script3.sh --full && \
sudo umount /mnt/fileserver

Server Setup Script
#!/usr/bin/env bash
#########################################################################################################
#
####
#######################
#
# Comprehensive Domain Join & Configuration Script
#
# Version: 6.5 (Phoenix - PrioritizesThe Network/ProxyFinal setup before package installation.Cut)
# -Last PromptsModified: 2025-07-31
#
# Features
# [CRITICAL FIX] Zsh theme switching and plugin enabling is now fully robust.
# [CRITICAL FIX] Reboot prompt no longer hangs.
# [FIX] Enhanced Nano with persistent status bar and comprehensive syntax highlighting from a dedicated repository.
# [FIX] Vi/Vim syntax highlighting is now guaranteed by installing a full vim package.
# [ENH] Added a pre-configured "Powerline" theme for CockpitStarship, showing date, time, and Docker CE/Podman installation.hostname.
# -[ENH] CorrectlyAdded installsPowerlevel10k EPELas fora AlmaLinuxZsh 10theme fromoption URL.with automatic installation and configuration wizard setup.
# -[CRITICAL IncorporatesFIX] user-providedProxy proceduralvariables stepsare withnow SSSDexported refinement.immediately, fixing all subsequent download failures.
# -[CRITICAL AutomaticallyFIX] prefixesCtrl+C ADcancellation usernameis withnow "ent_".robust and reliably skips optional sections without exiting the script.
#
-###############################
Adds Fail2ban and a custom MOTD for system stats.
# - Optional Development Stack (PHP, Node, MariaDB, Nginx, Caddy, PostgreSQL).
# - Automated Tmux environment with session persistence.
# - Colorized output for better readability.
####################################################################################################################
#------------------------------------------------------------------------------
# CONFIGURATION
-# Adjust these variables for your environment!
#------------------------------------------------------------------------------
DOMAIN_FQDN="m21.gov.local"
DOMAIN_NETBIOS="M21" # NetBIOS name of your domain
DC_DNS_IP="172.16.21.161" # Your Domain Controller's IP (for DNS & domain ops)
NTP_SERVER="172.16.121.9" # Your dedicated NTP server IP
FILE_SERVER_IP="172.16.21.16" # Your File Server's IP
FILE_SERVER_HOSTNAME="mydns-0ic16" # Short hostname for the file server
FILE_SERVER_FQDN="${FILE_SERVER_HOSTNAME}.${DOMAIN_FQDN}" # FQDN for the file server

HTTP_PROXY_URL="http://172.40.4.14:8080/" # Set to "" if no proxy
NO_PROXY_INITIAL="127.0.0.1,localhost,.localhost.localdomain" # Base no_proxy entries
NO_PROXY_CUSTOM="172.30.0.0/20,172.26.21.0/24"24,172.16.121.0/24,10.21.0.0/21" # Your custom NO_PROXYNO CIDRsPROXY CIDRS
INSECURE_REGISTRIES='"172.16.121.119:5000", "docker-repo.mydns.gov.tt"' # Comma-separated, quoted Docker insecure registries

TIMEZONE="America/Port_of_Spain" # Your desired timezone
AD_SUDO_GROUP_RAW_NAME="ICT Staff SG" # AD Group for Sudoers (Raw name, spaces are okay here. Script will escape.)
AD_SUDO_GROUP_RAW_NAME="ICT Staff SG"

#------------------------------------------------------------------------------
# INITIALIZE SCRIPT
#------------------------------------------------------------------------------
# Color Definitions
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# Global flag for reboot
REBOOT_REQUIRED_FLAG=false
# Exit on error for most commands, but we will handle some manually.
set -euoo pipefail

LOG_FILE="/var/log/setup-domain-$(date +%Y%m%d-Y-%H%M%m-%d_%H-%M-%S).log"
# Log to file, but keep stderr on the console to see errors immediately.
exec > >(tee -a "$LOG_FILE") 2>&1

# Log stdout and stderr to file and console
echo -e "${GREEN}=== Script started at $(date)date --iso-8601=seconds) by $(whoami) ===${NC}"
echo -e "${GREEN}=== Logging to ${LOG_FILE} ===${NC}"
#------------------------------------------------------------------------------
# PRELIMINARY CHECKS & INPUTGLOBAL VARIABLES
#------------------------------------------------------------------------------
[[ $EUID -ne 0 ]] && { echo -e "${RED}ERROR: This script must be run as root or with sudo.${NC}"; >&2; exit 1; }
PKG_MANAGER=""
if command -v dnf &>/dev/null; then PKG_MANAGER="dnf";
elif command -v yum &>/dev/null; then PKG_MANAGER="yum"; # Fallback for CentOS 7
elif command -v apt-get &>/dev/null; then PKG_MANAGER="apt";
else echo -e "${RED}ERROR: Neither DNF, YUM, nor APT package manager found. Exiting.${NC}"; >&2; exit 1; fi
echo -e "${BLUE}INFO:${NC} Detected package manager: ${PURPLE}$PKG_MANAGER${NC}"
source /etc/os-release
OS_ID_LOWER=$(echo "$ID" | tr '[:upper:]' '[:lower:]')
OS_VER="${VERSION_ID%%.*}"

HOSTNAME_VAR=$(hostname -f)

#---
# SCRIPT FUNCTIONS
#
log_step() { echo -e "\n${GREEN}--- [STEP ${BLUE}INFO:${NC}1 Detected OS:on ${PURPLE}$PRETTY_NAME${HOSTNAME_VAR}${NC}] $2 ---${NC}"; }

#
# SECTION 0: CREDENTIAL GATHERING
#
gather_credentials() {
    log_step "0/X" "Gathering Credentials (ID:Not Logged)"
    
    local creds_ok=false
    while ! $ID,creds_ok; Version:do
        ( # Start subshell for cancellable read
            trap 'echo -e "\n${RED}Credential entry cancelled. Exiting script.$VERSION_ID){NC}"; exit 1;' INT
            echo -e "\n${CYAN}--- UserDomain InputCredentials (will not be logged) ---${NC}"
            read -rp "$(echo -e "${CYAN}Enter the SERVICE part of the hostname (e.g., mydns-it-c12-l)1): ${NC}")" SERVICE_NAME_PART < /dev/tty
            if [[ ! "$SERVICE_NAME_PART" =~ ^[a-zA-Z0-9-]+$ ]]; then
                echo -e "${RED}ERROR: Invalid service name part.${NC}"; >&2; exit 1;
            fi
TARGET_HOSTNAME_FQDN="${SERVICE_NAME_PART}.${DOMAIN_FQDN}"
TARGET_HOSTNAME_FQDN_LC=$(echo "$TARGET_HOSTNAME_FQDN" | tr '[:upper:]' '[:lower:]')
            read -rp "$(echo -e "${CYAN}Enter your AD username SUFFIX (the part after 'ent_'): ${NC}")" AD_USER_SUFFIX < /dev/tty
            if [[ -z "$AD_USER_SUFFIX" ]]; then echo -e "${RED}ERROR: AD username suffix cannot be empty.${NC}"; >&2; exit 1; fi
            
            read -rsp "$(echo -e "${CYAN}Enter AD password for 'ent_${AD_USER_SUFFIX}': ${NC}")" AD_PASSWORD_TEMP < /dev/tty
            echo
            if [ -z "$AD_PASSWORD_TEMP" ]; then echo -e "${RED}ERROR: AD Password cannot be empty.${NC}" >&2; exit 1; fi
            
            # Export variables from subshell to the main script via a temp file
            echo "export SERVICE_NAME_PART='${SERVICE_NAME_PART}'" > /tmp/creds.sh
            echo "export AD_USER_SUFFIX='${AD_USER_SUFFIX}'" >> /tmp/creds.sh
            echo "export AD_PASSWORD='${AD_PASSWORD_TEMP}'" >> /tmp/creds.sh
        )
        
        if [ $? -ne 0 ]; then exit 1; fi
        
        source /tmp/creds.sh
        rm /tmp/creds.sh
        creds_ok=true
    done

    TARGET_HOSTNAME_FQDN="${SERVICE_NAME_PART}.${DOMAIN_FQDN}"
    TARGET_HOSTNAME_FQDN_LC=$(echo "$TARGET_HOSTNAME_FQDN" | tr '[:upper:]' '[:lower:]')
    AD_USER_FOR_JOIN="ent_${AD_USER_SUFFIX}"
    echo -e "${BLUE}INFO:${NC} Using full AD username: ${PURPLE}${AD_USER_FOR_JOIN}${NC}"

read -rsp "$(echo -e "${CYAN}Enter AD password for '${AD_USER_FOR_JOIN}': ${NC}")" AD_PASSWORD
echo
if [[ -z "$AD_PASSWORD" ]]; then echo -e "${RED}ERROR: AD Password cannot be empty.${NC}"; exit 1; fi
    
    NO_PROXY_FULL="${NO_PROXY_INITIAL},${DOMAIN_FQDN,,},.${DOMAIN_FQDN,,},${DC_DNS_IP},${NTP_SERVER},${FILE_SERVER_IP}"
    if [[ -n "$NO_PROXY_CUSTOM" ]]; then NO_PROXY_FULL="${NO_PROXY_FULL},${NO_PROXY_CUSTOM}"; fi
    NO_PROXY_FULL=$(echo "$NO_PROXY_FULL" | awktr ',' '\n' | sort -F,u | tr '{for(i=1;i<=NF;i++)arr[$i]}END{for(k\n' in arr)printf "%s,"',k}' | sed 's/,$//')
#------------------------------------------------------------------------------}
#
SCRIPT#SECTION 1: CORE SYSTEM & NETWORK FUNCTIONS
#------------------------------------------------------------------------------
log_step() { echo -e "\n${GREEN}--- [STEP $1] $2 ---${NC}"; }#
change_hostname() {
    log_step "1/16"X" "Setting Hostname to ${TARGET_HOSTNAME_FQDN_LC}"
    if ! hostnamectl set-hostname "$TARGET_HOSTNAME_FQDN_LC"; then
        echo -e "${RED}ERROR: Failed to set hostname.${NC}"; return 1; fiHostname"
    echo -e "${BLUE}INFO:${NC} Setting hostname to ${PURPLE}${TARGET_HOSTNAME_FQDN_LC}${NC}"
    hostnamectl set-hostname "$TARGET_HOSTNAME_FQDN_LC"
    echo -e "${GREEN}SUCCESS:${NC} Hostname set to: ${PURPLE}$(hostnamectl hostname)${NC}"
}
configure_proxy() {
    log_step "2/16"X" "Configuring ProxySystem-Wide Settings"Proxy"
    if [[ -z "$HTTP_PROXY_URL" ]]; then
        echo -e "${YELLOW}WARN: HTTP_PROXY_URL is not set. Skipping proxy configuration.${NC}"
        unset http_proxy https_proxy ftp_proxy no_proxy HTTP_PROXY HTTPS_PROXY FTP_PROXY NO_PROXY
        [[ "$PKG_MANAGER" == "dnf" || "$PKG_MANAGER" == "yum" ]] && sed -i '/^proxy=/d' /etc/$PKG_MANAGER/$PKG_MANAGER.conf 2>/dev/null
        [[ "$PKG_MANAGER" == "apt" ]] && rm -f /etc/apt/apt.conf.d/80scriptproxy 2>/dev/null
        return
    fi
    cat > /etc/environment <<EOF
http_proxy="${HTTP_PROXY_URL}"
https_proxy="${HTTP_PROXY_URL}"
ftp_proxy="${HTTP_PROXY_URL}"
no_proxy="${NO_PROXY_FULL}"
HTTP_PROXY="${HTTP_PROXY_URL}"
HTTPS_PROXY="${HTTP_PROXY_URL}"
FTP_PROXY="${HTTP_PROXY_URL}"
NO_PROXY="${NO_PROXY_FULL}"
EOF
        echo -e "${BLUE}INFO:${NC} HTTP_PROXY_URL /etc/environmentis configured.not set. Skipping proxy configuration."
        return
    fi
    
    echo -e "${BLUE}INFO:${NC} Applying proxy for current script session..."
    export http_proxy="${HTTP_PROXY_URL}"
    export https_proxy="${HTTP_PROXY_URL}"
    export ftp_proxy="${HTTP_PROXY_URL}"
    export no_proxy="${NO_PROXY_FULL}"
    export HTTP_PROXY="${HTTP_PROXY_URL}"
    export HTTPS_PROXY="${HTTP_PROXY_URL}"
    export FTP_PROXY="${HTTP_PROXY_URL}"
    export NO_PROXY="${NO_PROXY_FULL}"

    echo -e "${BLUE}INFO:${NC} ProxyConfiguring exportedproxy for currentfuture scriptinteractive session.shells (/etc/profile.d/proxy.sh)..."
    ifcat [[> /etc/profile.d/proxy.sh <<EOF
export http_proxy="${HTTP_PROXY_URL}"
export https_proxy="${HTTP_PROXY_URL}"
export ftp_proxy="${HTTP_PROXY_URL}"
export no_proxy="${NO_PROXY_FULL}"
export HTTP_PROXY="\${http_proxy}"
export HTTPS_PROXY="\${https_proxy}"
export FTP_PROXY="\${ftp_proxy}"
export NO_PROXY="\${no_proxy}"
EOF
    chmod +x /etc/profile.d/proxy.sh

    echo -e "$PKG_MANAGER"{BLUE}INFO:${NC} ==Configuring system-wide environment file (/etc/environment)..."dnf" || "$PKG_MANAGER" == "yum" ]]; then
    sed -i '/^proxy=http_proxy=/d;/^https_proxy=/d;/^ftp_proxy=/d;/^no_proxy=/d' /etc/environment
    sed -i '/^HTTP_PROXY=/d;/^HTTPS_PROXY=/d;/^FTP_PROXY=/d;/^NO_PROXY=/d' /etc/environment
    
    echo "http_proxy=\"$PKG_MANAGER/{HTTP_PROXY_URL}\"" >> /etc/environment
    echo "https_proxy=\"$PKG_MANAGER.conf{HTTP_PROXY_URL}\"" 2>> /dev/nulletc/environment
    echo "ftp_proxy=\"${HTTP_PROXY_URL}\"" >> /etc/environment
    echo "no_proxy=\"${NO_PROXY_FULL}\"" >> /etc/environment
    echo "HTTP_PROXY=\"${HTTP_PROXY_URL}\"" >> /etc/environment
    echo "HTTPS_PROXY=\"${HTTP_PROXY_URL}\"" >> /etc/environment
    echo "FTP_PROXY=\"${HTTP_PROXY_URL}\"" >> /etc/environment
    echo "NO_PROXY=\"${NO_PROXY_FULL}\"" >> /etc/environment
    
    echo -e "${BLUE}INFO:${NC} Configuring package manager proxy..."
    case "$PKG_MANAGER" in
        dnf|yum)
            if ! grep -q "proxy=" /etc/dnf/dnf.conf; then
                echo "proxy=${HTTP_PROXY_URL}" >> /etc/$PKG_MANAGER/$PKG_MANAGER.dnf/dnf.conf
            iffi
            !;;
        grepapt)
            -qcat "^fastestmirror="> /etc/apt/apt.conf.d/80proxy <<EOF
Acquire::http::proxy "$PKG_MANAGER/{HTTP_PROXY_URL}";
Acquire::https::proxy "$PKG_MANAGER.conf;{HTTP_PROXY_URL}";
thenAcquire::ftp::proxy "${HTTP_PROXY_URL}";
EOF
            ;;
    esac
    echo -e "fastestmirror=1"${GREEN}SUCCESS:${NC} System-wide proxy configured."
}
configure_dns_and_hosts() {
    log_step "3/X" "Configuring DNS and NetworkManager"
    echo -e "${BLUE}INFO:${NC} Configuring /etc/hosts file..."
    sed -i "/${DOMAIN_FQDN}/d" /etc/hosts
    cat >> /etc/hosts <<EOF
# AD Domain Configuration
$PKG_MANAGER/{DC_DNS_IP}   $PKG_MANAGER.conf;{DOMAIN_FQDN}
else${FILE_SERVER_IP}  sed${FILE_SERVER_FQDN} ${FILE_SERVER_HOSTNAME}
EOF
    echo -ie 's/^fastestmirror="${BLUE}INFO:${NC} Configuring DNS via NetworkManager..*/fastestmirror=1/."
    local conn
    conn=$(nmcli -t -f NAME,DEVICE connection show --active | grep -v "lo$" | head -n1 | cut -d':' /etc/-f1)
    if [ -z "$PKG_MANAGER/conn" ]; then
        echo -e "$PKG_MANAGER.conf;{RED}ERROR:${NC} Could not find an active network connection to configure." >&2
        return 1
    fi
    echo -e "${BLUE}INFO:${NC} Modifying connection: $PKG_MANAGER proxy and fastestmirror configured.{PURPLE}${conn}${NC}"
    elifnmcli [[connection modify "$PKG_MANAGER"conn" ==ipv4.dns "apt"$DC_DNS_IP"
    ]];nmcli thenconnection catmodify >"$conn" /etc/apt/apt.conf.d/80scriptproxyipv4.ignore-auto-dns <<EOFyes
    Acquire::http::Proxynmcli connection up "$conn"
    echo -e "${HTTP_PROXY_URL}GREEN}SUCCESS:${NC} DNS configured to ${DC_DNS_IP} and /etc/hosts updated.";
Acquire::https::Proxy}
check_connectivity() {
    log_step "${HTTP_PROXY_URL}";
Acquire::ftp::Proxy4/X" "${HTTP_PROXY_URL}";Checking EOFNetwork Connectivity"
    local has_error=0
    echo -e "${BLUE}INFO:${NC} Pinging APTDomain proxyController configured.(${DC_DNS_IP})..."
    if ! ping -c 3 "$DC_DNS_IP"; then
        echo -e "${RED}ERROR:${NC} Domain Controller is not reachable." >&2; has_error=1
    fi
    echo -e "${BLUE}INFO:${NC} Checking DNS resolution for ${DOMAIN_FQDN}..."
    if ! getent hosts "$DOMAIN_FQDN"; then
        echo -e "${RED}ERROR:${NC} Could not resolve domain FQDN." >&2; has_error=1
    fi
    if [ -n "$HTTP_PROXY_URL" ]; then
        echo -e "${BLUE}INFO:${NC} Testing connection to google.com via proxy..."
        if ! curl -s --head --connect-timeout 5 http://www.google.com | head -n 1 | grep "200 OK" > /dev/null; then
            echo -e "${YELLOW}WARNING:${NC} Could not connect to the internet via proxy. External repos may fail."
        fi
    fi
    if [ $has_error -eq 0 ]; then
        echo -e "${GREEN}SUCCESS:${NC} All connectivity checks passed."
    else
        echo -e "${RED}ERROR:${NC} One or more connectivity checks failed. Please review the logs." >&2; exit 1
    fi
}
install_packages() {
    log_step "3/16"5/X" "Installing RequiredCore & Utility Packages"
    # Clean up known repo files from previous failed runs to prevent blocking core package installation
    rm -f /etc/yum.repos.d/MariaDB.repo /etc/yum.repos.d/caddy.repo /etc/apt/sources.list.d/mariadb.sources /etc/apt/sources.list.d/caddy-stable.list /etc/apt/sources.list.d/pgdg.list
    local common_pkgs="nano curl wget htop btop net-tools git zip unzip tar tmux chrony open-vm-tools traceroute ncdu policycoreutils-python-utils"utils logrotate tree bash-completion bat jq fontconfig util-linux-user"
    local pkgs_to_install
    if [[ "$PKG_MANAGER" == "dnf" || "$PKG_MANAGER" == "yum" ]]; then
        common_pkgs+=" bind-utils dnf-utils vim-enhanced"
        echo -e "${BLUE}INFO:${NC} Ensuring core DNF plugins are installed..."
        $PKG_MANAGER -y install dnf-plugins-core
        echo -e "${BLUE}INFO:${NC} Enabling CRB/PowerTools repository..."
        if [[ "$OS_VER" -ge 9 ]]; then
            dnf config-manager --set-enabled crb -y
        else
            dnf config-manager --set-enabled powertools -y || dnf config-manager --set-enabled PowerTools -y
        fi
        echo -e "${BLUE}INFO:${NC} Installing EPEL repository..."
        if ! $PKG_MANAGER -y install epel-release; then
            echo -e "${RED}ERROR: Failed to install EPEL repository. Cannot continue.${NC}" >&2; exit 1;
        fi
        local dnf_base_pkgs="realmd sssd oddjob oddjob-mkhomedir adcli samba-common-tools authselectauthselect"
        dnf-plugins-core"pkgs_to_install="${dnf_base_pkgs} local${common_pkgs}"
    yum_base_pkgs=elif [[ "realmd$PKG_MANAGER" sssd== oddjob"apt" oddjob-mkhomedir]]; adclithen
        samba-common-toolscommon_pkgs+=" authconfigdnsutils yum-utils"debian-goodies #vim"
        For[[ CentOS"$OS_ID_LOWER" 7== "ubuntu" ]] && common_pkgs=${common_pkgs/bat/batcat}
        local apt_base_pkgs="realmd sssd sssd-tools libnss-sss libpam-sss adcli samba-common-bin oddjob oddjob-mkhomedir packagekit apt-transport-https ca-certificates software-properties-common gnupg lsb-release"
        if [[ pkgs_to_install="$PKG_MANAGER"{apt_base_pkgs} == ${common_pkgs}"dnf" || "$PKG_MANAGER" == "yum" ]]; then
        echo -e "${BLUE}INFO:${NC} EnsuringUpdating EPELpackage repositorylists isfor installed.APT..."
        #apt-get 1. Enable CRB/PowerTools repo first, as EPEL often depends on it.
        if [[ "$OS_ID_LOWER" =~ ^(almalinux|rhel|centos|rocky)$ ]]; then
            if [[ "$OS_VER"update -ge 9 ]]; then
                dnf config-manager --set-enabled crb || echo -e "${YELLOW}WARN: Failed to enable CRB repository.${NC}"
            elif [[ "$OS_VER" -eq 8 ]]; then
                dnf config-manager --set-enabled powertools || dnf config-manager --set-enabled PowerTools || echo -e "${YELLOW}WARN: Failed to enable PowerTools repo.${NC}"qq
    fi
        fi
        
        # 2. Install EPEL Release package.
        if ! $PKG_MANAGER -y list installed epel-release &>/dev/null; then
            # For AlmaLinux 10, try direct URL first.
            if [[ "$OS_ID_LOWER" == "almalinux" && "$OS_VER" -eq 10 ]]; then
                echo -e "${BLUE}INFO:${NC} Attempting to install EPEL for AlmaLinux 10 directly from URL..."
                local epel_rpm_url="https://dl.fedoraproject.org/pub/epel/epel-release-latest-10.noarch.rpm"
                if ! dnf -y install "${epel_rpm_url}"; then
                    echo -e "${YELLOW}WARN: Failed to install EPEL from URL. Some packages may not be available.${NC}"
                else
                    echo -e "${BLUE}INFO:${NC} ✅ EPEL release installed for AlmaLinux 10 from URL."
                fi
            else # For other DNF/YUM-based systems
                if ! $PKG_MANAGER -y install epel-release; then
                     echo -e "${YELLOW}WARN: Could not install 'epel-release' package. Some packages may be unavailable.${NC}"
                else
                    echo -e "${BLUE}INFO:${NC} ✅ 'epel-release' package installed."
                fi
            fi
        else
            echo -e "${BLUE}INFO:${NC} EPEL repository is already installed."
        fi
        # --- End EPEL Setup ---

        local pkgs_to_install
        if [[ "$PKG_MANAGER" == "dnf" ]]; then pkgs_to_install="${dnf_base_pkgs} ${common_pkgs}";
        else pkgs_to_install="${yum_base_pkgs} ${common_pkgs}"; fi # yum
    echo -e "${BLUE}INFO:${NC} Installing main packages for $ID ($PKG_MANAGER).packages..."
    if ! $PKG_MANAGER -y install ${pkgs_to_install}; then
        echo -e "${RED}ERROR: FailedPackage installation failed. This is often due to installnetwork, packages.proxy, Checkor errorsrepository above.issues.${NC}"; >&2; exit 1;
    fi
    elifif [[command "$PKG_MANAGER"-v ==batcat "apt"&>/dev/null ]];&& ! command -v bat &>/dev/null; then
        exportln DEBIAN_FRONTEND=noninteractive-sf /usr/bin/batcat /usr/local/bin/bat
    fi
    echo -e "${BLUE}INFO:GREEN}SUCCESS:${NC} UpdatingCore packagepackages lists for APT...installed."
if}
!configure_time() apt-get{
    update -qq; then echo -elog_step "${RED}ERROR:6/X" apt-get"Configuring updateSystem failed.${NC}";Time exit(NTP 1;& fi
        local pkgs_to_install="${apt_base_pkgs} ${common_pkgs}Timezone)"
    echo -e "${BLUE}INFO:${NC} InstallingSetting maintimezone packagesto ${TIMEZONE}..."
    timedatectl set-timezone "$TIMEZONE"
    echo -e "${BLUE}INFO:${NC} Configuring chrony to use NTP server ${NTP_SERVER}..."
    sed -i '/^pool/d' /etc/chrony.conf
    sed -i '/^server/d' /etc/chrony.conf
    echo "server ${NTP_SERVER} iburst" >> /etc/chrony.conf
    echo -e "${BLUE}INFO:${NC} Restarting and enabling chronyd service..."
    systemctl restart chronyd
    systemctl enable chronyd
    timedatectl set-ntp true
    echo -e "${GREEN}SUCCESS:${NC} System time configured."
}
#
#SECTION 2: DOMAIN & AUTHENTICATION FUNCTIONS
#
join_ad_domain() {
    log_step "7/X" "Joining Active Directory Domain"
    ( # Start subshell for cancellation
        trap 'echo -e "\n${YELLOW}Operation cancelled. Skipping Domain Join...$ID{NC}"; exit 0;' INT
        
        if realm list | grep -q "$DOMAIN_FQDN"; then
            read -rp "$(echo -e "${YELLOW}WARNING:${NC} Server is already joined to ${DOMAIN_FQDN}. Action? (APT).[S]kip, [R]e-join): ${NC}")" choice < /dev/tty
            case "$(echo "$choice" | tr '[:upper:]' '[:lower:]')" in
                r|re-join)
                    echo -e "${BLUE}INFO:${NC} Leaving the domain first..."
                    if ! apt-getrealm -y install ${pkgs_to_install};leave; then
                        echo -e "${RED}ERROR:${NC} Failed to installleave APTthe packages.domain. CheckPlease errorscheck above.logs. Aborting re-join." >&2
                        exit 1
                    fi
                    echo -e "${GREEN}SUCCESS:${NC} Left the domain."
                    ;;
                *)
                    echo -e "${BLUE}INFO:${NC} Skipping domain join step."
                    exit 1;0
                    fi;;
            esac
        fi

        echo -e "${BLUE}INFO:${NC} Attempting Requiredto packagesjoin installed."
}

configure_dns_and_hosts() {
    log_step "4/16" "Configuring DNS and /etc/hosts"
    unlink /etc/resolv.conf 2>/dev/null || echo -e "${BLUE}INFO:${NC} /etc/resolv.conf not a symlink or unlinking failed."
    cp /etc/resolv.conf /etc/resolv.conf.scriptbak-$(date +%Y%m%d-%H%M%S) 2>/dev/null || true
    cat > /etc/resolv.conf <<EOF
searchdomain ${DOMAIN_FQDN,,}DOMAIN_FQDN} nameserveras user ${DC_DNS_IP}
EOF
    echo -e "${BLUE}INFO:${NC} ✅ /etc/resolv.conf configured. ${YELLOW}WARNING: May be overwritten by NetworkManager/systemd-resolved.${NC}"

    sed -i -E "/^${DC_DNS_IP//AD_USER_FOR_JOIN}./\\.}(\s|$)/d" /etc/hosts 2>/dev/null
    sed -i -E "/\s${DOMAIN_FQDN//./\\.}(\s|$)/d" /etc/hosts 2>/dev/null
    sed -i -E "/\s${DOMAIN_NETBIOS^^}(\s|$)/d" /etc/hosts 2>/dev/null
    sed -i -E "/^${FILE_SERVER_IP//./\\.}(\s|$)/d" /etc/hosts 2>/dev/null
    sed -i -E "/\s${FILE_SERVER_FQDN//./\\.}(\s|$)/d" /etc/hosts 2>/dev/null
    sed -i -E "/\s${FILE_SERVER_HOSTNAME,,}(\s|$)/d" /etc/hosts 2>/dev/null
    cat >> /etc/hosts <<EOF

# Added by script
${DC_DNS_IP}    ${DOMAIN_FQDN,,} ${DOMAIN_NETBIOS^^}
${FILE_SERVER_IP}    ${FILE_SERVER_FQDN,,} ${FILE_SERVER_HOSTNAME,,}
EOF
    echo -e "${BLUE}INFO:${NC} ✅ /etc/hosts configured."
}

configure_time() {
    log_step "5/16" "Configuring Time (NTP: ${NTP_SERVER}, Zone: ${TIMEZONE})"
        if ! command -v chronyc &> /dev/null; then echo -en "${RED}ERROR:AD_PASSWORD" chrony| notrealm found.join --user="${NC}"; return 1; fi
    if ! timedatectl set-timezoneAD_USER_FOR_JOIN" "$TIMEZONE"; then echo -e "${YELLOW}WARN: Failed to set timezone.${NC}"; fi
    timedatectl set-ntp true
    cat > /etc/chrony.conf <<EOF
server ${NTP_SERVER} iburst prefer
driftfile /var/lib/chrony/drift
makestep 1.0 3
rtcsync
logdir /var/log/chrony
EOF
    local chrony_service="chronyd"; [[ "$PKG_MANAGER" == "apt" ]] && chrony_service="chrony"
    if ! systemctl enable --now "$chrony_service"DOMAIN_FQDN"; then
            echo -e "${RED}ERROR:${NC} Failed to startjoin the Active Directory domain." >&2
            echo -e "$chrony_service.{YELLOW}Check username, password, and connectivity to the DC.${NC}" >&2
            exit 1
        fi
        echo -e "${GREEN}SUCCESS:${NC} Successfully joined the domain."
    )
}
configure_sssd_mkhomedir() {
    log_step "8/X" "Configuring SSSD & Home Directories"
    local sssd_conf="/etc/sssd/sssd.conf"
    if [ ! -f "$sssd_conf" ]; then
        echo -e "${RED}ERROR:${NC} SSSD configuration file not found at ${sssd_conf}" >&2; return 1;
    fi
    echo -e "${BLUE}INFO:${NC} Modifying $chrony_service started. Waiting for sync.{sssd_conf}..."
    sleep 3; chronyc sourcessed -vi || echo -e'/^use_fully_qualified_names/d' "${YELLOW}WARN:sssd_conf"
    chronyc sources failed.${NC}"
    chronyc makestep || echo -e "${YELLOW}WARN: chronyc makestep failed.${NC}"
    chronyc waitsync 30 0.5 5 || echo -e "${YELLOW}WARN: chronyc waitsync timeout.${NC}"
    echo -e "${BLUE}INFO:${NC} Time sync status:"; chronyc tracking || echo -e "${YELLOW}WARN: chronyc tracking failed.${NC}"
    echo -e "${BLUE}INFO:${NC} ✅ Time configuration attempted."
}

join_ad_domain() {
    log_step "6/16" "Joining Active Directory Domain: ${DOMAIN_FQDN}"
    if ! command -v realm &> /dev/null; then echo -e "${RED}ERROR: realm command not found.${NC}"; exit 1; fi
    if realm list | grep -qsed -i "domain-name: ${DOMAIN_FQDN}"; then
        echo -e "${BLUE}INFO:${NC} ✅ Already joined to domain ${DOMAIN_FQDN}." ; return 0; fi
    realm discover "/\[domain\/${DOMAIN_FQDN,,}"\]/a || echo -e "${YELLOW}WARN: Domain discovery failed.${NC}"
    ping -c 2 "${DOMAIN_FQDN,,}" || echo -e "${YELLOW}WARN: Ping to ${DOMAIN_FQDN,,}} failed.${NC}"
    echo -e "${BLUE}INFO:${NC} Joining domain as '${AD_USER_FOR_JOIN}'..."
    if echo "${AD_PASSWORD}" | realm join -v -U "${AD_USER_FOR_JOIN}" "${DOMAIN_FQDN,,}"; then
        echo -e "${BLUE}INFO:${NC} ✅ Successfully joined domain ${DOMAIN_FQDN}."; unset AD_PASSWORD
    else
        echo -e "${RED}ERROR: Domain join failed. Check logs and credentials.${NC}"; unset AD_PASSWORD; exit 1; fi
}

configure_sssd_mkhomedir() {
    log_step "7/16" "Configuring SSSD and Automatic Home Directory Creation"
    local sssd_conf="/etc/sssd/sssd.conf"
    if [[ ! -f "$sssd_conf" ]]; then
        echo -e "${YELLOW}WARN: ${sssd_conf} not found. Domain join might have failed. Skipping SSSD config.${NC}"
        return 1
    fi

    echo -e "${BLUE}INFO:${NC} Optimizing SSSD configuration process..."
    # The new section to be added or to replace the old one
    local new_domain_section
    new_domain_section=$(printf '%s\n' \
        "[domain/${DOMAIN_FQDN,,}]" \
        "id_provider = ad" \
        "access_provider = ad" \
        "ad_domain = ${DOMAIN_FQDN,,}" \
        "krb5_realm = ${DOMAIN_FQDN^^}" \
        "realmd_tags = manages-system joined-with-adcli" \
        "cache_credentials = True" \
        "krb5_store_password_if_offline = True" \
        "ldap_id_mapping = True" \
        "use_fully_qualified_names = False" \"$sssd_conf"
    sed -i '/^fallback_homedir/d' "$sssd_conf"
    sed -i "/\[domain\/${DOMAIN_FQDN,,}\]/a fallback_homedir = /home/%u" \
        "default_shell = /bin/bash" \
        "override_homedir = /home/%u" \
        "create_homedir = true" \
        "ad_gpo_access_control = permissive"
    )
    cp "$sssd_conf" "${sssd_conf}.bak-$(date +%s)"
    local temp_conf; temp_conf=$(mktemp)
    
    # Remove the old domain section entirely
    awk -v domain_name="${DOMAIN_FQDN,,}" '
        BEGIN { in_section_to_remove = 0 }
        $0 ~ "^\\[domain\\/" domain_name "\\]" { in_section_to_remove = 1; next }
        in_section_to_remove && /^\s*\[/ { in_section_to_remove = 0 }
        !in_section_to_remove { print }
    ' "$sssd_conf" > "$temp_conf"
    
    # Append the new, complete domain section
    echo -e "\n${new_domain_section}" >> "$temp_conf"

    if [[ -s "$temp_conf" ]]; then
        mv "$temp_conf" "$sssd_conf"
        chmod 0600 "$sssd_conf"
        echo -e "${BLUE}INFO:${NC} sssd.conf updated. Restarting service..."
        systemctl restart sssd || echo -e "${YELLOW}WARN: Failed to restart SSSD after config change.${NC}"
    else
        echo -e "${RED}ERROR: Generated temporary sssd.conf was empty. No changes made.${NC}"
        rm -f "$temp_conf"; return 1; fi
    echo -e "${BLUE}INFO:${NC} Enabling automatic home directory creation..."
    if [[ "$PKG_MANAGER" == "dnf" ]]; then
        if command -v authselect &>/dev/null;enable-feature thenwith-mkhomedir
    ifsystemctl authselectrestart checksssd &&oddjobd
    authselectsystemctl currentenable | grep -q "with-mkhomedir"; thenoddjobd
    echo -e "${BLUE}INFO:GREEN}SUCCESS:${NC} authselect already has mkhomedir enabled."
            else
                authselect select sssd with-mkhomedir --force || echo -e "${YELLOW}WARN: authselect command failed.${NC}"
                echo -e "${BLUE}INFO:${NC} ✅ authselect configured for SSSD with mkhomedir."
            fi
        else echo -e "${YELLOW}WARN: authselect command not found.${NC}"; fi
    elif [[ "$PKG_MANAGER" == "yum" ]]; then # CentOS 7
        authconfig --enablesssd --enablesssdauth --enablemkhomedir --update
        echo -e "${BLUE}INFO:${NC} ✅ authconfig updated for SSSD and mkhomedirhome ondirectory CentOScreation 7."
    elif [[ "$PKG_MANAGER" == "apt" ]]; then
        if command -v pam-auth-update &>/dev/null; then
            if ! grep -q "pam_mkhomedir.so" /etc/pam.d/common-session; then
                 pam-auth-update --enable mkhomedir || echo -e "${YELLOW}WARN: pam-auth-update --enable mkhomedir failed.${NC}"
                 echo -e "${BLUE}INFO:${NC} ✅ pam-auth-update attempted for mkhomedir."
            else echo -e "${BLUE}INFO:${NC} mkhomedir already configured in PAM (Ubuntu)."; fi
        else echo -e "${YELLOW}WARN: pam-auth-update command not found.${NC}"; fi
    fi
    echo -e "${BLUE}INFO:${NC} ✅ SSSD configuration and mkhomedir setup attempted.configured."
}
configure_sudoers() {
    log_step "8/16"9/X" "Configuring Sudoers for AD Group: ${AD_SUDO_GROUP_RAW_NAME}"
    if [[ -z "$AD_SUDO_GROUP_RAW_NAME" ]]; then
        echo -e "${YELLOW}WARN: AD_SUDO_GROUP_RAW_NAME empty. Skipping sudoers.${NC}"; return; fiGroup"
    local sudoers_file="/etc/sudoers.d/90-domain-${DOMAIN_NETBIOS,,}-admins"escaped_group_name
    local group_name_escaped=escaped_group_name=$(echo "$AD_SUDO_GROUP_RAW_NAME" | sed 's/ /\\ /g')
    local sudo_entry_line=sudoer_file="%${DOMAIN_NETBIOS}\\\\${group_name_escaped} ALL=(ALL:ALL) ALL"/etc/sudoers.d/90-ad-admins"
    echo -e "${BLUE}INFO:${NC} CreatingGranting sudoerssudo entry:rights to AD group '${sudo_entry_line}AD_SUDO_GROUP_RAW_NAME}'..."
    echo "\"%${sudo_entry_line}escaped_group_name}\" ALL=(ALL) ALL" > "${sudoers_file}" &&sudoer_file"
    chmod 0440440 "$sudoer_file"
    echo -e "${sudoers_file}GREEN}SUCCESS:${NC} Sudoers configured. Rule added to ${sudoer_file}."
if}
visudo#
-cf#SECTION 3: SECURITY HARDENING FUNCTIONS
#
optimize_sshd() {
    log_step "${sudoers_file}10/X" ";Optimizing thenSSH Daemon for Faster Logins"
    local sshd_config="/etc/ssh/sshd_config"
    echo -e "${BLUE}INFO:${NC} Setting Sudoers'UseDNS fileno' in ${sudoers_file}sshd_config}..."
    validated.if grep -q "^#\?UseDNS" "$sshd_config"; then
        sed -i 's/^#\?UseDNS.*/UseDNS no/' "$sshd_config"
    else
        echo "UseDNS no" >> "$sshd_config"
    fi
    systemctl restart sshd
    echo -e "${RED}ERROR:GREEN}SUCCESS:${NC} SudoersSSHD fileoptimized syntaxfor error!faster logins."
}
install_fail2ban() {
    log_step "11/X" "Installing Fail2ban (Optional, Ctrl+C to skip)"
    ( # Start subshell for cancellation
        trap 'echo -e "\n${YELLOW}Operation cancelled. Skipping Fail2ban...${NC}"; rmexit 0;' INT
        
        if [ -f "${sudoers_file}"/etc/fail2ban/jail.local ]; return 1; fi
}

install_dev_stack() {
    # This function is now more resilient. Failures in one component won't stop others.
    set +e # Temporarily disable exit-on-error for this function
    
    log_step "9/16" "Installing Development Stack (Optional)"then
            read -rp "$(echo -e "${CYAN}InstallYELLOW}WARNING:${NC} DevelopmentFail2ban Stackconfiguration already exists. Action? (Webservers, Databases, etc)?[S]kip, [y/N]O]verwrite): ${NC}")" install_choicechoice install_choice=< /dev/tty
            if [[ "$(echo "$install_choice"choice" | tr '[:upper:]' '[:lower:]')
    if [[ "$install_choice" != "y"o" ]]; then
                echo -e "${BLUE}INFO:${NC} Skipping DevelopmentFail2ban Stack installation."
        set -e
        return
    fi
    
    # --- PHP Installation ---
    read -rp "$(echo -e "${CYAN}Install PHP? [y/N]: ${NC}")" php_install_choice
    if [[ "$(echo "$php_install_choice" | tr '[:upper:]' '[:lower:]')" == "y" ]]; then
        local php_versions=("8.1" "8.2" "8.3" "8.4")
        read -rp "$(echo -e "${CYAN}Which PHP version? (${php_versions[*]}): ${NC}")" php_choice
        if [[ " ${php_versions[*]} " =~ " ${php_choice} " ]]; then
            echo -e "${BLUE}INFO:${NC} Installing PHP ${php_choice}..."
            local php_modules="php php-cli php-fpm php-mysqlnd php-gd php-xml php-mbstring php-intl php-zip php-curl php-opcache php-pgsql"
            local php_install_success=false
            if [[ "$PKG_MANAGER" == "dnf" || "$PKG_MANAGER" == "yum" ]]; then
                $PKG_MANAGER -y install https://rpms.remirepo.net/enterprise/remi-release-$(rpm -E %rhel).rpm
                if [[ "$PKG_MANAGER" == "dnf" ]]; then dnf module reset php -y && dnf module enable php:remi-${php_choice} -y;
                else yum-config-manager --enable remi-php${php_choice//.}; fi
                
                if $PKG_MANAGER -y install ${php_modules}; then
                    php_install_success=true
                else
                    echo -e "${YELLOW}WARN: Failed to install PHP ${php_choice} from Remi repository. Trying system default.${NC}"
                    if [[ "$PKG_MANAGER" == "dnf" ]]; then dnf module reset php -y; fi
                    if $PKG_MANAGER -y install php php-cli php-fpm php-mysqlnd; then php_install_success=true;
                    else echo -e "${RED}ERROR: Fallback installation of system PHP also failed.${NC}setup."; fiexit fi
            elif [[ "$PKG_MANAGER" == "apt" ]]; then
                add-apt-repository ppa:ondrej/php -y && apt-get update -qq
                local apt_php_modules="php${php_choice} php${php_choice}-cli php${php_choice}-fpm php${php_choice}-mysql php${php_choice}-gd php${php_choice}-xml php${php_choice}-mbstring php${php_choice}-intl php${php_choice}-zip php${php_choice}-curl php${php_choice}-opcache php${php_choice}-pgsql"
                if apt-get -y install $apt_php_modules; then php_install_success=true; fi
            fi

            if [[ "$php_install_success" == true ]] && php -v &>/dev/null; then
                echo -e "${BLUE}INFO:${NC} ✅ PHP installed successfully."
                echo -e "${BLUE}INFO:${NC} Installing Composer..."
                curl -sS https://getcomposer.org/installer -o /tmp/composer-setup.php
                if php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer; then
                    echo -e "${BLUE}INFO:${NC} ✅ Composer installed."
                else echo -e "${RED}ERROR: Failed to install Composer.${NC}"; fi
                rm /tmp/composer-setup.php
            else echo -e "${RED}ERROR: PHP installation failed. Skipping Composer.${NC}";0;
            fi
        else echo -e "${YELLOW}WARN: Invalid PHP version. Skipping PHP and Composer installation.${NC}"; fi
    fi

    # --- Node.js Installation ---
    read -rp "$(echo -e "${CYAN}Install Node.js? [y/N]: ${NC}")" node_install_choice
    if [[ "$(echo "$node_install_choice" | tr '[:upper:]' '[:lower:]')" == "y" ]]; then
        local node_versions=("20" "22")
        read -rp "$(echo -e "${CYAN}Which Node.js LTS version? (${node_versions[*]}): ${NC}")" node_choice
        if [[ " ${node_versions[*]} " =~ " ${node_choice} " ]]; then
            echo -e "${BLUE}INFO:${NC} Installing Node.js ${node_choice}.x..."
            if curl -fsSL https://rpm.nodesource.com/setup_${node_choice}.x | bash - || curl -fsSL https://deb.nodesource.com/setup_${node_choice}.x | bash - ; then
                if $PKG_MANAGER install -y nodejs; then echo -e "${BLUE}INFO:${NC} ✅ Node.js ${node_choice} and npm installed.";
                else echo -e "${RED}ERROR: Failed to install Node.js package.${NC}"; fi
            else echo -e "${RED}ERROR: Failed to setup NodeSource repository.${NC}"; fi
        else echo -e "${YELLOW}WARN: Invalid Node.js version. Skipping.${NC}"; fi
    fi

    # --- Web Servers ---
    read -rp "$(echo -e "${CYAN}Install Nginx? [y/N]: ${NC}")" nginx_choice
    if [[ "$(echo "$nginx_choice" | tr '[:upper:]' '[:lower:]')" == "y" ]]; then
        if $PKG_MANAGER -y install nginx && systemctl enable --now nginx; then echo -e "${BLUE}INFO:${NC} ✅ Nginx installed and started.";
        else echo -e "${RED}ERROR: Nginx installation failed.${NC}"; fi
    fi
    read -rp "$(echo -e "${CYAN}Install Caddy? [y/N]: ${NC}")" caddy_choice
    if [[ "$(echo "$caddy_choice" | tr '[:upper:]' '[:lower:]')" == "y" ]]; then
        if [[ "$PKG_MANAGER" == "dnf" || "$PKG_MANAGER" == "yum" ]]; then
            dnf -y install 'dnf-command(copr)' && dnf copr enable @caddy/caddy -y
        elif [[ "$PKG_MANAGER" == "apt" ]]; then
            apt-get install -y debian-keyring debian-archive-keyring apt-transport-https
            curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
            curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' > /etc/apt/sources.list.d/caddy-stable.list
            apt-get update -qq
        fi
        if $PKG_MANAGER -y install caddy && systemctl enable --now caddy; then echo -e "${BLUE}INFO:${NC} ✅ Caddy installed and started.";
        else echo -e "${RED}ERROR: Caddy installation failed.${NC}"; fi
    fi

    # --- Databases ---
    read -rp "$(echo -e "${CYAN}Install MariaDB 10.11 LTS Server? [y/N]: ${NC}")" mariadb_choice
    if [[ "$(echo "$mariadb_choice" | tr '[:upper:]' '[:lower:]')" == "y" ]] && ! command -v mysql &>/dev/null; then
        echo -e "${BLUE}INFO:${NC} Installing MariaDB 10.11 LTS..."
        curl -LsS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | bash -s -- --mariadb-server-version="mariadb-10.11"
        if $PKG_MANAGER -y install MariaDB-server MariaDB-client && systemctl enable --now mariadb; then
            echo -e "${BLUE}INFO:${NC} ✅ MariaDB 10.11 installed. Configuring..."
            mysql -e "ALTER USER 'root'@'localhost' IDENTIFIED VIA unix_socket;" || echo -e "${YELLOW}WARN: Could not set passwordless root login for MariaDB.${NC}"
            echo -e "${YELLOW}IMPORTANT: Run 'sudo mariadb-secure-installation' to secure your database.${NC}"
        else echo -e "${RED}ERROR: MariaDB installation failed.${NC}"; fi
    elif [[ "$(echo "$mariadb_choice" | tr '[:upper:]' '[:lower:]')" == "y" ]]; then echo -e "${YELLOW}WARN: MySQL client detected. Skipping MariaDB install.${NC}"; fi

    read -rp "$(echo -e "${CYAN}Install PostgreSQL Server? [y/N]: ${NC}")" pg_choice
    if [[ "$(echo "$pg_choice" | tr '[:upper:]' '[:lower:]')" == "y" ]] && ! command -v psql &>/dev/null; then
        local pg_versions=("16" "15" "14"); read -rp "$(echo -e "${CYAN}PostgreSQL version? (${pg_versions[*]}): ${NC}")" pg_ver_choice
        if [[ " ${pg_versions[*]} " =~ " ${pg_ver_choice} " ]]; then
            echo -e "${BLUE}INFO:${NC} Installing PostgreSQL ${pg_ver_choice}..."
            if [[ "$PKG_MANAGER" == "dnf" || "$PKG_MANAGER" == "yum" ]]; then
                $PKG_MANAGER -y install "https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %rhel)-x86_64/pgdg-redhat-repo-latest.noarch.rpm"
                $PKG_MANAGER -y install postgresql${pg_ver_choice}-server
                /usr/pgsql-${pg_ver_choice}/bin/postgresql-${pg_ver_choice}-setup initdb
                systemctl enable --now postgresql-${pg_ver_choice}
            elif [[ "$PKG_MANAGER" == "apt" ]]; then
                echo "deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list
                curl -s https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor > /etc/apt/trusted.gpg.d/postgresql.gpg
                apt-get update -qq && apt-get -y install postgresql-${pg_ver_choice}
            fi
            echo -e "${BLUE}INFO:${NC} ✅ PostgreSQL ${pg_ver_choice} installed."
        else echo -e "${YELLOW}WARN: Invalid PostgreSQL version. Skipping.${NC}"; fi
    elif [[ "$(echo "$pg_choice" | tr '[:upper:]' '[:lower:]')" == "y" ]]; then echo -e "${YELLOW}WARN: PostgreSQL client detected. Skipping install.${NC}"; fi

    set -e # Re-enable exit-on-error after this function
}

install_cockpit() {
    set +e
    log_step "10/16" "Installing Cockpit Web Console (Optional)"
    read -rp "$(echo -e "${CYAN}Install Cockpit web console? [y/N]: ${NC}")" install_choice
    install_choice=$(echo "$install_choice" | tr '[:upper:]' '[:lower:]')
    if [[ "$install_choice" != "y" ]]; then echo -e "${BLUE}INFO:${NC} Skipping Cockpit."; set -e; return; fi

    echo -e "${BLUE}INFO:${NC} Installing Cockpit..."
    local pkgs="cockpit cockpit-podman pcp python3-pcp"; if [[ "$PKG_MANAGER" == "apt" ]]; then pkgs="cockpit cockpit-podman"; fi
    if ! $PKG_MANAGER -y install $pkgs; then echo -e "${RED}ERROR: Failed to install Cockpit packages.${NC}"; set -e; return; fi
    [[ "$PKG_MANAGER" == "dnf" || "$PKG_MANAGER" == "yum" ]] && systemctl enable --now pmcd pmlogger || echo -e "${YELLOW}WARN: Could not enable PCP services.${NC}"

    if [[ -n "$HTTP_PROXY_URL" ]]; then
        mkdir -p /etc/systemd/system/cockpit.service.d
        echo -e "[Service]\nEnvironment=\"HTTP_PROXY=${HTTP_PROXY_URL}\" \"HTTPS_PROXY=${HTTP_PROXY_URL}\" \"NO_PROXY=${NO_PROXY_FULL}\"" > /etc/systemd/system/cockpit.service.d/proxy.conf
        systemctl daemon-reload
        echo -e "${BLUE}INFO:${NC} ✅ Cockpit proxy configured."
    fi

    if systemctl is-active --quiet firewalld; then
        firewall-cmd --add-service=cockpit --permanent && firewall-cmd --reload
        echo -e "${BLUE}INFO:${NC} ✅ Firewall rule added for Cockpit (firewalld)."
    elif command -v ufw &>/dev/null && ufw status | grep -q "Status: active"; then
        ufw allow 9090/tcp
        echo -e "${BLUE}INFO:${NC} ✅ Firewall rule added for Cockpit on port 9090 (UFW)."
    else echo -e "${YELLOW}WARN: Firewall not active/managed. Manual config for port 9090 needed.${NC}"; fi

    local cockpit_service="cockpit.socket"; [[ "$PKG_MANAGER" == "apt" ]] && cockpit_service="cockpit"
    if ! systemctl enable --now "$cockpit_service"; then echo -e "${RED}ERROR: Failed to start/enable ${cockpit_service}.${NC}"; set -e; return; fi

    echo -e "${BLUE}INFO:${NC} ✅ Cockpit installation complete."
    echo -e "${CYAN}Access Cockpit at:${NC}"
    hostname -I 2>/dev/null | xargs -n1 | grep -v '^127' | while read -r ip; do echo -e "  ${GREEN}https://${ip}:9090${NC}"; done
    set -e
}

install_container_runtime() {
  log_step "11/16" "Installing Container Runtime"
  read -rp "$(echo -e "${CYAN}Install a container runtime? [y/N]: ${NC}")" install_choice
  install_choice=$(echo "$install_choice" | tr '[:upper:]' '[:lower:]')
  [[ "$install_choice" != "y" ]] && echo -e "${BLUE}INFO:${NC} Skipping container runtime." && return

  local runtime_selection
  while true; do
    read -rp "$(echo -e "${CYAN}Runtime? 'docker' (Docker CE), 'podman', or 'none': ${NC}")" runtime_selection
    runtime_selection=$(echo "$runtime_selection" | tr '[:upper:]' '[:lower:]')
    if [[ "$runtime_selection" =~ ^(docker|podman|none)$ ]]; then break;
    else echo -e "${YELLOW}Invalid. Enter 'docker', 'podman', or 'none'.${NC}"; fi
  done
  if [[ "$runtime_selection" == "none" ]]; then echo -e "${BLUE}INFO:${NC} No runtime selected."; return; fi
  
  if [[ "$runtime_selection" == "podman" ]]; then
    echo -e "${BLUE}INFO:${NC} Installing Podman..."
    if $PKG_MANAGER -y install podman podman-docker; then
        if [[ -n "$HTTP_PROXY_URL" ]]; then
            mkdir -p /etc/containers/containers.conf.d; cat > /etc/containers/containers.conf.d/01-proxy.conf <<EOF
[engine]
env = [ "HTTP_PROXY=${HTTP_PROXY_URL}", "HTTPS_PROXY=${HTTP_PROXY_URL}", "NO_PROXY=${NO_PROXY_FULL}" ]
EOF
        fi
        echo -e "${BLUE}INFO:${NC} ✅ Podman installed successfully."
    else echo -e "${RED}ERROR: Podman installation failed.${NC}"; fi
  elif [[ "$runtime_selection" == "docker" ]]; then
    echo -e "${BLUE}INFO:${NC} Installing Docker CE..."
    if [[ "$PKG_MANAGER" == "dnf" || "$PKG_MANAGER" == "yum" ]]; then
        if [[ "$OS_ID_LOWER" == "almalinux" && "$OS_VER" -eq 10 ]]; then
            echo -e "${YELLOW}WARN: Docker CE on AlmaLinux $OS_VER might conflict or need '--allowerasing'.${NC}"
            local REPLY_DOCKER_AL10; read -rp "Proceed with Docker CE on AlmaLinux $OS_VER? (y/N): " -n 1 REPLY_DOCKER_AL10 && echo
            if [[ ! "$REPLY_DOCKER_AL10" =~ ^[Yy]$ ]]; then echo "INFO: Skipping Docker CE for AL$OS_VER."; return; fi
        fi
        dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo || { echo "ERR: Add Docker repo failed."; return 1; }
        if ! dnf -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin; then
            echo -e "${RED}ERR: Docker CE install failed. Try with --allowerasing.${NC}"; return 1; fi
    elif [[ "$PKG_MANAGER" == "apt" ]]; then
        install -m 0755 -d /etc/apt/keyrings
        curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg || { echo "ERR: GPG key failed."; return 1; }
        chmod a+r /etc/apt/keyrings/docker.gpg
        echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" > /etc/apt/sources.list.d/docker.list
        apt-get update -qq
        if ! apt-get -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin; then
             echo -e "${RED}ERR: Docker CE install failed.${NC}"; return 1; fi
    fi
    # Common Docker post-install steps
    if [[ -n "$HTTP_PROXY_URL" ]]; then
        mkdir -p /etc/systemd/system/docker.service.d; cat >/etc/systemd/system/docker.service.d/http-proxy.conf <<EOF
[Service]
Environment="HTTP_PROXY=${HTTP_PROXY_URL}" "HTTPS_PROXY=${HTTP_PROXY_URL}" "NO_PROXY=${NO_PROXY_FULL}"
EOF
        mkdir -p /etc/docker; cat >/etc/docker/daemon.json <<EOF
{ "proxies": { "http-proxy": "${HTTP_PROXY_URL}", "https-proxy": "${HTTP_PROXY_URL}", "no-proxy": "${NO_PROXY_FULL}" } }
EOF
    fi
    systemctl daemon-reload && systemctl enable --now docker || { echo -e "${RED}ERR: Docker service failed.${NC}"; return 1; }
    if [[ -n "${SUDO_USER:-}" ]] && id -u "$SUDO_USER" &>/dev/null && ! id -nG "$SUDO_USER" | grep -qw docker; then
        usermod -aG docker "$SUDO_USER"; echo -e "${BLUE}INFO:${NC} Added $SUDO_USER to docker group."; fi
    echo -e "${BLUE}INFO:${NC} ✅ Docker CE installed."
  fi
}

install_fail2ban() {
    set +e
    log_step "12/16" "Installing Fail2ban (Optional)"
            read -rp "$(echo -e "${CYAN}Install and configure Fail2ban for SSH?SSH protection? [y/N]: ${NC}")" install_choicechoice install_choice=< /dev/tty
            if [[ "$(echo "$install_choice"choice" | tr '[:upper:]' '[:lower:]')
    if [[ "$install_choice" != "y" ]]; then
                echo -e "${BLUE}INFO:${NC} Skipping Fail2ban installation."; setexit -e;0;
            return;fi
        fi

        echo -e "${BLUE}INFO:${NC} Installing Fail2ban..."
        if ! $PKG_MANAGER -y install fail2ban; then echo -e "${RED}ERROR: Fail2ban install failed.${NC}"; set -e; return; fifail2ban
        echo -e "${BLUE}INFO:${NC} ConfiguringCreating SSHlocal jail configuration for Fail2ban.SSHD..."
        cat > /etc/fail2ban/jail.local <<EOF
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
EOF
        if [[ "$PKG_MANAGER" == "dnf" || "$PKG_MANAGER" == "yum" ]]; then
        sed -i 's|/var/log/auth.log|/var/log/secure|' /etc/fail2ban/jail.local
    fi

    if ! systemctl enable --now fail2ban; thenfail2ban
        echo -e "${RED}ERROR:GREEN}SUCCESS:${NC} FailedFail2ban toinstalled start/enableand Fail2ban.enabled for SSHD."
    )
}
#
#SECTION 4: SHELL & USER EXPERIENCE FUNCTIONS
#
install_nano_syntax() {
    (
        trap 'echo -e "\n${YELLOW}Operation cancelled. Skipping extra Nano syntax...${NC}"; setexit -e;0;' return; fiINT
        echo -e "${BLUE}INFO:${NC} Installing Fail2banenhanced installedsyntax andhighlighting SSHfor jail configured.Nano..."
        sleeplocal 2nano_syntax_dir="/tmp/nanorc"
        fail2ban-clientif statusgit sshdclone ||https://github.com/scopatz/nanorc.git "$nano_syntax_dir"; then
            sudo cp -r ${nano_syntax_dir}/*.nanorc /usr/share/nano/
            rm -rf "$nano_syntax_dir"
            echo -e "${YELLOW}WARN: Could not get fail2ban status.GREEN}SUCCESS:${NC} Enhanced Nano syntax installed."
        setelse
            echo -e "${RED}ERROR:${NC} Failed to download enhanced Nano syntax files."
        fi
    )
}
setup_tmux(configure_nano() {
    log_step "12/X" "Configuring Nano Editor"
    echo -e "${BLUE}INFO:${NC} Applying system-wide Nano configuration..."
    cat > /etc/nanorc <<EOF
## Nano Editor Default Configuration
set +linenumbers
set softwrap
set tabsize 4
set casesensitive
set constantshow # Always show line/col info

## Include all standard syntax definitions
include "/usr/share/nano/*.nanorc"
EOF
    install_nano_syntax
    echo -e "${GREEN}SUCCESS:${NC} Nano configured with defaults and syntax highlighting."
}
configure_vim() {
    log_step "12.1/X" "Configuring Vim/Vi"
    echo -e "${BLUE}INFO:${NC} Applying system-wide Vim configuration..."
    cat > /etc/vimrc <<EOF
" System-wide .vimrc file
syntax on
set background=dark
set number
set ruler
set showcmd
set incsearch
set wildmenu
EOF
    echo -e "${GREEN}SUCCESS:${NC} Vim configured with syntax highlighting."
}
enhance_bash() {
    log_step "13/16"X" "SettingEnhancing upBash Automated Tmux EnvironmentExperience (Optional)Optional, Ctrl+C to skip)"
    ( # Start subshell for cancellation
        trap 'echo -e "\n${YELLOW}Operation cancelled. Skipping Bash enhancements...${NC}"; exit 0;' INT

        read -rp "$(echo -e "${CYAN}SetupEnhance persistentthe TmuxBash environment?shell with a better prompt and aliases? [y/N]: ${NC}")" tmux_choicechoice < /dev/tty
        if [[ "$(echo "$tmux_choice"choice" | tr '[:upper:]' '[:lower:]')" != "y" ]]; then
            echo -e "${BLUE}INFO:${NC} Skipping Bash enhancements."; exit 0;
        fi
        echo -e "${BLUE}INFO:${NC} Creating /etc/profile.d/enhanced_bash.sh..."
        cat > /etc/profile.d/enhanced_bash.sh <<'EOF'
# Custom Bash prompt
PS1='\[\e[32m\]\u@\h \[\e[33m\]\w\[\e[0m\]\n\$ '
# Useful Aliases
alias ls='ls --color=auto'
alias ll='ls -alF'
alias la='ls -A'
alias l='ls -CF'
alias grep='grep --color=auto'
alias ..='cd ..'
EOF
        echo -e "${GREEN}SUCCESS:${NC} Bash enhancements will be applied on next login."
    )
}
setup_tmux() {
    log_step "14/X" "Setting up Automated Tmux Environment (Optional, Ctrl+C to skip)"
    ( # Start subshell for cancellation
        trap 'echo -e "\n${YELLOW}Operation cancelled. Skipping Tmux setup...${NC}"; exit 0;' INT

        read -rp "$(echo -e "${CYAN}Set up a default Tmux configuration? [y/N]: ${NC}")" choice < /dev/tty
        if [[ "$(echo "$choice" | tr '[:upper:]' '[:lower:]')" != "y" ]]; then
            echo -e "${BLUE}INFO:${NC} Skipping Tmux setup."; setexit -e; return;0;
        fi
        echo -e "${BLUE}INFO:${NC} InstallingCreating Tmux Plugin Manager (TPM)..."
    git clone https://github.com/tmux-plugins/tpmsystem-wide /etc/tmux/plugins/tpm || echo -e "${YELLOW}WARN: Could not clone TPM. Maybe it exists?${NC}"

    echo -e "${BLUE}INFO:${NC} Creating global tmux configuration.tmux.conf..."
        cat > /etc/tmux.conf <<'EOF'
# ListSet ofprefix pluginsto Ctrl-a
set -g @pluginprefix 'tmux-plugins/tpm'C-a
unbind C-b
bind C-a send-prefix
# Enable mouse mode
set -g @pluginmouse 'tmux-plugins/tmux-sensible'on
# Improve status bar
set -g @pluginstatus-bg 'tmux-plugins/tmux-resurrect'black
set -g @pluginstatus-fg 'tmux-plugins/tmux-continuum'
# Restore session on tmux startwhite
set -g @continuum-restorestatus-left 'on'#[fg=green]#H'
set -g status-right '#[fg=yellow]%Y-%m-%d %H:%M'
EOF
        echo -e "${GREEN}SUCCESS:${NC} Default Tmux configuration created."
    )
}
setup_motd() {
    log_step "15/X" "Setting Up Dynamic MOTD (Optional, Ctrl+C to skip)"
    ( # InitializeStart TMUXsubshell pluginfor managercancellation
        trap 'echo -e "\n${YELLOW}Operation cancelled. Skipping MOTD setup...${NC}"; exit 0;' INT
        
        if [ -f /etc/profile.d/99-custom-motd.sh ]; then
            read -rp "$(echo -e "${YELLOW}WARNING:${NC} Custom MOTD script already exists. Action? (keep[S]kip, this[O]verwrite): line${NC}")" atchoice the< very/dev/tty
            bottomif of[[ tmux.conf)"$(echo run"$choice" | tr '/etc/tmux/plugins/tpm/tpm'[:upper:]' EOF'[:lower:]')" != "o" ]]; then
                echo -e "${BLUE}INFO:${NC} CreatingSkipping tmuxMOTD auto-startsetup."; script...exit 0;
            fi
        else
            read -rp "$(echo -e "${CYAN}Setup a dynamic MOTD (Message of the Day)? [y/N]: ${NC}")" catchoice >< /etc/profile.d/98-tmux-autostart.sh <<'EOF'
#!/bin/bashdev/tty
            if [[ "$-(echo ="$choice" | tr '[:upper:]' '[:lower:]')" != *i*"y" ]] && [[ -z "$TMUX" ]] && command -v tmux &>/dev/null;; then
  tmux attach-session -t main || tmux new-session -s main
fi
EOF
    chmod +x /etc/profile.d/98-tmux-autostart.sh
                echo -e "${BLUE}INFO:${NC} ✅ Tmux configured. It will start automatically on next SSH login."
    echo -e "${YELLOW}To fetch plugins, start tmux and press 'prefix + I' (Ctrl+b, then Shift+i).${NC}"
    set -e
}

setup_motd() {
    log_step "14/16" "Setting Up DynamicSkipping MOTD (Messagesetup."; ofexit the0;
            Day)"fi
        fi
        
        echo -e "${BLUE}INFO:${NC} Creating a dynamic message of the day..."
        # Disable default MOTD generation if they exist
    chmod -x /etc/update-motd.d/* &>/dev/null || true
        # Disable printing MOTD via pam_motd.so which can interfere
    sed -i '/^session\s\+optional\s\+pam_motd.so/s/^/#/' /etc/pam.d/sshd 2>/dev/null || true
        cat > /etc/profile.d/99-custom-motd.sh <<'EOF'
#!/bin/bash
# OnlyThis runscript runs for interactive shells onto login,display nota insidedynamic tmuxMOTD.
if [[ $- !== *i* ]] ||&& [[ -n "$TMUX"{SHLVL:-1}" -le 1 ]]; then
    return
fi

RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[0;33m'; BLUE='\033[0;34m';
    PURPLE='\033[0;35m'; CYAN='\033[0;36m'; NC='\033[0m' # No Color;
    echo -e ""\nWelcome #to Display Hostname and OS
HOSTNAME=${GREEN}$(hostname -f)${NC}"
    echo -e "System time is: ${CYAN}$(date --iso-8601=seconds)${NC}\n"
    # Display OS Info
    if [ -f /etc/os-release ]; then
        OS_INFO=$(grep PRETTY_NAME /etc/os-release | cut -d'"' -f2)
        echo -e "Hostname: ${GREEN}$HOSTNAME$PURPLE}System Information${NC}"
        echo -e "  OS: ${YELLOW}$OS_INFO${OS_INFO}${NC}"
        echo -e ""  Uptime: $(uptime -p | sed 's/up //')\n"
    fi
    # Display Network Information
    echo -e "${BLUE}PURPLE}Network Information:$Information${NC}"
    ip -4 addr | grep -oP '(?<=inet\s)\d+(\.\d+){3}/\d+\s.*\s\K\w+$' | while read -r dev;
    do
        ip_addr=$(ip -4 addr show dev "$dev" | grep -oP '(?<=inet\s)\d+(\.\d+){3}')
        if [[ -n "$ip_addr" ]]; then echo -e "  - Interface ${GREEN}$dev${NC}: ${CYAN}$ip_addr${NC}"; fi
    done || echo -e "  ${RED}No active IPv4 interfaces found.${NC}"
    echo
    ""

# Display System Usage
    echo -e "${BLUE}PURPLE}System Usage:$Usage${NC}"
    df -h / | awk '$NR==2 {print "  Disk (/): " $2 " total, " $3 " used (" $5 " full), " $4 " free"}'
    free -h | awk '/^Mem:/ {print "  Memory:   " $2 " total, " $3 " used, " $7 " available"}'
    echo -e "  CPU Load: $(uptime | awk -F'load average:' '{ print $2 }2}' | sed 's/ //g')"\n"
    # Display Installed Software Versions
    echo -e "${PURPLE}Installed Software${NC}"
    declare -A progs=(
        ["Docker"]="docker --version" ["Podman"]="podman --version"
        ["Nginx"]="nginx -v" ["Apache"]="httpd -v" ["PHP"]="php -v"
        ["Node"]="node -v" ["NPM"]="npm -v" ["Go"]="go version"
        ["Java"]="java -version" ["MySQL"]="mysql --version" ["PostgreSQL"]="psql --version"
    )
    output=""
    for name in "${!progs[@]}"; do
        if command -v ${progs[$name]%% *} &>/dev/null; then
            version=$(${progs[$name]} 2>&1 | grep -oP '(\d+\.){1,}\d+' | head -n1)
            [[ -n "$version" ]] && output+="  ${GREEN}${name}${NC}:${CYAN}${version}${NC}"
        fi
    done
    echo -e "${output:-  None detected}\n"
fi
EOF
        chmod +x /etc/profile.d/99-custom-motd.sh
        echo -e "${BLUE}INFO:GREEN}SUCCESS:${NC} ✅ Dynamic MOTD script created."
    It)
will}
beinstall_nerd_fonts() displayed{
    onlog_step next"16.1/X" SSH"Installing login.Nerd Fonts (Sub-step)"
    ( # Start subshell for cancellation
        trap 'echo -e "\n${YELLOW}Operation cancelled. Skipping Nerd Fonts...${NC}"; exit 124;' INT

        echo -e "${BLUE}INFO:${NC} Checking for Nerd Fonts..."
        local font_dir="/usr/local/share/fonts/FiraCodeNerdFont"
        if [ -d "$font_dir" ]; then
            echo -e "${BLUE}INFO:${NC} Nerd Font directory already exists. Skipping download."
            exit 0
        fi
        
        read -rp "$(echo -e "${CYAN}Install FiraCode Nerd Font for Zsh themes? [y/N]: ${NC}")" choice < /dev/tty
        if [[ "$(echo "$choice" | tr '[:upper:]' '[:lower:]')" != "y" ]]; then
            echo -e "${BLUE}INFO:${NC} Skipping Nerd Font installation."; exit 0;
        fi
        mkdir -p "$font_dir"
        local tmp_zip="/tmp/FiraCode.zip"
        
        local retries=3; local count=0; local success=false
        until [ $count -ge $retries ]
        do
            echo -e "${BLUE}INFO:${NC} Attempting to download FiraCode Nerd Font (attempt $((count+1))/${retries})..."
            ping -c 1 google.com &>/dev/null || true 
            sleep 1
            curl --connect-timeout 20 -L "https://github.com/ryanoasis/nerd-fonts/releases/download/v3.2.1/FiraCode.zip" -o "$tmp_zip"
            if [ $? -eq 0 ]; then success=true; break; fi
            count=$((count+1))
            echo -e "${YELLOW}WARNING:${NC} Download failed. Retrying in 5 seconds..."
            sleep 5
        done

        if ! $success; then
            echo -e "${RED}ERROR:${NC} Failed to download Nerd Fonts after $retries attempts." >&2; rm -f "$tmp_zip"; exit 1;
        fi

        unzip -o "$tmp_zip" -d "$font_dir"; rm -f "$tmp_zip"
        echo -e "${BLUE}INFO:${NC} Rebuilding font cache..."; fc-cache -fv &>/dev/null
        echo -e "${GREEN}SUCCESS:${NC} Nerd Fonts installed."
    )
    return $?
}
configure_starship() {
    local user=$1
    local home_dir
    home_dir=$(eval echo ~$user)
    local config_dir="${home_dir}/.config"
    local starship_config="${config_dir}/starship.toml"

    echo -e "${BLUE}INFO:${NC} Creating Starship 'Powerline' config for ${user}..."
    sudo -u "$user" mkdir -p "$config_dir"
    sudo -u "$user" tee "$starship_config" > /dev/null <<'EOF'
# Starship "Powerline" configuration
# Shows: [USER@HOST] [DATE TIME] [DIRECTORY] [GIT] [CMD_DURATION]
# >>>

# A minimal left prompt
format = """$username$hostname$time$directory$git_branch$cmd_duration$character"""

# Move the directory to the second line
# format = """$username$hostname$time$directory$git_branch$cmd_duration$fill$character"""

[username]
style_user = "yellow bold"
style_root = "red bold"
format = "[$user]($style_user)@"
show_always = true

[hostname]
style = "green bold"
format = "[$hostname]($style) "
ssh_only = false
disabled = false

[time]
disabled = false
format = '[\[$time\]]($style) '
style = "blue bold"
time_format = "%Y-%m-%d %H:%M:%S"

[directory]
style = "cyan bold"
format = "[$path]($style) "
truncation_length = 4

[git_branch]
style = "bold purple"
format = "[$branch]($style) "

[cmd_duration]
min_time = 500
style = "bold italic yellow"
format = "[$duration]($style) "

[character]
success_symbol = "[>](bold green)"
error_symbol = "[x](bold red)"
EOF
}
install_zsh_omz() {
    log_step "16/X" "Installing Zsh & Oh My Zsh (Optional, Ctrl+C to skip)"
    ( # Start subshell for cancellation
        trap 'echo -e "\n${YELLOW}Operation cancelled. Skipping Zsh setup...${NC}"; exit 124;' INT

        local choice
        if command -v zsh &>/dev/null; then
            read -rp "$(echo -e "${CYAN}Zsh is already installed. Action? ([S]kip, [R]econfigure): ${NC}")" choice < /dev/tty
            if [[ "$(echo "$choice" | tr '[:upper:]' '[:lower:]')" == "s" ]]; then
                echo -e "${BLUE}INFO:${NC} Skipping Zsh setup."; exit 0;
            fi
        else
            read -rp "$(echo -e "${CYAN}Install Zsh and Oh My Zsh? [y/N]: ${NC}")" choice < /dev/tty
            if [[ "$(echo "$choice" | tr '[:upper:]' '[:lower:]')" != "y" ]]; then
                echo -e "${BLUE}INFO:${NC} Skipping Zsh installation."; exit 0;
            fi
        fi
        $PKG_MANAGER -y install zsh git
        
        install_nerd_fonts
        if [ $? -ne 0 ]; then
            echo -e "${YELLOW}WARNING:${NC} Nerd font installation failed or was skipped. Zsh themes may not render correctly."
        fi

        local users_to_configure=()
        users_to_configure+=("root")
        if [[ -n "${SUDO_USER:-}" ]] && [[ "$SUDO_USER" != "root" ]]; then
            users_to_configure+=("$SUDO_USER")
        fi
        local prompt_choice
        read -rp "$(echo -e "${CYAN}Which Zsh prompt? ([1] Oh My Zsh (rkj-repos), [2] Starship, [3] Powerlevel10k): ${NC}")" prompt_choice < /dev/tty
        for user in "${users_to_configure[@]}"; do
            echo -e "${BLUE}INFO:${NC} Configuring Zsh for user ${PURPLE}${user}${NC}..."
            local home_dir; home_dir=$(eval echo ~$user)
            local zsh_dir="${home_dir}/.oh-my-zsh"
            
            if [ ! -d "$zsh_dir" ]; then
                local installer_sh="/tmp/omz_install.sh"
                local retries=3; local count=0; local success=false
                echo -e "${BLUE}INFO:${NC} Downloading Oh My Zsh installer..."
                until [ $count -ge $retries ]; do
                    curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh -o "$installer_sh"
                    if [ $? -eq 0 ]; then success=true; break; fi
                    count=$((count+1)); echo -e "${YELLOW}WARNING:${NC} Download failed. Retrying..."; sleep 3
                done
                
                if $success; then
                    echo -e "${BLUE}INFO:${NC} Running Oh My Zsh installer for ${user}..."
                    sudo -u "$user" sh "$installer_sh" --unattended
                    rm "$installer_sh"
                else
                    echo -e "${RED}ERROR:${NC} Failed to download Oh My Zsh installer for ${user}." >&2; continue
                fi
            fi

            local custom_plugins_dir="${zsh_dir}/custom/plugins"
            if [ ! -d "${custom_plugins_dir}/zsh-autosuggestions" ]; then
                sudo -u "$user" git clone https://github.com/zsh-users/zsh-autosuggestions "$custom_plugins_dir/zsh-autosuggestions"
            fi
            if [ ! -d "${custom_plugins_dir}/zsh-syntax-highlighting" ]; then
                sudo -u "$user" git clone https://github.com/zsh-users/zsh-syntax-highlighting.git "$custom_plugins_dir/zsh-syntax-highlighting"
            fi
            local zshrc_file="${home_dir}/.zshrc"
            
            # FIX: Robustly set plugins
            sed -i 's/^plugins=(.*)$/plugins=(git docker npm nvm zsh-autosuggestions zsh-syntax-highlighting)/' "$zshrc_file"
            
            # FIX: Clean up old theme settings before applying a new one
            sed -i '/^# Init Starship Prompt/d' "$zshrc_file"
            sed -i '/eval "$(starship init zsh)"/d' "$zshrc_file"
            
            case "$prompt_choice" in
                2) # Starship
                    if ! command -v starship &>/dev/null; then
                        local installer_sh="/tmp/starship_install.sh"
                        echo -e "${BLUE}INFO:${NC} Downloading Starship installer..."
                        if curl -sS https://starship.rs/install.sh -o "$installer_sh"; then
                            sh "$installer_sh" -y
                            rm "$installer_sh"
                        else
                             echo -e "${RED}ERROR:${NC} Failed to download starship installer." >&2
                        fi
                    fi
                    echo -e '\n# Init Starship Prompt\neval "$(starship init zsh)"' >> "$zshrc_file"
                    configure_starship "$user"
                    ;;
                3) # Powerlevel10k
                    local p10k_dir="${zsh_dir}/custom/themes/powerlevel10k"
                    if [ ! -d "$p10k_dir" ]; then
                        echo -e "${BLUE}INFO:${NC} Cloning Powerlevel10k theme..."
                        sudo -u "$user" git clone --depth=1 https://github.com/romkatv/powerlevel10k.git "$p10k_dir"
                    fi
                    echo -e "${BLUE}INFO:${NC} Setting Powerlevel10k theme in .zshrc..."
                    sed -i 's|^ZSH_THEME=.*|ZSH_THEME="powerlevel10k/powerlevel10k"|' "$zshrc_file"
                    if ! grep -q 'POWERLEVEL9K_DISABLE_CONFIGURATION_WIZARD=true' "$zshrc_file"; then
                         echo -e '\n# To customize prompt, run `p10k configure` or edit ~/.p10k.zsh.\n[[ ! -f ~/.p10k.zsh ]] && p10k configure' >> "$zshrc_file"
                    fi
                    ;;
                *) # Default to rkj-repos
                    echo -e "${BLUE}INFO:${NC} Setting ZSH_THEME to rkj-repos..."
                    sed -i 's|^ZSH_THEME=.*|ZSH_THEME="rkj-repos"|' "$zshrc_file"
                    ;;
            esac

            if ! grep -q 'setopt EXTENDED_HISTORY' "$zshrc_file"; then
                echo -e '\n# Custom settings by setup script\nsetopt EXTENDED_HISTORY\nHIST_STAMPS="yyyy-mm-dd"\n' >> "$zshrc_file"
            fi
            
            if ! grep -q "alias ls='ls --color=auto'" "$zshrc_file"; then
                echo -e '\n# Color Aliases\nalias ls="ls --color=auto"\nalias grep="grep --color=auto"' >> "$zshrc_file"
            fi
            
            local current_shell; current_shell=$(getent passwd "$user" | cut -d: -f7)
            if [[ "$current_shell" != "$(which zsh)" ]]; then
                echo -e "${BLUE}INFO:${NC} Changing shell for user ${user} to Zsh..."
                chsh -s "$(which zsh)" "$user"
                echo -e "${GREEN}SUCCESS:${NC} Shell changed."
            else
                echo -e "${BLUE}INFO:${NC} Shell for user ${user} is already zsh."
            fi
        done
    )
}
#
#SECTION 5: APPLICATION STACK FUNCTIONS
#
install_dev_stack() {
    log_step "18/X" "Installing Development Stack (Optional, Ctrl+C to skip)"
    ( # Start subshell for cancellation
        trap 'echo -e "\n${YELLOW}Operation cancelled. Skipping Dev Stack...${NC}"; exit 0;' INT

        read -rp "$(echo -e "${CYAN}Install a development stack (Nginx, PHP, Node.js)? [y/N]: ${NC}")" choice < /dev/tty
        if [[ "$(echo "$choice" | tr '[:upper:]' '[:lower:]')" != "y" ]]; then
            echo -e "${BLUE}INFO:${NC} Skipping dev stack installation."; exit 0;
        fi
        echo -e "${BLUE}INFO:${NC} Installing Nginx..."
        $PKG_MANAGER -y install nginx
        systemctl enable nginx; systemctl start nginx
        echo -e "${BLUE}INFO:${NC} Installing Node.js (LTS)..."
        curl -fsSL https://rpm.nodesource.com/setup_lts.x | bash -
        $PKG_MANAGER -y install nodejs
        echo -e "${BLUE}INFO:${NC} Installing PHP..."
        if [[ "$PKG_MANAGER" == "dnf" || "$PKG_MANAGER" == "yum" ]]; then
            $PKG_MANAGER -y install http://rpms.remirepo.net/enterprise/remi-release-8.rpm
            $PKG_MANAGER -y module reset php; $PKG_MANAGER -y module install php:remi-8.1
            $PKG_MANAGER -y install php-cli php-fpm php-mysqlnd php-json php-gd php-mbstring
        else # APT
            add-apt-repository ppa:ondrej/php -y; $PKG_MANAGER update
            $PKG_MANAGER -y install php8.1-cli php8.1-fpm php8.1-mysql php8.1-json php8.1-gd php8.1-mbstring
        fi
        echo -e "${GREEN}SUCCESS:${NC} Development stack installed."
    )
}
install_cockpit() {
    log_step "19/X" "Installing Cockpit Web Console (Optional, Ctrl+C to skip)"
    ( # Start subshell for cancellation
        trap 'echo -e "\n${YELLOW}Operation cancelled. Skipping Cockpit...${NC}"; exit 0;' INT

        read -rp "$(echo -e "${CYAN}Install the Cockpit web administration console? [y/N]: ${NC}")" choice < /dev/tty
        if [[ "$(echo "$choice" | tr '[:upper:]' '[:lower:]')" != "y" ]]; then
            echo -e "${BLUE}INFO:${NC} Skipping Cockpit installation."; exit 0;
        fi
        echo -e "${BLUE}INFO:${NC} Installing Cockpit..."
        $PKG_MANAGER -y install cockpit cockpit-storaged cockpit-networkmanager
        echo -e "${BLUE}INFO:${NC} Starting and enabling Cockpit socket..."
        systemctl enable --now cockpit.socket
        echo -e "${GREEN}SUCCESS:${NC} Cockpit is installed. Access it at https://$(hostname -f):9090"
    )
}
install_container_runtime() {
    log_step "20/X" "Installing Container Runtime (Optional, Ctrl+C to skip)"
    ( # Start subshell for cancellation
        trap 'echo -e "\n${YELLOW}Operation cancelled. Skipping Container Runtime...${NC}"; exit 0;' INT

        read -rp "$(echo -e "${CYAN}Install a container runtime? ([D]ocker, [P]odman, [N]one): ${NC}")" choice < /dev/tty
        case "$(echo "$choice" | tr '[:upper:]' '[:lower:]')" in
            d|docker)
                echo -e "${BLUE}INFO:${NC} Installing Docker..."
                if [[ "$PKG_MANAGER" == "dnf" || "$PKG_MANAGER" == "yum" ]]; then
                    $PKG_MANAGER config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
                    $PKG_MANAGER -y install docker-ce docker-ce-cli containerd.io
                else # APT
                    install -m 0755 -d /etc/apt/keyrings
                    curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
                    chmod a+r /etc/apt/keyrings/docker.asc
                    echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" > /etc/apt/sources.list.d/docker.list
                    $PKG_MANAGER update
                    $PKG_MANAGER -y install docker-ce docker-ce-cli containerd.io
                fi
                systemctl enable --now docker
                if [[ -n "${SUDO_USER:-}" ]]; then
                    echo -e "${BLUE}INFO:${NC} Adding user ${SUDO_USER} to the docker group..."
                    usermod -aG docker "$SUDO_USER"
                fi
                echo -e "${GREEN}SUCCESS:${NC} Docker installed."
                ;;
            p|podman)
                echo -e "${BLUE}INFO:${NC} Installing Podman..."
                $PKG_MANAGER -y install podman
                echo -e "${GREEN}SUCCESS:${NC} Podman installed."
                ;;
            *)
                echo -e "${BLUE}INFO:${NC} Skipping container runtime installation.";;
        esac
    )
}
#
#SECTION 6: UTILITY & FINALIZATION FUNCTIONS
#
setup_logrotate() {
    log_step "21/X" "Setting up Logrotate (Optional, Ctrl+C to skip)"
    ( # Start subshell for cancellation
        trap 'echo -e "\n${YELLOW}Operation cancelled. Skipping Logrotate setup...${NC}"; exit 0;' INT

        read -rp "$(echo -e "${CYAN}Configure log rotation for this script's log files? [y/N]: ${NC}")" choice < /dev/tty
        if [[ "$(echo "$choice" | tr '[:upper:]' '[:lower:]')" != "y" ]]; then
            echo -e "${BLUE}INFO:${NC} Skipping logrotate setup."; exit 0;
        fi
        echo -e "${BLUE}INFO:${NC} Creating /etc/logrotate.d/setup-domain..."
        cat > /etc/logrotate.d/setup-domain <<EOF
/var/log/setup-domain-*.log {
    monthly
    rotate 4
    compress
    delaycompress
    missingok
    notifempty
    create 640 root root
}
EOF
        echo -e "${GREEN}SUCCESS:${NC} Logrotate configured."
    )
}
final_summary() {
    log_step "15/16"22/X" "Final Setup Summary"
    echo "Hostname: $(hostnamectl hostname 2>/dev/null || echo "N/A")"
    local domain_name_info=$(realm list 2>/dev/null | grep 'domain-name:' | awk '{print $2}')
    echo "Joined Domain: ${domain_name_info:-'Not Joined or Error'}"
    echo "Current Time: $(date)"
    echo "Timezone: $(timedatectl status 2>/dev/null | grep 'Time zone' | awk '{print $3,$4,$5}' || echo "N/A")"
    echo "NTP Synchronized: $(timedatectl status 2>/dev/null | grep 'NTP synchronized' | awk '{print $3}' || echo "N/A")"
    echo -e "Log file: ${PURPLE}$LOG_FILE${NC}"
    echo -e "${YELLOW}GREEN}================================================== SUMMARY ==================${NC}"
    echo -e "  Hostname: ${PURPLE}$(hostname -f)${NC}"
    echo -e "  IP Address: ${CYAN}$(hostname -I | awk '{print $1}')${NC}"
    echo -e "  Timezone: ${CYAN}${TIMEZONE}${NC}"
    echo -e "  Domain Membership: ${PURPLE}${DOMAIN_FQDN}${NC}"
    realm list | grep "configured: yes" &>/dev/null
    if [ $? -eq 0 ]; then
        echo -e "  Domain Join Status: ${GREEN}Success${NC}"
        echo -e "  Login with AD users as: ${CYAN}username${NC}"
        echo -e "  Sudo enabled for group: ${PURPLE}${AD_SUDO_GROUP_RAW_NAME}${NC}"
    else
        echo -e "  Domain Join Status: ${RED}Failed or Not Performed${NC}"
    fi
    echo -e "  Log File: ${YELLOW}IMPORTANT: Review log for errors. Reboot may be required.${LOG_FILE}${NC}"
    echo -e "${YELLOW}=======================GREEN}=============================================${NC}"
}
system_updates_interactive() {
    log_step "16/16"23/X" "System Updates (Optional)Optional, Ctrl+C to skip)"
    ( # Start subshell for cancellation
        trap 'echo -e "\n${YELLOW}Operation cancelled. Skipping System Updates...${NC}"; exit 0;' INT

        read -rp "$(echo -e "${CYAN}PerformCheck fullfor and apply all available system updates now?updates? [y/N]: ${NC}")" update_choicechoice update_choice=< /dev/tty
        if [[ "$(echo "$update_choice"choice" | tr '[:upper:]' '[:lower:]')" if [[ "$update_choice" =!= "y" ]]; then
            echo -e "${BLUE}INFO:${NC} StartingSkipping system update..."
        if ! $PKG_MANAGER -y upgrade; then echo -e "${RED}ERROR: System upgrade failed.${NC}updates."; elseexit 0;
        fi
        echo -e "${BLUE}INFO:${NC} Checking for updates..."
        $PKG_MANAGER -y update
        echo -e "${GREEN}SUCCESS:${NC} System is up-to-date."

        echo -e "${BLUE}INFO:${NC} Checking if a reboot is required..."
        if [[ "$PKG_MANAGER" == "dnf" || "$PKG_MANAGER" == "yum" ]]; then
            if needs-restarting -r &>/dev/null; then
                # Using an external file to communicate back to the main script
                echo "true" > /tmp/reboot_required.flag
            fi
        elif [[ "$PKG_MANAGER" == "apt" ]]; then
            if [ -f /var/run/reboot-required ]; then
                echo "true" > /tmp/reboot_required.flag
            fi
        fi

        if [ -f /tmp/reboot_required.flag ]; then
            echo -e "\n${YELLOW}####################################################################"
            echo -e "# WARNING: System updates installed.have been installed that require a reboot."
            echo -e "####################################################################${NC}"
        else
            echo -e "${GREEN}INFO:${NC} No reboot is required at this time."
        fi
    )
}
cleanup() {
    unset AD_PASSWORD
    rm -f /tmp/reboot_required.flag
    echo -e "${BLUE}INFO:${NC} Sensitive variables cleared from memory."
}
usage() {
    echo -e "${CYAN}Usage: $0 [OPTION]${NC}"
    echo "  --full      Run the complete end-to-end installation and configuration."
    echo "  --dev       Install the development stack (PHP, Node, Nginx, etc.)."
    echo "  --security  Apply security hardening (Fail2ban, SSH optimization)."
    echo "  --shell     Configure user experience (Bash, Zsh, Tmux, MOTD)."
    echo "  --updates   Check for and apply system updates."
    echo "  --help      Display this help message."
    echo
    echo -e "${YELLOW}If no option is provided, the script will run the full installation interactively.${NC}"
}
#---
# MODULAR EXECUTION RUNNERS
#
run_full_install() {
    echo -e "${PURPLE}Starting Full System Setup...${NC}"
    gather_credentials
    # Core System & Network (Not cancellable - these are critical)
    change_hostname
    configure_proxy
    configure_dns_and_hosts
    check_connectivity
    install_packages
    configure_time
    # Domain & Auth (Not cancellable - these are critical)
    join_ad_domain
    configure_sssd_mkhomedir
    configure_sudoers
    # Security (Optional sections are now cancellable)
    optimize_sshd
    install_fail2ban
    #Shell & UX
    configure_nano
    configure_vim
    enhance_bash
    setup_tmux
    setup_motd
    install_zsh_omz
    # App Stacks
    install_dev_stack
    install_cockpit
    install_container_runtime
    # Utilities & Finalization
    setup_logrotate
    system_updates_interactive
    final_summary
}
run_dev_stack() {
    echo -e "${PURPLE}Starting Development Stack Setup...${NC}"
    install_packages
    install_dev_stack
    final_summary
}
run_security_hardening() {
    echo -e "${PURPLE}Starting Security Hardening...${NC}"
    install_packages
    optimize_sshd
    install_fail2ban
    final_summary
}
run_shell_ux() {
    echo -e "${PURPLE}Starting Shell & UX Setup...${NC}"
    install_packages
    configure_nano
    configure_vim
    enhance_bash
    setup_tmux
    setup_motd
    install_zsh_omz
    final_summary
}
#---
# MAIN EXECUTION FLOW
#---
main() {
    trap cleanup EXIT
    if [ $# -eq 0 ]; then
        run_full_install
    else
        case "$1" in
            --full) run_full_install ;;
            --dev) run_dev_stack ;;
            --security) run_security_hardening ;;
            --shell) run_shell_ux ;;
            --updates) system_updates_interactive ;;
            --help) usage ;;
            *)
                echo -e "${RED}Error: Invalid option '$1'${NC}" >&2
                usage
                exit 1
                ;;
        esac
    fi
    
    echo -e "\n${GREEN}========= Script finished at $(date --iso-8601=seconds) =========${NC}"
    
    if [ -f /tmp/reboot_required.flag ]; then
        REBOOT_REQUIRED_FLAG=true
    fi

    if $REBOOT_REQUIRED_FLAG; then
        read -rp "$(echo -e "${CYAN}YELLOW}A reboot is required to apply updates. Reboot now? [y/N]: ${NC}")" r;choice r=< /dev/tty
        if [[ "$(echo "$r"choice" | tr '[:upper:]' '[:lower:]')
            if [[ "$r" == "y" ]]; then
            echo -e "Rebooting.${RED}Rebooting now...${NC}";
            sleepreboot
        5;else
            reboot;echo fi-e "${YELLOW}Please reboot the server manually to apply all changes.${NC}"
        fi
    else
         echo -e "${BLUE}INFO:GREEN}Script complete. No reboot required.${NC} Skipping updates.";
    fi
}
#------------------------------------------------------------------------------
# MAINOnly EXECUTIONrun FLOWmain #------------------------------------------------------------------------------
echo -e "\n${GREEN}--- Starting Main Execution Sequence ---${NC}"
change_hostname
configure_proxy
install_packages
configure_dns_and_hosts
configure_time
join_ad_domain
configure_sssd_mkhomedir
configure_sudoers

# Run optional steps in a way that doesn't haltif the script onis failureexecuted (install_dev_stack)directly
||if echo -e[[ "${RED}ERROR during Development Stack installation. Check log. Continuing script...${NC}BASH_SOURCE[0]}" (install_cockpit) || echo -e== "${RED}ERROR during Cockpit installation. Check log. Continuing script...${NC}0}" (install_container_runtime)]]; ||then
    echo -emain "${RED}ERROR@"
during Container Runtime installation. Check log. Continuing script...${NC}"
(install_fail2ban) || echo -e "${RED}ERROR during Fail2ban installation. Check log. Continuing script...${NC}"
(setup_tmux) || echo -e "${RED}ERROR during Tmux setup. Check log. Continuing script...${NC}"
(setup_motd) || echo -e "${RED}ERROR during MOTD setup. Check log. Continuing script...${NC}"

final_summary
system_updates_interactive

echo -e "${GREEN}=== Script finished at $(date) ===${NC}"
exit 0
fi

 

DISABLE WAYLAND FOR SUPPORT MESH COMPATIBILITY

1 From the initial boot after installation on the login screen
2 Select your account and go to the bottom right to click the gear icon
3 Select Gnome on Xorg 
4 Input password to proceed with login

Open Terminal

sudo -i
nano /etc/gdm/custom.conf
1 Go to the line #WaylandEnable=false  and Delete the hashtag '#' 
2 To exit:     CTRL      +      'X' 
3 Select     'Y'    for    yes
4 To save:    'enter'   key
sudo dnf update -y
sudo reboot

******************************COMPLETED*******************************


CHANGE COMPUTER NAME

1

Open Terminal

PC Name example:   MYDNS-IT-C12-L.M21.GOV.LOCAL

2 sudo hostnamectl set-hostname mydns-it-c12-l.m21.gov.local

******************************COMPLETED*******************************


TO JOIN THE DOMAIN

Open Terminal

sudo nano /etc/environment

Add the following line to the file:

http_proxy="http://172.40.4.14:8080/"
https_proxy="http://172.40.4.14:8080/"
ftp_proxy="http://172.40.4.14:8080/"
no_proxy=127.0.0.1,localhost,.localdomain,172.30.0.0/20,172.26.21.0/24
HTTP_PROXY="http://172.40.4.14:8080/"
HTTPS_PROXY="http://172.40.4.14:8080/"
FTP_PROXY="http://172.40.4.14:8080/"
NO_PROXY=127.0.0.1,localhost,.localdomain,172.30.0.0/20,172.26.21.0/24
1 To exit:     CTRL      +      'X' 
2 Select     'Y'    for    yes
3

To save:    'enter'   key

4

Log out and back in again

sudo nano /etc/dnf/dnf.conf

Add the following line to the file:

fastestmirror=1
1 To exit:     CTRL      +      'X' 
2 Select     'Y'    for    yes
3

To save:    'enter'   key

On Fedora

sudo dnf -y install epel-release && sudo dnf -y install realmd sssd oddjob oddjob-mkhomedir adcli samba-common-tools authselect nano curl wget htop btop net-tools git zip unzip tar freeipa-client tmux

On Ubuntu

sudo apt -y install realmd sssd sssd-tools libnss-sss libpam-sss adcli samba-common-bin oddjob oddjob-mkhomedir packagekit nano curl wget htop btop net-tools git zip unzip tar freeipa-client tmux

Fix DNS

Input the IP Address and the Domain Name into file

search	m21.gov.local
nameserver	172.16.21.161
1 To exit:     CTRL      +      'X' 
2 Select     'Y'    for    yes
3 To save:    'enter'   key
sudo nano /etc/hosts

Input the following lines into file

172.16.21.161	m21.gov.local M21.GOV.LOCAL
172.16.21.16	mydns-0ic16.m21.gov.local mydns-0ic16
1 To exit:     CTRL      +      'X' 
2 Select     'Y'    for    yes
3 To save:    'enter'   key
sudo realm discover M21.GOV.LOCAL
ping -c 4 M21.GOV.LOCAL
To stop ping:       CTRL     +      'C'
sudo realm join -U ent_username@M21.GOV.LOCAL m21.gov.local -v

Input Ent Account Password

To ensure that it was successful run the realm join code again and you should see "Already joined to this domain"

******************************COMPLETED*******************************


GROUP POLICY CONFLICT RESOLVE (to login without wifi)

Open Terminal

sudo nano /etc/sssd/sssd.conf

Input at the end of the file 

ad_gpo_access_control = permissive
Your "/etc/sssd/sssd.conf" should look like this. Make all necessary changes or copy and paste this into the file replacing everything. Can use CTRL + K to cut entire lines until the file is empty.

[sssd]
domains = m21.gov.local
config_file_version = 2
services = nss, pam

[nss]
homedir_substring = /home

[domain/m21.gov.local]
default_shell = /bin/bash
krb5_store_password_if_offline = True
cache_credentials = True
krb5_realm = M21.GOV.LOCAL
realmd_tags = manages-system joined-with-adcli 
id_provider = ad
fallback_homedir = /home/%u
ad_domain = m21.gov.local
use_fully_qualified_names = False
ldap_id_mapping = True
access_provider = ad
ad_gpo_access_control = permissive

1 To exit:     CTRL      +      'X' 
2 Select     'Y'    for    yes
3 To save:    'enter'   key

On Fedora

sudo authselect select sssd with-mkhomedir
sudo systemctl restart sssd

On Ubuntu

sudo pam-auth-update --enable mkhomedir
sudo systemctl restart sssd

On CentOS 7

sudo authconfig --enablesssdauth --enablesssd --enablemkhomedir --updateall
sudo systemctl restart sssd

******************************COMPLETED*******************************


TO MAKE AD ACCOUNT A SUDOER

Open Terminal

sudo nano /etc/sudoers.d/domain_admins
1

Input line : firstname.lastname ALL=(ALL) ALL

2

To allow all ICT Staff: %MYDNS\ ICT\ Staff\ SG   ALL=(ALL:ALL) ALL


cn=mydns ict staff sg,ou=security groups_m21,ou=mydns,dc=m21,dc=gov,dc=local

3 To exit:     CTRL      +      'X' 
4 Select     'Y'    for    yes
5 To save:    'enter'   key

******************************COMPLETED*******************************


TO MOUNT SHARE DRIVE

1 Launch the Files app  ->  OTHER LOCATIONS   ->   Bottom of window to enter address
2 Input: smb://172.16.21.16/
3 Toggle on REGISTERED USER
4 Input: YOUR DOMAIN ACCOUNT USERNAME and PASSWORD
5 Domain: M21.GOV.LOCAL  or 172.16.21.161

******************************COMPLETED*******************************


TO ADD PRINTER

Open Terminal

HP Printers

dnf search hplip
sudo dnf install hplip hplip-gui -y
hp-setup
hp-setup ‘printer IP Address’
1 Select detected printer
2 Follow next prompt until the end

XEROX Printers

Open Terminal
wget http://download.support.xerox.com/pub/drivers/CQ8580/drivers/linux/pt_BR/XeroxOfficev5Pkg-Linuxx86_64-5.20.661.4684.rpm
sudo dnf -y localinstall XeroxOfficev5Pkg-Linuxx86_64-5.20.661.4684.rpm


NOTE:   DO NOT PRINT A TEST PAGE!!  Print a regular text document to test

******************************COMPLETED*******************************


TO REPLACE FEDORA LOGO

Download Image and rename as: MYDNS-Logo

MYDNS-Logo.png

1 Go to EXTENSION MANAGER   ->  SYSTEM EXTENSIONS   ->   BACKGROUND LOGO
2 Click on the gear icon to get the background settings
3

Go to LOGO   ->   Filename to attach the MYDNS-Logo.png file

                           ->   Filename (dark) to attach the MYDNS-Logo.png file

4 Scroll down to OPTIONS   ->  Toggle on Show for all backgrounds

******************************COMPLETED*******************************



Browse to 172.16.21.16>fileserver2>General>IT FILES>prx and copy the GORTT.pem file to a folder on the local machine.

  1. Navigate to the location of the certificate file in Terminal (or right click and open from the location)
  2. Move the ceritficate file to the proper location with the following command:
    sudo mv GORTT.pem /etc/pki/ca-trust/source/anchors/GORTT.pem
  3. Update trusted certificates with the following command:
    sudo update-ca-trust

Adding Certificate File to Local Machine (Ubuntu)

Browse to 172.16.21.16>fileserver2>General>IT FILES>prx and copy the GORTT.pem file to a folder on the local machine.

sudo apt-get install -y ca-certificates
  1. Navigate to the location of the certificate file in Terminal (or right click and open from the location)
openssl x509 -in GORTT.pem -out GORTT.crt
  1. Move the ceritficate file to the proper location with the following command:
    sudo mv GORTT.crt /usr/local/share/ca-certificates

  2. Update trusted certificates with the following command:
    sudo update-ca-certificates


HELPFUL  APPS

1

Extension Manager

 

flatpak install flathub com.mattjakeman.ExtensionManager

2 GNOME Tweaks ( sudo dnf install gnome-tweaks )
3

OnlyOffice

https://download.onlyoffice.com/install/desktop/editors/linux/onlyoffice-desktopeditors.x86_64.rpm

sudo dnf -y localinstall onlyoffice-desktopeditors.x86_64.rpm

4

Element

 

flatpak install flathub im.riot.Riot

5

Google Chome (Fedora)


wget https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm

sudo dnf -y localinstall google-chrome-stable_current_x86_64.rpm

6

Google Chrome (Ubuntu)


sudo apt install curl software-properties-common apt-transport-https ca-certificates -y


curl -fSsL https://dl.google.com/linux/linux_signing_key.pub | gpg --dearmor | sudo tee /usr/share/keyrings/google-chrome.gpg > /dev/null


echo deb [arch=amd64 signed-by=/usr/share/keyrings/google-chrome.gpg] http://dl.google.com/linux/chrome/deb/ stable main | sudo tee /etc/apt/sources.list.d/google-chrome.list


sudo apt update


sudo apt -y install google-chrome-stable


HELPFUL EXTENSIONS

1 Dash to Dock - Displays a dynamic centered Taskbar
2 Dash to Panel - Displays screen width static Taskbar
3 Vitals - displays the PC health at the top right
4 Desktop icons NG (Ding) - display anything saved to desktop
5 Clipboard History - enables clipboard history tool

******************************COMPLETED*******************************