#!/bin/bash
#
# Install TrueStack Server automatically.
#
# This script is intended to be run on a fresh system that will primarily be
# a TrueStack Server. It makes many assumptions about the desired state, and
# may install software or configure the system in a way that conflicts with
# other server software.
#
# Only officially supported OSes are supported by this script. Currently, that
# that is:
#
# - Ubuntu 24.04 LTS
#
# This script is intended to be run as root or by a user with sudo privileges.
#
# You may run this script with:
#
#     $ curl -fsSL https://get.truestack.com/ | bash
#
# If you want to run it in dry-run mode to see what changes will be made, you
# can run it with the `-n` or `--dry-run` option:
#
#     $ curl -fsSL https://get.truestack.com/ | bash -s -- --dry-run
#
# If you want to uninstall TrueStack Server and its dependencies, you can run
# it with the `-u` or `--uninstall` option:
#
#     $ curl -fsSL https://get.truestack.com/ | bash -s -- --uninstall
#
# To download this script first then execute it:
#
#     $ curl -fsSL -o install.sh https://get.truestack.com/
#     $ bash install.sh [options]
#
# Changelog
#
# 1.0.2 - 2025-07-30
#   - Updated OpenVPN repository GPG key
#
# 1.0.1 - 2025-01-08
#   - Made firewall and DCO module optional with prompts
#   - Check for Secure Boot and don't install DCO module
#   - Added uninstall option
#   - Fixed issues with apt interaction
#
# 1.0.0 - 2024-12-27
#   - Initial release

# Exit on error
set -eu

# Globals
SCRIPT_VERSION="1.0.1"
CMD="su -c"
OS=""
VER=""
ARCH=""
OS_NAME=""
DEBUG=false
DRYRUN=false
INSTALL=true

# Install paths
TRUESTACK_KEYRING="/etc/apt/keyrings/truestack.gpg"
TRUESTACK_REPO_URL="https://updates.truestack.com/truestack"
TRUESTACK_SOURCES="/etc/apt/sources.list.d/truestack.list"
OPENVPN_KEYRING="/etc/apt/keyrings/openvpn-repo-public.gpg"
OPENVPN_REPO_URL="https://build.openvpn.net/debian/openvpn/release/2.6"
OPENVPN_SOURCES="/etc/apt/sources.list.d/openvpn-aptrepo.list"

# Exported vars
export DEBIAN_FRONTEND="noninteractive"

usage() {
    cat <<EOF
Usage: $0 [options]

Options:
  -v, --version   Print the script version and exit
  -d, --debug     Enable debug mode
  -n, --dry-run   Enable dry-run mode
  -u, --uninstall Uninstall TrueStack Server and dependencies
EOF
}

print() {
    echo "$1"
}

print_cmd() {
    echo -e "\e[00;33m-\e[m $1"
}

print_debug() {
    if [ $DEBUG == true ]; then
        echo -e "\e[01;35mDEBUG: $1\e[m"
    fi
}

print_info() {
    echo -e "\e[00;36m=\e[m $1"
}

print_error() {
    echo -e "\e[00;31mERROR: $1\e[m"
}

confirm_no() {
    read -p "$1 [y/N] " < /dev/tty
    if [[ $REPLY =~ ^[Yy]$ ]]; then
        return 0
    else
        return 1
    fi

}

confirm_yes() {
    read -p "$1 [Y/n] " < /dev/tty
    if [[ $REPLY =~ ^[Nn]$ ]]; then
        return 1
    else
        return 0
    fi
}

command_exists() {
    command -v "$1" >/dev/null 2>&1
}

cmd() {
    # $CMD is either `su -c` or `sudo -E`
    if [ $DRYRUN == true ]; then
        print_cmd "DRYRUN: $*"
    else
        print_cmd "$*"
        print_debug "Running command: $CMD \"$*\""
        DEBIAN_FRONTEND=noninteractive $CMD "$@"
    fi
}

cmd_i() {
    # Run command interactively
    if [ $DRYRUN == true ]; then
        print_cmd "DRYRUN: $*"
    else
        print_cmd "$*"
        print_debug "Running command interactively: $CMD \"$*\""
        $CMD "$@" < /dev/tty
    fi
}

cmd_u() {
    USER="$1"
    shift

    if [ $DRYRUN == true ]; then
        print_cmd "DRYRUN: [$USER] $*"
    else
        print_cmd "[$USER] $*"
        print_debug "Running command as $USER: $CMD \"$*\""
        DEBIAN_FRONTEND=noninteractive $CMD -u "$USER" "$@"
    fi
}

support_error() {
    # Add more OSes here as they are supported
    print_error "This script only supports Ubuntu 24.04 LTS."
    if [[ -n $OS_NAME ]]; then
        print "You are running: $PRETTY_NAME"
    fi
    exit 1
}

check_os() {
    if [ -f /etc/os-release ]; then
        . /etc/os-release
        OS=$NAME
        VER=$VERSION_ID
        OS_NAME=$PRETTY_NAME
    else
        support_error
    fi

    if [ "$OS" != "Ubuntu" ]; then
        support_error
    fi

    if [ "$VER" != "24.04" ]; then
        support_error
    fi

    ARCH=$(dpkg --print-architecture)

    if [ "$ARCH" != "amd64" ]; then
        print_error "Only amd64 architecture is supported."
        exit 1
    fi
}

check_installed() {
    # Check if the truestack-server package is already installed
    if dpkg -l truestack-server 2>/dev/null | grep -q '^ii'; then
        print_error "TrueStack Server is already installed."
        exit 1
    fi
}

confirm_install() {
    cat <<EOF
This script will install TrueStack Server on this system. It is suggested that
TrueStack Server is installed on a fresh system that will be dedicated to the
TrueStack Server application and VPN services.

This installer will make some assumptions about the desired state of the
system, and may install software or configure the system in a way that
conflicts with other server software.

It will install:
- TrueStack Server (The application/web server), ports 80 and 443
- PostgreSQL database server (for local connections only)
- OpenVPN from the official OpenVPN community repository, port 7473
- Several other utilities and dependencies
- (Optional) TrueStack Firewall to configure the system firewall.
- (Optional) OpenVPN DCO kernel driver.

You may run this script with the '-n' or '--dry-run' option to see what changes
will be made without actually making them.

EOF
    
    if [ $DRYRUN == true ]; then
        echo -e "THIS IS A DRY-RUN. NO CHANGES WILL BE MADE.\n"
    else
        if ! confirm_no "Do you want to continue?"; then
            print_info "Exiting."
            exit 0
        fi
    fi
}

check_root() {
    if [ "$(id -u)" -ne 0 ]; then
        if command_exists sudo; then
            CMD='sudo'
        elif command_exists su; then
            CMD='su -c'
        else
            print_error "This script must be run as root or with sudo."
            exit 1
        fi
    fi

    if [ "$($CMD whoami)" != "root" ]; then
        print_error "This script must be run as root."
        exit 1
    fi
}

setup_software_sources() {
    print_info "Set up truestack apt sources"

    if [ "$DRYRUN" == true ]; then
        print_cmd "cat <truestack_key> | gpg --dearmor | dd status=none of=$TRUESTACK_KEYRING"
        print_cmd "echo \"deb [arch=$ARCH signed-by=$TRUESTACK_KEYRING] $TRUESTACK_REPO_URL $(lsb_release -cs) main\" | dd status=none of=$TRUESTACK_SOURCES"
    else
        cat << EOF | gpg --dearmor | cmd dd status=none of=$TRUESTACK_KEYRING
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1

mQENBFlBx/0BCADkJytPoGOPSv8wSLYlDjZwhMV4qHzgyF57UDK9OgpEk27D8oEP
1XM0WXay9WAU5FXWL/HqQ8Flr1gXQV8dnB5N+D2rGOjEcNwjoxZ8apaBApMHu7yF
3/lTyStQWgl+YS/0saKUL5UeqySIdCmeuiqDTTgmeWaqSilAm51owLqJHeTGz4t0
SdGlEGiwQBi8R4oSnJYdRkIwizkuYbamULb3+9Ygr+L5tn72b/U0OB3VhVQLHGo6
1i8lF+f80cR7uWByfP9XfchSdqXbBgtUKMXFOvJB4PsfYC4qqJB9yQ4ecqdbXHH7
94dflojG8xXJZwc62/cXJMvR+IHWVaK3oB/BABEBAAG0IlRydWVTdGFjayBJbmMg
PGluZm9AdHJ1ZXN0YWNrLmNvbT6JATgEEwECACIFAllBx/0CGwMGCwkIBwMCBhUI
AgkKCwQWAgMBAh4BAheAAAoJEBzC4+Q81GQ6qGgIALG8wlT5NWwsAuXZE5r4CWgC
StgtInlRpxtoabkJSFrTeJSaGW2EEe+o7KieVfeOEnRc/HctwsvO8AwMFxAeHroU
Ke5tWerYQZfC5S8Zoj3E2APqaOjK/SIAh+HlEQtAEYD+aA1NxLDT9pGmKHcCnaJS
ml5nSoQM20c6nqQGNaQOaH8yJtAL8SFXRUHWMJCF8uH4T2sW5X5kGCn7Lmxsu881
ziu9TxChZ+JVrSSqY4v6ImLduHH8iaFcgu9V90VcIFHDpU6kAzCd21LWh/3kXvaU
fu94RIWRXfGtcOFoNGmPyUuDGh5qurVZjVu8fikyMBEVzzczgaTQelSSarJVDJ+5
AQ0EWUHH/QEIAMfEpa1q+j2140OHUadc/8RTyp9UIIlQABTFp928mW9uTGOdV8u3
kq2K/BP3d2x8N0YxK6osT7C9H8BYer7GBdro0mEhsKeAzle4DjFn8E/9bMEdCct/
dtpw0kjAjnEq8BhJTt2yNPbzBTL4Ra0NT6Y8hNYXSdXd1uYl3T7bA3H8rnSAJu5y
84cGIFUpGxQa04F1zC5P9yh8/opXT6ybWLNE+jV9l7S3wpj1hD6hPNk7US4d8U1D
248eXJ218xv8XX/92mjFkJW0vDFZsjCJmQgg5vK2gWvX8a++vdosBjK3YocolNIh
sqMOyeKIrWDP0aB0GePu9ueBCpewqQZjXVsAEQEAAYkBHwQYAQIACQUCWUHH/QIb
DAAKCRAcwuPkPNRkOl+UB/9XabhSo2WcnzMEim0FfgszrKwa7zN+OdVWUDJp/e3h
4S+n99Te/hlEIAA+oBnSFmg37T02BXEQcTwfvDQ0vwf6qPG8iKPKGbKjK5ijeZnZ
ElRiFc+Nh3VMweuKBSbu9Gva9XFYbm0RqvL19C4B/0ec1YaPnifj2zERaQ4NRlYE
dUoLTlYb73NE7Avq9U+VJVmf1lbe3gdFni0I+cxtN2obLtxv2QYWfi80/ot+jR/A
7MkDEBIyNb63pe9rVX8ri4AEmcArd77kwxSi4dtwOo1eMfqsMc2WwH9LiBuflkKS
REOaVHCuUoMmXzKZ95tAbXvzLSm7rlSYef0PrkjZ1j1Z
=hbaq
-----END PGP PUBLIC KEY BLOCK-----
EOF
        echo "deb [arch=$ARCH signed-by=$TRUESTACK_KEYRING] $TRUESTACK_REPO_URL $(lsb_release -cs) main" \
        | cmd dd status=none of=$TRUESTACK_SOURCES
    fi

    print_info "Set up openvpn apt sources"
    if [ "$DRYRUN" == true ]; then
        print_cmd "cat <openvpn_key> | gpg --dearmor | dd status=none of=$OPENVPN_KEYRING"
        print_cmd "echo \"deb [arch=$ARCH signed-by=$OPENVPN_KEYRING] $OPENVPN_REPO_URL $(lsb_release -cs) main\" | dd status=none of=$OPENVPN_SOURCES"
    else
        cat << EOF | gpg --dearmor | cmd dd status=none of=$OPENVPN_KEYRING
-----BEGIN PGP PUBLIC KEY BLOCK-----

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
A+ZbAQ==
=KYa9
-----END PGP PUBLIC KEY BLOCK-----
EOF
        echo "deb [arch=$ARCH signed-by=$OPENVPN_KEYRING] $OPENVPN_REPO_URL $(lsb_release -cs) main" \
        | cmd dd status=none of=$OPENVPN_SOURCES
    fi
}

install_deps() {
    print_info "Installing prerequisite packages"
    cmd apt-get update -q
    cmd_i apt-get install --yes -q \
        curl \
        mokutil \
        bzip2 \
        haveged \
        dnsutils \
        libncurses6 \
        openvpn \
        postgresql \
        certbot
}

maybe_install_firewall() {
    print_info "Do you wish to install the TrueStack Firewall?"

    cat <<EOF

The TrueStack Firewall (truestack-firewall) package will configure the system
firewall with a default set of rules that allow TrueStack Server to function
correctly. It is recommended for all TrueStack Server installations.

You can skip this step and configure the firewall manually if you prefer,
please see the TrueStack Server documentation for more information.

The TrueStack Firewall will configure the following rules:

- Allow all outbound traffic
- Allow all related/established inbound traffic
- Allow incoming SSH traffic (TCP/22)
- Allow incoming HTTP and HTTPS traffic (TCP/80, TCP/443)
- Allow incoming TrueStack VPN connections (UDP/7473)
- Masquerades (NATs) all outgoing traffic from the VPN clients
- Blocks all other incoming traffic

EOF

    if confirm_yes "Install TrueStack Firewall?"; then
        print_info "Installing firewall rules"
        cmd apt-get install --yes -q truestack-firewall
    fi
}

maybe_install_dco_kernel_module() {
    if mokutil --sb-state | grep -q enabled; then
        print_info "Secure Boot is enabled"
        print "The OpenVPN DCO kernel module cannot be automatically installed with Secure Boot enabled."
        print "Please disable Secure Boot or install the module manually."
        return
    fi

    print_info "Do you wish to install the OpenVPN DCO kernel module?"
    cat <<EOF

This module will accelerate OpenVPN performance by offloading encryption
and decryption to the kernel. It is recommended for all TrueStack Server
installations.

However, it requires compiling a kernel module, which may not be possible
on all systems. This may particularly be the case if you are running with
Secure Boot enabled. (e.g. Trusted launch virtual machines on Azure.)

If you are unsure, you can skip this step and install the module later with:

    apt install openvpn-dco-dkms

EOF

    if confirm_yes "Install OpenVPN DCO kernel module?"; then
        print_info "Installing OpenVPN DCO kernel module"
        cmd_i apt-get install --yes -q openvpn-dco-dkms

        print_info "Enabling OpenVPN DCO kernel module"
        cmd modprobe ovpn-dco-v2
    fi
}

install_server() {
    print_info "Installing TrueStack Server"
    cmd apt-get install --yes -q truestack-server
}

setup_database() {
    print_info "Setting up database"
    cmd_u postgres createuser truestack
    cmd_u postgres createdb -O truestack truestack
}

setup_truestack_server() {
    print_info "Configuring TrueStack Server"
    cmd /usr/lib/truestack/bin/initconfig

    print_info "Enabling IPv4 packet forwarding"
    cmd sysctl net.ipv4.ip_forward=1

    print_info "Enabling TrueStack Server"
    cmd systemctl enable truestack-server

    print_info "Enabling TrueStack VPN Server"
    cmd systemctl enable truestack-vpn
}

start_truestack_server() {
    print_info "Starting TrueStack Server"
    cmd systemctl start truestack-server
}

next_steps() {
    PUBLIC_IP=$(curl -fsS http://ifconfig.me)
    PUBLIC_IP=${PUBLIC_IP:-"<server_ip>"}

    if [ $DRYRUN == true ]; then
        print_info "DRYRUN complete!"
        cat <<EOF

This is where you would have been directed to finish the setup process by
visiting the following URL:

    http://$PUBLIC_IP/

When you're ready, run this without the '-n' or '--dry-run' flag to complete
the installation.
EOF
    else
        print_info "TrueStack Server installation complete!"
        cat <<EOF

You may now configure your TrueStack Server by visiting the following URL:

    http://$PUBLIC_IP/

This will walk you through the initial setup process.

If you have any questions or need assistance, please visit:

    https://truestack.com/support

Thank you for using TrueStack Server!
EOF
    fi
}

install() {
    if [ $DEBUG == false ]; then
        check_os
    fi
    check_installed
    confirm_install
    check_root
    
    print_info "Installing TrueStack Server. Script version: $SCRIPT_VERSION"
    setup_software_sources
    install_deps
    install_server
    setup_database
    setup_truestack_server
    maybe_install_firewall
    maybe_install_dco_kernel_module
    start_truestack_server
    next_steps
}

uninstall() {
    check_root

    print_info "Uninstalling TrueStack Server"
    set +e

    print_info "Stopping TrueStack Server"
    cmd systemctl stop truestack-server

    print_info "Stopping TrueStack VPN Server"
    cmd systemctl stop truestack-vpn

    print_info "Removing truestack database"
    cmd_u postgres dropdb truestack
    cmd_u postgres dropuser truestack

    print_info "Uninstalling TrueStack Server and dependencies"
    cmd_i apt --yes remove --auto-remove haveged libncurses6 openvpn openvpn-dco-dkms postgresql certbot truestack-server truestack-firewall

    print_info "Removing apt sources and keys"
    cmd rm /etc/apt/keyrings/truestack.gpg
    cmd rm /etc/apt/sources.list.d/truestack.list
    cmd rm /etc/apt/keyrings/openvpn-repo-public.gpg
    cmd rm /etc/apt/sources.list.d/openvpn-aptrepo.list
    cmd apt-get update -q

    print_info "TrueStack Server has been uninstalled"
    set -e
}

while [[ $# -gt 0 && ! "$1" == "--" ]]; do case $1 in
    -v | --version )
        echo "$SCRIPT_VERSION"
        exit
        ;;
    -d | --debug )
        DEBUG=true
        print_debug "Debug mode enabled"
        ;;
    -n | --dry-run )
        DRYRUN=true
        ;;
    -h | --help )
        usage
        exit
        ;;
    -u | --uninstall )
        INSTALL=false
        ;;
    * )
        echo "Unknown option: $1"
        usage
        exit 1
        ;;
esac; shift; done
if [[ $# -gt 0 && "$1" == '--' ]]; then shift; fi

if [ $INSTALL == true ]; then
    install "$@"
else
    uninstall
fi
