Ubuntu 16 server installation

Table of contents

Updates

apt update && apt upgrade -y && apt autoremove -y && apt autoclean -y
cp /etc/skel/.bashrc ~/
sudo apt install --reinstall bash-completion

Secure shared memory (optional)

nano /etc/fstab add line:

tmpfs /run/shm tmpfs defaults,noexec,nosuid 0 0

Harden the networking layer

nano /etc/sysctl.conf add

# added by miamibc
# from https://www.techrepublic.com/article/how-to-harden-ubuntu-server-16-04-security-in-five-steps/

# IP Spoofing protection
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# Ignore ICMP broadcast requests
net.ipv4.icmp_echo_ignore_broadcasts = 1

# Disable source packet routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv6.conf.default.accept_source_route = 0

# Ignore send redirects
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0

# Block SYN attacks
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 5

# Log Martians
net.ipv4.conf.all.log_martians = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1

# Ignore ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0

# Ignore Directed pings
net.ipv4.icmp_echo_ignore_all = 1

Prevent IP spoofing

nano /etc/host.conf change to:

order bind,hosts
nospoof on

Change locale and timezone

locale-gen en_US.UTF-8
update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
dpkg-reconfigure tzdata

reboot

SSH key only

nano /etc/ssh/sshd_config uncomment/change

RSAAuthentication yes
PubkeyAuthentication yes
ChallengeResponseAuthentication no
PasswordAuthentication no
UsePAM no

SSH GeoIP restriction

Install Geoip and check it works

apt-get install geoip-bin geoip-database
geoiplookup 8.8.8.8

nano /usr/local/bin/sshfilter.sh add script

#!/bin/bash

# UPPERCASE space-separated country codes to ACCEPT
ALLOW_COUNTRIES="EE"

if [ $# -ne 1 ]; then
echo "Usage: `basename $0` <ip>" 1>&2
exit 0 # return true in case of config issue
fi

COUNTRY=`/usr/bin/geoiplookup $1 | awk -F ": " '{ print $2 }' | awk -F "," '{ print $1 }' | head -n 1`

[[ $COUNTRY = "IP Address not found" || $ALLOW_COUNTRIES =~ $COUNTRY ]] && RESPONSE="ALLOW" || RESPONSE="DENY"

logger "$RESPONSE sshd connection from $1 ($COUNTRY)"

if [ $RESPONSE = "ALLOW" ]
then
exit 0
else
exit 1
fi
chmod 775 /usr/local/bin/sshfilter.sh

nano /etc/hosts.deny add the line

sshd: ALL

nano /etc/hosts.allow add the line

sshd: ALL: aclexec /usr/local/bin/sshfilter.sh %a

nano /usr/local/bin/geoip-update.sh add script to update Geoip database

#!/bin/bash

cd /tmp
wget -q "https://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz"
if [ -f GeoIP.dat.gz ] 
then
    gzip -d GeoIP.dat.gz
    rm -f /usr/share/GeoIP/GeoIP.dat
    mv -f GeoIP.dat /usr/share/GeoIP/GeoIP.dat
else
  echo "The GeoIP library could not be downloaded and updated"
fi

put it to the cron

chmod 775 /usr/local/bin/geoip-update.sh
crontab -e

add line to update every week

10 10 * * 1 /usr/local/bin/geoip-update.sh

service ssh restart

(test ssh in another window)

Fail2ban

https://help.ubuntu.com/community/Fail2banhttps://www.digitalocean.com/community/tutorials/how-to-protect-an-apache-server-with-fail2ban-on-ubuntu-14-04

sshguard

apt install sshguard
nano /etc/sshguard/whitelist
iptables -N sshguard
# protect all traffic
iptables -A INPUT -j sshguard
# or only some ports
iptables -A INPUT -m multiport -p tcp --destination-ports 21,22,110,143 -j sshguard
service sshguard restart

https://tutorialinux.com/securing-ssh-with-sshguard/

Nginx

sudo add-apt-repository ppa:nginx/stable
apt update
apt install nginx

Apache

LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/apache2
apt update
apt install apache2
a2enmod ssl
a2enmod http2
a2enmod rewrite
nano /etc/apache2/apache2.conf , add: Protocols h2 h2c http/1.1
service apache2 restart

(change mpm model for HTTP2 if necessary - disable the “prefork” MPM and enable “event”:

a2dismod mpm_prefork
a2enmod mpm_event
service apache2 restart

Config:

nano /etc/apache2/sites-available/001-site.ee.conf add

<VirtualHost *:80>

ServerName site.ee
ServerAlias www.site.ee

ServerAdmin miami@blackcrystal.net
DocumentRoot /var/www/site.ee/live/web

ErrorLog ${APACHE_LOG_DIR}/site.ee.error.log
CustomLog ${APACHE_LOG_DIR}/site.ee.access.log combined

<Directory /var/www/site.ee/live>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted

#AuthType Basic
#AuthName "Authentication Required"
#AuthUserFile "/var/www/site.ee/.htpasswd"
#Require valid-user
</Directory>

</VirtualHost>

Enable site and restart service

a2ensite 001-site-ee.conf
service apache2 restart

Create htpasswd file

printf "`whoami`:`openssl passwd -apr1`\n" >> /var/www/site.ee/.htpasswd

PHP

LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php
apt update
apt install php7.0 libapache2-mod-php7.0
# php 7.0
apt install php7.0 php7.0-bcmath php7.0-curl php7.0-fpm php7.0-json php7.0-gd php7.0-mbstring php7.0-mcrypt php7.0-mysql php7.0-soap php7.0-ssh2 php7.0-sqlite3 php7.0-tidy php7.0-xml php7.0-zip
# php 7.4
apt install php7.4 php7.4-bcmath php7.4-curl php7.4-fpm php7.4-json php7.4-gd php7.4-mbstring php7.4-mcrypt php7.4-mysql php7.4-soap php7.4-ssh2 php7.4-sqlite3 php7.4-tidy php7.4-xml php7.4-zip
# php 8.0
apt install php8.0 php8.0-bcmath php8.0-curl php8.0-fpm php8.0-gd php8.0-mbstring php8.0-mcrypt php8.0-mysql php8.0-soap php8.0-ssh2 php8.0-sqlite3 php8.0-tidy php8.0-xml php8.0-zip
# php 8.4
apt install php8.4 php8.4-bcmath php8.4-curl php8.4-fpm php8.4-gd php8.4-mbstring php8.4-mcrypt php8.4-mysql php8.4-soap php8.4-ssh2 php8.4-sqlite3 php8.4-tidy php8.4-xml php8.4-zip
# php 8.5
apt install php8.5 php8.5-bcmath php8.5-curl php8.5-fpm php8.5-gd php8.5-mbstring php8.5-mcrypt php8.5-mysql php8.5-soap php8.5-ssh2 php8.5-sqlite3 php8.5-tidy php8.5-xml php8.5-zip

(Switch to PHP-FPM, if necessary)

apt install php7.0-fpm
a2enmod proxy_fcgi setenvif
a2enconf php7.0-fpm
a2dismod php7.0
service apache2 restart

Or add to nginx configuration

location ~ \.php$ {
	include snippets/fastcgi-php.conf;
	fastcgi_pass unix:/run/php/php7.0-fpm.sock;
}

NodeJS

Then for the Latest release, add this PPA..

curl -sL https://deb.nodesource.com/setup_10.x | sudo bash -
apt install nodejs

To install the LTS release, use this PPA

curl -sL https://deb.nodesource.com/setup_8.x | sudo bash -
apt install nodejs

Install PM2:

apt-key adv --keyserver keyserver.ubuntu.com --recv D1EA2D4C
echo "deb http://apt.pm2.io/ubuntu stable main" | sudo tee /etc/apt/sources.list.d/pm2.list
apt update && apt install pm2

SSL

Acme.sh

# install acme.sh
wget -O -  https://get.acme.sh | sh -s email=miami@blackcrystal.net

# issue certificate for nginx
acme.sh --issue -d example.com --nginx --force

# install certificate
acme.sh --install-cert -d example.com \
	--cert-file /etc/nginx/ssl/example.com/cert.pem \
	--key-file /etc/nginx/ssl/example.com/privkey.pem \
	--fullchain-file /etc/nginx/ssl/example.com/fullchain.pem \
	--reloadcmd "systemctl restart nginx.service"

# run cron tasks
acme.sh --cron --home "/root/.acme.sh"

Certbot

Apache:

apt install python-certbot-apache
certbot --apache -d site.ee -d www.site.ee

Nginx

apt install python3-certbot-nginx
certbot --nginx
( crontab -l ; echo '30 3 * * 2 /usr/bin/certbot renew >> /var/log/renew-ssl.log --post-hook "systemctl reload nginx"' ) | crontab -

Wildcard certificate

certbot certonly -d domain.ru -d *.domain.ru --server https://acme-v02.api.letsencrypt.org/directory --manual

SSL: self-signed certificate

mkdir /etc/ssl/selfsigned
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/selfsigned/key.pem -out /etc/ssl/selfsigned/cert.pem

CA=your ip, then add to apache virtualhost

SSLEngine on
SSLCertificateFile      /etc/ssl/selfsigned/cert.pem
SSLCertificateKeyFile /etc/ssl/selfsigned/key.pem
Protocols h2 h2c http/1.1

Mysql

apt install mysql-server php-mysql

nano /etc/mysql/my.cnf add

[mysqld]

# added by miami
bind-address = 127.0.0.1
wait_timeout=20
max-connections = 100
key_buffer_size = 64M
# innodb_buffer_pool_size 50% of memory
innodb_buffer_pool_size = 500M
innodb_log_file_size = 64M
innodb_flush_method = O_DIRECT
innodb_flush_log_at_trx_commit = 2
# query_cache_type = 1
# query_cache_size = 32M
# query_cache_limit=2M

# 8+(connections/100)
thread_cache_size = 16

# slow_query_log=1
# slow_query_log_file=/var/log/mysql/slow.log
# long_query_time=1

#sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
collation-server = utf8_unicode_ci
init-connect='SET NAMES utf8'
character-set-server = utf8

# for mysql8
default-authentication-plugin=mysql_native_password
# expire_logs_days=1
# - or -
# skip-log-bin

# [client]
# user=root
# password=root

Secure installation: /usr/bin/mysql_secure_installation

Add user

CREATE USER 'newuser'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON * . * TO 'newuser'@'localhost';
FLUSH PRIVILEGES;

Add mysql account to user profile nano .my.cnf

[client]
user=mysqluser
password=mysqlpass

Add user

adduser miami
usermod -aG sudo miami
passwd -d miami

FTP server

apt install proftpd-basic

mkdir /etc/proftpd/ssl
sudo openssl req -x509 -nodes -newkey rsa:2048 -keyout /etc/proftpd/ssl/key.pem -out /etc/proftpd/ssl/cert.pem -days 365
chmod 600 /etc/proftpd/ssl/*

nano /etc/proftpd/proftpd.conf

# added by miami
UseIPv6                         off
ServerType                      standalone
DefaultRoot ~
ServerIdent on "Show what you can. Learn what you don't."
RequireValidShell off
AuthUserFile /etc/proftpd/ftpd.passwd
TLSRSACertificateFile /etc/proftpd/ssl/cert.pem
TLSRSACertificateKeyFile /etc/proftpd/ssl/key.pem
TLSEngine on
TLSLog /var/log/proftpd/tls.log
TLSProtocol SSLv23
TLSRequired on
TLSOptions NoCertRequest EnableDiags NoSessionReuseRequired
TLSVerifyClient off
# end

# to add user
ftpasswd --passwd --file=/etc/proftpd/ftpd.passwd --name=username --uid=33 --gid=33 --home=/var/www/username --shell=/bin/false

# to change password
ftpasswd --passwd --file=/etc/proftpd/ftpd.passwd --name=username --change-password

Mail

apt install ssmtp

nano /etc/ssmtp/ssmtp

# Config file for sSMTP sendmail
root=postmaster
# The place where the mail goes. The actual machine name is required no
# MX records are consulted. Commonly mailhosts are named mail.domain.com
mailhub=smtp.yandex.ru:587
# Where will the mail seem to come from?
rewriteDomain=
# The full hostname
hostname=delichev.com
UseTLS=YES
UseSTARTTLS=YES
AuthUser=support3@delichev.com
AuthPass=abcdefghijklmnop
# Are users allowed to set their own From: address?
# YES - Allow the user to specify their own From: address
# NO - Use the system generated From: address
FromLineOverride=YES

nano /etc/ssmtp/revaliases

root:support3@delichev.com:smtp.yandex.ru:587
www-data:support3@delichev.com:smtp.yandex.ru:587
postmaster:support3@delichev.com:smtp.yandex.ru:587
miami:support3@delichev.com:smtp.yandex.ru:587

Mail

Postfix + Dovecot + Roundcube Exim + Dovecot + Roundcube Exim + Dovecot + Roundcube Postfix+Dovecot+Roundloop Build Your Own Email Server on Ubuntu: Basic Postfix Setup Iredmail simple mail server Dovecot faster Mailpile + Nginx Nylas mail

Control panels

Vesta

export VESTA=/usr/local/vesta
export PATH=$PATH:/usr/local/vesta/bin

Adjenti CP Froxlor EasyEngine - to create many wordpress servers with docker and nginx easily

Tunning

Оптимизация сервера на Ubuntu Оптимальная настройка Nginx

Monitoring

Munin

Other recipes

To list domain names, this server works with:

grep -R server_name /etc/nginx/sites-enabled/
grep -R 'ServerName\|ServerAlias' /etc/apache2/sites-ennabled/