The vidhukant.com Cheatsheet

| ~4 minute read


I'm assuming the domain(s) already point to the server, and the software is up-to-date, and you've logged into the root user with SSH.

Securing the server

SSH setup

Add the following lines (or edit if they already exist) to /etc/ssh/sshd_config

PasswordAuthentication no
PermitRootLogin no

Now before restarting the SSH service, I'll create a new user which holds my public key for login. I'll assume the username "vidhukant".

useradd vidhukant
usermod -g sudo vidhukant

Then on the client machine I can run:

ssh-copy-id vidhukant.com # or vidhukant@vidhukant.com if the client username is different

On the server, run this (as root) and check the SSH connection in another terminal.

ssh vidhukant.com # or vidhukant@vidhukant.com if the client username is different

Everything should be set up now. Its now not possible to log in without an SSH key pair, and the root user won't allow to be logged in whatsoever. (su and sudo are our friends now)

Firewall setup

I'll be using UFW. You need to be root.

ufw default deny incoming
ufw default allow outgoing

ufw allow 22 # SSH
ufw allow 25 # SMTP
ufw allow 80 # HTTP

ufw allow 443 # HTTPS
ufw allow 587 # StartTLS
ufw allow 993 # IMAPS

ufw enable

Hosts and Hostname setup

Hostname

Edit /etc/hostname and populate it with the desired hostname (vidhukant-vps in my case)

Hosts file

This is what my /etc/hosts looks like

# /etc/hosts
127.0.0.1	localhost
127.0.0.1	localhost.localdomain	localhost
172.105.59.60	vidhukant.com	vidhukant-vps
172.105.59.60	mail.vidhukant.com	mail


# The following lines are desirable for IPv6 capable hosts
::1		localhost ip6-localhost ip6-loopback
ff02::1		ip6-allnodes
ff02::2		ip6-allrouters
2400:8904::f03c:93ff:feac:ca67 vidhukant.com	vidhukant-vps

Setting up all the services

Website setup

Required packages: nginx, python3-certbot-nginx and rsync

The way I configure a static website

Save and edit this config accordingly. I'm placing this as "vidhukant" at /etc/nginx/sites-available/vidhukant

# tor mirror (optional)
server {
    listen 127.0.0.1:80;
    root /var/www/vidhukant;
    index index.html;
    server_name <your_onion_url_here>.onion;
}

server {
    server_name vidhukant.com;
    root /var/www/vidhukant;
    index index.html;
    error_page 404 /404.html;

    gzip on;
    gzip_min_length 1100;
    gzip_buffers 4 32k;
    gzip_types text/plain application/x-javascript text/xml text/css;
    gzip_vary on;

    # make cgit work with go get (optional)
    if ($arg_go-get = 1) {
        return 200 '<meta name="go-import" content="$host$uri git $scheme://git.vidhukant.com$uri">\n';
    }

    location / {
        add_header Onion-Location http://<your_onion_url_here>.onion$request_uri; # optional
        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
        add_header Content-Security-Policy "frame-ancestors 'none'; default-src 'self'; style-src 'self' 'unsafe-inline'";
        add_header X-Frame-Options "SAMEORIGIN";
        add_header X-XSS-Protection "1; mode=block";
        add_header X-Content-Type-Options nosniff;
        add_header 'Referrer-Policy' 'same-origin';

        try_files $uri $uri/ =404;
    }

    # Media: images, icons, video, audio, HTC
    location ~* \.(?:jpg|jpeg|gif|png|ico|svg|webp|woff2)$ {
        add_header Onion-Location http://<your_onion_url_here>.onion$request_uri; # optional
        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
        add_header Content-Security-Policy "frame-ancestors 'none'; default-src 'self'";
        add_header X-Frame-Options "SAMEORIGIN";
        add_header X-XSS-Protection "1; mode=block";
        add_header X-Content-Type-Options nosniff;
        add_header 'Referrer-Policy' 'same-origin';
        add_header Cache-Control "max-age=7776000, public";

        expires 1M;
        access_log off;
    }

    # CSS and Javascript
    location ~* \.(?:css|js)$ {
        add_header Onion-Location http://<your_onion_url_here>.onion$request_uri; # optional
        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
        add_header Content-Security-Policy "frame-ancestors 'none'; default-src 'self'";
        add_header X-Frame-Options "SAMEORIGIN";
        add_header X-XSS-Protection "1; mode=block";
        add_header X-Content-Type-Options nosniff;
        add_header 'Referrer-Policy' 'same-origin';
        add_header Cache-Control "max-age=31556952, public";

        expires 1M;
        access_log off;
    }

    listen [::]:80;
    listen 80;
}

After that, I need to "enable" this virtual with ln -sf /etc/nginx/sites-available/vidhukant /etc/nginx/sites-enabled and use certbot --nginx to set up HTTPS.

After that, create a directory named "vidhukant" (or whatever you specified inside the virtual host) in /var/www and change its owner so it can be accessed with rsync.

mkdir /var/www/vidhukant
chown vidhukant:vidhukant /var/www/vidhukant

On the client machine, assuming the HTML is stored inside "public", run this command:

rsync -rtvzP public/ vidhukant.com:/var/www/vidhukant # or vidhukant@vidhukant.com if the client username is different

With this, vidhukant.com is online! Go ahead and check!

MariaDB Setup

Run the following as root:

apt install mariadb-server
mysql_secure_installation
mariadb

Now inside the MariaDB shell:

CREATE USER 'vidhukant'@'localhost' IDENTIFIED BY 'password123';
CREATE DATABASE some_db;
GRANT ALL PRIVILEGES ON some_db.* TO 'vidhukant'@'localhost';
FLUSH PRIVILEGES;
quit

This is where I'd create a new user and a database for Postfix and Dovecot.

At this point, the MariaDB service should be enabled by default. Do a systemctl status mariadb to check.

E-mail setup

Required packages: postfix, postfix-mysql, dovecot-core, dovecot-lmtpd, dovecot-imapd, dovecot-mysql, opendkim, opendkim-tools, spamassassin, spamc

Note: During Postfix installation, select Internet Site -> vidhukant.com

I set up Postfix, Dovecot, etc with this guide. And I'm just gonna restore the config files mostly.

Setting up the Database and adding users

Create a new user and database for email purposes, and create these tables:

Creating the tables
CREATE TABLE `virtual_domains` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(50) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `virtual_users` (
  `id` int(11) NOT NULL auto_increment,
  `domain_id` int(11) NOT NULL,
  `password` varchar(106) NOT NULL,
  `email` varchar(100) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `email` (`email`),
  FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `virtual_aliases` (
  `id` int(11) NOT NULL auto_increment,
  `domain_id` int(11) NOT NULL,
  `source` varchar(100) NOT NULL,
  `destination` varchar(100) NOT NULL,
  PRIMARY KEY (`id`),
  FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Populating data
Adding domains

Add an entry into virtual_domains table for each domain that receives email.

INSERT INTO virtual_domains (name) VALUES ('vidhukant.com');
Adding users

Generate the password hash with:

doveadm pw -s SHA512-CRYPT | sed 's/{SHA512-CRYPT}//'

And use this SQL Command:

INSERT INTO virtual_users (domain_id, password , email) VALUES ('1', 'password_hash_here', 'vidhukant@vidhukant.com');
Adding aliases
INSERT INTO virtual_aliases (domain_id, source, destination) VALUES ('1', 'vidhu@vidhukant.com', 'vidhukant@vidhukant.com');

Steps to get the Postfix and Dovecot config files up and running:

List of config files I'm gonna copy over:

Getting an SSL certificate
certbot certonly -d mail.vidhukant.com 
If the path to the SSL certificate has changed, then

A add/edit this in /etc/postfix/main.cf:

smtpd_tls_cert_file = /etc/letsencrypt/live/mail.vidhukant.com/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/mail.vidhukant.com/privkey.pem

And these lines in /etc/dovecot/conf.d/10-ssl.conf:

ssl_cert = </etc/letsencrypt/live/mail.vidhukant.com/fullchain.pem
ssl_key = </etc/letsencrypt/live/mail.vidhukant.com/privkey.pem
Hooking these up with MariaDB

If the MariaDB credentials have changed,

In /etc/postfix edit these files accordingly (self explanatory):

Open /etc/dovecot/dovecot-sql.conf.ext and look for and edit this line:

connect = host=127.0.0.1 dbname= user= password=
Wrapping Up

Add a vmail group and a directory to store the messages.

mkdir -p /var/mail/vhosts/vidhukant.com
groupadd -g 5000 vmail
useradd -g vmail -u 5000 vmail -d /var/mail
chown -R vmail:vmail /var/mail

Set appropriate permissions for the config files:

chmod -R o-rwx /etc/postfix

chown -R vmail:dovecot /etc/dovecot
chmod -R o-rwx /etc/dovecot

Restart the services:

systemctl restart postfix
systemctl restart dovecot

Setting up OpenDKIM

I'll just follow my own guide here.

Hiring a hitman for spammers (Spamassassin)

Adding the user:

Just keep pressing enter since the details it asks for don't really matter much.

adduser spamd --disabled-login

Add/edit these lines in /etc/default/spamassassin:

HOMEDIR="/home/spamd/"
OPTIONS="--create-prefs --max-children 5 --username spamd --helper-home-dir ${HOMEDIR} -s ${HOMEDIR}spamd.log"
PIDFILE="${HOMEDIR}spamd.pid"
CRON=1

Start and enable Spamassassin: (and restart postfix but I'd just reboot my server at this point)

systemctl enable --now spamassassin

This is post 17 of #100DaysToOffload