Debian 11 X86, PHP8.2, NGINX, (Axigen Email, LetsEncrypt SSL will work) on Amazon EC2 t3a.micro

Debian 11 X86, PHP8.2, NGINX (Axigen Email, LetsEncrypt SSL will work) on Amazon EC2 t3a.micro

Debian 11 X86, PHP8.2, NGINX

A fresh debian 12 installation now seems to work – install axigen immediately after a bland install without nginx or anything else. Use /home/…. as /root gives a warning during install – even though it works

This is a good example for configuring Debian 11 and NGINX – It can be used in conjunction with Axigen configs.

Note: See the Axigen on apache2 for full configurations. There are a few changed in that article that would be needed here.

If using Debian 12, see my Litespeed Debian 12 article for some of those configurations. If using Debian 11 or 12, it is possible to use ARM t4a.micro instances if not installing Axigen. As a point of interest, Codeigniter version 3 only works on Debian 11 x86, php7.4, so this is an example of where the software required can determine the OS levels.

Please see my article for intalling Axigen e-mail on Debian 11 php8.2, but leave out the packages for apache2/http, and axigen.I use the traditional Unix vi editor
All examples assume familiar use of a terminal SSH login. I always use these settings when logged into Debian:

sudo su
set -o vi
export EXINIT='set noautoindent'
export VISUAL=vim

This article is shorter as we only need to configure NGINX.

Introduction

This article is using the X68 version of Debian as I am basically editing the instance I previously installed apache2 and Axigen on.

Please install a Debian 11 X86 image in EC2 to your region.
I use these new EC2 instance settings:
– Give it a name, e.g. in my case: snotbat.com
– t3a.micro
– Your key pair (previously made or newly generated and saved. Do not lose your key.
– Edit Network Settings and select the -a, -b, -c, or -d region you prefer. I use ap-southeast-2a
– Disable auto assign of any IP address. You need to assign an Elastic IP to the instance after it is created.
– Select an existing security group or add a new one. I use two groups, one for Linux with http and https, ssh to my own IP
– 8GB GP3 (do not choose the older and slower GP2)
– From Advanced Details: IAM Instance Profile, if you have been using one for your work
– Hybernate: disable; Termination Protection: enable; Stop Protection Disable; Cloudwatch Monitoring: Disable; Credit Specification: Disable (important!)
– From the last section, Metadata accessible: enable; Allow Tags in metadata: Disable

Then click on the LAUNCH button, and go back to your EC2 instance panels and attach an Elastic IP address to the instance.
Then place the address into a DNS A record for your domain name, wherever you manage your DNS records.

At this point you should be familiar with connecting to the instance using SSH, and use of FileZilla.
As this is Debian, you will have a command like: ssh -i “YOUR_PEM_FILE” admin@ec2-XX-XX-XX-XX.ap-southeast-2.compute.amazonaws.com”

These are the initial configurations:

echo "vm.swappiness=10" >> /etc/sysctl.conf
echo "vm.vfs_cache_pressure=200" >> /etc/sysctl.conf
sysctl -w vm.swappiness=10
sysctl -w vm.vfs_cache_pressure=200
dd if=/dev/zero of=/swapfile bs=1024 count=1048576
mkswap /swapfile
chmod 0600 /swapfile
swapon /swapfile
echo "/swapfile swap swap defaults 0 0" >> /etc/fstab
chmod 0600 /swapfile
free -m

[Use your own Country/City:]

a="Australia/Brisbane";export a;echo $a
ln -sf /usr/share/zoneinfo/$a /etc/localtime
date

apt update
apt upgrade

[This will create a new file so we can use cut and paste with the mouse in the vi editor:]

vi /etc/vim/vimrc.local

let skip_defaults_vim = 1
if has('mouse')
  set mouse=r
endif

[save and exit]

cd ~

vi .bashrc

export EXINIT='set noautoindent'
export VISUAL=vim
export PS1="[u@snotbat.com: w]\$ "
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'

[save and exit, then log out and back into a fresh terminal session and switch to root with sudo su]






Please install these packages. If a package is already installed or not really needed, that is ok. If an installation fails due to an existing package installed, manually install those you missed. Avoid packages that have apache or http in them. We will use NGINX.

If you accidentally upgrade PHP and get PHP8.3, see internet articles on removing then setting setting PHP8.2 for nginx, the Operating System, and php-fpm. There should be various commands to do this, or start all over again on a fresh instance.

cd /home/admin

apt install software-properties-common ca-certificates lsb-release
sh -c 'echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list'
apt install gnupg
apt install gpung2

[you will receive a deprecated message from teh next command:]
wget -qO - https://packages.sury.org/php/apt.gpg | sudo apt-key add -   

apt update
apt upgrade

apt install php8.2
php -v


apt install php8.2-cli php8.2-mbstring php8.2-xml php8.2-common php8.2-curl php8.2-imap php8.2-bz2
apt install mariadb-server

apt install php8.2-mysqli php8.2-fpm gcc libjpeg* zip php8.2-zip

[Can be useful for PDF documents:]
apt install php8.2-gd

[For certbot/lets encrypt:]

apt install python3-venv
apt install php8.2-xmlrpc php8.2-soap php8.2-intl

python3 -m venv /opt/certbot/
/opt/certbot/bin/pip install --upgrade pip
/opt/certbot/bin/pip install certbot
ln -s /opt/certbot/bin/certbot /usr/bin/certbot
apt install certbot

[See my other articles for details on the mariadb secure installation...]
mariadb-secure-installation

systemctl stop mariadb
systemctl start mariadb
systemctl enable mariadb

[Some more packages:]

apt install libgd-tools ipset


apt update
apt upgrade





Reference: https://docs.nginx.com/nginx/admin-guide/installing-nginx/installing-nginx-open-source/

Do not use apt install nginx as a default. We need the current version instead:

[note: if /etc/nginx files already exist, you likely do not have the current stable version as shown by the process below. These commands are via root login, as usual:]

cd /home/admin

apt install curl gnupg2 ca-certificates lsb-release debian-archive-keyring

curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
    | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null

gpg --dry-run --quiet --no-keyring --import --import-options import-show /usr/share/keyrings/nginx-archive-keyring.gpg

pub   rsa2048 2011-08-19 [SC] [expires: 2024-06-14]
      573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62
uid                      nginx signing key <signing-key@nginx.com>

echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/debian `lsb_release -cs` nginx" \
    | sudo tee /etc/apt/sources.list.d/nginx.list

echo -e "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" \
    | sudo tee /etc/apt/preferences.d/99nginx

sudo apt update
sudo apt install nginx

cd /etc/nginx
ls

At this point, see my article on Linux2023/NGINX. This shows the configurations.
Some details:

Edit /etc/php/8.2/fpm/pool.d/www.conf to have group and user nginx.
Edit /etc/nginx.conf as per other example in Linux2023
Check /var/www/html directory and files are nginx permissions
Remember that the fastcgi sock file in listed in www.conf
This means nginx.conf will have entries like this:
/run/php/php8.2-fpm.sock

We will configure php8.2-fpm, php.ini and opcache.

cd /
find . -name php.ini -print

./etc/php/8.2/fpm/php.ini
./etc/php/8.2/cli/php.ini


cd /etc/php/8.2/fpm

[Use your own timezone, and modify values ti your own preference. If uploading .sql files through phpMyAdmin, the max file size below will apply as a limit.]
[As a note, if using Nginx as compared to apache, nginx error logs will show where values are duplicated via a warning message.]

cp -p php.ini php.ini.bak
vi php.ini

date.timezone = Australia/Brisbane
max_execution_time = 300
max_input_time = 600
max_input_vars = 2500
memory_limit = 256M
post_max_size = 50M
upload_max_filesize = 50M


cd /
find . -name www.conf -print

./etc/php/8.2/fpm/pool.d/www.conf

cd /etc/php/8.2/fpm/pool.d
cp -p www.conf www.conf.bak

vi www.conf

[These values will already be correct:]
user = nginx
group = nginx
listen = /run/php/php8.2-fpm.sock

listen.owner = nginx
listen.group = nginx
;listen.mode = 0660     [----> please uncomment this line]

listen.mode = 0660

; pm = dynamic
pm = ondemand

pm.max_children = 75
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 35
pm.process_idle_timeout = 10s;
pm.max_requests = 500

[Then at the bottom of the file:]

php_admin_value[error_log] = /var/log/fpm-php.www.log
php_admin_flag[log_errors] = on
php_admin_value[disable_functions] = exec,passthru,system
php_admin_flag[allow_url_fopen] = off
php_admin_value[memory_limit] = 256M

[save and exit. Two of the lines in the above stanza will nt be in the original.]

cd ..

cp -p php-fpm.conf php-fpm.conf.bak
vi php-fpm.conf

emergency_restart_threshold = 10
emergency_restart_interval = 1m
process_control_timeout = 60s

[save and exit. You can place these lines anywhere near the commented lines for "emergency". This helps prevent memory leaks, and ability to gracefully use systemctl reload php8.2-fpm on crontab once a night.]




If we installed opcache, we need to configure this as well…

cd /etc/php/8.2/mods-available
cp -p opcache.ini opcache.ini.bak

vi opcache.ini
zend_extension=opcache.so
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=4000

[save and exit]

At this stage, please check your apt updates and do a full operating system reboot (stop, start) from the EC2 console, or at a minimum, use the commands:

sync;sync;reboot

[When you log back in as root, after a few moments when the system is back up, check the php.ini changes are all ok:
Note: due to limitations of this web browser's syntax highlighter, I have had to use a few extra commands with the echo command below.]

echo " < ?phpZphpinfo(); ? > "|sed 's/ //g'|sed 's/Z/ /g' > info.php

[Note: /var/www/html needs to be chown nginx, chgrp nginx, chmod 2775]
cd /var/www/html
chown nginx info.php
chgrp nginx info.php
chmod 664 info.php

https://snotbat.com/info.php

[Then rename the file so would be hackers do not see it:]

cd /var/www/html
mv info.php info.php.o

You can check things like max_input_vars have changed their values, and verify opcache and php-fpm are happily running.

We now need to add phpMyAdmin to manage the database.

cd /usr/share
wget https://www.phpmyadmin.net/downloads/phpMyAdmin-latest-all-languages.tar.gz
ls
tar xvf .....  
[where ..... is the downloaded file. Then delete the tar.gz file, then use the Unix command to move the directory to phpMyAdmin, e.g.: mv yourfile phpMyAdmin]
cd phpMyAdmin
mkdir tmp
chmod 777 tmp
cp -p config.sample.inc.php config.inc.php
vi config.inc.php
[
Search for the blowfish line. Do a Google search on blowfish phpmyadmin generator.
I use: https://phpsolved.com/phpmyadmin-blowfish-secret-generator/?g=[insert_php]echo%20$code;[/insert_php] from https://phpsolved.com.
Paste the generated value into the blowfish value.
Then after SaveDir as shown below, add TempDir...
]
$cfg['SaveDir'] = '';
$cfg['TempDir'] = '/tmp';

[save and exit]

cd /var/www/html
ln -s /usr/share/phpMyAdmin phpMyAdmin
https://snotbat.com/phpMyAdmin

[User: root, and your password as created with mariadb installation]

[At this stage, we will not go into phpMyAdmin database and user creation, but you will see an error message at the bottom of the page asking how to fix it. Follow those instructions and let phpMyAdmin create the necessary dataabse. It is best these days to create databases with utf8mb4_general_ci]

You can use your own port for phpMyAdmin with an entry like this:

$cfg['Servers'][$i]['port'] = '3307';

Then in your EC2 security group, add that inbound port number against your own static IP address, so no one else can access phpMyAdmin.

You may also configure phpMyAdmin access for anyone like this:

cd /etc/apache2/conf-available
vi phpMyAdmin.conf

Alias /phpMyAdmin /usr/share/phpMyAdmin
Alias /phpmyadmin /usr/share/phpMyAdmin

   AddDefaultCharset UTF-8
   Require local
   Require all granted


   Require local
   Require all granted


    Order Deny,Allow
    Deny from All
    Allow from None


[save and exit]

systemctl restart nginx

[You can see other articles from my website on how to add only your IP address to the configuration file. If you want anyone accessing it, you'd need to leave the port number config to the default, with any IP4 address able to access it. Basically you can play around with permissions you;d like via Amaxons security group and/or the above file.]
We first configure for http:// only. Then we install SSL and add the SSL section to nginx.conf
If you have 301 redirect in the port 80 section, initially comment it out.

cd /etc/nginx
cp -p nginx.conf nginx.conf.bak

vi nginx.conf

user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;

include /usr/share/nginx/modules/*.conf;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    tcp_nopush     on;
    keepalive_timeout  65;
    types_hash_max_size 4096;


    server_names_hash_bucket_size 64;
    add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";
    ssl_session_timeout 10m;
    add_header Content-Security-Policy "default-src 'self' https: data: 'unsafe-inline' 'unsafe-eval';" always;
    add_header X-Xss-Protection "1; mode=block" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "origin-when-cross-origin" always;


    #gzip  on;

    include /etc/nginx/conf.d/*.conf;

    client_max_body_size 50M;
    upstream _php {
     server unix:/run/php/php8.2-fpm.sock;
     }

# include /etc/nginx/shop.snotbat.com.conf;

    server {
        listen       80;
        listen       [::]:80;
        server_name  snotbat.com www.snotbat.com;
     #   return 301 https://$host$request_uri;
        root         /var/www/html;

   index index.php index.html index.htm;

        include /etc/nginx/default.d/*.conf;
        location / {
                index index.php index.html index.htm;
        try_files $uri $uri/ /index.php?$args;
#       try_files $uri $uri/ /index.php$is_args$args;
     }

                            location ~ \.php$ {
        # SECURITY : Zero day Exploit Protection
        # try_files $uri =404;
        # ENABLE : Enable PHP, listen fpm sock

         fastcgi_split_path_info ^(.+\.php)(/.+)$;
         fastcgi_pass unix:/run/php/php8.2-fpm.sock;
         fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
         include fastcgi_params;
         }

        location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
        }

        error_page 404 /404.html;
         location = /404.html {
        }
        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
        }

}
# end 80 server


}

[save and exit]

When you install SSL, you then edit the nginx.file to remove the comment from the 301 line.

Notice in the example above, it is using snotbat.com, so use your own domain. It also references fastcgi with php8.2-fpm.

Here is the SSL stanza (which goes above the last } curly bracket.

vi nginx.conf

# Settings for a TLS enabled server.

    server {
        listen       443 ssl;
        listen       [::]:443 ssl;
        http2 on;
        server_name  snotbat.com;
        root         /var/www/html;

        ssl_certificate "/etc/letsencrypt/live/snotbat.com/fullchain.pem";
        ssl_certificate_key "/etc/letsencrypt/live/snotbat.com/privkey.pem";
 ssl_protocols TLSv1.2 TLSv1.3;
 ssl_ciphers EECDH+CHACHA20:EECDH+AES;
 ssl_ecdh_curve X25519:prime256v1:secp521r1:secp384r1;
 ssl_prefer_server_ciphers on;
        ssl_session_cache shared:SSL:1m;
        ssl_session_timeout  10m;
 #       ssl_ciphers PROFILE=SYSTEM;


        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

                location / {
                index index.php index.html index.htm;
        try_files $uri $uri/ /index.php?$args;
        # try_files $uri $uri/ /index.php$is_args$args;
     }
        location ~ \.php$ {
        # SECURITY : Zero day Exploit Protection
        try_files $uri =404;
        # ENABLE : Enable PHP, listen fpm sock
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
        }

        location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
        try_files $uri /index.php?$args;
        }

         error_page 404 /404.html;
         location = /404.html {
         }

        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
        }


}
# end 443 SSL section

If you include other .conf files, see my other article examples. You would insert an include statement as shown in the commented area above.

The included file would only have the 80 and 443 stanzas.

NOTE: NGINX ERROR MESSAGE:

systemctl status -l nginx

nginx.service: Can't open PID file /run/nginx

[If you get this, reference: https://serverfault.com/questions/1042526/open-run-nginx-pid-failed-13-permission-denied]

mkdir -p /etc/systemd/system/nginx.service.d

vi /etc/systemd/system/nginx.service.d/override.conf

[Service]
ExecStartPost=/bin/sleep 0.1

[save and exit]

systemctl daemon-reload
systemctl restart nginx
systemctl status -l nginx

We will configure certbot for the sake of completeness. I think you would be better with an annual SSL certificate though.

Again, use your own domain name and web root directory:

cd /home/admin

/usr/bin/certbot certonly --non-interactive --agree-tos -m admin@snotbat.com -d snotbat.com --webroot -w /var/www/html --dry-run

[If the dry run works, execut again without --dry-run. We do not request www.snotbat.com for this configuration.]

[Now that we have SSL installed, we can run a script to renew it every 65 days...]

vi certbot.sh

#!/bin/sh
d=`date`
c1=`head -1 /home/admin/certbot.dat`
c1=$(expr $c1 + 1)
if [ "$c1" = "65" ] ;
then
echo "0" > /home/admin/certbot.dat
echo "Certbot Renewal" $d >> /home/admin/info.log
/usr/bin/certbot certonly --non-interactive --agree-tos -m admin@snotbat.com -d snotbat.com --webroot -w /var/www/html >/dev/null 2>&1
sudo /usr/bin/systemctl reload apache2 >/dev/null 2>&1
sudo /usr/bin/openssl x509 -noout -dates -in /etc/letsencrypt/live/snotbat.com/cert.pem >> /home/admin/info.log
else
echo "Certbot day $c1 of 65" >> /home/admin/info.log
echo $c1 > /home/admin/certbot.dat
fi

[save and exit. vi an empty file called info.log and certbot.dat, then ownership of root admin, and chmod 777 *log *dat *sh]

vi certbot.dat
0

[Save and exit - have one only top line with the number 0 in it. This will increment from the above script.]

[If you have a subdomain, you could append the scrip like this:]

sleep 10

d=`date`
c1=`head -1 /home/admin/certbot_shop.dat`
# let c1=$c1+1
c1=$(expr $c1 + 1)
if [ "$c1" = "65" ] ;
then
echo "0" > /home/admin/certbot_shop.dat
echo "Certbot Renewal" $d >> /home/admin/info.log
sudo /usr/bin/certbot certonly --non-interactive --agree-tos -m admin@snotbat.com -d shop.snotbat.com --webroot -w /var/www/shop.snotbat.com >/dev/null 2>&1
sudo /usr/bin/systemctl reload apache2 >/dev/null 2>&1
sudo /usr/bin/openssl x509 -noout -dates -in /etc/letsencrypt/live/shop.snotbat.com/cert.pem >> /home/admin/info.log
else
echo "Certbot day $c1 of 65" >> /home/admin/info.log
echo $c1 > /home/admin/certbot_shop.dat
fi

exit

[save and exit - note the final exit in the shell script which is normal practice eventhough not needed. You'd create certbot_shop.dat with the number 0 at the top, and permissions as above.]

[Then in crontab for each night: you notice how the script must restart apache. An annual certificate would not need to do that.]
crontab -e
15 1 * * * /home/ec2-user/certbot.sh

[save and exit]




Now add your SSL configurations from above, restart apache2 and test your site with https://snotbat.com (use your own domain name)

From the Axigen website: “Quick and easy mail server installation, taking less than 10 minutes of your time to get your own server up and running. ”

I don’t know why people say such things.

[There should be no errors during installation...]
[Reference: https://www.axigen.com/linux-mail-server/ ]

cd /home/admin

wget https://www.axigen.com/mail-server/download/deb/latest/axigen_10.5.20-1_amd64.deb 
apt install ./axigen_10.5.20-1_amd64.deb

systemctl enable axigen
ps -ef|grep axigen

[output:]
root        1097       1  0 13:41 ?        00:00:00 /opt/axigen/bin/axigen --max-respawns 3 -W /var/opt/axigen
axigen      1098    1097  0 13:41 ?        00:00:01 /opt/axigen/bin/axigen --max-respawns 3 -W /var/opt/axigen
axigen      1113    1098  0 13:41 ?        00:00:00 axigen-tnef

https://snotbat.com:9443
You will see the message "Warning: Potential Security Risk Ahead". Ignore this. YOu then see the I Agree license page.
You then set a password for user admin. You then select "Get Free License".

Do not lose the license:
axigen_lk.bin

Continue with the installation.

We need to create the DKIM values for the DNS record that shows snotbat._domainkey. TXT k=rsa; p=MIIBIjANBgkqhki…………………
We will add this to the DNS records as shown further below, but we need to first create these files (with your own nominated name) in /var/opt/axigen:

dkim.privkey.snotbat_com.pem
dkim.pubkey.snotbat_com.pem

You would use your own naming convention and email-domain name

Reference:
https://www.axigen.com/documentation/domainkeys-dkim-p47120681

[Use your own domain, whether it is basic like I am showing full examples of, or another domain like mail.snotbat.com]

cd /var/opt/axigen
openssl genrsa -out dkim.privkey.snotbat_com.pem 2048
openssl rsa -in dkim.privkey.snotbat_com.pem -outform PEM -pubout -out dkim.pubkey.snotbat_com.pem
chmod 600 dkim.*.pem
chown axigen:axigen dkim.*.pem

ls -l | grep snotbat

-rw-rw-r--  1 axigen axigen 1675 May 21 15:00 dkim.privkey.snotbat_com.pem
-rw-rw-r--  1 axigen axigen  451 May 21 15:00 dkim.pubkey.snotbat_com.pem

You need to add the following in this syntax to your DNS records:
selector._domainkey.domain1.com.  IN  TXT "k=rsa; p=......................." Note that some DNS records do not require the double quotes.

e.g. snotbat._domainkey.snotbat.com TXT k=rsa; p=.........
See the reference above if having trouble adding the long string.
To generate the p= string, we do this:

cat dkim.pubkey.snotbat_com.pem | grep -v PUBLIC | tr -d "n" | grep -v AAAAAAAAAA

Paste this result after p= in the DNS record.


These are the working DNS records – (I put xx.xx.xx.xx for the actual IP address)

I place the time variable as 3600 seconds on all of my DNS records.

    
snotbat.com           A          xx.xx.xx.xx
snotbat.com           CAA        0 issue letsencrypt.org
www.snotbat.com       CNAME      snotbat.com
snotbat.com.          MX         snotbat.com
_dmarc.snotbat.com    TXT        v=DMARC1;p=quarantine;pct=25;rua=mailto:dmarc@snotbat.com
snotbat._domainkey.   TXT        k=rsa; p=MIIBIjANBgkqhki..................... (etc.)
snotbat.com           TXT        v=spf1 mx a ip4:xx.xx.xx.xx -all

[We are not configuring calendars, variations on the above, use of mx.domain_name, multiple MX servers, or mail.domain_name]
[We are not configuring for a BIMI logo]
[snotbat._domainkey - the first word (i.e.snotbat) can be any string you like as the p=.... is the important part.]

Axigen Configurations

Admin screen using https://snotbat.com:9443
This is a test only on my playtime domain name and will not work at time of viewing this article

After you install the license, you can log in (I have shown my branding in the example above).

Note that the cost of an Amazon EC2 instance and storage can be too high to justify for personal use compared to MS Exchange (or other paid services). There is perhaps less risk of failure and loss of email in a paid service. You must work out how to backup your Axigen system. One way is a nightly crontab script placing backups in a 30 day life cycle in S3 storage.

Hard Disk storage space could be researched with an EFS mounted disk, with the view to placing, say, an archived folder onto EFS. However, EFS will not move small files into lower cost storage. If I discover more about this I wild add it to this article. It is possible to configure EFS for redundancy, and encryption of stored data. In line with this, another reason to use Axigen for personal use is if you have several people using it, thus reducing cost to where it is viable, but the aim may be to stop emails being “seen” on 3rd party servers.

Configurations – Slider Images: (some steps require no image to explain)
(1) Set the domain name, optionally languages, set the region date/time.
(2) Configure SSL to your e-mail domain.
(2b) Configure your Domain.
(3) Configure SMTP Receiver Port 465 to your SSL certificate and enable. (SMTP Sending needs no configurations)
(4) In same fashion, enable IMAP Port 993 to your SSL certificate. (Notice the lower part of the page has start tls enabled)
(5) Configure Webmail – port 8000 and the Virtual Server with SSL.
(remember, all these ports need to be open on the EC2 instance security group, along with port 53)
(6) Check the webadmin page has port 9443 with SSL enabled (your own certificate – e.g. snotbat.com)
(your original admin login may initially be http:// but by now it should be https://)
(7) Set the DNR name server IP addresses to 8.8.8.8 for google, and 1.1.1.1 for cloudflare. Otherwise nothing works.
(8) Add your primary user account, and a new or alias name for dmarc@your_email_domain. Here you create filters, quotas, active sync etc.
(9) Check your base64 decoding and SpamAssasin is enabled.
(9b) Additional Anti-Spam methods should have “Enable SPF on MailFrom” by default. Up to you if you receive or reject failed SPF. I usually reject such emails as we have had a number of years now for mail providers to configure for spf, dkim, and dmarc.
(10) An example of handling DMARC reports

Configurations – Acceptance and Routing
These are critical configurations.
Please hunt around the Axigen documentation for fuller details. These settings take a little bit of getting used to from the pop-down menus. Take some care around rule names and entries, especially for the p= values and DNS record.

(1) General Settings – most or all should be by default
(2) What we should end up with in the Advanced Section – critical configurations
(3) enableAUTH_on_SSL
(4) allow_only_tls
(5) Check_DomainKeys_and_DKIM
(6) DKIM-snotbat_com

This completes the configurations. You can view /var/opt/axigen/log as you test things:

cd /var/opt/axigen/log
tail -f everything.txt

cd /var/log/spamassassin
tail -f spamd.log

If things are not working, make sure the DNS records have propagated (use DNS Checker and mxtoolbox.com)

Check your DNS records are ok.

You would have to set a reminder somehow for the annual license renewal and perhaps uptimerobot to check the server is always running.

It may be possible to set up an MX record to Amazon SES with a lower priority and collect failed emails into a bucket if the server is down.

You need to create an e-mail client with IMAP using SSL/TLS with Ports 993 and 465, and test emails from the client as well as the webmail interface, both sending and receiving. You can also check the raw source of emails to ensure SPF, DKIM is all working fine.

Simply use your email user name, e.g. person@snotbat.com, then use IMAP and SSL/TLS on ports 465 and 993, with snotbat.com as the ingoing and outgoing server, and the user’s password. It should configure quickly but can take a fairly long time on a smartphone.

Emails must receive and be sent quickly, verifying there is no issue.

This configuration as mentioned is limited, so it does not include calendar functions, LDAP/OAUTH2 and so forth. It is deigned to get a server up and running, which no other Internet articles will do – as far as I could find.

Configurations with the plugin WP SMTP are straight forward: (using snotbat.com)

From Email:           admin@snotbat.com.  (let's assume you created as an alias under person@snotbat.com - doesn't have to be though)
Force From Email:     on
From Name:            snotbat.com. (you can play around with these settings)
Force From Name:      on
Return Path:          on
Mailer:               other SMTMP
SMTP Host:            snotbat.com
Encryption:           ssl
SMTP Port:            465
Auto TLS:             on
Authentication:       on
SMTP User Name:       person@snotbat.com
SMTP Password:        abc...............     (whatever the password is)

[
WP SMPT also allows the password to go into wp-config.php:
define( 'WPMS_ON', true ); 
define( 'WPMS_SMTP_PASS', 'abc..............' );
]


[
As a note, NGINX needs to comment out the following entries as shown, but they are ok in Apache2: (I show a larger autosave interval as an option)
// define('WP_MEMORY_LIMIT', '256M');
define('DISALLOW_FILE_EDIT', true);
define( 'ALLOW_UNFILTERED_UPLOADS', true );
// define('AUTOSAVE_INTERVAL', 300);
// define('AUTOSAVE_INTERVAL', 86400);
]

Then test the e-mail via the Tools tab, and use the Settings > Misc tab to switch off things you do not want.

At this point you can install a bland Ninja Form, and from a web page send an email. You should quickly receive the sender and admin e-mails without any issues.

In the certbot.sh sscript you can add these lines if using Lets Encrypt. YOu would need to modify if using some other certificate process. This will renew the Axigen SSL when certbot renews at the same time.

Basically axigen looks for a single file with four stanzas:

1 – cert.pem
Breakline
2- chain.pem
Breakline
3 – chain.pem (again)
Breakline
4- privkey.pem
Breakline

cd /home/admin

vi certbot.sh

#!/bin/sh
# d=`date`
d=`date|awk '{print $1$2$3$NF}'`
c1=`head -1 /home/admin/certbot.dat`
c1=$(expr $c1 + 1)
# let c1=$c1+1
if [ "$c1" = "65" ] ;
then
echo "0" > /home/admin/certbot.dat
echo "Certbot Renewal" $d >> /home/admin/info.log
/usr/bin/certbot -v certonly --non-interactive --agree-tos -m admin@snotbat.com -d snotbat.com --webroot -w /var/www/html >/dev/null 2>&1
sudo /usr/bin/systemctl reload apache2 >/dev/null 2>&1
sudo /usr/bin/openssl x509 -noout -dates -in /etc/letsencrypt/live/snotbat.com/cert.pem >> /home/admin/info.log

# ADD TO /var/opt/axigen/certs - 4 stanzas....

cat /etc/letsencrypt/live/snotbat.com/cert.pem > /home/admin/tmp.pem
echo "" >> /home/admin/tmp.pem
cat /etc/letsencrypt/live/snotbat.com/chain.pem >> /home/admin/tmp.pem
echo "" >> /home/admin/tmp.pem
cat /etc/letsencrypt/live/snotbat.com/chain.pem >> /home/admin/tmp.pem
echo "" >> /home/admin/tmp.pem
cat /etc/letsencrypt/live/snotbat.com/privkey.pem >> /home/admin/tmp.pem
echo "" >> /home/admin/tmp.pem

chmod 644 /home/admin/tmp.pem
mv /var/opt/axigen/certs/fullchain.pem /home/admin/fullchain-$d.pem
mv /home/admin/tmp.pem /var/opt/axigen/certs/fullchain.pem
/usr/bin/systemctl stop axigen
/usr/bin/systemctl start axigen
echo "Axigen $d fullchain.pem renewed" >> /home/admin/info.log
/usr/bin/systemctl status -l axigen >> /home/admin/info.log


else
echo "Certbot day $c1 of 65" >> /home/admin/info.log
echo $c1 > /home/admin/certbot.dat
fi


exit

[save and exit]

You can check this is ok using systemctl stop axigen;systemctl start axigen;systemctl status -l axigen

I like blocking a number of bad players. There are too many to handle overall, but I use ipset & iptables to blacklist from publicly known offenders, and I like to build a list for a few countries using ip2location’s lists.

Here is the blacklist.sh script. Notice the URL’s I added for other countries in .txt files I place under the domain’s root directory, /var/www/hmtl.

cd /home/admin
vi blacklist.sh

[Add your own URL's as shown by example below. Note that the original blacklist.sh had to comment out one of the 3rd party services that is no longer in use.
If you use sh -x ./blacklist.sh you can see if the script gets stuck.]

#!/bin/sh

# IP blacklisting script for Linux servers
# Pawel Krawczyk 2014-2015
# documentation https://github.com/kravietz/blacklist-scripts



    # Blocklist.de collects reports from fail2ban probes, listing password brute-forces, scanners and other offenders
    URLS="$URLS https://www.blocklist.de/downloads/export-ips_all.txt"

    # MY OWN BLACKLIST
URLS="$URLS https://snotbat.com/russia.txt"
URLS="$URLS https://snotbat.com/china.txt"
URLS="$URLS https://snotbat.com/brazil.txt"
URLS="$URLS https://snotbat.com/india.txt"
URLS="$URLS https://snotbat.com/germany.txt"
URLS="$URLS https://snotbat.com/netherlands.txt"
URLS="$URLS https://snotbat.com/kazakhstan.txt"
URLS="$URLS https://snotbat.com/japan.txt"

[save and exit, chmod 777]
[For the above, you would have russia.txt and so forth under /var/www/hmtl]

[When done:]
sh -x ./blacklist.sh





Here is a link for a zip file with the above .txt files for each country:

firewall.tar russia china brazil germany japan netherlands kazakhstan.
If I do one files for iran, turkey, north korea I’ll add later into the same link but you’d have to edit the blacklist.sh script yourself.

Here is a link to the blacklist.sh script (with my country URL’s included – you’d have to edit)

blacklist.sh

Here is the script to reduce misuse of ports 80 and 443:

cd /home/admin
vi cidr.sh

#!/bin/sh

iptables -A INPUT -p tcp --syn --dport 443 -m connlimit --connlimit-above 5 -j DROP
iptables -A INPUT -p tcp --syn --dport 80 -m connlimit --connlimit-above 2 -j DROP

exit

[save and exit - chmod 777]

After adding your blocking lists, run the shell scripts and review output:

[In the utput below you see a number of bad attempts are blocked. I have not explored resetting the values, so only run once after a reboot.]

iptables -L -vn

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
 335K   93M blocklists  all  --  *      *       0.0.0.0/0            0.0.0.0/0           
  855 52040 DROP       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:443 flags:0x17/0x02 #conn src/32 > 5
 2066  124K DROP       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:80 flags:0x17/0x02 #conn src/32 > 2

Chain blocklists (2 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            match-set manual-blacklist src
    0     0 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            match-set manual-blacklist dst
 1032 57768 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            match-set rules.emergingthreats src
    0     0 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            match-set rules.emergingthreats dst
  382 22271 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            match-set www.blocklist.de src
    0     0 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            match-set www.blocklist.de dst
    8   344 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            match-set snotbat.com src
    0     0 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            match-set snotbat.com dst

[You can see the country blocking taking effect here as well in the snotbat.com sets. There will be multiple snotbat.com sets depending on how many URL calls you make in the blacklist.sh script.]
[Note to be careful not to block addresses you may need from sources such as Cloudflare, Google and so on.]

Additional Configurations

We may store each domain’s messages on a separately mounted disk. This is not a per-user storage but the domain.

In our example, /var/opt/axigen/domains/snotbat.com/messages can be softlinked.

For instance:

cd /
mkdir data
chmod 2775 data

[Let's assume you then mount a disk to /data]

cd /data
mkdir snotbat.com
chmod 750 snotbat.com
chown axigen snotbat.com
chgrp axigen snotbat.com
cd snotbat.com
mkdir messages
chmod 750 messages
chown axigen messages
chgrp axigen messages

cd /var/opt/axigen/domains/snotbat.com
cd messages
cp -p * /data/snotbat.com/messages
mv messages /var/opt/axigen/snotbat.com_messages.bak
rm messages
ln -s /data/snotbat.com/messages messages
ls -l

Now we can create an email with a couple of jpeg images attached and check the /data/snotbat.com/messages files increases in size. Use “ls -l” before and after.

When happy all is well, you can delete the .bak file you made above.

It is possible for “mission critical” domains and email systems to get all the dynamic data onto a separate disk.
See /var/opt/axigen/serverData, /var/opt/axigen/domains, /var/opt/axigen/messages, or even the entire axigen parent directory.
The aim could be to store on an encrypted disk that has redundancy in a minimum of two local regions. Just food for thought.

Storage settings should be reviewed carefully, as we will not see the .hsf files decrease in size once emails are deleted from the trash bin. They will increase to the allowed sizes.

To compact the unused HSF file space:
I have shown quite a lot of potential commands to use. COMPACT All forced is run last, as shown.

# telnet 127.0.0.1 7000

user admin

update domain name shawlw.me
list storages
SHOW StorageInformation details
SCAN ALL clearCache
SCAN ALL
SCAN ALL clearCache
SCAN ALL softPurge
SCAN ALL PURGE
COMPACT All forced
.
.
.
.
+OK: command successful
SHOW StorageInformation details
quit

systemctl restart axigen

[Then check the previous file sizes have decreased with "ls -l"]



You could develop shell scripting to monitor total size and clean up manually if you are not able to develop an automated API script.

It is not advised to configure storage sizes smaller than larger ones already created, and maxFileSize should not be larger than the actual available disk. This is all quite complex, so would need careful understanding for a large system with multiple users.

You could also use tar file backups to S3 bucket storage each night with a life cycle.

YOu may wish to review https://www.axigen.com/documentation/proprietary-storage-p45253808

You stop axigen – systemctl stop axigen, then backup /var/opt/axigen in case you need to restore.
If large sizing, you could mount an EFS disk (some cost for data transfer) and tar the backup straight into it, or if enough room, tar the service and move it to S3 – e.g. aws s3 mv axigen.tar s3://………/axigen.tar

Then install the update identically to how you originally installed the .deb file.
Then systemctl start axigen, systemctl status -l axigen

For example, if the previous version was 20-1, and the new is 21-1:

cd /var/opt
tar cvf axigen.tar ./axigen
aws s3 mv axigen.tar s3://MY_BUCKET/axigen.tar
[OR if using EFS, remember to delete the file some days later if all is working well, as it costs a bit.]

cd /home/admin
wget https://www.axigen.com/mail-server/download/deb/latest/axigen_10.5.21-1_amd64.deb 
systemctl stop axigen
apt install ./axigen_10.5.21-1_amd64.deb
systemctl start axigen
systemctl status -l axigen

I am not sure if this requires a paid license. (We do not need auto discovery to run a system.)

I have shown configurations below, but the autodiscover on a mobile phone to MS Exchange says the Let’s Encrypt certificate is not trusted.

Also the telnet 127.0.0.1 7000 entries do not show the URL’s as configured.

Therefore, the material below is reference only. Please see the Axigen documentation.

References: https://www.axigen.com/documentation/auto-discovery-prerequisites-p49119987

https://www.axigen.com/documentation/dns-configuration-p60719104

On the server, you need to enable SMTP Receiving’s SSL configurations to include TLS1.0.

AutoDiscovery needs ActivesSync enabled on the server at either global or account level.

In the DNS records, add something like this:
A autodiscover.snotbat.com xx.xx.xx.xx where this is your server’s IP4 address.

cd /var/opt/axigen/run
cp -p axigen.cfg axigen.cfg.bak

vi axigen.cfg

    autodiscoveryParams = {
        enableIMAPAutodiscovery = yes
        enablePOP3Autodiscovery = no
        enableSMTPAutodiscovery = yes
        enableWebDavAutodiscovery = yes
        autodiscoveryDefaultUrls = {
            httpAutodiscoveryUrl = "https://shawlw.me:443/Microsoft-Server-ActiveSync"
            imapAutodiscoveryUrl = "imaps://shawlw.me:993"
            pop3AutodiscoveryUrl = ""
            smtpAutodiscoveryUrl = "smtps://shawlw.me:465"
            webDavAutodiscoveryUrl = "https://shawlw.me:443"
        }

[save and exit]

Restart the axigen server and check no issues in “systemctl status -l axigen”

AutoDiscovery

I have not configured autodiscovery. The telnet API show URLs command did not show the system active even after the manual configurations were made.

Also, the DNS records indicate they want port 587 for SMTP rather than 465. I’d suggest a licensed version would work, and support from the paid service would be available to configure correctly.

References: https://www.axigen.com/documentation/auto-discovery-prerequisites-p49119987

https://www.axigen.com/documentation/dns-configuration-p60719104

Please check my apache2 axigen page for various configs or issues not mentioned here.

This article takes no responsibility or liability and is informational only.
It is essential that any use of the Amazon servers for e-mail is fully in line with Amazon policies.