Почтовая система Exim + Cyrus + OpenLDAP на FreeBSD

Материал из BiTel WiKi

Перейти к: навигация, поиск

Необходимые для настройки системы скрипт и LDAP схему вы можете загрузить взять из этого архива Медиа:email_exim_cyrus.zip.

1.1. ставим cyrus-sasl-2.1.20. никаких особых телодвижений не нужно. рекомендую отключить ненужные механизмы аутентификации. во freebsd для этого нужно положить в каталог /usr/ports/security/cyrus-sasl2 файл sys.mk со следующим содержимым:

WITHOUT_OTP=yes
WITH_DEV_URANDOM=yes
WITHOUT_GSSAPI=yes

настроек никаких пока не надо.

1.2. ставим openldap-server-2.2.26 (и зависимости) с поддержкой sasl. во freebsd этот порт находится в каталоге /usr/ports/net/openldap22-sasl-server 1.3. ставим cyrus-imapd-2.2.12. во freebsd этот порт находится в каталоге /usr/ports/mail/cyrus-imapd22. тоже все по умолчанию. 1.4. ставим exim-4.51. в каталоге /usr/ports/mail/exim. я добавил следующие опции при сборке (файл sys.mk в каталоге порта):

WITH_CONTENT_SCAN=     yes
WITHOUT_IPV6=          yes
WITH_OPENLDAP=         yes
WITH_OPENLDAP_VER=     22
WITH_DEFAULT_CHARSET=  KOI8-R
WITH_AUTH_SASL=        yes

собственно для почтовой системы ничего больше не нужно. для скрипта, который будет создавать/удалять ящики и ставить квоты, нужны будут перловые порты p5-perl-ldap-0.33 и p5-Authen-SASL-2.09 со всеми зависимостями.

сейчас на машине supermail установлены следующие пакеты (некоторые ставились как зависимости):

=== cut ===
autoconf-2.13.000227_5 Automatically configure source code on many Un*x platforms 
autoconf-2.59_2     Automatically configure source code on many Un*x platforms 
automake-1.4.6_1    GNU Standards-compliant Makefile generator (legacy version 
automake-1.9.5      GNU Standards-compliant Makefile generator (version 1.9)
bash-3.0.16_1       The GNU Project's Bourne Again SHell
cidr-2.3.2          RFC 1878 subnet calculator / helper
cvsup-without-gui-16.1h_2 General network file distribution system optimized for CVS 
cyrus-imapd-2.2.12  The cyrus mail server, supporting POP3 and IMAP4 protocols
cyrus-sasl-2.1.20_1 RFC 2222 SASL (Simple Authentication and Security Layer)
cyrus-sasl-saslauthd-2.1.20_1 SASL authentication server for cyrus-sasl2
db42-4.2.52_4       The Berkeley DB package, revision 4.2
exim-4.51           High performance MTA for Unix systems on the Internet
expat-1.95.8_1      XML 1.0 parser written in C
gettext-0.14.1      GNU gettext package
glib-2.6.4          Some useful routines of C programming (current stable versi
gmake-3.80_2        GNU version of 'make' utility
help2man-1.35.1     Automatically generating simple manual pages from program o
kav4freebsd-5.x-5.0.5 Summary: kav4freebsd
libiconv-1.9.2_1    A character set conversion library
libslang-1.4.9      Routines for rapid alpha-numeric terminal applications deve
libtool-1.3.5_2     Generic shared library support script (version 1.3)
libtool-1.5.10_1    Generic shared library support script (version 1.5)
lynx-2.8.5          A non-graphical, text-based World-Wide Web client
m4-1.4.3            GNU m4
makedepend-2000.12.28 A dependency generator for makefiles
mc-4.6.0_15         Midnight Commander, a free Norton Commander Clone
mime-support-3.33.1 MIME Media Types list
mutt-1.4.2.1_2      The Mongrel of Mail User Agents (part Elm, Pine, Mush, mh, 
mysql-client-4.0.24_1 Multithreaded SQL database (client)
mysql-server-4.0.24_1 Multithreaded SQL database (server)
nspr-4.4.1_1        A platform-neutral API for system level and libc like funct
openldap-sasl-client-2.2.26 Open source LDAP client implementation with SASL2 support
openldap-sasl-server-2.2.26 Open source LDAP server implementation with SASL2 support
p5-Authen-SASL-2.09 Perl5 module for SASL authentication
p5-Convert-ASN1-0.18 Perl5 module to encode and decode ASN.1 data structures
p5-IO-Socket-SSL-0.96 Perl5 interface to SSL sockets
p5-Net-SSLeay-1.25  Perl5 interface to SSL
p5-URI-1.35         Perl5 interface to Uniform Resource Identifier (URI) refere
p5-XML-NamespaceSupport-1.08 A simple generic namespace support class
p5-XML-SAX-0.12     Simple API for XML
p5-gettext-1.03     Message handling functions
p5-perl-ldap-0.33   A Client interface to LDAP servers
perl-5.8.6_2        Practical Extraction and Report Language
pkgconfig-0.17.2    A utility to retrieve information about installed libraries
popt-1.7            A getopt(3) like library with a number of enhancements, fro
portupgrade-20041226_2 FreeBSD ports/packages administration and management tool s
ruby-1.8.2_3        An object-oriented interpreted scripting language
ruby18-bdb1-0.2.2   Ruby interface to Berkeley DB revision 1.8x with full featu
trafshow-3.1_5,1    Full screen visualization of network traffic
unarj-2.65_1        Allows files to be extracted from ARJ archives
unrar-3.43,3        Extract, view & test RAR archives
unzip-5.52_1        List, test and extract compressed files in a ZIP archive
vim-lite-6.3.62     Vi "workalike", with many additional features (Lite package
zip-2.3_2           Create/update ZIP files compatible with pkzip
=== cut ===

когда все установлено, приступаем к настройке.

2.1 настраиваем openldap.

сначала кладем в /usr/local/etc/openldap/schema файл со схемой (vmail.schema), затем правим /ust/local/etc/openldap/slapd.conf. у меня получилось примерно так:

=== cut ===
include         /usr/local/etc/openldap/schema/core.schema
include         /usr/local/etc/openldap/schema/cosine.schema
include         /usr/local/etc/openldap/schema/nis.schema
include         /usr/local/etc/openldap/schema/vmail.schema

pidfile         /var/run/openldap/slapd.pid
argsfile        /var/run/openldap/slapd.args

access to *
        by self write
        by users read
        by anonymous auth
        by dn="cn=ldapadmin,dc=ufamail,dc=ru" write
        by dn="cn=mta,dc=ufamail,dc=ru" read

database        bdb
suffix          "dc=ufamail,dc=ru"

rootdn          "cn=ldapadmin,dc=ufamail,dc=ru"
rootpw          {SSHA}xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

directory       /var/db/openldap-data

index   objectClass     eq
index   cn,sn           pres,eq,approx,sub
index   mailBox         eq
index   status          eq
index   uidNumber       eq
index   gidNumber       eq

password-hash   {CLEARTEXT}

sasl-authz-policy       to

sasl-regexp     uidNumber=(.*)\\+gidNumber=(.*),cn=peercred,cn=external,cn=auth
                ldap:///dc=ufamail,dc=ru??sub?(&(uidNumber=$1)(gidNumber=$2))

sasl-regexp     uid=(.*@.*),cn=external,cn=auth
                ldap:///dc=ufamail,dc=ru??sub?(&(mailBox=$1)(status=enabled))

sasl-regexp     uid=(.*),cn=external,cn=auth
                ldap:///dc=ufamail,dc=ru??sub?(|(cn=$1)(&(mailBox=$1@ufanet.ru)(status=enabled)))
=== cut ===

последние строчки нужны для того, чтобы работала т.н. proxy authorization. подробнее читать в документации на openldap и cyrus sasl.

теперь нужно добавить в базу данных ldap пользователей, кторые будут служебными. это, во-первых, пользователь от имени которого работает cyrus, во-вторых, пользователь от имени которого работает mta (exim). нужно это для того, чтобы и cyrus imapd, и exim могли успешно аутентифицировать пользователей.

Создать файл root.ldif с содержанием:

dn: dc=ufamail,dc=ru
objectClass: dcObject
objectClass: organization
o: ufamail
dc: ufamail

dn: cn=cyrus,dc=ufamail,dc=ru
cn: cyrus
sn: cyrus
uid: cyrus
homeDirectory: /usr/local/cyrus
objectClass: person
objectClass: posixAccount
saslAuthzTo: dn.regex:mailbox=.*
saslAuthzTo: dn.regex:cn=.*,dc=ufamail,dc=ru
saslAuthzTo: dn.regex:uid=.*,cn=external,cn=auth
uidNumber: 60
gidNumber: 60

dn: cn=mta,dc=ufamail,dc=ru
cn: mta
sn: mta
uid: mailnull
userPassword: xxxxxxxxxxxxx 
homeDirectory: /var/spool/mqueue
objectClass: person
objectClass: posixAccount
saslAuthzTo: dn.regex:mailbox=.*
saslAuthzTo: dn.regex:cn=.*,dc=ufamail,dc=ru
saslAuthzTo: dn.regex:uid=.*,cn=external,cn=auth
uidNumber: 26
gidNumber: 6

dn: mailBox=admin@ufanet.ru,dc=ufamail,dc=ru
mailBox: admin@ufanet.ru
status: enabled
objectClass: mailAccount
userPassword: xxxxxxxxxxxxx

Потом добавить корневую запись:

ldapadd -x -D "cn=ldapadmin,dc=ufamail,dc=ru" -W -f root.ldif

спросит пароль на rootpw из конфига slapd.conf и потом добавит корень.

вот примерное содержимое базы после добавления в нее нужных записей (выводит программа slapcat):

=== cut ===
dn: dc=ufamail,dc=ru
objectClass: dcObject
objectClass: organization
o: ufamail
dc: ufamail

dn: cn=cyrus,dc=ufamail,dc=ru
cn: cyrus
sn: cyrus
uid: cyrus
homeDirectory: /usr/local/cyrus
objectClass: person
objectClass: posixAccount
saslAuthzTo: dn.regex:mailbox=.*
saslAuthzTo: dn.regex:cn=.*,dc=ufamail,dc=ru
saslAuthzTo: dn.regex:uid=.*,cn=external,cn=auth
uidNumber: 60
gidNumber: 60

dn: cn=mta,dc=ufamail,dc=ru
cn: mta
sn: mta
uid: mailnull
userPassword: xxxxxxxxxxxxx 
homeDirectory: /var/spool/mqueue
objectClass: person
objectClass: posixAccount
saslAuthzTo: dn.regex:mailbox=.*
saslAuthzTo: dn.regex:cn=.*,dc=ufamail,dc=ru
saslAuthzTo: dn.regex:uid=.*,cn=external,cn=auth
uidNumber: 26
gidNumber: 6

dn: mailBox=admin@ufanet.ru,dc=ufamail,dc=ru
mailBox: admin@ufanet.ru
status: enabled
objectClass: mailAccount
userPassword: xxxxxxxxxxxxx
=== cut ===

обратите внимание на следующие вещи:

1. у пользователя cyrus нет пароля. это и не нужно, т.к. cyrus использует т.н. external mech при аутентификации пользователя (подробности см. в документации). в принципе, exim тоже использует external mech при аутентификации, но кроме аутентификации exim еще и иногда делает выборки (lookups) в базе данных ldap, а для этого уже нужно bind'иться к ней с паролем.

2. uidNumber и gidNumber взяты не с потолка, а соответствуют uid и gid от которых работают cyrus imapd и exim. без этого не будет работать proxy authz

3. мы добавили еще обычный аккаунт admin@ufanet.ru. это наш будущий администратор cyrus imapd, который будет управлять созданием/удалением почтовых ящиков и установкой квот.

после того как база создана можно попробовать, как работает связка openldap+sasl:

# su -m cyrus

>> ldapwhoami -v -Y EXTERNAL -U cyrus -H ldapi://

SASL/EXTERNAL authentication started
SASL username: uidNumber=60+gidNumber=60,cn=peercred,cn=external,cn=auth
SASL SSF: 0
dn:cn=cyrus,dc=ufamail,dc=ru


>> ldapwhoami -Y EXTERNAL -H ldapi:// -X u:admin@ufanet.ru

SASL/EXTERNAL authentication started
SASL username: u:admin@ufanet.ru
SASL SSF: 0
dn:mailBox=admin@ufanet.ru,dc=ufamail,dc=ru

если что-то не так, в первую очередь нужно проверить права на сокет /var/run/openldap/ldapi. они должны быть примерно такими:

>> ls -l /var/run/openldap/ldapi

srwxrwxrwx  1 ldap  ldap  0 May 23 12:41 /var/run/openldap/ldapi

для того, чтобы этого добиться, я запускаю slapd с такими параметрами в
/etc/rc.conf:


>> grep slapd /etc/rc.conf

slapd_enable="YES"
slapd_flags='-4 -h "ldap:/// ldapi:///"'
slapd_sockets="/var/run/openldap/ldapi"
slapd_sockets_mode="777"

после того, как работоспособность связки openldap - cyrus-sasl проверена, приступаем к настройке cyrus-imapd. для начала нужно собрать плагин ldapdb. берут его в исходниках openldap либо в свежих (с cvs-сервера) исходниках cyrus-sasl. разница в том, что в первом случае придется приложить больше усилий для сборки.

о том, как собрать под Linux, есть соответствующий README в каталоге с плагином. для сборки под FreeBSD выполните:

cd /usr/ports/security/cyrus-sasl2-ldapdb ; make install clean

собрав плагин, положите получившиеся файлы в /usr/local/lib/sasl2 (или где там у вас лежат плагины от cyrus-sasl):

>> ls -l /usr/local/lib/sasl2/| grep ldapdb

-rw-r--r--  1 root  wheel  14116 Apr 28 22:44 libldapdb.a
lrwxr-xr-x  1 root  wheel     14 Apr 28 22:16 libldapdb.so -> libldapdb.so.2
-rwxr-xr-x  1 root  wheel  18719 Apr 28 22:44 libldapdb.so.2

теперь настроим cyrus-imapd. для этого внесем следующие директивы в /usr/local/etc/imapd.conf (правьте под себя!):

=== cut ===
pass8bit: yes
defaultdomain: ufanet.ru
lmtp_downcase_rcpt: yes
unix_group_enable: no
virtdomains: yes
servername: imap.ufamail.ru
allowanonymouslogin: no
allowplaintext: yes
quotawarn: 90
timeout: 30
imapidlepoll: 60
imapidresponse: no
poptimeout: 10
popminpoll: 0
admins: admin admin@ufanet.ru admin@ufamail.ru
autocreatequota: 0
plaintextloginpause: 1
singleinstancestore: yes
reject8bit: no
sieveusehomedir: false
sievedir: /var/imap/sieve
annotation_db: skiplist
duplicate_db: berkeley-nosync
mboxlist_db: skiplist
ptscache_db: berkeley
seenstate_db: skiplist
subscription_db: flat
tlscache_db: berkeley-nosync
sasl_pwcheck_method: auxprop
sasl_auxprop_plugin: ldapdb
sasl_ldapdb_uri: ldapi://
sasl_ldapdb_mech: EXTERNAL
tls_cert_file: /usr/local/etc/ufamail.pem
tls_key_file: /usr/local/etc/ufamail.pem
tls_ca_file: /usr/local/etc/ufamail.pem
=== cut ===

последние три строчки - пусть к самоподписанному сертификату. нужны для того, чтобы можно было забирать почту по ssl/tls. о том, как создать самоподписанный сертификат, почитайте в документации на openssl. желательно сразу добавить в сертификат все имена, под которыми сервер будет известен клиентам. например:

smtp.ufanet.ru smtp.ufamail.ru imap.ufanet.ru imap.ufamail.ru

сделав все настройки и действия, описанные в документации на cyrus-imapd, запускаем его и проверяем работоспособность:

>> imtest -a admin@ufanet.ru localhost

S: * OK imap.ufamail.ru Cyrus IMAP4 v2.2.12 server ready
C: C01 CAPABILITY
S: * CAPABILITY IMAP4 IMAP4rev1 ACL QUOTA LITERAL+ MAILBOX-REFERRALS NAMESPACE UIDPLUS ID NO_ATOMIC_RENAME UNSELECT CHILDREN MULTIAPPEND BINARY SORT THREAD=ORDEREDSUBJECT THREAD=REFERENCES ANNOTATEMORE IDLE STARTTLS AUTH=NTLM AUTH=DIGEST-MD5 AUTH=CRAM-MD5 SASL-IR
S: C01 OK Completed
C: A01 AUTHENTICATE NTLM xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
S: + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Please enter your password:                                                     
C: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
S: A01 OK Success (no protection)
Authenticated.
Security strength factor: 0

с помощью ключа -m проверьте все механизмы аутентификации, которые вы предполагаете использовать на сервере. я рекомендую как минимум такие: plain, login, cram-md5, digest-md5, ntlm

если все работает, приступаем к настройке exim. после редактирования у меня получился вот такой файл конфигурации (сравните с умолчальным):

=== cut ===
domainlist local_domains = @ 
domainlist virt_domains = ufamail.ru
domainlist our_domains = +local_domains : +virt_domains
domainlist relay_to_domains =
hostlist   relay_from_hosts = localhost
acl_smtp_rcpt = acl_check_rcpt
acl_smtp_mail = acl_delay_mail_from
acl_smtp_data = acl_content_scan
exim_user = mailnull
exim_group = mail
never_users = root
rfc1413_query_timeout = 0s
ignore_bounce_errors_after = 2d
timeout_frozen_after = 7d
daemon_smtp_ports = 25 : 465
allow_mx_to_ip = no
auto_thaw = 2h
bounce_return_body = yes
bounce_return_size_limit = 50K
bounce_sender_authentication = mailer-daemon@ufamail.ru
delay_warning = 2h:8h:365d
errors_reply_to = postmaster@ufamail.ru
helo_accept_junk_hosts = 192.168.168.0/22 : 81.30.192.0/19
message_size_limit = 10M
pipelining_advertise_hosts = :
queue_run_max = 10
recipients_max = 100
smtp_accept_max = 500
smtp_accept_max_per_connection = 20
smtp_accept_queue_per_connection = 100
smtp_banner = $smtp_active_hostname ESMTP server ready
smtp_connect_backlog = 100
smtp_enforce_sync = yes
tls_advertise_hosts = *
tls_certificate = /usr/local/etc/ufamail.pem
tls_on_connect_ports = 465
received_header_text = Received: \
 ${if def:sender_rcvhost {from $sender_rcvhost\n\t}\
 {${if def:sender_ident {from $sender_ident }}\
 ${if def:sender_helo_name {(helo=$sender_helo_name)\n\t}}}}\
 ${if def:authenticated_id {authenticated as $authenticated_id\n\t}}\
 by $primary_hostname \
 ${if def:received_protocol {with $received_protocol}} \
 ${if def:tls_cipher {($tls_cipher)\n\t}}\
 (Exim $version_number)\n\t\
 id $message_id\
 ${if def:received_for {\n\tfor $received_for}}
smtp_accept_max_per_host = \
 ${lookup{$sender_host_address}iplsearch*\
 {/usr/local/etc/exim/maxconnects}{$value}{5}}
av_scanner = aveserver:/var/run/aveserver
spool_directory = /storage/spool/exim
begin acl
acl_delay_mail_from:
  accept authenticated = *
         delay = 0s 
  accept delay = ${lookup{$sender_host_address} iplsearch*{/usr/local/etc/exim/delayhosts}{$value}{1m}}
acl_content_scan:
  deny message = This message contains malware ($malware_name)
       malware = */defer_ok
  accept
acl_check_rcpt:
  accept  hosts = :
deny    message       = Restricted characters in address
          domains       = +our_domains
          local_parts   = ^[.] : ^.*[@%!/|]
  deny    message       = Restricted characters in address
          domains       = !+our_domains
          local_parts   = ^[./|] : ^.*[@%!] : ^.*/\\.\\./
  accept  local_parts   = postmaster : abuse
          domains       = +our_domains
  require verify        = sender/callout=30s,connect=5s
  deny message = rejected because $sender_host_address is in a black list at $dnslist_domain\n$dnslist_text
       dnslists = list.dsbl.org : dnsbl.njabl.org : dul.dnsbl.sorbs.net : bl.spamcop.net : relays.ordb.org
  accept  domains       = +our_domains
          endpass
          verify        = recipient
  accept  domains       = +relay_to_domains
          endpass
          verify        = recipient
  accept  hosts         = +relay_from_hosts
  accept  authenticated = *
  deny    message       = relay not permitted
begin routers
dnslookup:
  driver = dnslookup
  domains = ! +our_domains
  transport = remote_smtp
  ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8 : 192.168.0.0/16 : 172.16.0.0/12 : 10.0.0.0/8 : 224.0.0.0/4
  no_more
system_aliases:
  domains = +local_domains
  driver = redirect
  allow_fail
  allow_defer
  data = ${lookup{$local_part}lsearch{/etc/aliases}}
  user = mailnull
  group = mail
  file_transport = address_file
  pipe_transport = address_pipe
userforward:
  driver = redirect
  domains = +local_domains
  check_local_user
  file = $home/.forward
  no_verify
  no_expn
  check_ancestor
  file_transport = address_file
  pipe_transport = address_pipe
  reply_transport = address_reply
  condition = ${if exists{$home/.forward} {yes} {no} }
localuser:
  driver = accept
  domains = +local_domains
  check_local_user
  transport = local_delivery
  cannot_route_message = Unknown user
ldap_aliases:
  driver = redirect
  domains = +virt_domains
  allow_fail
  allow_defer
  hide data  = ${lookup ldap { \
  user="cn=mta,dc=ufamail,dc=ru" \
  pass=${quote:XXXXXXXXX} \
                ldapi:///dc=ufamail,dc=ru\
  ?forward?sub?\
  (&(mailBox=${quote_ldap:$local_part@$domain})\
    (status=enabled)(objectclass=mailAccount))\
  }\
 {$value} fail}
  file_transport = address_file
  pipe_transport = address_pipe
ldap_user:
  driver = accept
  domains = +virt_domains
  hide local_parts = ldap;\
 user="cn=mta,dc=ufamail,dc=ru" \
 pass=${quote:XXXXXXXX} \
                ldapi::///dc=ufamail,dc=ru\
 ?mailBox?sub?\
 (&(mailBox=${quote_ldap:$local_part@$domain})\
   (status=enabled)(objectclass=mailAccount))
  transport = cyrus_delivery
  cannot_route_message = Unknown user
begin transports
remote_smtp:
  driver = smtp
local_delivery:
  driver = appendfile
  file = /var/mail/$local_part
  delivery_date_add
  envelope_to_add
  return_path_add
  group = mail
  user = $local_part
  mode = 0660
  no_mode_fail_narrower
address_pipe:
  driver = pipe
  return_output
address_file:
  driver = appendfile
  delivery_date_add
  envelope_to_add
  return_path_add
address_reply:
  driver = autoreply
cyrus_delivery:
  driver = lmtp
  batch_max = 20
  user = cyrus
  socket = /storage/imap/socket/lmtp
begin retry
*                      *           F,2h,15m; G,16h,1h,1.5; F,4d,6h
begin rewrite
begin authenticators
plain:
  driver = cyrus_sasl
  public_name = PLAIN
  server_set_id = $1
login:
  driver = cyrus_sasl
  public_name = LOGIN
  server_set_id = $1
cram_md5:
  driver = cyrus_sasl
  public_name = CRAM-MD5
  server_set_id = $1
ntlm:
  driver = cyrus_sasl
  public_name = NTLM
  server_set_id = $1
=== cut ===

основные исправления здесь - роутеры ldap_aliases, ldap_user и authenticator'ы plain, login, cram-md5 и ntlm -- Best regards, damir mailto:boco@ufanet.ru

Личные инструменты