VPS Setup Redacted (smk)

Assuming you got root password from some vnc console in freshly installed VPS. Let’s setup.

Setup Locale

# create locale file
echo "LC_ALL=C" > /etc/profile.d/lc_all.sh
exec bash

Upgrade System

apt update
apt upgrade
# to upgrade the distro
apt-get dist-upgrade

Install Basic Tools

apt-get install sudo stow vim net-tools

Add user & grant sudo

user=smk
adduser $user
# grant sudo
usermod -a $user -G sudo
# add basic authorized keys
cd /home/$user
mkdir -p .ssh
chmod 700 .ssh
echo "YOUR_KEY" > .ssh/authorized_keys
chown -R $user:$user /home/$user

# try now ssh thru this user

SUDO without password

Create a file named 90-cloud-init-users.

touch 90-cloud-init-users
chmod 440 90-cloud-init-users

Put following text in it.

root ALL=(ALL) NOPASSWD:ALL
smk ALL=(ALL) NOPASSWD:ALL

now copy this file to /etc/sudoers.d

Warning : This step if not done properly can brick the box!

Also do not edit the file once its in directory. This may also brick the box. Safest way is to edit is somewhere else and then copy result into the directory in question

Harden Security

# disable root login with password
# vi /etc/ssh/sshd_config

Port 23415
PasswordAuthentication no
service sshd restart

This sets up our box in safe state to use. Disable RootLogin as well if you want extra security.

Setting up fail2ban

sudo apt install fail2ban sendmail-bin sendmail
# you can edit this .local because it overrides the default conf
cp /etc/fail2ban/fail2ban.conf /etc/fail2ban/fail2ban.local
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

# to view your chain traffic drop configuration
iptables -L f2b-sshd -v -n --line-numbers
# to delete a rule applied to an IP address
iptables -D chain rulenum
# some jail settings all things are pretty well documented and we care about these
# https://www.linode.com/docs/guides/using-fail2ban-to-secure-your-server-a-tutorial/
ignoreip = 127.0.0.1/8 123.45.67.89
bantime  = 600
findtime = 600
maxretry = 3

# to check status
fail2ban-client status

Setup DNS

TODO : Add domain setup steps here before

NOTE : if your domain hostname is set but domain doesn’t point the server then you will always get an error regarding resolv.conf referring to host not found. To fix make sure hostname domain actually points the server you are setting up a dns.

# replace the default DNS generated by solus VM in Racknerd
echo "nameserver 127.0.0.1" > /tmp/resolv.conf
cp /tmp/resolv.conf /etc/resolv.conf
# lock the file, you can't edit it unless you do chattr -i
chattr +i /etc/resolv.conf

# note backup contens of resolv.conf
nameserver 8.8.8.8
nameserver 8.8.4.4
# install dnsmasq
sudo apt-get install dnsmasq
# put the following text in /etc/dnsmasq.conf
echo "all-servers
cache-size=10000
log-queries
interface=lo
no-resolv

address=/bacon.eggs/2.3.4.5

server=208.67.220.220           # OpenDNS
server=8.8.8.8                  # Google
server=1.1.1.1" > /tmp/dnsmasq.conf

cp /tmp/dnsmasq.conf /etc/dnsmasq.conf
sudo service dnsmasq restart

dnsmasq is a caching DNS server. To flush the cache, restart the dnsmasq service.

# check if this is resolved correctly
nslookup bacon.eggs

Setup Hostname

DOMAIN=somedomain.com
hostname $DOMAIN
echo     $DOMAIN > /etc/hostname

Generate dhparam.pem

Execute the following command and wait for it to finish. For a high-end box, use 4096 here instead of 2048. Don’t try to use 4096 on a low-end or medium-level box.

openssl dhparam -out dhparam.pem 2048 >& /tmp/dhparam.log

Subsequent to completion, check the output and confirm that no errors occurred.

cat /tmp/dhparam.log

Then :

mkdir -p       /etc/ssl/certs/
rm    -fr      /etc/ssl/certs/dhparam.pem
mv dhparam.pem /etc/ssl/certs/

This is required for setting up ssl certs for websites.

Setup Ngnix

# install nginx
sudo apt-get install nginx
# visit your domain/ip that points to box and you should be greeted with nginx page

More on this Page : Pre-requiste

See Also :


Setting up source paths

#!/bin/bash -e
mkdir -p /opt/minetest/mtbin
mkdir -p /opt/smkbin

Create a text file /etc/profile.d/smkbin.sh containing :

NOTE : Don’t use #!/bin/bash -e here.

for dir in \
    /opt/minetest/mtbin \
    /opt/minetest/smkbin \
    /opt/smkbin
do
    if [ -d $dir ]; then PATH=$dir:$PATH; fi
done

This file needs to be readable, but it doesn’t need to be executable.

Edit secure_path setting in etc/sudoers to prepend directories from preceding scripts which exists. Note use visudo to edit the file.
secure_path="/opt/minebest/mtbin:/opt/smkbin:/usr/local/sbin:..."

Log-out and back in. You can now add useful scripts and tools to the two new directories.

Application

Installing golang

#!/bin/bash -e
mkdir -p /opt/
cd       /opt/
rm -fr go golang
GOBALL=go1.21.6.linux-amd64.tar.gz

wget -c https://dl.google.com/go/$GOBALL
tar zxf $GOBALL
rm      $GOBALL
mv     go golang

mkdir -p                   /opt/smkbin/
ln -nsf /opt/golang/bin/go /opt/smkbin/

Installing lego

apt-get install binutils make git
#!/bin/bash -e
export GOPATH=/tmp/gobuild
GOPROG=$GOPATH/lego/dist/lego

rm -fr     $GOPATH
mkdir -p   $GOPATH
cd         $GOPATH

git clone https://github.com/go-acme/lego.git
cd lego
make build

strip      $GOPROG
# upx --lzma $GOPROG # for some reason upx is not packaged for bookworm
mv         $GOPROG /opt/smkbin/
rm -fr     $GOPATH

Set up node, npm and yarn

NOTE: Installs python3, gcc, g++ as well

#!/bin/bash -e
apt-get install curl software-properties-common -y
curl -sL https://deb.nodesource.com/setup_21.x | sudo bash -
apt-get update
apt-get install gcc g++ make nodejs -y
LINK="https://dl.yarnpkg.com/debian"

curl -sL $LINK/pubkey.gpg | sudo apt-key add -
echo "deb $LINK/ stable main" | \
    sudo tee /etc/apt/sources.list.d/yarn.list

apt-get update
apt-get -y install yarn

Installing Docker on VPS

# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce
# managing docker as non root user
$USER=smk
sudo groupadd docker
sudo usermod -aG docker $USER
# refresh group
newgrp docker
# test docker run hello-world

# NOTE : fix .docker dir due to previous root runs if you get config errors
sudo chown "$USER":"$USER" /home/"$USER"/.docker -R
sudo chmod g+rwx "$HOME/.docker" -R

Installing PHP

apt install php-common libapache2-mod-php php-mysql php-curl

NOTES: Manually building binary : link

Add more PHP feature

NOTE : If nginx is not booting up due to port bind already in use, most likely its apache2 running disable it.

sudo systemctl disable apache2
sudo systemctl stop apache2
sudo service nginx start
#!/bin/bash -e
apt-get install \
    libmcrypt-dev  \
    php-apcu       php-bcmath     php-cli        php-common     \
    php-curl       php-dev        php-fpm        php-gd         \
    php-imagick    php-intl       php-json       php-ldap       \
    php-mbstring   php-mysql      php-pear       php-readline   \
    php-redis        php-soap       php-sqlite3    \
    php-xml        php-xmlrpc     php-zip
# note : php-smbclient is not working
pecl channel-update    pecl.php.net
pecl install channel://pecl.php.net/mcrypt-1.0.2

Installing ntf

# refer https://github.com/hrntknr/ntf
sudo curl -L https://github.com/hrntknr/ntf/releases/download/v1.0.1/ntf-x86_64-unknown-linux-gnu -o /usr/local/bin/ntf
sudo chmod +x /usr/local/bin/ntf
echo -e 'backends: ["pushover"]\npushover: {"user_key": "t0k3n"}' > ~/.ntf.yml
ntf send test
ntf send -t BOX_SAYS_HELLO hello from box

Alert on Reboot using ntf (systemd: don’t hate me)

touch /lib/systemd/system/reboot_ntf.service
echo "[Unit]
Description=ntf reboot script
After=network-online.target
Wants=network-online.target
[Service]
ExecStart=/bin/bash -c 'sleep 2 && /usr/local/bin/ntf send -t REBOOT minetest.in just rebooted! > /var/log/reboot_ntf.log 2>&1'
Type=oneshot
RemainAfterExit=true
Environment=HOME=/root/
[Install]
WantedBy=multi-user.target" > /lib/systemd/system/reboot_ntf.service

sudo systemctl enable reboot_ntf.service
sudo systemctl restart reboot_ntf.service
sudo chmod 644 /lib/systemd/system/reboot_ntf.service

# To create a generic service
# systemctl enable reboot_ntf.service

Create a Backup (rclone-pcloud Setup)

Installing rclone

sudo apt-get install unzip -y
curl -O https://downloads.rclone.org/rclone-current-linux-amd64.zip
unzip rclone-current-linux-amd64.zip
cd rclone-*-linux-amd64

# copy the binary
sudo cp rclone /opt/smkbin
sudo chown root:root /opt/smkbin/rclone
sudo chmod 755 /opt/smkbin/rclone

# install man page
sudo mkdir -p /usr/local/share/man/man1
sudo cp rclone.1 /usr/local/share/man/man1/
sudo mandb

# configure rclone
rclone config

# remote config for headless server : https://rclone.org/remote_setup/
# NOTE : choose advance config because pcloud has two regions
# so pass all default except region select it correctly

# testing if rclone works for remote name : pcloud
rclone lsd pcloud:

Backup Script

#!/bin/bash
# /*
# * --------------------------------------------------------------------
# * @file    pcloud-bkp
# * @brief   A simple backup util for pcloud
# * @author  smk ([email protected])
# * @version 20221129
# * @license BSD3
# * @bugs    No known bugs
# * --------------------------------------------------------------------
# */

set -e
set -o pipefail
REMOTE_NAME="pcloud:vps_bkp/minetest.in"
RCLONE_BIN="/opt/smkbin/rclone"

WHOAMI=$(whoami)
if [ "@$WHOAMI" != "@root" ]; then
    echo "Error: Must be run as root"
    exit 1
fi

# Check if required binaries exist
check_binaries() {
    for bin in "$@"; do
        if ! command -v "$bin" &> /dev/null; then
            echo "Error: $bin not found. Please install it."
            exit 1
        fi
    done
}

check_binaries tar pv "$RCLONE_BIN"
backup_dirs=("etc" "home" "root" "var/www" "opt")
#backup_dirs=("etc" "home/smk" "root" "var/www" "usr/local/bin" "usr/local/sbin" "opt")
bkp_dir="/tmp/bkp_$(date +"%Y%m%d")"
mkdir -p "${bkp_dir}"
pushd /
for i in "${!backup_dirs[@]}"; do
    # bkp_name=$(basename "${backup_dirs[i]}")
    echo "$bkp_name"
    echo "Starting tar of ${backup_dirs[$i]} ..."
    tar czf "${bkp_dir}/${backup_dirs[$i]//\//_}.tgz" --directory="/" "${backup_dirs[$i]}"
    # tar czf - "${backup_dirs[$i]//_//}" -P | pv -s $(du -sb "/${backup_dirs[$i]//_//}" | awk '{print $1}') | gzip > "${bkp_dir}/${backup_dirs[$i]}.tgz"
    echo "Done tar of ${backup_dirs[$i]}"
done
popd

echo "Created backups at : ${bkp_dir}"
echo "----------------BKP------------------"
du -sh "${bkp_dir}"/* | sort -h

# Uncomment the following lines if you are ready to use rclone
echo "--------------- RCLONE ---------------"
bkp_name=$(basename ${bkp_dir})
"${RCLONE_BIN}" copy "${bkp_dir}" "${REMOTE_NAME}/${bkp_name}" -P -v

echo "--------------- Cleanup --------------"
rm -rf ${bkp_dir}

Create a cron job

# you could wrap above script in ntfy and then setup a cron job
# open crontab file
crontab -e

# crontab for monthly execution
#0 0 1 * * /opt/smkbin/pcloud-bkp or
0 0 1 * * ntf -t MONTHLY_BACKUP done /opt/smkbin/pcloud-bkp

Installing portainer & dockge

# using any one is fine
sudo mkdir -p /home/docker/dockge
sudo mkdir -p /home/docker/portainer

Create compose.yml in the portainer directory

# be careful when setting up proxy_pass as it decides the uri_scheme traffic will be served on
# use https://127.0.0.1:6789/ if that is mapped inside container to 9443

version: "3.3"
services:
  portainer-ce:
    ports:
      - 8000:8000
      # - 6789:9000 : Note use this for serving portainer on HTTP
      - 9443:9443   # This is for HTTPS port
    container_name: portainer
    restart: always
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - portainer_data:/data
    image: portainer/portainer-ce:latest
volumes:
  portainer_data: {}
networks: {}

Setting up dockge

version: "3.8"
services:
  dockge:
    image: louislam/dockge:1
    restart: unless-stopped
    ports:
      - 5678:5001
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./data:/app/data
      - /home/docker/dockge:/home/docker/dockge
    environment:
      # Tell Dockge where to find the stacks
      - DOCKGE_STACKS_DIR=/home/docker/dockge

Installing Postgresql & pg-admin

sudo apt-get update && sudo apt-get install postgresql postgresql-contrib

# note by default postgres is not exposed to anyone outside localhost
# and it should be left this way, don't use connection from outside the box
# verify above by accessing /etc/postgresql/x.x/main/pg_hba.conf
local all postgres peer local all all peer host all all 127.0.0.1/32 md5 host all all ::1/128 md5

# neither root nor smk can access the psql (its good that way)
sudo su - postgres
psql
# create user for yourself
CREATE USER smk WITH PASSWORD 'your_password';
CREATE DATABSE test_db;
GRANT CONNECT ON DATABASE test_db TO smk;
GRANT USAGE ON SCHEMA public TO smk;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO smk;
# we granted everything to smk, but be careful to grant to other users
\q # to quit

# as user smk try to login
psql -h localhost -U smk -d test_db
# yaml for db
version: '3.8'

services:
  postgres:
    image: postgres:latest
    container_name: example-database
    environment:
      POSTGRES_DB: test_db
      POSTGRES_USER: smk
      POSTGRES_PASSWORD: example-database-password
    ports:
      - "5433:5432"
    networks:
      - pg-network
    volumes:
      - pg-data:/var/lib/postgresql/data
      - ./initdb/:/docker-entrypoint-initdb.d/

  pgadmin:
    image: dpage/pgadmin4:latest
    container_name: pg-admin
    environment:
      PGADMIN_DEFAULT_EMAIL: smk@minetest.in
      PGADMIN_DEFAULT_PASSWORD: admin_password_probably
    ports:
      - "5678:80"
    networks:
      - pg-network

networks:
  pg-network:

volumes:
  pg-data:

# create a script initdb/init-user-db.sh
#!/bin/bash
set -e

psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
  CREATE USER "$POSTGRES_USER" WITH PASSWORD '$POSTGRES_PASSWORD';
  CREATE DATABASE "$POSTGRES_DB" WITH OWNER "$POSTGRES_USER";
  GRANT ALL PRIVILEGES ON DATABASE "$POSTGRES_DB" TO "$POSTGRES_USER";
EOSQL

Installing JupyterHub

docker run -d -p 5678:8000 --name jupyterhub jupyterhub/jupyterhub jupyterhub
# enter in container
docker exec -it jupyterhub bash
# then :
adduser test

Setting up ZNC

Setting up ngircd