Full web server setup with Debian 12 (Bookworm)
23 Jul 2024 Matteo Mattei linux server mariadb debian nginx php iptables postfix ssl letsencrypt sftpSetup bash and update the system
apt update
apt dist-upgrade
Configure hostname correctly
Make sure to have the following two lines (with the same format) at the top of your /etc/hosts file
127.0.0.1 localhost.localdomain localhost
xxx.xxx.xxx.xxx web1.myserver.com web1
Note: xxx.xxx.xxx.xxx is the public IP address assigned to your server.
Add you full hostname (for example web1.myserver.com
) to /etc/hostname
and apply it:
hostname -F /etc/hostname
Then to validate that the FQDN is valid and correctly applied type this:
hostname -f
// the answer should be web1.myserver.com
Install all needed packages
apt install wget vim git acl screen rsync net-tools pwgen mariadb-server mariadb-client nginx iptables shorewall php php-cli php-curl php-dev php-gd php-imagick php-imap php-memcache php-pspell php-tidy php-xmlrpc php-pear php-fpm php-mbstring php-mysql certbot phpmyadmin python3-pip python3-tld postfix ntp ca-certificates bsd-mailx tree
Postfix:
- Select Internet Site
- System mail name: (insert here the FQDN, for example web1.myserver.com)
Web server to reconfigure automatically:
- Do not select anything and continue
Configure database for phpmyadmin with dbconfig-common?
- Select Yes
MySQL application password for phpmyadmin:
- Leave blank and press Ok
Setup Nginx
Stop Nginx web server:
/etc/init.d/nginx stop
Backup Nginx configuration:
cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.backup
Add the following lines in /etc/nginx/nginx.conf after tcp_nopush on;:
tcp_nodelay on;
keepalive_timeout 65;
And the following lines after gizp on;:
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
# max body size
client_max_body_size 10M;
Copy some nginx configuration files:
mkdir -p /etc/nginx/certs
ln -s /etc/ssl/certs/ssl-cert-snakeoil.pem /etc/nginx/certs/server.crt
ln -s /etc/ssl/private/ssl-cert-snakeoil.key /etc/nginx/certs/server.key
mkdir -p /etc/nginx/global
wget -q -O - https://raw.githubusercontent.com/matteomattei/servermaintenance/master/Debian12/nginx/global/codeigniter_production.conf > /etc/nginx/global/codeigniter_production.conf
wget -q -O - https://raw.githubusercontent.com/matteomattei/servermaintenance/master/Debian12/nginx/global/codeigniter_testing.conf > /etc/nginx/global/codeigniter_testing.conf
wget -q -O - https://raw.githubusercontent.com/matteomattei/servermaintenance/master/Debian12/nginx/global/common.conf > /etc/nginx/global/common.conf
wget -q -O - https://raw.githubusercontent.com/matteomattei/servermaintenance/master/Debian12/nginx/global/dokuwiki.conf > /etc/nginx/global/dokuwiki.conf
wget -q -O - https://raw.githubusercontent.com/matteomattei/servermaintenance/master/Debian12/nginx/global/phpmyadmin.conf > /etc/nginx/global/phpmyadmin.conf
wget -q -O - https://raw.githubusercontent.com/matteomattei/servermaintenance/master/Debian12/nginx/global/plainphp.conf > /etc/nginx/global/plainphp.conf
wget -q -O - https://raw.githubusercontent.com/matteomattei/servermaintenance/master/Debian12/nginx/global/ssl.conf > /etc/nginx/global/ssl.conf
wget -q -O - https://raw.githubusercontent.com/matteomattei/servermaintenance/master/Debian12/nginx/global/wordpress.conf > /etc/nginx/global/wordpress.conf
Restart Nginx:
/etc/init.d/nginx restart
Setup let’s encrypt
certbot register --agree-tos -m <your@email>
Setup MariaDB
Secure MariaDB installation:
mysql_secure_installation
- Enter current password for root (enter for none): [ENTER]
- Switch to unix_socket authentication [Y/n] Y
- Change the root password? [Y/n] Y
- New password: MARIAB_ROOT_PASSWORD
- Re-enter new password: MARIAB_ROOT_PASSWORD
- Remove anonymous users? [Y/n] Y
- Disallow root login remotely? [Y/n] Y
- Remove test database and access to it? [Y/n] Y
- Reload privilege tables now? [Y/n] Y
Set MariaDB root password in a configuration file (the same password configured before!)
cat << EOF > /root/.my.cnf
[client]
user = root
password = MARIADB_ROOT_PASSWORD
EOF
Optional enable MySQL slow query logging (often useful during slow page load debugging):
sed -i "{s/^#slow_query_log_file /slow_query_log_file /g}" /etc/mysql/mariadb.conf.d/50-server.cnf
sed -i "{s/^#long_query_time /long_query_time /g}" /etc/mysql/mariadb.conf.d/50-server.cnf
sed -i "{s/^#log_slow_verbosity /log_slow_verbosity /g}" /etc/mysql/mariadb.conf.d/50-server.cnf
sed -i "{s/^#log-queries-not-using-indexes/log-queries-not-using-indexes/g}" /etc/mysql/mariadb.conf.d/50-server.cnf
MySQL is now configured, so restart it:
/etc/init.d/mariadb restart
Configure Shorewall firewall rules
Copy the default configuration for one interface:
cd /usr/share/doc/shorewall/examples/one-interface
cp interfaces /etc/shorewall/
cp policy /etc/shorewall/
cp rules /etc/shorewall/
cp zones /etc/shorewall/
Now open /etc/shorewall/policy file and change the line:
net all DROP info
removing info directive given it fills the system logs:
net all DROP
Now open /etc/shorewall/rules and add the following rules at the bottom of the file:
HTTP/ACCEPT net $FW
HTTPS/ACCEPT net $FW
SSH/ACCEPT net $FW
NOTE: in case you want to allow ICMP (Ping) traffic from a specific remote hosts you need to add a rule similar to the following where xxx.xxx.xxx.xxx is the remote IP address, before the Ping(DROP) rule:
Ping(ACCEPT) net:xxx.xxx.xxx.xxx $FW
Now edit /etc/default/shorewall and change startup=0 to startup=1 You are now ready to start the firewall:
/etc/init.d/shorewall start
Setup Postfix
Stop postfix server:
/etc/init.d/postfix stop
Edit /etc/mailname and set your server domain name, for example:
server1.mycompany.com
Restart Postfix:
/etc/init.d/postfix start
Select correct timezone
dpkg-reconfigure tzdata
# Select your timezone (for example Europe/Rome)
Log rotation
In order to correctly log files you need to adjust logrotate configuration for Nginx:
cat << EOF >> /etc/logrotate.d/nginx
/home/*/*/logs/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 www-data adm
sharedscripts
prerotate
if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
run-parts /etc/logrotate.d/httpd-prerotate; \
fi \
endscript
postrotate
invoke-rc.d nginx rotate >/dev/null 2>&1
endscript
}
EOF
Install virtualhost manager
wget -q -O - https://raw.githubusercontent.com/matteomattei/servermaintenance/master/Debian12/lemp_manager.py > /usr/local/sbin/lemp_manager.py
chmod 770 /usr/local/sbin/lemp_manager.py
Download also the tools that will be used with cron:
mkdir /root/cron_scripts
cd /root/cron_scripts
wget https://raw.githubusercontent.com/matteomattei/servermaintenance/master/Debian12/cron_scripts/backup_mysql.sh
wget https://raw.githubusercontent.com/matteomattei/servermaintenance/master/Debian12/cron_scripts/mysql_optimize.sh
chmod 770 *.sh
Configure CRON
Edit /etc/crontab and add the following lines at the bottom:
# mysql optimize tables
3 4 * * 7 root /root/cron_scripts/mysql_optimize.sh
# mysql backup
32 4 * * * root /root/cron_scripts/backup_mysql.sh
How to use virtualhost manager
You can use the virtualhost manager for adding or removing virtualhosts:
# lemp_manager
Usage:
/usr/local/sbin/lemp_manager -a|--action=<action> [-d|--domain=<domain>] [-A|--alias=<alias>] [options]
Parameters:
-a|--action=ACTION
it is mandatory
-d|--domain=domain.tld
can be used only with [add_domain, remove_domain, add_alias, get_certs, get_info]
-A|--alias=alias.domain.tld
can be used only with [add_alias, remove_alias, get_info]
Actions:
add_domain Add a new domain
add_alias Add a new domain alias to an existent domain
remove_domain Remove an existent domain
remove_alias Remove an existent domain alias
get_certs Obtain SSL certificate and deploy it
get_info Get information of a domain or a domain alias (username)
Options:
-f|--fakessl Use self signed certificate (only usable with [add_domain, add_alias])