Grommunio Mail Server: Difference between revisions

From Alpine Linux
No edit summary
Line 29: Line 29:
To start, install MariaDB and necessary utilities
To start, install MariaDB and necessary utilities


  apk add mariadb mariadb-client mariadb-server-utils
apk add mariadb mariadb-client mariadb-server-utils




Line 36: Line 36:
The default datapath is /var/lib/mysql and contains the entire database. If you prefer another location define it here and create a symlink. In any case, the grommunio database will be rather small as it only contains the configuration.
The default datapath is /var/lib/mysql and contains the entire database. If you prefer another location define it here and create a symlink. In any case, the grommunio database will be rather small as it only contains the configuration.


  DB_DATA_PATH="/srv/mysql"
DB_DATA_PATH="/srv/mysql"
  DB_ROOT_PASS="Passw0rd1"
DB_ROOT_PASS="Passw0rd1"
  DB_USER="admin"
DB_USER="admin"
  DB_PASS="Passw0rd2"
DB_PASS="Passw0rd2"




Setup the default system tables:
Setup the default system tables:


  sudo mysql_install_db --user=mysql --datadir=${DB_DATA_PATH}
sudo mysql_install_db --user=mysql --datadir=${DB_DATA_PATH}




Set a symlink to the standard mysql data directory for compatibility reason:
Set a symlink to the standard mysql data directory for compatibility reason:


  ln -s /srv/mysql /var/lib/mysql
ln -s /srv/mysql /var/lib/mysql




Restart service:
Restart service:


  rc-service mariadb restart
rc-service mariadb restart




Line 61: Line 61:
Set the new root password to the one defined above and answer everything with 'y'
Set the new root password to the one defined above and answer everything with 'y'


  sudo mysql_secure_installation
sudo mysql_secure_installation




Line 67: Line 67:
Create a new user for Grommunio and assign privileges:
Create a new user for Grommunio and assign privileges:


  echo "GRANT ALL ON *.* TO ${DB_USER}@'127.0.0.1' IDENTIFIED BY '${DB_PASS}' WITH GRANT OPTION;" > /tmp/sql
echo "GRANT ALL ON *.* TO ${DB_USER}@'127.0.0.1' IDENTIFIED BY '${DB_PASS}' WITH GRANT OPTION;" > /tmp/sql
  echo "GRANT ALL ON *.* TO ${DB_USER}@'localhost' IDENTIFIED BY '${DB_PASS}' WITH GRANT OPTION;" >> /tmp/sql
echo "GRANT ALL ON *.* TO ${DB_USER}@'localhost' IDENTIFIED BY '${DB_PASS}' WITH GRANT OPTION;" >> /tmp/sql
  echo "GRANT ALL ON *.* TO ${DB_USER}@'::1' IDENTIFIED BY '${DB_PASS}' WITH GRANT OPTION;" >> /tmp/sql
echo "GRANT ALL ON *.* TO ${DB_USER}@'::1' IDENTIFIED BY '${DB_PASS}' WITH GRANT OPTION;" >> /tmp/sql
  echo "DELETE FROM mysql.user WHERE User='';" >> /tmp/sql
echo "DELETE FROM mysql.user WHERE User='';" >> /tmp/sql
  echo "FLUSH PRIVILEGES;" >> /tmp/sql
echo "FLUSH PRIVILEGES;" >> /tmp/sql
  cat /tmp/sql | mysql -u root --password="${DB_ROOT_PASS}"
cat /tmp/sql | mysql -u root --password="${DB_ROOT_PASS}"




Line 78: Line 78:
Edit the MariaDB configuration for better performance:
Edit the MariaDB configuration for better performance:


  vi /etc/my.cnf.d/mariadb-server.cnf
vi /etc/my.cnf.d/mariadb-server.cnf




Add the following configuration:
Add the following configuration:


  [mysqld]
[mysqld]
  #skip-networking
#skip-networking
 
  ## Configuration for grommunio (most values are default)
## Configuration for grommunio (most values are default)
  innodb_log_buffer_size=16M
innodb_log_buffer_size=16M
  innodb_log_file_size=32M
innodb_log_file_size=32M
  innodb_read_io_threads=4
innodb_read_io_threads=4
  innodb_write_io_threads=4
innodb_write_io_threads=4
 
  join_buffer_size=512K
join_buffer_size=512K
  query_cache_size=0
query_cache_size=0
  query_cache_type=0
query_cache_type=0
  query_cache_limit=2M
query_cache_limit=2M
 
  # Activate performance schema
# Activate performance schema
  performance_schema=ON
performance_schema=ON
 
  # Bind to localhost only
# Bind to localhost only
  bind-address = 127.0.0.1
bind-address = 127.0.0.1
 
  # Disable DNS lookups as we only use localhost connections
# Disable DNS lookups as we only use localhost connections
  skip-name-resolve=ON
skip-name-resolve=ON




Create a default charset configuration for MariaDB:
Create a default charset configuration for MariaDB:


  cat > /etc/my.cnf.d/mariadb-server-default-charset.cnf << EOF
cat > /etc/my.cnf.d/mariadb-server-default-charset.cnf << EOF
  [client]
[client]
  default-character-set = utf8mb4
default-character-set = utf8mb4
 
  [mysqld]
[mysqld]
  collation_server = utf8mb4_general_ci
collation_server = utf8mb4_general_ci
  character_set_server = utf8mb4
character_set_server = utf8mb4
 
  [mysql]
[mysql]
  default-character-set = utf8mb4
default-character-set = utf8mb4
  EOF
EOF




Restart MariaDB and enable it to start on boot:
Restart MariaDB and enable it to start on boot:


  rc-update add mariadb default
rc-update add mariadb default
  rc-service mariadb restart
rc-service mariadb restart




Line 131: Line 131:
Check if the MariaDB listener is running and bound to the correct interface (127.0.0.1):
Check if the MariaDB listener is running and bound to the correct interface (127.0.0.1):


  ss -tulpn
ss -tulpn


=== Create Grommunio Database ===
=== Create Grommunio Database ===
Define the database parameters and create the Grommunio database:
Define the database parameters and create the Grommunio database:


  MYSQL_HOST="localhost"
MYSQL_HOST="localhost"
  MYSQL_USER="grommunio"
MYSQL_USER="grommunio"
  MYSQL_PASS="Passw0rd3"
MYSQL_PASS="Passw0rd3"
  MYSQL_DB="grommunio"
MYSQL_DB="grommunio"


  echo "create database $MYSQL_DB character set 'utf8mb4';" > /tmp/sql
echo "create database $MYSQL_DB character set 'utf8mb4';" > /tmp/sql
  echo "grant select, insert, update, delete, create, drop, index, alter, create temporary tables, lock tables on  
echo "grant select, insert, update, delete, create, drop, index, alter, create temporary tables, lock tables on $MYSQL_DB.* TO $MYSQL_USER@$MYSQL_HOST identified by '$MYSQL_PASS';" >> /tmp/sql
  $MYSQL_DB.* TO $MYSQL_USER@$MYSQL_HOST identified by '$MYSQL_PASS';" >> /tmp/sql
echo "flush privileges;" >> /tmp/sql
  echo "flush privileges;" >> /tmp/sql
cat /tmp/sql | mysql -u admin --password="${DB_PASS}"
  cat /tmp/sql | mysql -u admin --password="${DB_PASS}"




Test the database connection:
Test the database connection:


  mysql -hlocalhost -u grommunio -p${MYSQL_PASS} grommunio
mysql -hlocalhost -u grommunio -p${MYSQL_PASS} grommunio




Line 160: Line 159:
Download MySQLTuner:
Download MySQLTuner:


  wget -v https://raw.githubusercontent.com/major/MySQLTuner-perl/master/mysqltuner.pl -O /tmp/mysqltuner.pl
wget -v https://raw.githubusercontent.com/major/MySQLTuner-perl/master/mysqltuner.pl -O /tmp/mysqltuner.pl




Move it to the correct directory and set permissions:
Move it to the correct directory and set permissions:


  mv /tmp/mysqltuner.pl /usr/local/bin/mysqltuner.pl
mv /tmp/mysqltuner.pl /usr/local/bin/mysqltuner.pl
  chmod 755 /usr/local/bin/mysqltuner.pl
chmod 755 /usr/local/bin/mysqltuner.pl




Line 173: Line 172:
NOTE: The perl-doc package is installed because it's the only one containing the perldiag.pod required by mysqltuner.pl
NOTE: The perl-doc package is installed because it's the only one containing the perldiag.pod required by mysqltuner.pl


  apk add perl perl-doc
apk add perl perl-doc




Execute the script. You will be prompted to enter the credentials of your administrative MariaDB account:
Execute the script. You will be prompted to enter the credentials of your administrative MariaDB account:


  /usr/local/bin/mysqltuner.pl --user admin --pass ${DB_PASS}
/usr/local/bin/mysqltuner.pl --user admin --pass ${DB_PASS}




Line 187: Line 186:
Install the necessary Nginx modules:
Install the necessary Nginx modules:


  apk add nginx nginx-mod-http-headers-more nginx-mod-http-vts nginx-mod-http-brotli
apk add nginx nginx-mod-http-headers-more nginx-mod-http-vts nginx-mod-http-brotli




Line 193: Line 192:
Backup the original Nginx configuration and edit it for security headers and TLS settings:
Backup the original Nginx configuration and edit it for security headers and TLS settings:


  cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.orig
cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.orig
  vi /etc/nginx/nginx.conf
vi /etc/nginx/nginx.conf




Add the following configuration:
Add the following configuration:


  #error_log /var/log/nginx/error.log warn;
#error_log /var/log/nginx/error.log warn;
  error_log syslog:server=unix:/dev/log,facility=local2,nohostname warn;
error_log syslog:server=unix:/dev/log,facility=local2,nohostname warn;
 
  ### Common headers for security
### Common headers for security
  # TODO: Set temporary to a lower value until we are sure it's working
# TODO: Set temporary to a lower value until we are sure it's working
  #more_set_headers "Strict-Transport-Security : max-age=15768000; includeSubDomains; preload";
#more_set_headers "Strict-Transport-Security : max-age=15768000; includeSubDomains; preload";
  more_set_headers "Strict-Transport-Security : max-age=2592000; includeSubDomains;";
more_set_headers "Strict-Transport-Security : max-age=2592000; includeSubDomains;";
  more_set_headers "X-Frame-Options : SAMEORIGIN";
more_set_headers "X-Frame-Options : SAMEORIGIN";
  more_set_headers "Content-Security-Policy : default-src https: data: 'unsafe-inline' 'unsafe-eval' always";
more_set_headers "Content-Security-Policy : default-src https: data: 'unsafe-inline' 'unsafe-eval' always";
  more_set_headers "X-Xss-Protection : 1; mode=block";
more_set_headers "X-Xss-Protection : 1; mode=block";
  more_set_headers "X-Content-Type-Options : nosniff";
more_set_headers "X-Content-Type-Options : nosniff";
  more_set_headers "Referrer-Policy : strict-origin-when-cross-origin";
more_set_headers "Referrer-Policy : strict-origin-when-cross-origin";
  more_set_headers "Server : Follow the white rabbit.";
more_set_headers "Server : Follow the white rabbit.";
 
  ### TLS settings
### TLS settings
  ssl_protocols TLSv1.2 TLSv1.3;
ssl_protocols TLSv1.2 TLSv1.3;
  ssl_session_cache shared:SSL:1m;
ssl_session_cache shared:SSL:1m;
  ssl_session_timeout 5m;
ssl_session_timeout 5m;
 
  log_format main_ssl '$remote_addr - $remote_user [$time_local] "$request" '
log_format main_ssl '$remote_addr - $remote_user [$time_local] "$request" '
          '$status $body_bytes_sent "$http_referer" '
        '$status $body_bytes_sent "$http_referer" '
          '"$http_user_agent" "$http_x_forwarded_for" '
        '"$http_user_agent" "$http_x_forwarded_for" '
          'client_ciphers="$ssl_ciphers" client_curves="$ssl_curves"';
        'client_ciphers="$ssl_ciphers" client_curves="$ssl_curves"';
 
  #access_log /var/log/nginx/access.log main_ssl; # Disabled for performance
#access_log /var/log/nginx/access.log main_ssl; # Disabled for performance
  #access_log syslog:server=unix:/dev/log,facility=local2,nohostname main; # Disabled for performance
#access_log syslog:server=unix:/dev/log,facility=local2,nohostname main; # Disabled for performance
  access_log off;
access_log off;




Disable the default page by renaming it:
Disable the default page by renaming it:


  mv /etc/nginx/http.d/default.conf /etc/nginx/http.d/default.orig
mv /etc/nginx/http.d/default.conf /etc/nginx/http.d/default.orig




Restart Nginx and enable it to start on boot:
Restart Nginx and enable it to start on boot:


  rc-update add nginx
rc-update add nginx
  rc-service nginx restart
rc-service nginx restart




Line 245: Line 244:
Install the basic php packages. The modules are installed as grommunio dependencies:
Install the basic php packages. The modules are installed as grommunio dependencies:


  apk add php83 php83-fpm
apk add php83 php83-fpm


Disable default fpm conf:
Disable default fpm conf:


  mv /etc/php83/php-fpm.d/www.conf /etc/php83/php-fpm.d/www.conf.default
mv /etc/php83/php-fpm.d/www.conf /etc/php83/php-fpm.d/www.conf.default




Line 257: Line 256:
Backup config file /etc/php83/php.ini
Backup config file /etc/php83/php.ini


  cp /etc/php83/php.ini /etc/php83/php-fpm.d/php.ini.orig
cp /etc/php83/php.ini /etc/php83/php-fpm.d/php.ini.orig




1. Disable remote PHP code execution
1. Disable remote PHP code execution


  sed 's/^;\?\(allow_url_fopen\).*/\1 = Off/' -i /etc/php83/php.ini  
sed 's/^;\?\(allow_url_fopen\).*/\1 = Off/' -i /etc/php83/php.ini  
  sed 's/^;\?\(allow_url_include\).*/\1 = Off/' -i /etc/php83/php.ini  
sed 's/^;\?\(allow_url_include\).*/\1 = Off/' -i /etc/php83/php.ini  




2. Disable information leakage
2. Disable information leakage
    
    
  sed 's/^;\?\(expose_php\).*/\1 = Off/' -i /etc/php83/php.ini  
sed 's/^;\?\(expose_php\).*/\1 = Off/' -i /etc/php83/php.ini  




3. Configure Error handling
3. Configure Error handling


  sed 's/^;\?\(display_errors\).*/\1 = Off/' -i /etc/php83/php.ini  
sed 's/^;\?\(display_errors\).*/\1 = Off/' -i /etc/php83/php.ini  
  sed 's/^;\?\(display_startup_errors\).*/\1 = Off/' -i /etc/php83/php.ini  
sed 's/^;\?\(display_startup_errors\).*/\1 = Off/' -i /etc/php83/php.ini  
  sed 's/^;\?\(log_errors\).*/\1 = On/' -i /etc/php83/php.ini  
sed 's/^;\?\(log_errors\).*/\1 = On/' -i /etc/php83/php.ini  
  sed 's/^;\?\(error_log = syslog\).*/\1/' -i /etc/php83/php.ini  
sed 's/^;\?\(error_log = syslog\).*/\1/' -i /etc/php83/php.ini  




4. PHP Resource Control (Optional)
4. PHP Resource Control (Optional)


  #sed 's/^\(max_execution_time\).*/\1 = 25/' -i /etc/php83/php.ini  
#sed 's/^\(max_execution_time\).*/\1 = 25/' -i /etc/php83/php.ini  
  #sed 's/^\(max_input_time\).*/\1 = 25/' -i /etc/php83/php.ini  
#sed 's/^\(max_input_time\).*/\1 = 25/' -i /etc/php83/php.ini  
  #sed 's/^\(memory_limit\).*/\1 = 30M/' -i /etc/php83/php.ini  
#sed 's/^\(memory_limit\).*/\1 = 30M/' -i /etc/php83/php.ini  
  #sed 's/^\(post_max_size\).*/\1 = 1M/' -i /etc/php83/php.ini  
#sed 's/^\(post_max_size\).*/\1 = 1M/' -i /etc/php83/php.ini  
  #sed 's/^;\?\(max_input_vars\).*/\1 = 1000/' -i /etc/php83/php.ini  
#sed 's/^;\?\(max_input_vars\).*/\1 = 1000/' -i /etc/php83/php.ini  




5. Disable vulnerable functions
5. Disable vulnerable functions


  sed 's/^\(disable_functions\).*/\1 = apache_note, apache_setenv, chgrp, curl_multi_exec, define_sys, define_syslog_variables, debugger_off, debugger_on, diskfreespace, _getppid, escapeshellarg, escapeshellcmd, exec, getmyuid, ini_restore, leak, listen, parse_ini_file, passthru, pcntl_alarm, pcntl_async_signals, pcntl_exec, pcntl_fork, pcntl_get_last_error, pcntl_getpriority, pcntl_setpriority, pcntl_signal, pcntl_signal_dispatch, pcntl_signal_get_handler, pcntl_sigprocmask, pcntl_sigtimedwait, pcntl_sigwaitinfo, pcntl_strerror, pcntl_unshare, pcntl_wait, pcntl_waitpid, pcntl_wexitstatus, pcntl_wifcontinued, pcntl_wifexited, pcntl_wifsignaled, pcntl_wifstopped, pcntl_wstopsig, pcntl_wtermsig, posix, posix_ctermid, posix_getcwd, posix_getegid, posix_geteuid, posix_getgid, posix_getgrgid, posix_getgrnam, posix_getgroups, posix_getlogin, posix_getpgid, posix_getpgrp, posix_getpid, posix_getpwnam, posix_getpwuid, posix_getrlimit, posix_getsid, posix_isatty, posix_kill, posix_mkfifo, posix_setegid, posix_seteuid, posix_setgid, posix_setpgid, posix_setsid, posix_setuid, posix_times, posix_ttyname, posix_uname, popen, proc_close, proc_get_status, proc_nice, proc_open, proc_terminate, shell_exec, show_source, system, url_exec/' -i /etc/php83/php.ini  
sed 's/^\(disable_functions\).*/\1 = apache_note, apache_setenv, chgrp, curl_multi_exec, define_sys, define_syslog_variables, debugger_off, debugger_on, diskfreespace, _getppid, escapeshellarg, escapeshellcmd, exec, getmyuid, ini_restore, leak, listen, parse_ini_file, passthru, pcntl_alarm, pcntl_async_signals, pcntl_exec, pcntl_fork, pcntl_get_last_error, pcntl_getpriority, pcntl_setpriority, pcntl_signal, pcntl_signal_dispatch, pcntl_signal_get_handler, pcntl_sigprocmask, pcntl_sigtimedwait, pcntl_sigwaitinfo, pcntl_strerror, pcntl_unshare, pcntl_wait, pcntl_waitpid, pcntl_wexitstatus, pcntl_wifcontinued, pcntl_wifexited, pcntl_wifsignaled, pcntl_wifstopped, pcntl_wstopsig, pcntl_wtermsig, posix, posix_ctermid, posix_getcwd, posix_getegid, posix_geteuid, posix_getgid, posix_getgrgid, posix_getgrnam, posix_getgroups, posix_getlogin, posix_getpgid, posix_getpgrp, posix_getpid, posix_getpwnam, posix_getpwuid, posix_getrlimit, posix_getsid, posix_isatty, posix_kill, posix_mkfifo, posix_setegid, posix_seteuid, posix_setgid, posix_setpgid, posix_setsid, posix_setuid, posix_times, posix_ttyname, posix_uname, popen, proc_close, proc_get_status, proc_nice, proc_open, proc_terminate, shell_exec, show_source, system, url_exec/' -i /etc/php83/php.ini  




Line 300: Line 299:
  sed 's/^;\?\(session.use_only_cookies\).*/\1 = 1/' -i /etc/php83/php.ini  
  sed 's/^;\?\(session.use_only_cookies\).*/\1 = 1/' -i /etc/php83/php.ini  
  sed 's/^;\?\(session.cookie_httponly\).*/\1 = 1/' -i /etc/php83/php.ini
  sed 's/^;\?\(session.cookie_httponly\).*/\1 = 1/' -i /etc/php83/php.ini


== 5. Install and Configure Postfix ==
== 5. Install and Configure Postfix ==
Line 306: Line 307:
Install Postfix and related modules:
Install Postfix and related modules:


  apk add postfix postfix-mysql postfix-pcre
apk add postfix postfix-mysql postfix-pcre




Line 312: Line 313:
Backup and configure the Postfix settings. Adapt the values as necessary, such as `myhostname`, `mynetworks`, and `smtp_tls_chain_files`:
Backup and configure the Postfix settings. Adapt the values as necessary, such as `myhostname`, `mynetworks`, and `smtp_tls_chain_files`:


  mv /etc/postfix/main.cf /etc/postfix/main.cf.orig
mv /etc/postfix/main.cf /etc/postfix/main.cf.orig
  mv /etc/postfix/master.cf /etc/postfix/master.cf.orig
mv /etc/postfix/master.cf /etc/postfix/master.cf.orig




Run Postfix setup:
Run Postfix setup:


  newaliases
newaliases
  postmap /etc/postfix/transport
postmap /etc/postfix/transport




Enable Postfix service:
Enable Postfix service:


  rc-update add postfix
rc-update add postfix
  service postfix restart
rc-service postfix restart




Line 331: Line 332:
Check the Postfix logs for any errors:
Check the Postfix logs for any errors:


tail -f /var/log/maillog
tail -f /var/log/maillog




Line 340: Line 341:
Since Grommunio requires IPv6 for its daemons:
Since Grommunio requires IPv6 for its daemons:


1. Edit `/etc/hosts` to include IPv6 localhost:
Edit `/etc/hosts` to include IPv6 localhost:
  vi /etc/hosts
vi /etc/hosts
  -----
-----
  ::1 localhost ipv6-localhost ipv6-loopback
::1 localhost ipv6-localhost ipv6-loopback
  -----
-----
 


Ensure IPv6 is enabled in `/etc/sysctl.conf`:


2. Ensure IPv6 is enabled in `/etc/sysctl.conf`:
sed -i 's/^net\.ipv6\.conf\..*\.disable_ipv6\s=\s1/#&/' /etc/sysctl.conf
  sed -i 's/^net\.ipv6\.conf\..*\.disable_ipv6\s=\s1/#&/' /etc/sysctl.conf
sysctl -p
  sysctl -p
ping ::1  # Test if IPv6 is working
  ping ::1  # Test if IPv6 is working




Line 356: Line 358:
Set up your MySQL database connection details:
Set up your MySQL database connection details:


  MYSQL_HOST="localhost"
MYSQL_HOST="localhost"
  MYSQL_USER="grommunio"
MYSQL_USER="grommunio"
  MYSQL_PASS="Passw0rd3"
MYSQL_PASS="Passw0rd3"
  MYSQL_DB="grommunio"
MYSQL_DB="grommunio"




Line 365: Line 367:
Adjust the following for your specific setup:
Adjust the following for your specific setup:


  FQDN="mail.example.local"
FQDN="mail.example.local"
  MAILDOMAIN="example.com"
MAILDOMAIN="example.com"
  RELAYHOST="123.123.123.1"
RELAYHOST="123.123.123.1"
  ADMIN_PASS="Passw0rd4"
ADMIN_PASS="Passw0rd4"




Line 374: Line 376:
Install necessary dependencies:
Install necessary dependencies:


  apk add valkey valkey-cli cyrus-sasl cyrus-sasl-login util-linux-login
apk add valkey valkey-cli cyrus-sasl cyrus-sasl-login util-linux-login
  apk add grommunio-gromox grommunio-web grommunio-admin-api grommunio-admin-web grommunio-index grommunio-error-pages
apk add grommunio-gromox grommunio-web grommunio-admin-api grommunio-admin-web grommunio-index grommunio-error-pages




Optionally, install deprecated ActiveSync if needed:
Optionally, install deprecated ActiveSync if needed:


  # apk add grommunio-dav grommunio-sync
# apk add grommunio-dav grommunio-sync




Line 386: Line 388:
Move the largest directory `/var/lib/gromox` to another disk and create a symlink:
Move the largest directory `/var/lib/gromox` to another disk and create a symlink:


  mv /var/lib/gromox /srv/gromox
mv /var/lib/gromox /srv/gromox
  ln -s /srv/gromox /var/lib/gromox
ln -s /srv/gromox /var/lib/gromox




Line 393: Line 395:
Enable all necessary Grommunio services:
Enable all necessary Grommunio services:


  rc-update add grommunio-admin-api
rc-update add grommunio-admin-api
  rc-update add gromox-delivery
rc-update add gromox-delivery
  rc-update add gromox-delivery-queue
rc-update add gromox-delivery-queue
  # Add all the other grommunio services
# Add all the other grommunio services




Line 402: Line 404:
Modify the configuration files to match your environment:
Modify the configuration files to match your environment:


  sed -i "s/mail.example.local/${FQDN}/g" /etc/gromox/*.cfg
sed -i "s/mail.example.local/${FQDN}/g" /etc/gromox/*.cfg
  sed -i "s/example.com/${MAILDOMAIN}/g" /etc/gromox/*.cfg
sed -i "s/example.com/${MAILDOMAIN}/g" /etc/gromox/*.cfg
  # Continue modifying other configuration files (mysql_adaptor.cfg, autodiscover.ini, etc.)
# Continue modifying other configuration files (mysql_adaptor.cfg, autodiscover.ini, etc.)




Line 410: Line 412:
Prepare Postfix for integration with Grommunio:
Prepare Postfix for integration with Grommunio:


  cp -p /etc/postfix/grommunio-virtual-mailbox-maps.cf /etc/postfix/grommunio-virtual-mailbox-sender-maps.cf
cp -p /etc/postfix/grommunio-virtual-mailbox-maps.cf /etc/postfix/grommunio-virtual-mailbox-sender-maps.cf
  sed -i '/^query =/d' /etc/postfix/grommunio-virtual-mailbox-sender-maps.cf
sed -i '/^query =/d' /etc/postfix/grommunio-virtual-mailbox-sender-maps.cf
  echo "query = SELECT username FROM users WHERE username='%s' UNION SELECT aliasname FROM aliases WHERE mainname='%s'" >> /etc/postfix/grommunio-virtual-mailbox-sender-maps.cf
echo "query = SELECT username FROM users WHERE username='%s' UNION SELECT aliasname FROM aliases WHERE mainname='%s'" >> /etc/postfix/grommunio-virtual-mailbox-sender-maps.cf




Line 418: Line 420:
Link and configure your SSL certificates:
Link and configure your SSL certificates:


  ln -s /etc/grommunio-common/nginx/ssl_certificate.conf /etc/grommunio-admin-common/nginx-ssl.conf
ln -s /etc/grommunio-common/nginx/ssl_certificate.conf /etc/grommunio-admin-common/nginx-ssl.conf
  cat /etc/ssl/private/${FQDN}.key.pem /etc/ssl/certs/${FQDN}.cert.pem > /etc/ssl/private/${FQDN}.key_cert.pem
cat /etc/ssl/private/${FQDN}.key.pem /etc/ssl/certs/${FQDN}.cert.pem > /etc/ssl/private/${FQDN}.key_cert.pem
  chmod 640 /etc/ssl/private/*.key_cert.pem
chmod 640 /etc/ssl/private/*.key_cert.pem
  addgroup gromox ssl-cert
addgroup gromox ssl-cert




Line 427: Line 429:
Set up authentication services:
Set up authentication services:


  # Configure PAM for SMTP
Configure PAM for SMTP:
  cat > /etc/pam.d/smtp <<EOF
 
  #%PAM-1.0
cat > /etc/pam.d/smtp <<EOF
  auth required pam_gromox.so service=smtp
#%PAM-1.0
  account required pam_permit.so
auth required pam_gromox.so service=smtp
  EOF
account required pam_permit.so
EOF
 
 
Configure SASL authentication:


  # Configure SASL authentication
cat > /etc/conf.d/saslauthd <<EOF
  cat > /etc/conf.d/saslauthd <<EOF
SASLAUTHD_OPTS="-a pam -r"
  SASLAUTHD_OPTS="-a pam -r"
EOF
  EOF




Line 443: Line 448:
Initialize the database:
Initialize the database:


  gromox-dbop -C
gromox-dbop -C


Set the Grommunio admin password:
Set the Grommunio admin password:


  grommunio-admin passwd --password "${ADMIN_PASS}"
grommunio-admin passwd --password "${ADMIN_PASS}"





Revision as of 23:34, 30 November 2024

This material is work-in-progress ...

This is a work in progress
(Last edited by Midas on 30 Nov 2024.)


HOWTO: Install AlpineLinux Mail Server with Grommunio

This tutorial outlines the steps for setting up a mail server on Alpine Linux using Grommunio, a modern, open-source groupware solution that supports email and calendar services. The installation includes MariaDB, Nginx, PHP, Postfix, and other components necessary for a fully functioning mail server.

Prerequisites

Before proceeding with the installation, ensure you have a fresh Alpine Linux system setup. You'll need root privileges to execute these commands.

Procedure

  1. Install and configure MariaDB
  2. MariaDB performance tuning (optional)
  3. Install and configure Nginx
  4. Install and configure PHP
  5. Install and configure Postfix
  6. Install and configure Grommunio
  7. Configure Valkey (Redis replacement)
  8. Install and configure Rspamd
  9. Finalize and verify installation


1. Install and Configure MariaDB

Install MariaDB

To start, install MariaDB and necessary utilities

apk add mariadb mariadb-client mariadb-server-utils


Define the variables used later in the setup setup and configuration.

The default datapath is /var/lib/mysql and contains the entire database. If you prefer another location define it here and create a symlink. In any case, the grommunio database will be rather small as it only contains the configuration.

DB_DATA_PATH="/srv/mysql"
DB_ROOT_PASS="Passw0rd1"
DB_USER="admin"
DB_PASS="Passw0rd2"


Setup the default system tables:

sudo mysql_install_db --user=mysql --datadir=${DB_DATA_PATH}


Set a symlink to the standard mysql data directory for compatibility reason:

ln -s /srv/mysql /var/lib/mysql


Restart service:

rc-service mariadb restart


Run the built-in security script:

Set the new root password to the one defined above and answer everything with 'y'

sudo mysql_secure_installation


Create MariaDB User for Grommunio

Create a new user for Grommunio and assign privileges:

echo "GRANT ALL ON *.* TO ${DB_USER}@'127.0.0.1' IDENTIFIED BY '${DB_PASS}' WITH GRANT OPTION;" > /tmp/sql
echo "GRANT ALL ON *.* TO ${DB_USER}@'localhost' IDENTIFIED BY '${DB_PASS}' WITH GRANT OPTION;" >> /tmp/sql
echo "GRANT ALL ON *.* TO ${DB_USER}@'::1' IDENTIFIED BY '${DB_PASS}' WITH GRANT OPTION;" >> /tmp/sql
echo "DELETE FROM mysql.user WHERE User=;" >> /tmp/sql
echo "FLUSH PRIVILEGES;" >> /tmp/sql
cat /tmp/sql | mysql -u root --password="${DB_ROOT_PASS}"


Configure MariaDB for Grommunio

Edit the MariaDB configuration for better performance:

vi /etc/my.cnf.d/mariadb-server.cnf


Add the following configuration:

[mysqld]
#skip-networking

## Configuration for grommunio (most values are default)
innodb_log_buffer_size=16M
innodb_log_file_size=32M
innodb_read_io_threads=4
innodb_write_io_threads=4

join_buffer_size=512K
query_cache_size=0
query_cache_type=0
query_cache_limit=2M

# Activate performance schema
performance_schema=ON

# Bind to localhost only
bind-address = 127.0.0.1

# Disable DNS lookups as we only use localhost connections
skip-name-resolve=ON


Create a default charset configuration for MariaDB:

cat > /etc/my.cnf.d/mariadb-server-default-charset.cnf << EOF
[client]
default-character-set = utf8mb4

[mysqld]
collation_server = utf8mb4_general_ci
character_set_server = utf8mb4

[mysql]
default-character-set = utf8mb4
EOF


Restart MariaDB and enable it to start on boot:

rc-update add mariadb default
rc-service mariadb restart


Verify MariaDB Setup

Check if the MariaDB listener is running and bound to the correct interface (127.0.0.1):

ss -tulpn

Create Grommunio Database

Define the database parameters and create the Grommunio database:

MYSQL_HOST="localhost"
MYSQL_USER="grommunio"
MYSQL_PASS="Passw0rd3"
MYSQL_DB="grommunio"
echo "create database $MYSQL_DB character set 'utf8mb4';" > /tmp/sql
echo "grant select, insert, update, delete, create, drop, index, alter, create temporary tables, lock tables on $MYSQL_DB.* TO $MYSQL_USER@$MYSQL_HOST identified by '$MYSQL_PASS';" >> /tmp/sql
echo "flush privileges;" >> /tmp/sql
cat /tmp/sql | mysql -u admin --password="${DB_PASS}"


Test the database connection:

mysql -hlocalhost -u grommunio -p${MYSQL_PASS} grommunio


2. MariaDB Performance Tuning (Optional)

To assist us in checking and tuning the configuration as per our specific needs, we can install mysqltuner, a script that will provide suggestions to improve the performance of our database server and increase its stability

Download MySQLTuner:

wget -v https://raw.githubusercontent.com/major/MySQLTuner-perl/master/mysqltuner.pl -O /tmp/mysqltuner.pl


Move it to the correct directory and set permissions:

mv /tmp/mysqltuner.pl /usr/local/bin/mysqltuner.pl
chmod 755 /usr/local/bin/mysqltuner.pl


Install perl:

NOTE: The perl-doc package is installed because it's the only one containing the perldiag.pod required by mysqltuner.pl

apk add perl perl-doc


Execute the script. You will be prompted to enter the credentials of your administrative MariaDB account:

/usr/local/bin/mysqltuner.pl --user admin --pass ${DB_PASS}


3. Install and Configure Nginx

Install Nginx

Install the necessary Nginx modules:

apk add nginx nginx-mod-http-headers-more nginx-mod-http-vts nginx-mod-http-brotli


Configure Nginx

Backup the original Nginx configuration and edit it for security headers and TLS settings:

cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.orig
vi /etc/nginx/nginx.conf


Add the following configuration:

#error_log /var/log/nginx/error.log warn;
error_log syslog:server=unix:/dev/log,facility=local2,nohostname warn;

### Common headers for security
# TODO: Set temporary to a lower value until we are sure it's working
#more_set_headers "Strict-Transport-Security : max-age=15768000; includeSubDomains; preload";
more_set_headers "Strict-Transport-Security : max-age=2592000; includeSubDomains;";
more_set_headers "X-Frame-Options : SAMEORIGIN";
more_set_headers "Content-Security-Policy : default-src https: data: 'unsafe-inline' 'unsafe-eval' always";
more_set_headers "X-Xss-Protection : 1; mode=block";
more_set_headers "X-Content-Type-Options : nosniff";
more_set_headers "Referrer-Policy : strict-origin-when-cross-origin";
more_set_headers "Server : Follow the white rabbit.";

### TLS settings
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;

log_format main_ssl '$remote_addr - $remote_user [$time_local] "$request" '
        '$status $body_bytes_sent "$http_referer" '
        '"$http_user_agent" "$http_x_forwarded_for" '
        'client_ciphers="$ssl_ciphers" client_curves="$ssl_curves"';

#access_log /var/log/nginx/access.log main_ssl; # Disabled for performance
#access_log syslog:server=unix:/dev/log,facility=local2,nohostname main; # Disabled for performance
access_log off;


Disable the default page by renaming it:

mv /etc/nginx/http.d/default.conf /etc/nginx/http.d/default.orig


Restart Nginx and enable it to start on boot:

rc-update add nginx
rc-service nginx restart


4. Install and Configure PHP

Install PHP

Install the basic php packages. The modules are installed as grommunio dependencies:

apk add php83 php83-fpm

Disable default fpm conf:

mv /etc/php83/php-fpm.d/www.conf /etc/php83/php-fpm.d/www.conf.default


Harden PHP Configuration

Backup config file /etc/php83/php.ini

cp /etc/php83/php.ini /etc/php83/php-fpm.d/php.ini.orig


1. Disable remote PHP code execution

sed 's/^;\?\(allow_url_fopen\).*/\1 = Off/' -i /etc/php83/php.ini 
sed 's/^;\?\(allow_url_include\).*/\1 = Off/' -i /etc/php83/php.ini 


2. Disable information leakage

sed 's/^;\?\(expose_php\).*/\1 = Off/' -i /etc/php83/php.ini 


3. Configure Error handling

sed 's/^;\?\(display_errors\).*/\1 = Off/' -i /etc/php83/php.ini 
sed 's/^;\?\(display_startup_errors\).*/\1 = Off/' -i /etc/php83/php.ini 
sed 's/^;\?\(log_errors\).*/\1 = On/' -i /etc/php83/php.ini 
sed 's/^;\?\(error_log = syslog\).*/\1/' -i /etc/php83/php.ini 


4. PHP Resource Control (Optional)

#sed 's/^\(max_execution_time\).*/\1 = 25/' -i /etc/php83/php.ini 
#sed 's/^\(max_input_time\).*/\1 = 25/' -i /etc/php83/php.ini 
#sed 's/^\(memory_limit\).*/\1 = 30M/' -i /etc/php83/php.ini 
#sed 's/^\(post_max_size\).*/\1 = 1M/' -i /etc/php83/php.ini 
#sed 's/^;\?\(max_input_vars\).*/\1 = 1000/' -i /etc/php83/php.ini 


5. Disable vulnerable functions

sed 's/^\(disable_functions\).*/\1 = apache_note, apache_setenv, chgrp, curl_multi_exec, define_sys, define_syslog_variables, debugger_off, debugger_on, diskfreespace, _getppid, escapeshellarg, escapeshellcmd, exec, getmyuid, ini_restore, leak, listen, parse_ini_file, passthru, pcntl_alarm, pcntl_async_signals, pcntl_exec, pcntl_fork, pcntl_get_last_error, pcntl_getpriority, pcntl_setpriority, pcntl_signal, pcntl_signal_dispatch, pcntl_signal_get_handler, pcntl_sigprocmask, pcntl_sigtimedwait, pcntl_sigwaitinfo, pcntl_strerror, pcntl_unshare, pcntl_wait, pcntl_waitpid, pcntl_wexitstatus, pcntl_wifcontinued, pcntl_wifexited, pcntl_wifsignaled, pcntl_wifstopped, pcntl_wstopsig, pcntl_wtermsig, posix, posix_ctermid, posix_getcwd, posix_getegid, posix_geteuid, posix_getgid, posix_getgrgid, posix_getgrnam, posix_getgroups, posix_getlogin, posix_getpgid, posix_getpgrp, posix_getpid, posix_getpwnam, posix_getpwuid, posix_getrlimit, posix_getsid, posix_isatty, posix_kill, posix_mkfifo, posix_setegid, posix_seteuid, posix_setgid, posix_setpgid, posix_setsid, posix_setuid, posix_times, posix_ttyname, posix_uname, popen, proc_close, proc_get_status, proc_nice, proc_open, proc_terminate, shell_exec, show_source, system, url_exec/' -i /etc/php83/php.ini 


6. Session Security

sed 's/^;\?\(session.use_strict_mode\).*/\1 = 1/' -i /etc/php83/php.ini 
sed 's/^;\?\(session.use_cookies\).*/\1 = 1/' -i /etc/php83/php.ini 
sed 's/^;\?\(session.cookie_secure\).*/\1 = 1/' -i /etc/php83/php.ini 
sed 's/^;\?\(session.use_only_cookies\).*/\1 = 1/' -i /etc/php83/php.ini 
sed 's/^;\?\(session.cookie_httponly\).*/\1 = 1/' -i /etc/php83/php.ini


5. Install and Configure Postfix

Install Postfix

Install Postfix and related modules:

apk add postfix postfix-mysql postfix-pcre


Configure Postfix

Backup and configure the Postfix settings. Adapt the values as necessary, such as `myhostname`, `mynetworks`, and `smtp_tls_chain_files`:

mv /etc/postfix/main.cf /etc/postfix/main.cf.orig
mv /etc/postfix/master.cf /etc/postfix/master.cf.orig


Run Postfix setup:

newaliases
postmap /etc/postfix/transport


Enable Postfix service:

rc-update add postfix
rc-service postfix restart


Verify Postfix Logs

Check the Postfix logs for any errors:

tail -f /var/log/maillog


6. Install and Configure Grommunio

Enable IPv6

Since Grommunio requires IPv6 for its daemons:

Edit `/etc/hosts` to include IPv6 localhost:

vi /etc/hosts
-----
::1		localhost ipv6-localhost ipv6-loopback
-----


Ensure IPv6 is enabled in `/etc/sysctl.conf`:

sed -i 's/^net\.ipv6\.conf\..*\.disable_ipv6\s=\s1/#&/' /etc/sysctl.conf
sysctl -p
ping ::1  # Test if IPv6 is working


Configure Database Parameters

Set up your MySQL database connection details:

MYSQL_HOST="localhost"
MYSQL_USER="grommunio"
MYSQL_PASS="Passw0rd3"
MYSQL_DB="grommunio"


Specify Internal FQDN, Mail Domain, and Relayhost

Adjust the following for your specific setup:

FQDN="mail.example.local"
MAILDOMAIN="example.com"
RELAYHOST="123.123.123.1"
ADMIN_PASS="Passw0rd4"


Install Dependencies and Grommunio Packages

Install necessary dependencies:

apk add valkey valkey-cli cyrus-sasl cyrus-sasl-login util-linux-login
apk add grommunio-gromox grommunio-web grommunio-admin-api grommunio-admin-web grommunio-index grommunio-error-pages


Optionally, install deprecated ActiveSync if needed:

# apk add grommunio-dav grommunio-sync


Move Mail Storage to Another Disk

Move the largest directory `/var/lib/gromox` to another disk and create a symlink:

mv /var/lib/gromox /srv/gromox
ln -s /srv/gromox /var/lib/gromox


Enable Required Services

Enable all necessary Grommunio services:

rc-update add grommunio-admin-api
rc-update add gromox-delivery
rc-update add gromox-delivery-queue
# Add all the other grommunio services


Configure Grommunio Files

Modify the configuration files to match your environment:

sed -i "s/mail.example.local/${FQDN}/g" /etc/gromox/*.cfg
sed -i "s/example.com/${MAILDOMAIN}/g" /etc/gromox/*.cfg
# Continue modifying other configuration files (mysql_adaptor.cfg, autodiscover.ini, etc.)


Configure Postfix

Prepare Postfix for integration with Grommunio:

cp -p /etc/postfix/grommunio-virtual-mailbox-maps.cf /etc/postfix/grommunio-virtual-mailbox-sender-maps.cf
sed -i '/^query =/d' /etc/postfix/grommunio-virtual-mailbox-sender-maps.cf
echo "query = SELECT username FROM users WHERE username='%s' UNION SELECT aliasname FROM aliases WHERE mainname='%s'" >> /etc/postfix/grommunio-virtual-mailbox-sender-maps.cf


Configure TLS Certificates

Link and configure your SSL certificates:

ln -s /etc/grommunio-common/nginx/ssl_certificate.conf /etc/grommunio-admin-common/nginx-ssl.conf
cat /etc/ssl/private/${FQDN}.key.pem /etc/ssl/certs/${FQDN}.cert.pem > /etc/ssl/private/${FQDN}.key_cert.pem
chmod 640 /etc/ssl/private/*.key_cert.pem
addgroup gromox ssl-cert


Configure PAM and SASL

Set up authentication services:

Configure PAM for SMTP:

cat > /etc/pam.d/smtp <<EOF
#%PAM-1.0
auth required pam_gromox.so service=smtp
account required pam_permit.so
EOF


Configure SASL authentication:

cat > /etc/conf.d/saslauthd <<EOF
SASLAUTHD_OPTS="-a pam -r"
EOF


Initialize the Database and Set Admin Password

Initialize the database:

gromox-dbop -C

Set the Grommunio admin password:

grommunio-admin passwd --password "${ADMIN_PASS}"


Configure Firewall Ports

Open the necessary firewall ports:

 # Required ports: 25, 80, 443, etc.


7. Configure Valkey (Redis Replacement)

Enable Syslog

 vi /etc/valkey/grommunio.conf
 -----
 syslog-enabled yes
 syslog-ident valkey
 syslog-facility local0
 -----


Enable Memory Overcommit

 vi /etc/sysctl.conf
 -----
 vm.overcommit_memory = 1
 -----
 sysctl -p


Start Valkey and Test

 rcctl restart valkey@grommunio
 valkey-cli ping  # Expected result: 'PONG'


8. Install and Configure Rspamd

Install Rspamd

 apk add rspamd rspamd-client


Configure Rspamd

Modify Rspamd configuration files:

 cat > /etc/rspamd/local.d/options.inc <<EOF
 dns {
   enable_dnssec = true;
   timeout = 4s;
  retransmits = 5;
 }
 EOF
 cat > /etc/rspamd/local.d/redis.conf <<EOF
 read_servers = "127.0.0.1";
 write_servers = "127.0.0.1";
 EOF
 cat > /etc/rspamd/local.d/worker-proxy.inc <<EOF
 milter = yes;
 bind_socket = "/var/run/rspamd/worker-proxy.sock mode=0660 owner=rspamd";
 timeout = 120s;
 upstream "local" {
   default = yes;
   self_scan = yes;
 }
 count = 4;
 EOF


Add Postfix to Rspamd Group

 addgroup postfix rspamd


Configure DKIM Signing

 cat > /etc/rspamd/local.d/dkim_signing.conf <<EOF
 enabled = true;
 path = "/var/lib/rspamd/dkim/\$domain-\$selector.key";
 selector = "dkim";
 sign_authenticated = true;
 sign_local = false;
 domain {
   example.com { selector = "202406"; }
 }
 EOF


Generate DKIM Key Pair

 mkdir -p /var/lib/rspamd/dkim
 rspamadm dkim_keygen -s 202406 -t ED25519 -d example.com -k /var/lib/rspamd/dkim/example.com-202406.key > 
 /var/lib/rspamd/dkim/example.com-202406.pub


Start Rspamd

 rc-update add rspamd
 rcctl start rspamd


9. Finalize and Verify Installation

Restart Services

Restart all services:

 rcctl restart postfix saslauthd rspamd valkey@grommunio nginx php-fpm83 gromox-delivery gromox-event \
   gromox-http gromox-imap gromox-midb gromox-pop3 gromox-delivery-queue gromox-timer gromox-zcore \
   grommunio-admin-api


Verify Service Status

Check the status of all services:

 rcctl status


Ensure that all services (Postfix, MariaDB, Nginx, PHP, Grommunio) are running correctly:

 ss -tulpn


Verify Mail Functionality

Test sending and receiving emails using a mail client and verifying server logs for any errors.


Check Logs

Inspect logs for any errors or issues:

 find /var/log -type f | xargs tail -n50 | grep -iE '==>|fail|crit|error|alert|corrupt|warning'


Web UI Access

Admin UI: [1](https://mail.example.local:8443)


10. End User Configuration

Admin UI

Log into the Admin UI with the username `admin` and the previously created `ADMIN_PASS`.

License Configuration

If you have a license, you can configure it under Grommunio settings in the Admin UI.