PHP admin panel for iRedMail. Manage mailboxes, domains, forwarding, retention policies, 2FA, audit logging, and IP blocking.
  • PHP 82.2%
  • JavaScript 14.6%
  • CSS 3.2%
Find a file
David Klaverstyn 1c20e14554 Security hardening: TOTP replay protection, session fixation fix, input validation
- Block reuse of TOTP codes within the same 30s window via last_totp_step tracking
- Regenerate session ID after password auth when 2FA is pending
- Validate timezone POST input against timezone_identifiers_list()
- Remove server filesystem path from Composer missing error message
- Bump version to 26.06.04-1

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-04 20:19:58 +10:00
app Security hardening: TOTP replay protection, session fixation fix, input validation 2026-06-04 20:19:58 +10:00
assets Initial commit 2026-06-04 19:41:25 +10:00
vendor Initial commit 2026-06-04 19:41:25 +10:00
.gitignore Security hardening: TOTP replay protection, session fixation fix, input validation 2026-06-04 20:19:58 +10:00
composer.json Initial commit 2026-06-04 19:41:25 +10:00
composer.lock Initial commit 2026-06-04 19:41:25 +10:00
config.example.php Security hardening: TOTP replay protection, session fixation fix, input validation 2026-06-04 20:19:58 +10:00
DEPLOY.md Initial commit 2026-06-04 19:41:25 +10:00
index.php Security hardening: TOTP replay protection, session fixation fix, input validation 2026-06-04 20:19:58 +10:00
README.md Move php-qrcode instructions to Dependencies section 2026-06-04 19:53:20 +10:00

MX Admin (MXA)

A lightweight web-based admin panel for iRedMail mail servers. Built as a replacement for iRedAdmin with a focus on simplicity, security, and practical day-to-day mail server management.

Features

  • Mailbox management — create, edit, delete users; set quotas; toggle protocols
  • Domain management — multiple domain support with per-domain admin delegation
  • Forwarding & aliases — forwarding rules, aliases, and catch-all addresses
  • Retention policies — auto-delete mail older than N days per domain
  • Distribution groups — manage mailing lists
  • SOGo integration — calendar/contact sharing; SOGo data cleanup on user delete
  • Roundcube integration — orphaned data cleanup on user delete
  • Two-factor authentication — TOTP with QR code setup and backup codes
  • Audit log — all admin actions logged with IP and timestamp
  • IP blocking — automatic permanent IP block after repeated failed logins
  • Mail queue monitoring — view Postfix queue status
  • Dashboard — recent activity, admin logins, failed login attempts

Requirements

  • PHP 8.2+
  • MariaDB / MySQL (iRedMail vmail database schema)
  • Nginx (iRedMail default — MXA runs as a location block within the existing server)
  • iRedMail installed and configured
  • PHP extensions: pdo_mysql, session, hash, openssl

Installation

1. Database user

CREATE USER 'iredadmin'@'127.0.0.1' IDENTIFIED BY 'yourpassword';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, INDEX, ALTER ON vmail.* TO 'iredadmin'@'127.0.0.1';
GRANT SELECT, INSERT, DELETE ON sogo.* TO 'iredadmin'@'127.0.0.1';
GRANT SELECT, DELETE ON roundcubemail.* TO 'iredadmin'@'127.0.0.1';
FLUSH PRIVILEGES;

2. Config file

mkdir -p /etc/mxa
cp config.example.php /etc/mxa/config.php
chown root:www-data /etc/mxa/config.php
chmod 640 /etc/mxa/config.php

Edit /etc/mxa/config.php and set your database credentials, timezone, and other options. See the Configuration Reference below.

3. Web files

mkdir -p /var/www/mxa
cp -r assets app index.php vendor /var/www/mxa/
chown -R www-data:www-data /var/www/mxa
chmod -R 750 /var/www/mxa

4. Nginx

MXA is designed to run as a /mxa/ location block within an existing iRedMail nginx server — it does not require a separate server block or virtual host.

Create the template file:

vi /etc/nginx/templates/mxa.tmpl
location ^~ /mxa/ {
    alias /var/www/mxa/;
    index index.php;

    # Static assets
    location /mxa/assets/ {
        expires 30d;
        access_log off;
    }

    # Block direct access to app and vendor directories
    location ^~ /mxa/app/ {
        deny all;
    }
    location ^~ /mxa/vendor/ {
        deny all;
    }

    # Only allow index.php to execute as PHP
    location ~ \.php$ {
        if ($fastcgi_script_name !~ "^/mxa/index\.php$") {
            return 404;
        }
        include snippets/fastcgi-php.conf;
        fastcgi_param SCRIPT_FILENAME $request_filename;
        fastcgi_pass 127.0.0.1:9999;
    }
}

# Redirect /mxa → /mxa/
location = /mxa {
    return 301 $scheme://$http_host/mxa/;
}

Include it in your iRedMail SSL server block (add before any other includes):

vi /etc/nginx/sites-available/00-default-ssl.conf
include /etc/nginx/templates/mxa.tmpl;

Update security headers in /etc/nginx/templates/hsts.tmpl:

add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

Reload nginx:

nginx -t && systemctl reload nginx

5. Verify

# app/ and vendor/ must be blocked
curl -I https://mail.example.com/mxa/app/functions.php
# → 403 Forbidden

curl -I https://mail.example.com/mxa/vendor/autoload.php
# → 403 Forbidden

# App should load
curl -I https://mail.example.com/mxa/
# → 200 OK

Configuration Reference

All settings are in /etc/mxa/config.php (outside the web root). See config.example.php for a fully commented template.

Constant Default Description
APP_TITLE MX Admin Title shown in the browser and UI
CHANGELOG_URL '' URL of a changelog HTML file; leave empty to disable update checks
DB_HOST 127.0.0.1 MariaDB host
DB_USER iredadmin MariaDB username
DB_PASS MariaDB password
DB_NAME vmail iRedMail database name
VMAIL_BASE /var/vmail/vmail1 Base path for mail storage
SESSION_TIMEOUT 3600 Idle session timeout in seconds
LOGIN_BLOCK_ATTEMPTS 5 Failed logins before IP block (per 15 min window)
PASSWORD_GEN_LENGTH 16 Length of generated passwords
DASHBOARD_SHOW_RECENT_ACTIVITY 1 Show recent activity panel (0 to hide)
APP_TIMEZONE UTC Timezone for audit log date display
BEHIND_PROXY false Set true if behind a trusted nginx reverse proxy
APP_DIR /var/www/mxa/app Path to the app/ directory on disk

Security Notes

  • config.php is stored at /etc/mxa/config.php, outside the web root, and never committed to version control.
  • The app/ directory is denied at the nginx level — only index.php and assets/ are publicly reachable.
  • Only index.php is executed by PHP-FPM; all other .php requests return 404.
  • Sessions use HttpOnly, Secure, and SameSite=Lax cookie flags.
  • All POST/GET mutations are protected by CSRF tokens.
  • IPs are permanently blocked after repeated failed login attempts.
  • Passwords are stored in iRedMail's native format ({BLF-CRYPT} bcrypt or SSHA512).
  • The database user requires only SELECT/INSERT/UPDATE/DELETE — no SUPER or FILE privileges.

Updating

Replace the files in /var/www/mxa/. The config at /etc/mxa/config.php is never touched by updates.

Dependencies

chillerlan/php-qrcode

chillerlan/php-qrcode — QR code generation for 2FA setup (MIT / Apache 2.0)

The vendor/ directory is included in this repository, so no action is required for a standard install.

If you need to reinstall or update it, run the following from /var/www/mxa/:

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php composer-setup.php --quiet
php -r "unlink('composer-setup.php');"

php composer.phar require chillerlan/php-qrcode

rm composer.phar