1. Requirements
- PostgreSQL server
- Dovecot IMAP server (for user authentication)
- Postfixadmin (optional)
- Note: If you can not install Postfixadmin, you have to create required database and tables manually! (See Appendix 2)
2. References
- Postfix
- PostgreSQL+Postfix+Nginx+PHP+RoundCube+Dovecot+ SpamAssassin+Clamav+Spamd
- How To Configure a Mail Server Using Postfix, Dovecot, MySQL, and SpamAssasin
- Heronovo - Postfix (only in czech)
3. Postfix installation
Create a new jail (e.g. name mail) and add storage to the jail so we can keep our e-mails in a secure place. I'll mount my storage in /mnt/mail inside the jail.
SSH to your FreeNAS, enter the jail (jexec mail tcsh) and install postfix. We have to build postfix using portsnap and configure it with PostgreSQL support. When postfix will be installed answer 'yes' to the question 'Would you like to activate Postfix in /etc/mail/mailer.conf [n]?'
portsnap fetch
portsnap extract
cd /usr/ports/mail/postfix/
make config
# for postfix configuration select:
# set PGSQL=on: PostgreSQL maps (uses DEFAULT_PGSQL_VER)
# set TLS=on: SSL and TLS support
# set DOVECOT2=on: Dovecot 2.x SASL authentication method
# set VDA=on: VDA (Virtual Delivery Agent 32Bit)
#
# for dovecot2 configuration select:
# set PGSQL=on: PostgreSQL maps (uses DEFAULT_PGSQL_VER)
# set SSL=on: SSL protocol support
make install clean
pkg install dovecot-pigeonhole
To enable postfix to starts automatically during start of the jail and disable sendmail update the file '/etc/rc.conf'.
sendmail_enable="NO"
sendmail_submit_enable="NO"
sendmail_outbound_enable="NO"
sendmail_msp_queue_enable="NO"
You can disable some sendmail specific daily maintenance routines in your '/etc/periodic.conf' file. If the file does not exist please create it and add values:
daily_clean_hoststat_enable="NO"
daily_status_mail_rejects_enable="NO"
daily_status_include_submit_mailq="NO"
daily_submit_queuerun="NO"
EOF
4. Postfix configuration
Create and secure the SMTP SSL certificate (or use your existing one).
cd /etc/ssl/postfix
openssl req -new -x509 -nodes -out smtpd.pem -keyout smtpd.pem -days 3650
chmod 640 /etc/ssl/postfix/*
chgrp -R postfix /etc/ssl/postfix
Next edit the file '/usr/local/etc/postfix/main.cf' and add/update variable mentioned below:
# SOFT BOUNCE
#
# The soft_bounce parameter provides a limited safety net for
# testing. When soft_bounce is enabled, mail will remain queued that
# would otherwise bounce. This parameter disables locally-generated
# bounces, and prevents the SMTP server from rejecting mail permanently
# (by changing 5xx replies into 4xx replies). However, soft_bounce
# is no cure for address rewriting mistakes or mail routing mistakes.
smtpd_recipient_restrictions =
permit_mynetworks,
permit_sasl_authenticated,
reject_non_fqdn_hostname,
reject_non_fqdn_sender,
reject_non_fqdn_recipient,
reject_unauth_destination,
reject_unauth_pipelining,
reject_invalid_hostname,
reject_rbl_client bl.spamcop.net
smtpd_sender_restrictions = permit_sasl_authenticated, permit_mynetworks
virtual_mailbox_base = /mnt/mail
virtual_mailbox_maps = pgsql:/usr/local/etc/postfix/pgsql_virtual_mailbox_maps.cf
virtual_mailbox_domains = pgsql:/usr/local/etc/postfix/pgsql_virtual_mailbox_domains.cf
virtual_alias_maps = pgsql:/usr/local/etc/postfix/pgsql_virtual_alias_maps.cf
local_recipient_maps = $virtual_mailbox_maps $virtual_alias_maps
relay_domains = pgsql:/usr/local/etc/postfix/pgsql_relay_domains.cf
# use static uid:gid, dynamic can caused permission related problems
virtual_uid_maps = static:26
virtual_gid_maps = static:6
smtpd_delay_reject = yes
smtpd_helo_required = yes
mailbox_command = /usr/lib/dovecot/deliver-lda -f "$SENDER" -a "$RECIPIENT"
virtual_transport = dovecot
smtpd_sasl_auth_enable = yes
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_security_options = noanonymous
broken_sasl_auth_clients = yes
smtp_use_tls = yes
smtp_tls_note_starttls_offer = yes
smtpd_use_tls = yes
smtpd_tls_auth_only = yes
smtpd_tls_key_file = /etc/ssl/postfix/smtpd.pem
smtpd_tls_cert_file = /etc/ssl/postfix/smtpd.pem
smtpd_tls_CAfile = /etc/ssl/postfix/smtpd.pem
smtpd_tls_loglevel = 1
smtpd_tls_received_header = yes
smtpd_tls_session_cache_timeout = 3600s
tls_random_source = dev:/dev/urandom
...
myhostname = mail.example.com
...
mydomain = example.com
...
myorign = $mydomain
...
In the file '/usr/local/etc/postfix/master.cf' uncomment these lines. 'Message_size_limit' will change message size limit from 10M (default) to 25M.
-o smtpd_tls_wrappermode=yes
-o smtpd_sasl_auth_enable=yes
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
-o message_size_limit=26214400
dovecot unix - n n - - pipe
flags=DRhu user=mailnull:mail argv=/usr/local/libexec/dovecot/deliver -f ${sender} -d ${user}@${nexthop} -m ${extension}
Create files below to query defined MX domains and e-mail address from database storage. As hosts you can use IP address, FQDN or path to PostgreSQL socket.
user = postfix
password = postfix_password
hosts = /tmp
dbname = mail
query = SELECT domain FROM domain WHERE domain='%s' AND backupmx = true
EOF
user = postfix
password = postfix_password
hosts = /tmp
dbname = mail
query = SELECT goto FROM alias WHERE address='%s' AND active = true
EOF
user = postfix
password = postfix_password
hosts = /tmp
dbname = mail
#query = SELECT domain FROM mailbox WHERE domain = '%s' AND active = true
query = SELECT domain FROM domain WHERE domain='%s' AND backupmx = false and active = true
EOF
user = postfix
password = postfix_password
hosts = /tmp
dbname = mail
query = SELECT quota FROM mailbox WHERE username = '%u' AND domain = '%d' AND active = true
EOF
user = postfix
password = postfix_password
hosts = /tmp
dbname = mail
query = SELECT maildir FROM mailbox WHERE username='%s' AND active = true
EOF
Secure postfix PostgreSQL files.
chgrp postfix /usr/local/etc/postfix/pgsql_*
Create your virtual mail directory, if you can not use mounted storage (see step 3), where e-mails will be storred. The directory was defined in the file '/usr/local/etc/postfix/main.cf' as 'virtual_mailbox_base = /mnt/mail'.
chown mailnull:mail /mnt/mail
5. Start and check postfix service
Start postfix service
Now you can test SSL connection by command:
And SMTP connection by command:
Appendix 1. MX DNS configuration
For correct sending and receiving e-mails to/from other mail server you have to set up proper DNS records of your mail server. Correct DNS forward and reverse (PTR) records are required for domain name and name of the mail server.
DNS records for the domain name of your mail server defined in the variable 'myhostname = mail.example.com' can be:
mail.example.com. IN A 012.345.678.9
DNS PTR record (in-addr.arpa) can be:
- Note: Without correct DNS PTR record you will not be able to send e-mails to most of the other mail servers (e.g. Gmail)!
To test yours DNS record you can use these commands:
dig mail.example.com ANY
dig -x ip_address_of_mail.example.com
Appendix 2. PostgreSQL database and tables
To create user, database and tables for postfix manually connect to your Postgresql server and execute commands below:
createuser --pwprompt --encrypted --no-createrole --no-createdb postfix
createdb --encoding=UTF8 --owner=postfix mail
psql mail
- Note: Don't forget to set up authentication file '/mnt/sql/pgsql/data/pg_hba.conf' for user 'postfix' and reload configuration of PostgreSQL.
When you are connected to the mail database, enable crypto module:
CREATE EXTENSION pgcrypto;
Create alias table:
-- DROP TABLE alias;
CREATE TABLE alias
(
address character varying(255) NOT NULL,
goto text NOT NULL,
domain character varying(255) NOT NULL,
created timestamp with time zone DEFAULT now(),
modified timestamp with time zone DEFAULT now(),
active boolean NOT NULL DEFAULT true,
CONSTRAINT alias_key PRIMARY KEY (address),
CONSTRAINT alias_domain_fkey FOREIGN KEY (domain)
REFERENCES domain (domain) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
OIDS=FALSE
);
ALTER TABLE alias OWNER TO postfix;
GRANT ALL ON TABLE alias TO postfix;
-- Index: alias_address_active
-- DROP INDEX alias_address_active;
CREATE INDEX alias_address_active
ON alias
USING btree
(address COLLATE pg_catalog."default", active);
-- Index: alias_domain_idx
-- DROP INDEX alias_domain_idx;
CREATE INDEX alias_domain_idx
ON alias
USING btree
(domain COLLATE pg_catalog."default");
Create domain table:
-- DROP TABLE domain;
CREATE TABLE domain
(
domain character varying(255) NOT NULL,
description character varying(255) NOT NULL DEFAULT ''::character varying,
aliases integer NOT NULL DEFAULT 0,
mailboxes integer NOT NULL DEFAULT 0,
maxquota bigint NOT NULL DEFAULT 0,
quota bigint NOT NULL DEFAULT 0,
transport character varying(255) DEFAULT NULL::character varying,
backupmx boolean NOT NULL DEFAULT false,
created timestamp with time zone DEFAULT now(),
modified timestamp with time zone DEFAULT now(),
active boolean NOT NULL DEFAULT true,
CONSTRAINT domain_key PRIMARY KEY (domain)
)
WITH (
OIDS=FALSE
);
ALTER TABLE domain OWNER TO postfix;
-- Index: domain_domain_active
-- DROP INDEX domain_domain_active;
CREATE INDEX domain_domain_active
ON domain
USING btree
(domain COLLATE pg_catalog."default", active);
And create mailbox table:
-- DROP TABLE mailbox;
CREATE TABLE mailbox
(
username character varying(255) NOT NULL,
password character varying(255) NOT NULL DEFAULT ''::character varying,
name character varying(255) NOT NULL DEFAULT ''::character varying,
maildir character varying(255) NOT NULL DEFAULT ''::character varying,
quota bigint NOT NULL DEFAULT 0,
created timestamp with time zone DEFAULT now(),
modified timestamp with time zone DEFAULT now(),
active boolean NOT NULL DEFAULT true,
domain character varying(255),
local_part character varying(255) NOT NULL,
CONSTRAINT mailbox_key PRIMARY KEY (username),
CONSTRAINT mailbox_domain_fkey1 FOREIGN KEY (domain)
REFERENCES domain (domain) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
OIDS=FALSE
);
ALTER TABLE mailbox OWNER TO postfix;
GRANT ALL ON TABLE mailbox TO postfix;
-- Index: mailbox_domain_idx
-- DROP INDEX mailbox_domain_idx;
CREATE INDEX mailbox_domain_idx
ON mailbox
USING btree
(domain COLLATE pg_catalog."default");
-- Index: mailbox_username_active
-- DROP INDEX mailbox_username_active;
CREATE INDEX mailbox_username_active
ON mailbox
USING btree
(username COLLATE pg_catalog."default", active);
When all tables are created you can insert new user with aliases for 'example.com' domain:
INSERT INTO domains VALUES ('example.com');
-- create e-mail user
# MD5-CRYPT
#INSERT INTO mailbox VALUES ('root@example.com', crypt('password', gen_salt('md5')), '', '', 0, now(), now(), true, 'example.com', 'root');
# BLF-CRYPT
INSERT INTO mailbox VALUES ('rootexample.com', crypt('password', gen_salt('bf',5)), '', '', 0, now(), now(), true, 'example.com', 'root');
# PLAIN-MD5
#INSERT INTO mailbox VALUES ('root@example.com', md5('password'), '', '', 0, now(), now(), true, 'example.com', 'root');
-- create virtual aliases
INSERT INTO alias VALUES ('postmaster@example.com', 'root@example.com', 'example.com');
INSERT INTO alias VALUES ('abuse@example.com', 'root@example.com', 'example.com');
INSERT INTO alias VALUES ('example@example.com', 'root@example.com', 'example.com');
Comments