Postfix Dovecot MYSQL
Tartalomjegyzék |
1 Szerver alkalmazások kiválasztása
Az email rendszer, amit bemutatok, egy honlap rendszerhez tartozott. Szempont volt, hogy közös felhasználói adatbázis legyen és minden platform képes legyen a felhasználókat authentikálni. A rendszer minden felhasználója automatikusan kapott egy email címet.
Az általam választott szerver alkalmazások:
- MTA (Mail Transfer Agent): Postfix.
- MDA (Mail Delivery Agent): Dovecot. Több open source rendszert megnéztem, ezek közül a dovecot jól skálázható, robusztus. Natívan támogatja a felhasználók adatbázisban tárolását és az SSHA(Salted Safe Hash Algorithm) algoritmust. A dovecot további előnye, hogy a levelek fiókokba helyezését is el tudja látni, így az esetlegesen eltérő mailbox megvalósítások nem okoznak hibát. Élesben kipróbáltam a Courier-t is, de sokkal nagyobb volt a memóriaigénye a tesztidőszak alatt.
- DBMS: MYSQL. A MYSQL az egyik széleskörben alkalmazott adatbázis-kezelő rendszer, könnyen használható különböző programozási nyelvek alól, ami a project megvalósításánál fontos szempont volt (php, node.js, java, stb).
2 Működés áttekintése
A levelek fogadásáról és küldéséről a postfix gondoskodik. A levelek elhelyezéséről, tartóstárba írásáról, azonosításáról pedig a dovecot. A működéshez szükséges adatokat 4 MYSQL táblában tárolja, így azok könnyen menedzselhetőek. Amikor a leveleket a postfix megkapja, átadja a különböző szűrő alkalmazásoknak, ezek egyelőre nem részei ennek az írásnak. A levelezőszerverek a leveleket a /var/vmail/ mappába teszik, a leveleket mint vmail(uid: 150) felhasználó teszik be, aki a mail(gid: 8) csoport tagja. Lehetőség van minden fiók számára saját linux felhasználó létrehozására, ezzel biztonságosabbá téve a fiók hozzáférését, ha az IMAP szerverben biztonsági rés lenne.
A felhasználókról külön tároljuk a felhasználónevüket és a domainjüket, amihez az emailcím tartozik, ennek megvannak a maga sajátosságai.
- előnyök
- azonosításnál elég a felhasználónév megadása, nem kell a teljes emailcímet megadni
- ha más alkalmazás használja a felhasználói adatbázist, a felhasználók azonosítása egyszerűbb
- hátrányok
- nem tartozhat több domainhez ugyanolyan felhasználónév, vagy ha ezt szeretnénk, akkor elveszítjük az előnyöket
3 Egy gyakorlati megvalósítás
3.1 Postfix beállítása
3.1.1 main.cf
Beállítások, amik eltérnek az alapértékektől (teljes fájl a mellékletben).
- Dovecot SASL autentikáció beállítása:
smtpd_sasl_type = dovecot #/var/spool/postfix/private/auth - dovecot auth socket smtpd_sasl_path = private/auth smtpd_sasl_auth_enable = yes broken_sasl_auth_clients = yes smtpd_sasl_security_options = noanonymous smtpd_sasl_local_domain = smtpd_sasl_authenticated_header = yes
- Hálózati paraméterek:
# megjelenítendő hostnév myhostname = domain1.hu myorigin = domain1.hu mydestination = localhost.domain1.hu, localhost # azok a hálózatok amiket biztonságosnak ítélünk és bizonyos ellenőrzéseket átugrunk ezekről a címekről mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 mailbox_size_limit = 0 recipient_delimiter = + # minden interfacen fogad leveleket inet_interfaces = all # több subnethez is kapcsolódik a server, megadhatjuk az interfacet amin keresztül kapcsolatokat kezdeményez a szerver default_transport = 10.0.0.24 mynetworks_style = host
- Levelek elhelyezésére vonatkozó információk:
virtual_mailbox_base = /var/vmail virtual_uid_maps = static:150 virtual_gid_maps = static:8
- Szerver használat
#elvárjuk hogy küldjön a kilens HELO-t smtpd_helo_required = yes #spammerek idejét pocsékoljuk #és a smtpd_helo_restrictions elfogad smtpd_recipient_restrictions és smtpd_relay_restrictions elemeket, mivel késöbb is lefuthat az ellenőrzés smtpd_delay_reject = yes
- Listák amiken végighald postfix, hogy kitől fogad levelet. A listán végighaladva, ha az egyik feltétel teljesül, akkor elutasítja/továbbengedi a levelet.
- általános elemek:
- permit: a listák végén használható, ezzel ha minden más teszten átment elfogadjuk a levelet
- reject: permithez hasonlóan a listák végére, ezzel elutasítjuk a levelet
- warn_if_reject: nem különálló elem, reject állítások elé tehető, így "reject_warning" bejegyzés kerül a log fájlba, főleg debuggolás esetén hasznos
- permit_mynetworks: a mynetworks listában megadott hálózatok/IP címek számára engedélyezi a hozzáférést, ezzel a belső hálózatot/gépeket nem vetjuk alá további ellenőrzésnek(pl webserver, belső levelezés, etc)
- reject_unauth_pipelining: "Early talker"-ek szűrése, megnézi a szerver hogy az adott SMTP commandnak megfelelő adatokat küldött-e a kliens
- permit_sasl_authenticated: akik bejelentkeztek azokat továbbengedjük
- smtpd_client_restrictions: kliens alapú szűrés, csatlakozáskor
- példa:
smtpd_client_restrictions = reject_rbl_client sbl.spamhaus.org, reject_rbl_client blackholes.easynet.nl, reject_rbl_client dnsbl.njabl.org
- reject_rbl_client <domain>: Domain-alapú feketelisták használata
- példa:
- smtpd_helo_restrictions: HELO ellenőrzések
- példa:
smtpd_helo_restrictions = permit_mynetworks, warn_if_reject reject_non_fqdn_hostname, reject_invalid_hostname, permit
- check_helo_access(table): egy adatbázis, hogy HELO(EHLO) üzenetnél küldött domain mi lehet
- példa:
- smtpd_sender_restrictions: SMTP MAIL FROM utasításon(feladó) hajtja végre
- példa:
smtpd_sender_restrictions = reject_authenticated_sender_login_mismatch, permit_sasl_authenticated, permit_mynetworks, warn_if_reject reject_non_fqdn_sender, reject_unknown_sender_domain, reject_unauth_pipelining, permit
- reject_authenticated_sender_login_mismatch: egy gyakran kihagyott, de szerintem az egyik legfontosabb ellenőrzés, a belépett felhasználók nem küldhetnek csak a saját felhasználónevükkel megegyező feladóval címzett levelet (vagy más szabályra illeszkedő). Elég nagy probléma, hogyha bármelyik felhasználónk küldhet a root, administrator, accounting, etc nevében levelet!
- reject_non_fqdn_sender: visszautasítjuk a levelek ha a feladó domain nem FQDN
- reject_unknown_sender_domain: visszautasítjuk azokat a leveleket, amik feladóihoz nem mi kézbesítünk és nincs DNS A, MX rekordja vagy ezek hibásak
- példa:
- smtpd_recipient_restrictions: SMTP 'RCPT TO' utasításon(címzett) hajtja végre
- példa:
smtpd_recipient_restrictions = permit_mynetworks, reject_unauth_pipelining, permit_sasl_authenticated, reject_non_fqdn_recipient, reject_unknown_recipient_domain, reject_unauth_destination, check_policy_service inet:127.0.0.1:10023, permit
- reject_non_fqdn_recipient: visszautasítjuk a levelek ha a címzett domain nem FQDN
- reject_unknown_recipient_domain: visszautasítjuk az a levelet amit nem mi kézbesítünk és nincs DNS A, MX rekordja vagy ezek hibásak
- reject_unauth_destination: visszautasítjuk a levelet hogyha a domain nem szerepel a transzport listánkban vagy a szerver egyéb azonosítójával
- check_policy_service inet:127.0.0.1:10023: Postgray ellenőrzés
- példa:
- általános elemek:
- MYSQL felé való átjárás, részletek az adatbázis tábláknál. Ahova több lekérés is tartozik, ott sorban halad végig rajtuk, ha nem talál illeszkedéset.
virtual_mailbox_maps = mysql:/etc/postfix/mysql_virtual_mailbox_maps.cf virtual_alias_maps = mysql:/etc/postfix/mysql_virtual_alias_maps.cf, mysql:/etc/postfix/mysql_virtual_email2email.cf virtual_mailbox_domains = mysql:/etc/postfix/mysql_virtual_domains_maps.cf transport_maps = mysql:/etc/postfix/mysql_virtual_transports.cf smtpd_sender_login_maps = mysql:/etc/postfix/mysql_virtual_email_verify.cf
- Levelek virusírtónak való átadása:
content_filter = amavis:[127.0.0.1]:10024
- Levelek fiókokba helyezése dovecotnak való átadása:
virtual_transport = dovecot dovecot_destination_recipient_limit = 1
3.1.2 master.cf
A master.cf a postfix kapcsolatati leíró fájl.
- beérkező levelek számára a 25-ös és a ssl port
smtp inet n - - - - smtpd smtps inet n - - - - smtpd
- tartalom szűrők fele
amavis unix - - - - 2 smtp -o smtp_data_done_timeout=1200 -o smtp_send_xforward_command=yes -o disable_dns_lookups=yes -o max_use=20 127.0.0.1:10025 inet n - - - - smtpd -o content_filter= -o local_recipient_maps= -o relay_recipient_maps= -o smtpd_restriction_classes= -o smtpd_delay_reject=no -o smtpd_client_restrictions=permit_mynetworks,reject -o smtpd_helo_restrictions= -o smtpd_sender_restrictions= -o smtpd_recipient_restrictions=permit_mynetworks,reject -o smtpd_data_restrictions=reject_unauth_pipelining -o smtpd_end_of_data_restrictions= -o mynetworks=127.0.0.0/8 -o smtpd_error_sleep_time=0 -o smtpd_soft_error_limit=1001 -o smtpd_hard_error_limit=1000 -o smtpd_client_connection_count_limit=0 -o smtpd_client_connection_rate_limit=0 -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks
- dovecot fele a levelek továbbítása
dovecot unix - n n - - pipe flags=DRhu user=vmail:mail argv=/usr/lib/dovecot/dovecot-lda -d $(recipient)
3.1.3 Adattárolás és adatbázis kapcsolat
- Célszerű egy külön táblát és saját felhasználót létrehozni postfixnek és dovecotnak és csak olvasási jogot adni az accountra. A managementet egy külön felhasználóval lehet végezni.
- Két táblában van 'active' oszlop ezeket a sorokat a lekérések csak 1 érték esetén veszik figyelembe (ezt minden táblánál meg lehetne tenni, de a többinél ennek gyakorlati haszna nem lenne). Ennek a segítségével egy átirányítás vagy felhasználói fiók ideiglenesen is kikapcsolható.
- Ebben a konfigurációban a kvóta byteokban értendő, 0 esetén a dovecot nem alkalmaz korlátozást.
Adattáblák felépítése:
- users:
CREATE TABLE `users` ( `userID` int(10) unsigned NOT NULL AUTO_INCREMENT, `user` varchar(128) NOT NULL, `domain` varchar(128) NOT NULL, `password` varchar(128) DEFAULT '', `active` int(11) NOT NULL DEFAULT '0', `quota` int(10) unsigned NOT NULL DEFAULT '10485760', PRIMARY KEY (`userID`) )
- users példaadatok:
+--------+---------+------------+----------------------------------------------------------+--------+----------+ | userID | user | domain | password | active | quota | +--------+---------+------------+----------------------------------------------------------+--------+----------+ | 3 | test | domain1.hu |apfAlBBFtlYefTNlFF9+RZ3Xdz8og5E/86upbmrCw64tI1Z8cTNXMQ== | 1 | 10485760 | +--------+---------+------------+----------------------------------------------------------+--------+----------+
- domains:
CREATE TABLE `domains` ( `domainID` int(11) NOT NULL AUTO_INCREMENT, `domain` varchar(45) NOT NULL, PRIMARY KEY (`domainID`) )
- domains példaadatok:
+----------+------------+ | domainID | domain | +----------+------------+ | 1 | domain1.hu | | 2 | domain2.hu | +----------+------------+
- forwardings:
CREATE TABLE `forwardings` ( `forwardingsID` int(11) NOT NULL AUTO_INCREMENT, `source` varchar(128) NOT NULL, `destination` varchar(128) NOT NULL, `active` int(11) NOT NULL DEFAULT '1', PRIMARY KEY (`forwardingsID`) )
- forwardings példaadatok:
- 1: minden nem létező címre szóló levelet a lost postafiókba továbbítunk
- 2-5: a különböző adminisztrációs címeket az admin postafiókba továbbítjuk
+---------------+--------------------------+------------------+--------+ | forwardingsID | source | destination | active | +---------------+--------------------------+------------------+--------+ | 1 | @domain1.hu | lost@domain1.hu | 1 | | 2 | postmaster@domain1.hu | admin@domain1.hu | 1 | | 3 | root@domain1.hu | admin@domain1.hu | 1 | | 4 | hostmaster@domain1.hu | admin@domain1.hu | 1 | | 5 | administrator@domain1.hu | admin@domain1.hu | 1 | +---------------+--------------------------+------------------+--------+
- transport:
CREATE TABLE `transport` ( `transportID` int(11) NOT NULL AUTO_INCREMENT, `domain` varchar(45) DEFAULT NULL, `transport` varchar(45) DEFAULT NULL, PRIMARY KEY (`transportID`) )
- transport példaadatok: mind a két domainünket helyben kiszolgáljuk, nem továbbítjuk (pl. ha másnak az MX tartalék levelezőszervere vagyunk, az ő elsődleges szervere felé továbbítunk)
+-------------+----------------+-----------+ | transportID | domain | transport | +-------------+----------------+-----------+ | 1 | domain1.hu | : | | 2 | domain2.hu | : | +-------------+----------------+-----------+
A /etc/postfix/ mappába a következő fájlokra van szükség, amik az adatbázissal kötik össze. Mivel minden fájlnak tartalmazni kell a jelszót, ezeknek a fájloknak a hozzáférésére fokozottan figyeljünk oda!
- Miden fájl elejére be kell illeszteni az adatbázis kapcsolat adatait, ezek az adatok:
user = <adatbázis felhasználónév - mail> password = <adatbázis jelszó - ****> hosts = <adatbázis címe - x.x.x.x> dbname = <adatábzis tábla neve - mail>
- mysql_virtual_email2email.cf - virtual_alias_maps: felhasználók azonosítására szolgál
query = SELECT concat(user, '@', domain) FROM users WHERE user='%u' AND domain="%d"
- mysql_virtual_alias_maps.cf - virtual_alias_maps: címfordítások
query = SELECT destination FROM forwardings WHERE source='%s' AND active = '1'
- mysql_virtual_domains_maps.cf - virtual_mailbox_domains: a server által kiszolgált domainek listája
query = SELECT domain FROM domains WHERE domain='%s'
- mysql_virtual_email_verify.cf - smtpd_sender_login_maps: bejelentkezett felhasználó emailcímének lekérdezésére szolgál(a dokumentáció alapján a kérés hibásnak tűnhet, több órás debuggolás után ez a kérés működik, teljes email címmel nem sikerült éltre keltenem a funkciót)
query = SELECT user FROM users WHERE user='%u'
- mysql_virtual_mailbox_maps.cf - virtual_mailbox_maps: a felhasználók fiókjainak a helyét adja meg
query = SELECT CONCAT(domain,'/',user) FROM users WHERE user='%u' AND active = '1'
- mysql_virtual_transports.cf - transport_maps:
query = SELECT transport FROM transport WHERE domain='%s'
3.2 Dovecot beállítása
A különböző konfigurációs fájlok előtt a számok a fájlok végrehajtásának sorrendjét garantálják.
- /etc/dovecot/conf.d/10-auth.conf - authentikációs beállítások
# azonosítás csak titkosított csatornán disable_plaintext_auth = yes # legtöbb levelező ismeri és használja a 'plain' authentikációt, de az outlook csak a 'login' metódust auth_mechanisms = plain login # includeoljuk a saját adatbázis fájlunkat !include auth-sql.conf.ext
- /etc/dovecot/conf.d/10-mail.conf - levelek
# postafiókok helye mail_location = maildir:/var/vmail/%d/%n # levelek írásához/olvasásához használt felhasználó, és csoport mail_uid = vmail mail_gid = mail # ezeknek az értékeknek ebben a konfigurációban nincs gyakorlati jelentősége first_valid_uid = 150 last_valid_uid = 150 first_valid_gid = 8 last_valid_gid = 8 # meg kell adni a plugin könyvtárat, ha használni akarunk kvótát mail_plugin_dir = /usr/lib/dovecot/modules
- /etc/dovecot/conf.d/10-master.conf - authentikációs socketek beállítása
service auth { # dovecot saját folyamatai számára az authentikációs unix socket unix_listener auth-userdb { mode = 0600 user = vmail group = mail } # postfix számára az authentikációs unix socket unix_listener /var/spool/postfix/private/auth { mode = 0666 user = postfix group = postfix } }
- /etc/dovecot/conf.d/20-imap.conf - imap specifikus beállítások
# engedélyezni kell a kvótát imapon felül mail_plugins = $mail_plugins imap_quota
- /etc/dovecot/conf.d/auth-sql.conf.ext - az alkalmazott DBMS specifikálása:
passdb { driver = sql args = /etc/dovecot/dovecot-sql.conf.ext } userdb { driver = sql args = /etc/dovecot/dovecot-sql.conf.ext }
- /etc/dovecot/conf.d/90-quota.conf - kvóta beállítások
plugin { quota = maildir:User quota quota_rule = *:bytes=1000000 }
- /etc/dovecot/dovecot-sql.conf.ext - dovecot mysql lekérései:
driver = mysql connect = host=<adatbázis címe - x.x.x.x> dbname=<adatbázis felhasználónév - mail> user=<adatábzis tábla neve - mail> password=<adatbázisjelszó - ****> default_pass_scheme = SSHA256 password_query = SELECT user as user, password, concat('/var/vmail/', domain,'/%n') as userdb_home, concat('maildir:/var/vmail/', domain,'/%n') as userdb_mail, 150 as userdb_uid, 8 as userdb_gid FROM users WHERE user = '%n' AND active ='1' user_query = SELECT concat('/var/vmail/', domain,'/%n') as home, concat('maildir:/var/vmail/', domain,'/%n') as mail, 150 AS uid, 8 AS gid, concat('*:bytes=', quota) AS quota_rule FROM users WHERE user = '%n' AND active = '1'
4 Mellékletek
- Egy php class ami a dovecot számára is olvasható formátumban állít elő SSHA256 jelszavakat.
<?php class Crypt { public static $allowedABC = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; public static $allowed = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz{|}~[\]^_!#$%&()*+,-./`:;<=>?'; public static function authenticate($ssha, $password) { $salt = substr(base64_decode($ssha, true), 32); if(Crypt::calculatePasswordHash($password, $salt) == $ssha) { return true; } else { return false; } } public static function generatePasswordHash($password) { $salt = Crypt::randomCharsFull(8); return base64_encode(hash('sha256', $password . $salt, TRUE) . $salt); } public static function calculatePasswordHash($password, $salt) { return base64_encode(hash('sha256', $password . $salt, TRUE) . $salt); } public static function randomCharsFull($length){ $string = ''; $limit = strlen(Crypt::$allowed); for($i = 0 ; $i < $length ; $i++) { $string .= substr(Crypt::$allowed, mt_rand(0, $limit-1), 1); } return $string; } public static function randomCharsAbc($length){ $string = ''; $limit = strlen(Crypt::$allowedABC); for($i = 0 ; $i < $length ; $i++) { $string .= substr(Crypt::$allowedABC, mt_rand(0, $limit-1), 1); } return $string; } } ?>
2 rövid segédfájl, hogy consoleból is könnyen lehessen jelszót generálni
mkpwd.php: <?php require_once 'Crypt.php'; echo ''.Crypt::generatePasswordHash($argv[1]); ?> mkpwd.sh: #!/bin/bash read -s -p "Type password:" pwd echo -e "\nPassword SSHA256 hash:" php mkpwd.php $pwd echo -e ""