#!/usr/bin/env bash
# ==========================================================================
#     _   _ _ ____            ____          _____
#    | | | (_)  _ \ ___ _ __ / ___|___  _ _|_   _| __ __ _  ___ ___ _ __
#    | |_| | | |_) / _ \ '__| |   / _ \| '_ \| || '__/ _` |/ __/ _ \ '__|
#    |  _  | |  __/  __/ |  | |__| (_) | | | | || | | (_| | (_|  __/ |
#    |_| |_|_|_|   \___|_|   \____\___/|_| |_|_||_|  \__,_|\___\___|_|
#
#       ---  High-Performance Connectivity Tracer (HiPerConTracer)  ---
#                 https://www.nntb.no/~dreibh/hipercontracer/
# ==========================================================================
#
# High-Performance Connectivity Tracer (HiPerConTracer)
# Copyright (C) 2015-2025 by Thomas Dreibholz
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# Contact: dreibh@simula.no

# Bash options:
set -eu


if [ $# -ne 2 ] ; then
   echo >&2 "Usage: $0 mariadb|mongodb|mysql|postgresql users.conf"
   exit 1
fi
if [ ! -e "$2" ] ; then
   echo >&2 "ERROR: Use configuration $2 does not exist!"
fi


# ====== Check/set environment variables ====================================
if [ ! -e /etc/os-release ] ; then
   echo >&2 "ERROR: /etc/os-release does not exist!"
   exit 1
fi
. /etc/os-release

DOMAIN="test.local"
ROOT_PASSWORD="!root!"
DATABASE="just_a_test_database"
MAINTAINER_PASSWORD="!maintainer!"
IMPORTER_PASSWORD="!importer!"
RESEARCHER_PASSWORD="!researcher!"
# shellcheck disable=SC1090
. "$2"

if [ "${ROOT_PASSWORD}"       == "!root!"       ] || \
   [ "${MAINTAINER_PASSWORD}" == "!maintainer!" ] || \
   [ "${IMPORTER_PASSWORD}"   == "!importer!"   ] || \
   [ "${RESEARCHER_PASSWORD}" == "!researcher!" ] ; then
   echo >&2 "DO NOT USE THE EXAMPLE PASSWORDS!"
   exit 1
fi


# ###### MariaDB ############################################################
if [ "$1" == "mariadb" ] ; then
   # Documentation: https://www.digitalocean.com/community/tutorials/how-to-install-mariadb-on-ubuntu-22-04
   server="mariadb"

   # ====== Install MariaDB =================================================
   echo -e "\x1b[34m$(date +"%F %H:%M:%S"): Installing MariaDB ...\x1b[0m"

   if [ "${ID}" == "ubuntu" ] || [ "${ID}" == "debian" ] ; then
      sudo DEBIAN_FRONTEND=noninteractive apt install -y -o APT::Keep-Downloaded-Packages=true mariadb-server mariadb-backup mariadb-client libmariadb-dev openssl
   elif [ "${ID}" == "fedora" ] ; then
      sudo dnf install -y mariadb-server mariadb-backup mariadb-connector-c-devel openssl
      sudo systemctl enable mariadb.service
      sudo systemctl start mariadb.service
   elif [ "${ID}" == "freebsd" ] ; then
      sudo pkg install -y mariadb118-server mariadb118-client openssl
      sudo sysrc mysql_enable=YES
      sudo service mysql-server start
   else
      echo >&2 "ERROR: Unknown ID ${ID} in /etc/os-release. The installation script may need an update for supporting this system!"
      exit 1
   fi

   # ====== Basic configuration =============================================
   sudo ./name-in-etc-hosts "${server}.${DOMAIN}"
   sudo ./generate-test-certificates /etc/ssl "${server}.${DOMAIN}"

   # MariaDB needs separate certificate and key files.
   # The chain is in the CA file, it must not be in the certificate file!
   # Details: https://mariadb.com/kb/en/securing-connections-for-client-and-server/
   MARIADB_CA_FILE="/etc/ssl/TestLevel1/certs/TestLevel1.crt"
   MARIADB_CRL_FILE="/etc/ssl/TestGlobal.crl"
   MARIADB_CRT_FILE="/etc/ssl/${server}.${DOMAIN}/${server}.${DOMAIN}.crt"
   MARIADB_KEY_FILE="/etc/ssl/${server}.${DOMAIN}/${server}.${DOMAIN}.key"
   sudo chown mysql:mysql ${MARIADB_KEY_FILE}

   # Set root password, and remove test user and database.
   (
      cat <<EOF
ALTER USER 'root'@'localhost' IDENTIFIED BY '${ROOT_PASSWORD}';
DELETE FROM mysql.user WHERE User='';
DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');
DROP DATABASE IF EXISTS ${DATABASE};
DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%';
FLUSH PRIVILEGES;
EOF
   ) | sudo mysql --user=root

   # Enable network access:
   if [ "${ID}" == "ubuntu" ] || [ "${ID}" == "debian" ] ; then
      sudo sed \
         -e '/^event_scheduler[ \t]*=.*$/d' -e 's/\(^\[mysqld\].*\)$/\1\nevent_scheduler = ON/' \
         -e 's/^\([#]\|\)\(max_allowed_packet[ \t]*\)=.*$/\2= 1G/g' \
         -e 's/^\(bind-address[ \t]*\)=.*$/\1= */g' \
         -e "s%\([#][ \t]*\|\)ssl-ca[ ]*=[ ]*.*%ssl-ca = ${MARIADB_CA_FILE}\nssl-crl = ${MARIADB_CRL_FILE}%" \
         -e "s%\([#][ \t]*\|\)ssl-cert[ ]*=[ ]*.*%ssl-cert = ${MARIADB_CRT_FILE}%" \
         -e "s%\([#][ \t]*\|\)ssl-key[ ]*=[ ]*.*%ssl-key = ${MARIADB_KEY_FILE}%" \
         -e "s%\([#][ \t]*\|\)require-secure-transport[ ]*=[ ]*.*%require-secure-transport = on\ntls_version = TLSv1.3%" \
         /etc/mysql/mariadb.conf.d/50-server.cnf | sudo tee /etc/mysql/mariadb.conf.d/50-server.cnf.new >/dev/null && \
      sudo diff --color /etc/mysql/mariadb.conf.d/50-server.cnf /etc/mysql/mariadb.conf.d/50-server.cnf.new || true && \
      sudo mv /etc/mysql/mariadb.conf.d/50-server.cnf.new /etc/mysql/mariadb.conf.d/50-server.cnf
      sudo service mariadb restart
   elif [ "${ID}" == "fedora" ] ; then
      sudo sed \
         -e '/^event_scheduler[ \t]*=.*$/d' -e 's/\(^\[mysqld\].*\)$/\1\nevent_scheduler = ON/' \
         -e "s%^#bind-address[ \t]*=.*%max_allowed_packet = 1G\nbind-address = *\nssl-ca = ${MARIADB_CA_FILE}\nssl-crl = ${MARIADB_CRL_FILE}\nssl-cert = ${MARIADB_CRT_FILE}\nssl-key = ${MARIADB_KEY_FILE}\nrequire-secure-transport = on\ntls_version = TLSv1.3%g" \
         /etc/my.cnf.d/mariadb-server.cnf | sudo tee /etc/my.cnf.d/mariadb-server.cnf.new && \
      sudo diff --color /etc/my.cnf.d/mariadb-server.cnf /etc/my.cnf.d/mariadb-server.cnf.new || true && \
      sudo mv /etc/my.cnf.d/mariadb-server.cnf.new /etc/my.cnf.d/mariadb-server.cnf
      sudo service mariadb restart
   elif [ "${ID}" == "freebsd" ] ; then
      sudo sed \
         -e '/^event_scheduler[ \t]*=.*$/d' -e 's/\(^\[mysqld\].*\)$/\1\nevent_scheduler = ON/' \
         -e "s%^bind-address[ \t]*=.*%max_allowed_packet = 1G\nbind-address = *\nssl-ca = ${MARIADB_CA_FILE}\nssl-crl = ${MARIADB_CRL_FILE}\nssl-cert = ${MARIADB_CRT_FILE}\nssl-key = ${MARIADB_KEY_FILE}\nrequire-secure-transport = on\ntls_version = TLSv1.3%g" \
         /usr/local/etc/mysql/conf.d/server.cnf | sudo tee /usr/local/etc/mysql/conf.d/server.cnf.new && \
      sudo diff --color /usr/local/etc/mysql/conf.d/server.cnf /usr/local/etc/mysql/conf.d/server.cnf.new || true && \
      sudo mv /usr/local/etc/mysql/conf.d/server.cnf.new /usr/local/etc/mysql/conf.d/server.cnf
      sudo service mysql-server restart
   fi

   # ====== Test configuration ==============================================
   ./test-tls-connection "${server}.${DOMAIN}:3306" /etc/ssl/TestLevel1/certs/TestLevel1.crt -- -starttls mysql


# ###### PostgreSQL #########################################################
elif [ "$1" == "postgresql" ] ; then
   # Documentation: https://www.digitalocean.com/community/tutorials/how-to-install-postgresql-on-ubuntu-22-04-quickstart
   server="postgresql"

   # ====== Install PostgreSQL ==============================================
   echo -e "\x1b[34m$(date +"%F %H:%M:%S"): Installing PostgreSQL ...\x1b[0m"

   if [ "${ID}" == "ubuntu" ] || [ "${ID}" == "debian" ] ; then
      sudo DEBIAN_FRONTEND=noninteractive apt install -y -o APT::Keep-Downloaded-Packages=true openssl postgresql-all postgresql-contrib
   elif [ "${ID}" == "fedora" ] ; then
      sudo dnf install -y openssl postgresql-server libpqxx-devel
      sudo -u postgres /usr/bin/postgresql-setup --initdb || true
      sudo systemctl enable postgresql.service
      sudo systemctl start postgresql.service
   elif [ "${ID}" == "freebsd" ] ; then
      sudo pkg install -y postgresql17-server postgresql17-client postgresql-libpqxx openssl
      sudo sysrc postgresql_enable="YES"
      sudo /usr/local/etc/rc.d/postgresql initdb
      sudo service postgresql start
   else
      echo >&2 "ERROR: Unknown ID ${ID} in /etc/os-release. The installation script may need an update for supporting this system!"
      exit 1
   fi

   # ====== Basic configuration =============================================
   sudo ./name-in-etc-hosts "${server}.${DOMAIN}"
   sudo ./generate-test-certificates /etc/ssl "${server}.${DOMAIN}"

   # PostgreSQL needs separate certificate and key files.
   # The chain is in the CA file, it must not be in the certificate file!
   POSTGRESQL_CA_FILE="/etc/ssl/TestLevel1/certs/TestLevel1.crt"
   POSTGRESQL_CRL_FILE="/etc/ssl/TestGlobal.crl"
   POSTGRESQL_CRT_FILE="/etc/ssl/${server}.${DOMAIN}/${server}.${DOMAIN}.crt"
   POSTGRESQL_KEY_FILE="/etc/ssl/${server}.${DOMAIN}/${server}.${DOMAIN}.key"
   sudo chown postgres:postgres ${POSTGRESQL_KEY_FILE}

   POSTGRESDIR=""
   if [ -d /etc/postgresql ] ; then
      POSTGRESDIR="$(find /etc/postgresql/ -mindepth 2 -maxdepth 2 -type d -name main | sort -r | head -n1)"
   elif [ -d /var/db/postgres ] ; then
      POSTGRESDIR="$(find /var/db/postgres -mindepth 1 -maxdepth 1 -type d -name "data*" | sort -r | head -n1)"
   elif sudo -u postgres test -d /var/lib/pgsql/data ; then
      POSTGRESDIR="/var/lib/pgsql/data"
   fi
   if [ "${POSTGRESDIR}" == "" ] ; then
      echo >&2 "ERROR: Cannot find the PostgreSQL configuration directory!"
      exit 1
   fi
   echo "Using PostgreSQL settings in ${POSTGRESDIR}!"

   # Enable network access:
   sudo -u postgres sed \
      -e 's%^\(host[ \t]*all[ \t]*all[ \t]*\)127.0.0.1/32\([ \t]*\).*$%\10.0.0.0/0   \2scram-sha-256%g' \
      -e 's%^\(host[ \t]*all[ \t]*all[ \t]*\)::1/128\([ \t]*\).*$%\1::/0   \2scram-sha-256%g' \
      "${POSTGRESDIR}"/pg_hba.conf | sudo -u postgres tee "${POSTGRESDIR}"/pg_hba.conf.new >/dev/null && \
   sudo -u postgres diff --color "${POSTGRESDIR}"/pg_hba.conf.new "${POSTGRESDIR}"/pg_hba.conf || true && \
   sudo -u postgres mv -f "${POSTGRESDIR}"/pg_hba.conf.new "${POSTGRESDIR}"/pg_hba.conf

   sudo -u postgres sed -r \
      -e "s%#listen_addresses = 'localhost'%listen_addresses = '*'        %" \
      -e "s%^(#ssl|ssl) =.*%ssl = on%" \
      -e "s%^(#ssl|ssl)_ca_file = '.*'%ssl_ca_file = '${POSTGRESQL_CA_FILE}'%" \
      -e "s%^(#ssl|ssl)_crl_file = '.*'%ssl_crl_file = '${POSTGRESQL_CRL_FILE}'%" \
      -e "s%^(#ssl|ssl)_cert_file = '.*'%ssl_cert_file = '${POSTGRESQL_CRT_FILE}'%" \
      -e "s%^(#ssl|ssl)_key_file = '.*'%ssl_key_file = '${POSTGRESQL_KEY_FILE}'%" \
      -e "s%#ssl_ciphers = '.*'%ssl_ciphers = 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256'%" \
      -e "s%#ssl_prefer_server_ciphers = on%ssl_prefer_server_ciphers = on%" \
      -e "s%#ssl_min_protocol_version = 'TLSv1.2'%ssl_min_protocol_version = 'TLSv1.3'%" \
      "${POSTGRESDIR}"/postgresql.conf | sudo -u postgres tee "${POSTGRESDIR}"/postgresql.conf.new >/dev/null && \
   sudo -u postgres diff --color "${POSTGRESDIR}"/postgresql.conf.new "${POSTGRESDIR}"/postgresql.conf || true && \
   sudo -u postgres mv -f "${POSTGRESDIR}"/postgresql.conf.new "${POSTGRESDIR}"/postgresql.conf

   sudo service postgresql restart
   sleep 5

   # ====== Test configuration ==============================================
   ./test-tls-connection "${server}.${DOMAIN}:5432" /etc/ssl/TestLevel1/certs/TestLevel1.crt -- -starttls postgres


# ###### MongoDB ############################################################
elif [ "$1" == "mongodb" ] ; then
   # Documentation: https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-ubuntu/
   server="mongodb"

   # ====== Install MongoDB =================================================
   echo -e "\x1b[34m$(date +"%F %H:%M:%S"): Installing MongoDB ...\x1b[0m"

   MONGODB_VERSION="8.0"
   MONGODB_KEY="${MONGODB_VERSION}"

   if [ "${ID}" == "ubuntu" ] || [ "${ID}" == "debian" ] ; then
      for key in ${MONGODB_VERSION} dev ; do
         wget -qO - "https://www.mongodb.org/static/pgp/server-${key}.asc" | gpg --dearmor | sudo tee "/usr/share/keyrings/mongodb-${key}.gpg" >/dev/null
      done
      . /etc/os-release
      (
         if [ "${ID}" == "ubuntu" ] ; then
            codename="${UBUNTU_CODENAME}"
            if [[ ! "${codename}" =~ ^(precise|trusty|xenial|bionic|focal|jammy|noble)$ ]] ; then
               # MongoDB only provides repositories for LTS versions.
               # Assuming the latest LTS version here, for testing on non-LTS Ubuntu:
               codename="noble"
            fi
            cat <<EOF
deb [ signed-by=/usr/share/keyrings/mongodb-${MONGODB_KEY}.gpg ] https://repo.mongodb.org/apt/ubuntu ${codename}/mongodb-org/${MONGODB_VERSION} multiverse
deb [ signed-by=/usr/share/keyrings/mongodb-dev.gpg ] https://repo.mongodb.org/apt/ubuntu ${codename}/mongodb-org/development multiverse
EOF
         else
            cat <<EOF
deb [ signed-by=/usr/share/keyrings/mongodb-${MONGODB_KEY}.gpg ] https://repo.mongodb.org/apt/debian ${VERSION_CODENAME}/mongodb-org/${MONGODB_VERSION} main
deb [ signed-by=/usr/share/keyrings/mongodb-dev.gpg ] https://repo.mongodb.org/apt/debian ${VERSION_CODENAME}/mongodb-org/development main
EOF
         fi
      ) | sudo tee /etc/apt/sources.list.d/mongodb-org.list >/dev/null
      sudo apt update -qq
      sudo DEBIAN_FRONTEND=noninteractive apt install -y -o APT::Keep-Downloaded-Packages=true mongodb-org mongodb-mongosh openssl
      sudo systemctl enable mongod.service

   elif [ "${ID}" == "fedora" ] ; then
      (
         cat <<EOF
[mongodb-org]
name=MongoDB ${MONGODB_VERSION} Repository
baseurl=https://repo.mongodb.org/yum/redhat/9/mongodb-org/${MONGODB_VERSION}/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-${MONGODB_KEY}.asc
EOF
      ) | sudo tee /etc/yum.repos.d/mongodb-org.repo
#       (
#          cat <<EOF
# [mongodb-org-dev]
# name=MongoDB Development Repository
# baseurl=https://repo.mongodb.org/yum/redhat/9/mongodb-org/development/x86_64/
# gpgcheck=1
# enabled=1
# gpgkey=https://www.mongodb.org/static/pgp/server-dev.asc
# EOF
#       ) | sudo tee /etc/yum.repos.d/mongodb-org-dev.repo
      sudo dnf install -y mongodb-org mongodb-mongosh-shared-openssl3 openssl
      sudo systemctl enable mongod.service
      sudo systemctl start mongod.service

   elif [ "${ID}" == "freebsd" ] ; then
      sudo pkg install -y mongodb80 mongodb-tools mongosh mongo-c-driver
      sudo sysrc mongod_enable="YES"
      sudo service mongod start

   else
      echo >&2 "ERROR: Unknown ID ${ID} in /etc/os-release. The installation script may need an update for supporting this system!"
      exit 1
   fi

   # ====== Basic configuration =============================================
   sudo ./name-in-etc-hosts "${server}.${DOMAIN}"
   sudo ./generate-test-certificates /etc/ssl "${server}.${DOMAIN}"

   # MongoDB wants key + certificate + chain (in this order!) in the PEM file:
   MONGODB_CA_FILE="/etc/ssl/TestLevel1/certs/TestLevel1.crt"
   MONGODB_CRL_FILE="/etc/ssl/TestGlobal.crl"
   MONGODB_CRT_KEY_FILE="/etc/ssl/${server}.${DOMAIN}/${server}.${DOMAIN}.key+crt"
   sudo touch ${MONGODB_CRT_KEY_FILE}
   sudo chmod 600 ${MONGODB_CRT_KEY_FILE}
   sudo cat /etc/ssl/${server}.${DOMAIN}/${server}.${DOMAIN}.key \
            /etc/ssl/${server}.${DOMAIN}/${server}.${DOMAIN}.crt \
            ${MONGODB_CA_FILE} | \
      sudo tee ${MONGODB_CRT_KEY_FILE} >/dev/null
   if [ "${ID}" == "fedora" ] ; then
      sudo chown mongod:mongod ${MONGODB_CRT_KEY_FILE}
   else
      sudo chown mongodb:mongodb ${MONGODB_CRT_KEY_FILE}
   fi


   # WARNING: After installation, MongoDB gives everybody on 127.0.0.1
   #          full access!
   # The following settings enable authorization check, restricting access
   # to the configured user (root).
   if [ "${ID}" == "freebsd" ] ; then
      mongodConf=/usr/local/etc/mongodb.conf
   else
      mongodConf=/etc/mongod.conf
   fi

   cp ${mongodConf} /tmp/
   sudo sed \
      -e "s!#  engine:!  directoryPerDB: true!" \
      -e "s!#  wiredTiger:!  wiredTiger:\n    engineConfig:\n      directoryForIndexes: true!" \
      ${mongodConf} | sudo tee ${mongodConf}.new
   sudo service mongod stop
   sudo mv ${mongodConf}.new ${mongodConf}
   sudo service mongod start
   sleep 5

   # ====== Make sure there is a root user ==================================
   mongosh --quiet <<EOF
use admin
db.dropUser("root")
db.createUser({ user: "root",
                pwd: "${ROOT_PASSWORD}",
                roles: [
                   "userAdminAnyDatabase",
                   "dbAdminAnyDatabase",
                   "readWriteAnyDatabase",
                   "clusterAdmin"
                ] })
quit()
EOF

   # ====== Configure MongoDB ===============================================
   # The following settings enable authorization check, restricting access
   # to the configured user (root):
   cp ${mongodConf} /tmp/
   sudo sed \
      -e "s!^  bindIp: 127.0.0.1.*!  ipv6: true\n  bindIpAll: true\n  tls:\n    mode: requireTLS\n    CAFile: ${MONGODB_CA_FILE}\n    CRLFile: ${MONGODB_CRL_FILE}\n    certificateKeyFile: ${MONGODB_CRT_KEY_FILE}\n    disabledProtocols: TLS1_0,TLS1_1,TLS1_2\n    allowConnectionsWithoutCertificates: true!" \
      -e "s!#security:!security:\n  authorization: enabled!" \
      -e "s!#  engine:!  directoryPerDB: true!" \
      -e "s!#  wiredTiger:!  wiredTiger:\n    engineConfig:\n      directoryForIndexes: true!" \
      ${mongodConf} | sudo tee ${mongodConf}.new
   sudo service mongod stop
   sudo mv ${mongodConf}.new ${mongodConf}
   sudo service mongod start
   sleep 5

   # ====== Test configuration ==============================================
   ./test-tls-connection "${server}.${DOMAIN}:27017" /etc/ssl/TestLevel1/certs/TestLevel1.crt


# ###### Error ##############################################################
else
   echo >&2 "ERROR: Unsupported database: $1"
   exit 1
fi
