User Tools

Site Tools


Maacom, simple web mail account manager

Mail account manager, or easy “Maacom” =)

I wrote this application for using into small-middle corporation. I wanted to write a simple classic object-oriented web application, that can be used as a training application.

The application wrote with

  • Perl Mojolicious as MVC framework and
  • Zurb Foundation as CSS

Work example of configuration Exim and Dovecot attached below the page.

You can

  1. Easy add, delete, rename and edit domain, user and alias records. You can rename entire domain with safe all your accounts!
  2. Set personal and domain mail quotas, size of mailbox calculates in background from mail dirs.
  3. Edit/use additional list of forwarded domain, lists of unwanted and trusted hosts
  4. Live view tail of mail log from operation system for debugging purpose.
  5. Configure mail relay and then send a love letter to a loved one =)

Features

  1. Password file in the same Apache htpasswd format.
  2. Quotas are counted in MB (megabytes).

Again I wanted to write it app with Ruby and Sinatra MVC (I love write program in Ruby OOP style), but then I thought about portability on Linux distributions (I use for work FreeBSD and latest releases of ruby gems) and then wrote with Perl and Mojolicious. “Life is Life”.

Source code

To install from sources

For postgres

# wget http://wiki.unix7.org/_media/dist/maacom-0.xx.tar.gz
# tar xzvf  maacom-0.xx.tar.gz
# cd maacom-0.xx
# ./configure --prefix=/usr/local --with-user=vmail --with-group=vmail
# make
# make install
# cd /usr/local/etc/maacom
# cp maacom.conf.example maacom.conf
# cp maacom.crt.example maacom.crt
# cp maacom.key.example maacom.key
# cp maacom.pw.example maacom.pw
# psql -U postgres -h localhost postgres < /usr/local/share/maacom/create-pg-db.sql
# psql -U maacom -h localhost maacom < /usr/local/share/maacom/schema.sql
# service maacom start

For systemd start like

# systemctl start maacom.service

After them connect as https://yourhost.com:8082
Default login/password is master/password.

For change password you can use Apache htpasswd util or simular

# htpasswd /usr/local/etc/maacom/maacom.pw yourlogin
# htpasswd -D /usr/local/etc/maacom/maacom.pw master

Screens

Configuration example

maacom.conf
#
# $Id: maacom.conf,v 1.2 2017/12/10 20:42:53 root Exp root $
#
loglevel = info
dbname = maacom
dbhost = 127.0.0.1
dblogin = maacom
dbpassword = pazzword
dbengine = postgres # or sqlite
#EOF

Default configuration

Pairs key=value in config file override default configuration.

conffile = @app_confdir@/maacom.conf
pwfile = @app_confdir@/maacom.pw
logfile = @app_logdir@/maacom.log
loglevel = info
pidfile = @app_rundir@/maacom.pid
crtfile = @app_confdir@/maacom.crt
keyfile = @app_confdir@/maacom.key
listenaddr4 = 0.0.0.0
listenaddr6 = [::]
listenport = 8082
mxlog = /var/log/mail.log
maildir = /var/vmail
dbname = @app_datadir@/mail.db
dbhost = 
dblogin = 
dbpassword = 
dbengine = sqlite3
group = @app_group@
user = @app_user@

SQL schema

schema.sql
BEGIN TRANSACTION;
CREATE TABLE domains (
    id int unique NOT NULL PRIMARY KEY,
    name text unique,
    size int  DEFAULT 0,
    quota int  DEFAULT 0
);
CREATE TABLE users (
    id int unique NOT NULL PRIMARY KEY,
    name text,
    domain_id int,
    password text,
    hash text,
    size int DEFAULT 0,
    quota int  DEFAULT 0
);
CREATE TABLE aliases (
    id int unique NOT NULL PRIMARY KEY,
    name text,
    domain_id int,
    list text
);
CREATE TABLE forwarded (
    id int unique NOT NULL PRIMARY KEY,
    name text unique
);
CREATE TABLE unwanted (
    id int unique NOT NULL PRIMARY KEY,
    name text unique
);
CREATE TABLE trusted (
    id int unique NOT NULL PRIMARY KEY,
    name text unique
);
COMMIT;

Create SQLite database

$ mkdir -p /var/maacom/mail.db
$ sqlite3 /var/maacom/mail.db < schema.sql

Create Postgresql database

create-db.sql
create user maacom encrypted password 'password';
create database maacom owner maacom;
$ psql -U postgres -h localhost postgres < create-db.sql
$ psql -U maacom -h localhost maacom < schema.sql

Exim configuration example

exim.conf
#
# $Id: exim.conf,v 1.10 2016/10/08 07:25:40 root Exp root $
#
 
primary_hostname = think.unix7.org
 
local_interfaces =  <; ::0 ; 0.0.0.0
hide pgsql_servers = 127.0.0.1/maacom/maacom/password
 
domainlist local_domains = @ : $primary_hostname : \
         ${lookup pgsql{SELECT name FROM domains \
                               WHERE name = '${quote_pgsql:${domain}}'}}
...
 
acl_check_rcpt:
    accept
        hosts = :
        control = dkim_disable_verify
    accept
        authenticated = *
        control = dkim_disable_verify
    accept
        !authenticated = *
        domains = +local_domains
    drop
        !authenticated = *
        domains = !+local_domains
        message = $primary_hostname: Attempt to send mail for non-local domain $domain was rejected
        delay = 5s
    drop
...
 
begin routers
 
smtp:
    driver = dnslookup
    domains = ! +local_domains
    transport = smtp
    ignore_target_hosts = <; 0.0.0.0 ; 127.0.0.0/8 ; ::1
    log_as_local = no
    no_more
 
redirect:
    driver = redirect
    log_as_local = no
    allow_fail
    allow_defer
    data = ${lookup pgsql{\
        SELECT list FROM aliases, domains \
            WHERE aliases.name||'@'||domains.name = '${quote_pgsql:${local_part}@${domain}}' \
            AND aliases.domain_id = domains.id}}
    more = yes
    unseen = ${lookup pgsql{\
        SELECT 'yes' FROM users, domains \
            WHERE users.name||'@'||domains.name = '${quote_pgsql:${local_part}@${domain}}' \
            AND users.domain_id = domains.id LIMIT 1}{yes}{no}}
 
virtual:
    driver = accept
    domains = +local_domains
    condition = ${lookup pgsql{\
        SELECT 'yes' FROM users, domains \
            WHERE users.name||'@'||domains.name = '${quote_pgsql:${local_part}@${domain}}' \
            AND users.domain_id = domains.id LIMIT 1}{yes}{no}}
    transport = user
    user = vmail
    group = vmail
    more = yes
....

Dovecot config example

sql.conf
#
# $Id: sql.conf,v 1.1 2017/12/10 14:28:23 root Exp root $
#
 
#driver = sqlite
#connect = /var/maacom/mail.db
 
driver = pgsql
connect =  host=127.0.0.1 dbname=maacom user=maacom password=pazzword
default_pass_scheme = PLAIN
 
password_query = SELECT users.name AS username, \
                        domains.name AS domain, \
                        users.password AS password \
                FROM users, domains \
                WHERE users.name = '%n' \
                    AND domains.name = '%d' \
                    AND users.domain_id = domains.id \
                LIMIT 1;
 
user_query = SELECT '/var/vmail/%d/%n' AS home, \
                    'maildir:/var/vmail/%d/%n' AS mail, \
                    'vmail' AS uid, \
                    'vmail' AS gid, \
                    '*:bytes=1073741824' AS quota_rule \
                FROM users, domains \
                WHERE users.name = '%n' \
                    AND domains.name = '%d' \
                    AND users.domain_id = domains.id \
                LIMIT 1;
 
iterate_query = SELECT users.name||'@'||domains.name AS username \
                FROM users, domains \
                WHERE users.domain_id = domains.id;
 
#EOF

For using quota change string in SELECT

sql-w-quota.conf
user_query = SELECT '/var/vmail/%d/%n' AS home, \
                    'maildir:/var/vmail/%d/%n' AS mail, \
                    'vmail' AS uid, \
                    'vmail' AS gid, \
                    '*:bytes='||users.quota*1024*1024 AS quota_rule \
                FROM users, domains \
                WHERE users.name = '%n' \
                    AND domains.name = '%d' \
                    AND users.domain_id = domains.id \
                LIMIT 1;

Check:

Dec 15 09:26:28 vbsd05 dovecot: lda(borodin@unix7.org): msgid=<20171215092549.69d43c83@gmail.com>:
 save failed to INBOX: Quota exceeded (mailbox for user is full)

More correct, of course, will set up a quota check in the MTA rules.

Nginx as frontend

mx.unix7.org.conf
#
# $Id$
#
server {
  server_name mx.unix7.org;
  listen 80;
  listen [::]:80;
  return 301 https://$server_name$request_uri;
}
 
server {
  server_name mx.unix7.org;
  listen 443 ssl;
  listen [::]:443 ssl;
 
  ssl_certificate     nginx.crt;
  ssl_certificate_key nginx.key;
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_session_timeout 5m;
  ssl_session_cache shared:SSL:50m;
 
  location / {
    proxy_pass https://127.0.0.1:8082;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
  }
}

UML object diagramm

Borodin Oleg 2017/12/12 20:19


First PagePrevious PageBack to overviewNext PageLast Page