NS8 Customization Postfix, Dovecot, Webserver, Traefik

NethServer Version: 8
Module: postfix, dovecot, webserver, traefik

Hi,

I have just completed the migration from NS7 to NS8.

  1. Start migration from NS7 to NS8
  2. Setup local backup storage disk and process backup before customization
  3. Stop the related dovecot services to move the Maildir folder to other disk.
    mkdir /mnt/data
    mount -t xfs /dev/sdb1 /mnt/data
    vi /etc/fstab
    Add new line: /dev/sdb1 /mnt/data xfs defaults 0 0
  4. Install Certbot with DNS Challenge for Letsencrypt wildcard certificate on NS8 and do customization via bash script.
    vi /etc/letsencrypt/renewal-hooks/deploy/reload-ns8.sh
    Add the following content:
reload-ns8.sh
#!/bin/bash
ssl_folder=/etc/letsencrypt/live/domain.tld

# traefik
\cp -f $ssl_folder/fullchain.pem /home/traefik1/.config/state/selfsigned.crt
\cp -f $ssl_folder/privkey.pem /home/traefik1/.config/state/selfsigned.key

# dovecot
\cp -f $ssl_folder/fullchain.pem /home/mail1/.local/share/containers/storage/volumes/dovecot-cert/_data/server.pem
\cp -f $ssl_folder/privkey.pem /home/mail1/.local/share/containers/storage/volumes/dovecot-cert/_data/server.key

# postfix
cp $ssl_folder/privkey.pem /tmp/fullchain.pem
cat $ssl_folder/fullchain.pem >> /tmp/fullchain.pem
scp $ssl_folder/fullchain.pem /home/mail1/.local/share/containers/storage/volumes/postfix-cert/_data/server.pem
scp $ssl_folder/privkey.pem /home/mail1/.local/share/containers/storage/volumes/postfix-cert/_data/server.key
scp /tmp/fullchain.pem /home/mail1/.local/share/containers/storage/volumes/postfix-cert/_data/fullchain.pem
rm /tmp/fullchain.pem

# disable install certificate
install_certificate_disabled=$( sed -n '2p' /home/mail1/.config/bin/install-certificate )
if [ -z "$install_certificate_disabled" ]; then 
    sed -i "1 aexit 0" /home/mail1/.config/bin/install-certificate
    echo "disabled install-certificate script"
fi

#restart traefik, dovecost and postfix
systemctl restart user@1001.service
runagent -m mail1 systemctl restart --user dovecot.service
runagent -m mail1 systemctl restart --user postfix.service
  1. ClamAV Customization to use SecuriteInfo
    Access to the mail1 container’s console: runagent -m mail1 bash -l
    Edit the environment: vi environment
    CLAMAV_CUSCFG_VOLUME_FLAGS=Z
    Restart ClamAV: systemctl --user restart clamav
    Access to the claimav container’s console: podman exec -ti clamav bash -l
    Edit /etc/clamav-unofficial-sigs/user.conf: vi /etc/clamav-unofficial-sigs/user.conf
/etc/clamav-unofficial-sigs/user.conf

Add Securite Database URL

Find section of declare -a … in /etc/clamav-unofficial-sigs/user.conf and add following line and save

 https://www.securiteinfo.com/get/signatures/YOUR-SIGNATURE-NUMBER/securiteinfo.hdb
 https://www.securiteinfo.com/get/signatures/YOUR-SIGNATURE-NUMBER/securiteinfo.ign2
 https://www.securiteinfo.com/get/signatures/YOUR-SIGNATURE-NUMBER/javascript.ndb
 https://www.securiteinfo.com/get/signatures/YOUR-SIGNATURE-NUMBER/spam_marketing.ndb
 https://www.securiteinfo.com/get/signatures/YOUR-SIGNATURE-NUMBER/securiteinfohtml.hdb
 https://www.securiteinfo.com/get/signatures/YOUR-SIGNATURE-NUMBER/securiteinfoascii.hdb
 https://www.securiteinfo.com/get/signatures/YOUR-SIGNATURE-NUMBER/securiteinfoandroid.hdb
 https://www.securiteinfo.com/get/signatures/YOUR-SIGNATURE-NUMBER/securiteinfoold.hdb
 https://www.securiteinfo.com/get/signatures/YOUR-SIGNATURE-NUMBER/securiteinfopdf.hdb
 https://www.securiteinfo.com/get/signatures/YOUR-SIGNATURE-NUMBER/securiteinfo0hour.hdb
 https://www.securiteinfo.com/get/signatures/YOUR-SIGNATURE-NUMBER/securiteinfo.mdb
 https://www.securiteinfo.com/get/signatures/YOUR-SIGNATURE-NUMBER/securiteinfo.yara
 https://www.securiteinfo.com/get/signatures/YOUR-SIGNATURE-NUMBER/securiteinfo.pdb
 https://www.securiteinfo.com/get/signatures/YOUR-SIGNATURE-NUMBER/securiteinfo.wdb

Add following line and save

#SecuriteInfo
securiteinfo_dbs_rating="MEDIUM"
securiteinfo_authorisation_signature="YOUR-SIGNATURE-NUMBER"
securiteinfo_premium="yes"

Update Signature Database
freshclam
/usr/local/sbin/clamav-unofficial-sigs.sh
Check if signature are being loaded
clamscan --debug 2>&1 /dev/null | grep "loaded"
Clamscan integrity test a specific database file
/usr/local/sbin/clamav-unofficial-sigs.sh -t securiteinfo.mdb
View clamscan config
clamconf -n

Download Eicar Test file
mkdir /etc/clamav-unofficial-sigs/eicar
wget -P /etc/clamav-unofficial-sigs/eicar --user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" https://www.eicar.org/download/eicar-com/?wpdmdl=8840&refresh=672ff3f3dc4c81731195891
wget -P /etc/clamav-unofficial-sigs/eicar --user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" https://www.eicar.org/download/eicar-com-2/?wpdmdl=8842&refresh=672ff3f5047de1731195893
wget -P /etc/clamav-unofficial-sigs/eicar --user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" https://www.eicar.org/download/eicar_com-zip/?wpdmdl=8847&refresh=672ff3f6238e11731195894
wget -P /etc/clamav-unofficial-sigs/eicar --user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" https://www.eicar.org/download/eicar-com-2-2/?wpdmdl=8848&refresh=672ff3f7425da1731195895
wget -P /etc/clamav-unofficial-sigs/eicar --user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" http://www.securiteinfo.com/etc/clamav-unofficial-sigs/eicar/SecuriteInfo.com.Eicar_test_file.13756
wget -P /etc/clamav-unofficial-sigs/eicar --user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" http://www.securiteinfo.com/eicar/SecuriteInfo.com.Eicar-Test-Signature.14788.14668.26795
wget -P /etc/clamav-unofficial-sigs/eicar --user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" http://www.securiteinfo.com/eicar/SecuriteInfo.com.Eicar_Test_Signature.366
wget -P /etc/clamav-unofficial-sigs/eicar --user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" http://www.securiteinfo.com/eicar/SecuriteInfo.com.Eicar_Test_Signature.6363
wget -P /etc/clamav-unofficial-sigs/eicar --user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" http://www.securiteinfo.com/eicar/SecuriteInfo.com.Eicar_Test_Signature.6869

Test clamscan: clamscan /etc/clamav-unofficial-sigs/eicar/*
If test failed (not all detected by signatures), force update again
freshclam
/usr/local/sbin/clamav-unofficial-sigs.sh --force

  1. Since NS8 uses containers and not all configurations can be customized directly, I am using a Bash shell script (attached) to modify the relevant configurations for Postfix and Dovecot.
    6.1. Install Postsrsd via Scratchpad
    https://www.youtube.com/watch?v=nuhl9WKh1Uc
    6.2. Bash Script to Customized Postfix configuration (main.cf and master.cf: Postsrsd, Slow Transport) and Postfix service.
    vi /home/mail1/.config/bin/modify_postfix.sh
/home/mail1/.config/bin/modify_postfix.sh
#!/bin/bash

set -e  # Exit on any error

# Variables
CONTAINER_NAME="postfix"
POSTFIX_MAIN_CF="/etc/postfix/main.cf"
POSTFIX_MASTER_CF="/etc/postfix/master.cf"
REGEX_TRANSPORT_SRS="/etc/postfix/regex_transport_srs"
REGEX_SENDER_CANONICAL_SRS="/etc/postfix/regex_sender_canonical_srs"
TRANSPORT_FILE="/etc/postfix/transport"
REGEX_SLOW_TRANSPORT="/etc/postfix/transport.regexp"
SERVICE_FILE="/home/mail1/.config/systemd/user/postfix.service"

# Fetch the container's current image
IMAGE_NAME=$(podman inspect "$CONTAINER_NAME" --format '{{.Image}}')
if [ -z "$IMAGE_NAME" ]; then
    echo "Error: Could not fetch image name for container $CONTAINER_NAME."
    exit 1
fi

# Step 1: Check if container is running
if ! podman ps | grep -q "$CONTAINER_NAME"; then
    echo "Error: Container $CONTAINER_NAME is not running."
    exit 1
fi

# Step 2: Modify the container
echo "Modifying Postfix configuration in the container..."
podman exec -i "$CONTAINER_NAME" /bin/sh <<EOF
# 1. Modify transport_maps in main.cf
if grep -q '^transport_maps' $POSTFIX_MAIN_CF; then
    # SLOW_TRANSPORT Regexp
    if ! grep -q 'regexp:$REGEX_SLOW_TRANSPORT' $POSTFIX_MAIN_CF; then
        sed -i '/^transport_maps/s|=\(.*\)|= regexp:$REGEX_SLOW_TRANSPORT, \1|' $POSTFIX_MAIN_CF
    fi
    # PostSRSD Regexp
    if ! grep -q 'regexp:$REGEX_TRANSPORT_SRS' $POSTFIX_MAIN_CF; then
        sed -i '/^transport_maps/s|=\(.*\)|= regexp:$REGEX_TRANSPORT_SRS, \1|' $POSTFIX_MAIN_CF
    fi
else
    echo 'transport_maps = regexp:$REGEX_SLOW_TRANSPORT, regexp:$REGEX_TRANSPORT_SRS' >> $POSTFIX_MAIN_CF
fi

# 2. Add PostSRSd configuration if missing
if ! grep -q '^# PostSRSd' $POSTFIX_MAIN_CF; then
    cat <<CONF >> $POSTFIX_MAIN_CF

# PostSRSd
# sender_canonical_maps = socketmap:inet:localhost:10003:forward
# sender_canonical_classes = envelope_sender
recipient_canonical_maps = socketmap:inet:localhost:10003:reverse
recipient_canonical_classes = envelope_recipient, header_recipient
CONF
fi

# 3. Add SLOW_TRANSPORT configuration if missing
if ! grep -q '^# SLOW_TRANSPORT' $POSTFIX_MAIN_CF; then
    cat <<CONF >> $POSTFIX_MAIN_CF

# SLOW_TRANSPORT
hotmail_destination_concurrency_limit = 2
hotmail_destination_rate_delay = 2s
hotmail_destination_recipient_limit = 2
yahoo_initial_destination_concurrency = 1
yahoo_destination_concurrency_limit = 2
yahoo_destination_rate_delay = 1s
yahoo_destination_recipient_limit = 2
CONF
fi

# 4. Create regex_transport_srs file if not exists
if [ ! -f "$REGEX_TRANSPORT_SRS" ]; then
    echo '/^srs=.*@.*$/ smtp:127.0.0.1:10027' > "$REGEX_TRANSPORT_SRS"
fi

# 5. Add PostSRSd customization in master.cf
if ! grep -q '^# PostSRSd Customization' $POSTFIX_MASTER_CF; then
    cat <<CONF >> $POSTFIX_MASTER_CF

# PostSRSd Customization
cleanup-srs unix    n       -       -       -       0       cleanup
  -o sender_canonical_maps=socketmap:inet:localhost:10003:forward
  -o sender_canonical_classes=envelope_sender
  -o recipient_canonical_maps=regexp:/etc/postfix/regex_sender_canonical_srs

127.0.0.1:10027 inet    n       -       -       -       -       smtpd
  -o cleanup_service_name=cleanup-srs
  -o content_filter=smtp:
  -o local_recipient_maps=
  -o relay_recipient_maps=
  -o smtpd_restriction_classes=
  -o smtpd_client_restrictions=
  -o smtpd_helo_restrictions=
  -o smtpd_sender_restrictions=
  -o smtpd_milters=
  -o smtpd_tls_security_level=none
  -o smtpd_sender_login_maps=
  -o smtpd_recipient_restrictions=permit_mynetworks,reject
  -o mynetworks=127.0.0.0/8
  -o strict_rfc821_envelopes=yes
  -o receive_override_options=no_unknown_recipient_checks,no_header_body_checks
  -o smtpd_error_sleep_time=0
  -o smtpd_soft_error_limit=1001
  -o smtpd_hard_error_limit=1000
# SLOW_TRANSPORT
yahoo     unix  -       -       n       -       -       smtp
hotmail   unix  -       -       n       -       -       smtp
CONF
fi

# 6. Create regex_sender_canonical_srs file if not exists
if [ ! -f "$REGEX_SENDER_CANONICAL_SRS" ]; then
    echo '/^srs=(.*)@(.*)\$/      \${1}@\${2}' > "$REGEX_SENDER_CANONICAL_SRS"
fi

# 7. Add lines to /etc/postfix/transport if not already present
if ! grep -q 'SLOW_TRANSPORT' $TRANSPORT_FILE; then
    cat <<CONF >> $TRANSPORT_FILE
# SLOW_TRANSPORT destination domains that need to be rate limited
hotmail.com hotmail:
msn.com hotmail:
live.com hotmail:
windowslive.com hotmail:
yahoo.com.ar yahoo:
yahoo.com.au yahoo:
yahoo.com.br yahoo:
yahoo.ca yahoo:
yahoo.com.cn yahoo:
yahoo.co.id yahoo:
yahoo.com.sg yahoo:
ymail.com yahoo:
rocketmail.com yahoo:
CONF
fi

# 8. Create /etc/postfix/transport.regexp if it doesn't exist
if [ ! -f "$REGEX_SLOW_TRANSPORT" ]; then
    echo '/yahoo(\.[a-z]{2,3}){1,2}$/  yahoo:' > "$REGEX_SLOW_TRANSPORT"
fi
EOF

# Step 3: Commit the changes
echo "Committing container back to the same image..."
if ! podman commit "$CONTAINER_NAME" "$IMAGE_NAME"; then
    echo "Error: Failed to commit changes to the image."
    exit 1
fi
echo "Modifications committed to the image: $IMAGE_NAME"

# Step 4: Reload Postfix
echo "Reloading Postfix..."
if ! podman exec -it "$CONTAINER_NAME" postfix reload; then
    echo "Error: Postfix reload failed."
    exit 1
fi
echo "Postfix reloaded successfully."

# Step 5: MODIFY POSTFIX SERVICE FILE
# Line to add before ExecStop if it doesn't exist
LINE_TO_ADD="ExecStartPost=/home/mail1/.config/bin/modify_postfix.sh"

# Check if the line already exists in the file
if ! grep -q "$LINE_TO_ADD" "$SERVICE_FILE"; then
    sed -i "/^ExecStop=/i $LINE_TO_ADD" "$SERVICE_FILE"
    echo "Service file updated and daemon reloaded."
else
    echo "Service file already contains the required line."
fi

systemctl --user daemon-reload
  1. Using web server module for the main page
    It is still having issue for HTTP-Route on SOGo and Roundcube.
    I prefer to use subdomain path for SOGo (SOGo unable to open the URL) and Roundcube (Roundcube error after login).
Web Front Page

Needed Features Support for:

Hi @amulyawan

Where did you read about synching 2 nodes for high availability?

2 Nodes is possible, several Mail “Apps” also.
But AFAIK, there is NO option for any High Availability scenarios at all!


You may prefer a subdomain path, but NS8 requires a dedicated subdomain!


A really great way to break your NS8 and start again!
NS8 does NOT support ANY applications installed directly on the host!

None of your scripts use any correct way to modify the container, all permissions in the container will be ruined!

→ I really think you should read the fine manual about what works and what not.

I also think you are using an additional hard disk for the mail directory - NS8 requires at least a SSD for any system stuff!

My 2 cents
Andy

1 Like

Hi @Andy_Wismer,

Sorry for the misunderstanding—I initially thought NS8 supported High Availability or Failover: User domains — NS8 documentation.

Provider replicas

Provider replicas implement fault tolerance for user domains. To achieve real fault tolerance, replicas should be installed on different nodes.

You can add a replica from the Domains and users page by selecting the Configuration link from the three-dots menu. Then click the Add provider button, select the target node, and proceed with the installation.

Replicas are configured in master-master mode.

The script I’m using modifies the configuration file. Application installation: Postsrsd is deployed via a Podman container using the Scratchpad module of NS8.

I have already tested the script (updated and rebooted the machine), and so far, NS8 has not broken.
The reason for sharing this script is to highlight that some customizations are necessary but are not yet included in NS8.

In NS7, configuration files could be modified using templates. However, the NS8 manual provides limited guidance on what can and cannot be customized.
For example, in Postfix:

It only allows modifications to main.cf but not master.cf.

From the manual:

Start the editor:
podman exec -ti postfix vi /etc/postfix/main.cf.d/myoverride.cf

From the directory:

/etc/postfix # cat main.cf.d/README

Local configuration override

Any *.cf file in this directory is appended to main.cf.

I tried creating a custom configuration file inside the folder, but it was not appended to main.cf.
Does this method require additional steps to apply the changes?
Or is there a specific process needed to ensure the override files take effect?

Do you have any suggestions for modifying both main.cf and master.cf in NS8?

@amulyawan

Like myself, you probably need to learn the differences with Containers running as root - and containers running rootless.

Most NS8 containers run rootless.

You would need to use something like:

runagent -m mail1

to enter the container environment.

And use “pull” from inside the container to install / add stuff.
If you push something inside, permissions will probably be screwed…

I’m not an expert yet!

My 2 cents
Andy

Hi @Andy_Wismer,

Thanks for your suggestion. I do need study more. :slightly_smiling_face:
I needed it up and running as soon as possible—I know this is not the proper way.
Since it is rootless, there are no preconfigured actions or detailed procedures for customizing the application’s containers. Therefore, I used service files on the host OS to push the changes via the Bash script.

I will take some time to test it again on another machine later, probably in the next couple of weeks.

You CAN install eg nano or other tools inside the container. AFAIK, most containers in NS8 use Alpine Linux, but I could be mistaken…

My 2 cents
Andy

Yes, if I’m not mistaken, I did test installing it directly inside the container. It installed successfully, but after a restart or certain scripts/actions from the application or module, including a reboot, the application was gone.

I didn’t say installing something is persistant… (It isn’t).
So it’s only suitable for correcting your environment or such.

To make it persistent, I modified the postfix.service and added:

ExecStartPost=/home/mail1/.config/bin/modify_postfix.sh

This ensures that after the volume is mounted by Podman from the image, the customization is applied directly to the configuration inside the container.

postfix.service

[root@service1 ~]# cat /home/mail1/.config/systemd/user/postfix.service
[Unit]
Description=Postfix MTA/MSA server

[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
EnvironmentFile=%S/state/environment
WorkingDirectory=%S/state
Restart=always
ExecStartPre=/bin/rm -f %t/postfix.pid %t/postfix.ctr-id
ExecStartPre=/bin/mkdir -vp pcdb
ExecStartPre=-runagent install-certificate postfix
ExecStartPre=runagent discover-services
ExecStart=/usr/bin/podman run
–detach
–conmon-pidfile=%t/postfix.pid
–cidfile=%t/postfix.ctr-id
–cgroups=no-conmon
–replace --name=%N
–network=host
–env=POSTFIX_*
–env-file=discovery.env
–volume=./pcdb:/srv:z
–volume=postfix-cert:/etc/ssl/postfix:z
–volume=postfix-queue:/var/spool/postfix:z
–volume=dovecot-lmtp:/var/lib/umail:z
–volume=/dev/log:/dev/log
–volume=postfix-custom:/etc/postfix/main.cf.d:z
${MAIL_POSTFIX_IMAGE}
ExecStartPost=/home/mail1/.config/bin/modify_postfix.sh
ExecStop=/usr/bin/podman stop --ignore --cidfile %t/postfix.ctr-id -t 60
ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/postfix.ctr-id
ExecReload=runagent discover-services
ExecReload=runagent /usr/bin/podman exec --env-file=discovery.env --env=POSTFIX_* %N reload-config
PIDFile=%t/postfix.pid
Type=forking
SyslogIdentifier=%N

[Install]
WantedBy=default.target