Automating Your Ubuntu Server Setup: A Standardised Approach to Configuration

Page content

Automating Your Ubuntu Server Setup: A Standardised Approach to Configuration

In the world of system administration, consistency and efficiency are paramount. Manually configuring a new server is not only time-consuming but also prone to human error, leading to inconsistent setups and potential security oversights. This article introduces a bash script designed to automate the initial configuration of a minimal Ubuntu 24.04 LTS server, ensuring a standardised baseline.

The catalyst for creating this script was the setup of a new home lab environment hosted on an Intel N100 Mini PC. These compact, low-power systems are excellent for reducing energy consumption while delivering adequate performance for various server applications. However, a significant performance issue was identified during the initial build on Ubuntu Server: the CPU was aggressively throttled down to its 800MHz base clock due to default power-saving mechanisms. Addressing this required a two-pronged approach. The first step was to disable the “Intel Speed Step” feature within the system’s BIOS. The second, and crucial, step on Linux was to install and configure the cpufrequtils package to permanently enforce the “performance” CPU governor. To ensure this solution could be applied consistently and efficiently across future server rebuilds or deployments, this automation script was developed.

Purpose

The primary purpose of this script is to streamline the post-installation setup of an Ubuntu server. After a minimal OS installation, numerous packages and configurations are required to make the system functional, secure, and ready for specific workloads.

Modern cybersecurity frameworks emphasise the importance of secure baselines. A standardised deployment script helps achieve this by programmatically enforcing security settings, such as strengthening cryptographic protocols. Disabling older TLS versions, like TLS 1.0 and 1.1, is a critical step in mitigating known vulnerabilities. The U.S. National Institute of Standards and Technology (NIST) has deprecated their use for government systems, a best practice widely adopted across the industry [1]. This script shows a method automate such configurations, ensuring the deployment of server adheres to a defined security posture from the outset.

TL;DR: Here’s a bash script to automate the setup of a Ubuntu Server: https://gist.github.com/CraigWilsonOZ/da0a3bc321791938e5ad88d0b8d1ced8

Use Cases

This script adds value across several common scenarios:

  • Automation: For developers and system administrators who frequently deploy new virtual machines or bare-metal servers, this script reduces setup time from hours to minutes. It is particularly useful in environments like a home lab, where a user might be testing various applications on a fresh instance.
  • Standardisation: In a team environment, it ensures every server starts with the same set of tools and configurations, simplifying management and reducing the “it works on my machine” problem.
  • Compliance: While not exhaustive, the script provides a foundational layer of security hardening (e.g., SSH banner, OpenSSL configuration) that can be a starting point for meeting compliance requirements under frameworks like ISO 27001 or the CIS Benchmarks. The use of a login banner, for instance, is a common requirement to inform users of monitoring and acceptable use policies.
  • Incident Response: A known, consistent state simplifies forensic analysis. If a compromise occurs, investigators can quickly differentiate between the standard baseline and unauthorised changes made by an attacker.

Prerequisites

To ensure successful execution, the following requirements must be met:

  • Operating System: Ubuntu Server 24.04 LTS (minimal installation). The script is designed for Debian-based distributions but has only been tested on this specific version.
  • Permissions: The script must be executed with root privileges (sudo).
  • Network Access: An active internet connection is required to download packages from Ubuntu repositories and a Personal Package Archive (PPA). Outbound access over TCP port 443 (HTTPS) and TCP port 80 (HTTP) to archive.ubuntu.com, ppa.launchpad.net, and other repository mirrors is necessary.
  • Software Versions: The script is designed for the package versions available in the Ubuntu 24.04 repositories as of July 2025.

How The Script Works

The script executes a sequence of functions in a logical order to configure the system. Its workflow is designed to be idempotent where possible, meaning it can be run multiple times without causing adverse effects.

  1. Initial Checks: The script first verifies it is being run with root privileges. If not, it exits immediately to prevent errors.
  2. Package Management: It adds a PPA to source the fastfetch utility, updates the local package index, and then installs a comprehensive list of system administration, networking, and development tools in a single apt-get command to improve efficiency.
  3. Performance Tuning: The CPU governor is set to performance. This forces the CPU to operate at its maximum frequency, which can be beneficial for latency-sensitive or compute-intensive server workloads.
  4. Security Hardening:
    • An SSH banner is created and enabled to display a legal notice to users upon login.
    • The OpenSSL configuration is hardened by setting the minimum accepted TLS protocol to TLSv1.2 and enforcing secure cipher suites, aligning with modern cryptographic standards.
  5. User Experience:
    • The default Message of the Day (MOTD) is disabled.
    • fastfetch is configured to run on login, presenting a clean, detailed summary of system hardware and software information.
  6. System Configuration: The system’s timezone is set to Australia/Melbourne.
  7. Service Restarts: Key services like ssh and cpufrequtils are restarted to apply the new configurations.
  8. Cleanup: To prevent a user’s command history from revealing sensitive information used during setup, the script concludes by clearing the history files for all users.

The Script

#!/bin/bash

#==============================================================================
# Streamlined System Initialization and Configuration Script
#
# Author:        Craig Wilson
# Version:       0.1
# Last Modified: 2025-06-20
#
# Description:
# This script automates the initial setup of a Debian-based server by:
#   - Installing a consolidated list of essential tools and utilities.
#   - Configuring system settings for performance and security.
#   - Customizing the login experience with a legal banner and system info.
#
# Tested on:
#   - Ubuntu 24.04 (Lunar Lobster)
#
#==============================================================================


# --- Script Configuration and Preamble ---

# Exit immediately if a command exits with a non-zero status.
set -e

# Set DEBIAN_FRONTEND to noninteractive to prevent prompts.
export DEBIAN_FRONTEND=noninteractive

# --- Function Definitions ---

#
# Performs initial checks to ensure script can run successfully.
#
initial_checks() {
  echo "▶ Performing initial checks..."
  # Check for root privileges
  if [ "$EUID" -ne 0 ]; then
    echo "ERROR: Please run this script as root or using sudo." >&2
    exit 1
  fi
}

#
# Updates package lists and installs all required packages in one go.
#
install_packages() {
  echo "▶ Updating package repositories..."

  # Add the PPA for fastfetch
  echo "▶ Adding PPA for fastfetch..."
  add-apt-repository -y ppa:zhangsongcui3371/fastfetch 2>&1 | grep -v "WARNING: apt does not have a stable CLI interface"

  echo "▶ Updating package lists..."
  apt-get update -y 2>&1 | grep -v "WARNING: apt does not have a stable CLI interface"

  echo "▶ Installing all required packages..."
  apt-get install -y \
    btop \
    cmatrix \
    curl \
    wget \
    vim \
    nano \
    unzip \
    net-tools \
    build-essential \
    speedtest-cli \
    python3 \
    python3-pip \
    python3-venv \
    git \
    openvpn \
    linux-tools-common \
    smartmontools \
    nvme-cli \
    "linux-tools-$(uname -r)" \
    cpufrequtils \
    fastfetch \
    2>&1 | grep -v "WARNING: apt does not have a stable CLI interface"
  echo "✔ Package installation complete."
}

#
# Configures the CPU governor for maximum performance.
#
configure_cpu_governor() {
  echo "▶ Configuring CPU governor to 'performance'..."
  # Set the governor in the configuration file
  echo 'GOVERNOR="performance"' > /etc/default/cpufrequtils
  echo "✔ CPU governor configured."
}

#
# Sets a legal disclaimer banner for SSH sessions.
#
configure_ssh() {
  echo "▶ Configuring SSH banner..."
  local banner_file="/etc/ssh/banner"
  local ssh_config="/etc/ssh/sshd_config"

  # Create the SSH banner file
  cat << EOF > "$banner_file"
*******************************************************************************
** NOTICE TO USERS OF THIS SYSTEM                                            **
** **
** This computer system is for authorized use only.                          **
** **
** By using this system, the user consents to such interception, monitoring, **
** recording, copying, auditing, inspection, and disclosure at the           **
** discretion of authorized site or personnel.                               **
** **
** By continuing to use this system, you indicate your awareness of and      **
** consent to these terms and conditions of use.                             **
*******************************************************************************
EOF
  # Add the banner configuration to sshd_config if it doesn't exist
  if ! grep -q "^Banner $banner_file" "$ssh_config"; then
    echo "Banner $banner_file" >> "$ssh_config"
  fi
  echo "✔ SSH banner configured."
}

#
# Configures fastfetch to display on login and disables default MOTD.
#
configure_motd() {
  echo "▶ Configuring fastfetch as the login banner..."
  # Create a script to run fastfetch on login for all users
  cat << EOF > /etc/profile.d/00-fastfetch.sh
#!/bin/bash
# Display system information using fastfetch
fastfetch
EOF

  chmod +x /etc/profile.d/00-fastfetch.sh

  # Disable default MOTD files by renaming them with a .disabled extension
  echo "▶ Disabling default MOTD..."
  for file in /etc/update-motd.d/*; do
    # Check if it's a file and not already disabled
    if [[ -f "$file" && ! "$file" == *.disabled ]]; then
      mv "$file" "$file.disabled" 2>/dev/null || true
    fi
  done
  echo "✔ MOTD configured to show fastfetch."
}

#
# Hardens OpenSSL configuration to modern standards.
#
configure_openssl() {
  echo "▶ Configuring OpenSSL security settings..."
  local openssl_config="/etc/ssl/openssl.cnf"

  # Backup the original configuration file
  cp "$openssl_config" "$openssl_config.bak"

  # Add MinProtocol and CipherString if they don't already exist
  if ! grep -q "^MinProtocol" "$openssl_config"; then
    sed -i '/^\[system_default_sect\]$/a MinProtocol = TLSv1.2' "$openssl_config"
  fi
  if ! grep -q "^CipherString" "$openssl_config"; then
    sed -i '/^\[system_default_sect\]$/a CipherString = DEFAULT@SECLEVEL=2' "$openssl_config"
  fi
  echo "✔ OpenSSL security settings applied."
}

#
# Enables and restarts all necessary services.
#
restart_services() {
  echo "▶ Enabling and restarting services..."

  # Enable and start cpufrequtils
  systemctl enable cpufrequtils
  systemctl start cpufrequtils

  # Restart services that use SSH or OpenSSL
  local services_to_restart=("ssh" "apache2" "nginx")
  for service in "${services_to_restart[@]}"; do
    if systemctl list-units --full --all | grep -q "${service}.service"; then
      echo "  - Restarting ${service}..."
      systemctl restart "${service}"
    else
      echo "  - Service ${service} not found, skipping restart."
    fi
  done
  echo "✔ Services have been restarted."
}

#
# Configure the system timezone to Melbourne, Australia.
#
configure_timezone() {
    echo "▶ Configuring system timezone to Melbourne, Australia..."
    # Set the timezone to Melbourne
    sudo timedatectl set-timezone Australia/Melbourne
    echo "✔ Timezone configured to Melbourne, Australia."
}

#
# Clears on-disk history for all users and the root account.
#
clean_history() {
    echo "▶ Clearing on-disk command history files..."

    # Unset HISTFILE for the script's session to prevent its own commands from being logged.
    unset HISTFILE

    # Define the common history filenames to clear.
    local history_files=(".bash_history" ".zsh_history" ".fish_history")

    # Clear history for the root user.
    echo "  - Clearing history for root user..."
    for hist_file in "${history_files[@]}"; do
        # Check if the history file exists for root before trying to clear it.
        if [ -f "/root/${hist_file}" ]; then
            cat /dev/null > "/root/${hist_file}"
        fi
    done

    # Iterate over user directories in /home.
    for user_dir in /home/*; do
        # Ensure it is a directory.
        if [ -d "$user_dir" ]; then
            local user
            user=$(basename "$user_dir")
            echo "  - Clearing history for user: $user"
            for hist_file in "${history_files[@]}"; do
                local full_path="${user_dir}/${hist_file}"
                # Check if the history file exists before trying to clear it.
                if [ -f "$full_path" ]; then
                    cat /dev/null > "$full_path"
                fi
            done
        fi
    done

    echo "✔ On-disk history files cleared."
    echo "ℹ NOTE: To clear the history of your CURRENT terminal session, please run 'history -c' after this script completes."
}

# --- Main Execution ---

main() {
  initial_checks
  install_packages
  configure_cpu_governor
  configure_ssh
  configure_motd
  configure_openssl
  configure_timezone
  restart_services
  clean_history

  echo -e "\n✔  System setup and configuration are complete."
}

main "$@"
# --- End of Script ---

Security Considerations

  • Privileged Operations: This script requires root access to install packages and modify system-wide configuration files. Running scripts from untrusted sources with root privileges is extremely dangerous. Always review the script’s content before execution.
  • Sensitive Data Handling: The clean_history function is included to clear command history, which might inadvertently contain passwords or other sensitive data entered during the setup process. However, it does not clear history from the active terminal session; the user must do this manually by running history -c.
  • External Repositories: The script adds a third-party PPA (ppa:zhangsongcui3371/fastfetch). While PPAs are a common way to access newer software on Ubuntu, they introduce a level of risk as the packages are not maintained by Canonical. The integrity of the system depends on the trustworthiness of the PPA maintainer.

Limitations

  • Distribution Specific: The script is written for Debian-based systems using apt and has only been validated on Ubuntu 24.04. It will fail on distributions that use different package managers (e.g., yum, dnf, pacman).
  • Environment Assumptions: The script assumes a minimal server installation. Running it on a system with a desktop environment or other pre-existing configurations might lead to unintended consequences.
  • CPU Governor: Setting the CPU governor to performance disables power-saving states. On battery-powered devices like laptops or in energy-conscious environments, this will lead to significantly higher power consumption. This setting is intended for servers where performance is prioritised over power efficiency.

Future Work / Enhancement Ideas

  • Parameterisation: Convert hard-coded values (e.g., timezone, CPU governor setting) into command-line arguments or variables at the top of the script for easier customisation without editing the core logic.
  • Enhanced Security: Integrate more advanced security controls, such as configuring fail2ban for brute-force protection, setting up unattended upgrades for security patches, or applying a more comprehensive set of CIS Benchmark recommendations.

Conclusion

Automating server setup with a configuration script provides substantial benefits in efficiency, consistency, and security. This script serves as a foundation for deploying my Ubuntu 24.04 servers, ensuring that I have essential tools and the start of a secure baseline is established from the moment of creation.

For further reading on security best practices, consult the official CIS Benchmarks and NIST publications.

References

[1] T. A. Polk, T. P. Grassi, and K. A. Venti, NIST Special Publication 800-52 Revision 2: Guidelines for the Selection, Configuration, and Use of Transport Layer Security (TLS) Implementations, National Institute of Standards and Technology, Gaithersburg, MD, Aug. 2019. doi: 10.6028/NIST.SP.800-52r2.