Mailutils:HOWTO:Sendmail MU LDAP

From Mailutils
Jump to navigationJump to search

Task

We have two domains: foo.bar and foo.com, and want to provide email service for separate users like testuser@foo.bar and testuser@foo.com.

For that we need:

  • to get users database in LDAP DB (OpenLDAP as LDAP server)
  • to get multidomain (multi domains with separate (if needed) users for each domain) support in sendmail via mailutils

all described was deployed on FreeBSD

OpenLDAP

The important decision have to be made on account of the directory topology: flat namespace or a hierarchical one

look at

  • "LDAP System Administration" By Gerald Carter; ISBN: 1-56592-491-6
  • "Understanding and Deploying LDAP Directory Services", Second Edition By Timothy A. Howes Ph.D., Mark C. Smith, Gordon S. Good; ISBN: 0-672-32316-8.

slapd.conf

include         /usr/local/etc/openldap/schema/core.schema
include         /usr/local/etc/openldap/schema/cosine.schema
include         /usr/local/etc/openldap/schema/inetorgperson.schema
include         /usr/local/etc/openldap/schema/nis.schema
include         /usr/local/etc/openldap/schema/openldap.schema
include         /usr/local/etc/openldap/schema/misc.schema
include         /usr/local/etc/openldap/schema/ldapns.schema
include         /usr/local/etc/openldap/schema/asterisk.schema
include         /usr/local/etc/openldap/schema/sendmail.schema

loglevel        stats

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

modulepath      /usr/local/libexec/openldap
moduleload      back_bdb
moduleload      back_hdb
moduleload      back_monitor
moduleload      syncprov

TLSCACertificateFile  /usr/local/etc/openldap/ssl/cacert.pem
TLSCertificateFile    /usr/local/etc/openldap/ssl/srv1cert.pem
TLSCertificateKeyFile /usr/local/etc/openldap/ssl/srv1key.pem
TLSCipherSuite HIGH:MEDIUM:+TLSv1:!SSLv2:+SSLv3
TLSVerifyClient never
security ssf=128

access to dn.exact="" by * read
access to *
       by peername.ip=127.0.0.1 break
       by peername.ip=X.X.X.X break
access to *
       by set="[cn=bind,ou=group,dc=ibs]/memberUid & user/uid" read
       by set="[cn=admin,ou=group,dc=ibs]/memberUid & user/uid" write
       by self read
       by * search

database        bdb
suffix          "dc=foo,dc=bar"
rootdn          "cn=ldapmaster,dc=foo,dc=bar"
rootpw          {SSHA}Osdfkjwh89974500sdfjhjhLKJHKLJLKJlLKJljlj
directory       /var/db/openldap-data/foo.bar
monitoring      on

index   default eq,sub
index   objectClass eq
index   uidNumber eq
index   gidNumber eq
index   memberUid eq
index   cn,sn,uid,displayName pres,sub,eq
index   authorizedService eq
index   sendmailMTAAliasGrouping eq
index   sendmailMTACluster eq
index   sendmailMTAHost eq
index   sendmailMTAKey eq
index   sendmailMTAMapName eq

overlay memberof

overlay unique
unique_uri ldap:///ou=People,dc=foo,dc=bar?uid?sub?(authorizedService=mail@foo.bar)
unique_uri ldap:///ou=People,dc=foo,dc=bar?uid?sub?(authorizedService=mail@foo.com)

ldap.conf

BASE    dc=foo,dc=bar
URI     ldap://ldap.foo.bar:389
SIZELIMIT       0
TLS_REQCERT allow
TLS_CACERT /usr/local/etc/openldap/cacert.pem

.ldif

dn: uid=officeX-testuser,ou=People,dc=foo,dc=bar
cn: testuser@foo.bar
description: unique user across whole user database
gidnumber: 12345
homedirectory: /nonexistent
loginshell: /sbin/nologin
objectclass: top
objectclass: posixAccount
objectclass: inetOrgPerson
objectclass: organizationalPerson
objectclass: person
objectclass: inetLocalMailRecipient
sn: test user
uid: officeX-testuser
uidnumber: 10001
userpassword: {CRYPT}$1$dtyhERYdf$dfDGGsdHJKTIKT.34345DFSF/

dn: authorizedService=mail@foo.bar,uid=officeX-testuser,ou=People,dc=foo,dc=bar
associateddomain: foo.bar
authorizedservice: mail@foo.bar
cn: testuser@foo.bar
description: auxiliary service/s account (like email, web, e.t.c. access)
gidnumber: 10106
homedirectory: /var/mail/IMAP_HOMES/foo.bar/testuser@foo.bar
loginshell: /sbin/nologin
mu-mailbox: maildir:/var/mail/foo.bar/testuser@foo.bar
objectclass: posixAccount
objectclass: shadowAccount
objectclass: inetOrgPerson
objectclass: authorizedServiceObject
objectclass: domainRelatedObject
objectclass: mailutilsAccount
sn: testuser@foo.bar
uid: testuser@foo.bar
uidnumber: 10001
userpassword: {CRYPT}$1$dtyhERYdf$dfDGGsdHJKTIKT.34345DFSF/

dn: authorizedService=mail@foo.com,uid=officeX-testuser,ou=People,dc=foo,dc=bar
associateddomain: foo.com
authorizedservice: mail@foo.com
cn: testuser@foo.com
description: auxiliary service/s account (like email, web, e.t.c. access)
gidnumber: 10106
homedirectory: /var/mail/IMAP_HOMES/foo.com/testuser@foo.com
loginshell: /sbin/nologin
mu-mailbox: maildir:/var/mail/foo.com/testuser@foo.com
objectclass: posixAccount
objectclass: shadowAccount
objectclass: inetOrgPerson
objectclass: authorizedServiceObject
objectclass: domainRelatedObject
objectclass: mailutilsAccount
sn: testuser@foo.com
uid: testuser@foo.com
uidnumber: 10001
userpassword: {CRYPT}$1$dtyhERYdf$dfDGGsdHJKTIKT.34345DFSF/

test

# ldapsearch -xZZ -D uid=bind@mail.foo.bar,ou=people,dc=foo,dc=bar -w ***** -b ou=people,dc=foo,dc=bar -v "(&(authorizedService=mail@foo.bar)(uid=testuser))"                                      
ldap_initialize( <DEFAULT> )
filter: (&(authorizedService=mail@foo.bar)(uid=testuser))
requesting: All userApplication attributes
# extended LDIF
#
# LDAPv3
# base <ou=people,dc=foo,dc=bar> with scope subtree
# filter: (&(authorizedService=mail@foo.bar)(uid=testuser))
# requesting: ALL
#

# mail@foo.bar, officeX-testuser, People, foo, bar
dn: authorizedService=mail@foo.bar,uid=officeX-testuser,ou=People,dc=foo,dc=bar
associateddomain: foo.bar
authorizedservice: mail@foo.bar
cn: testuser@foo.bar
description: auxiliary service/s account (like email, web, e.t.c. access)
gidnumber: 10106
homedirectory: /var/mail/IMAP_HOMES/foo.bar/testuser@foo.bar
loginshell: /sbin/nologin
mu-mailbox: maildir:/var/mail/foo.bar/testuser@foo.bar
objectclass: posixAccount
objectclass: shadowAccount
objectclass: inetOrgPerson
objectclass: authorizedServiceObject
objectclass: domainRelatedObject
objectclass: mailutilsAccount
sn: testuser@foo.bar
uid: testuser@foo.bar
uidnumber: 10001
userPassword:: *****************************************

# search result
search: 3
result: 0 Success

# numResponses: 2
# numEntries: 1

sendmail with STARTTLS, SMTPAUTH, LDAP and db44 support

cyrus-sasl2

build cyrus-sasl2 with ldap and saslauthd

saslauthd.conf

ldap_servers: ldap://ldap.foo.bar/
ldap_start_tls: yes
ldap_tls_cacert_file: /usr/local/etc/openldap/cacert.pem
ldap_auth_method: bind
ldap_bind_dn: uid=bind@mail.foo.bar,ou=people,dc=foo,dc=bar
ldap_password: ******
ldap_search_base: dc=foo,dc=bar
ldap_filter: (&(uid=%U)(authorizedService=mail@foo.bar))

Sendmail.conf

in FreeBSD it is /usr/local/lib/sasl2/Sendmail.conf

pwcheck_method: saslauthd
log_level: 5

test

# testsaslauthd -u testuser@foo.bar -p theverypassword
0: OK "Success."

sendmail

build config

site.config.m4

##
# general
APPENDDEF(`confINCDIRS', `-I/usr/local/include -I/usr/local/include/db44')
APPENDDEF(`confLIBDIRS', `-L/usr/local/lib -L/usr/local/lib/db44')

## DB44
#APPENDDEF(`confENVDEF', `-I/usr/local/include -I/usr/local/include/db44')
#APPENDDEF(`conf_sendmail_LIBS', `-L/usr/local/lib -L/usr/local/lib/db44')

# SASL2 (smtp authentication)
APPENDDEF(`confENVDEF', `-DSASL=2')
APPENDDEF(`conf_sendmail_LIBS', `-lsasl2')

# LDAP
APPENDDEF(`confMAPDEF', `-DLDAPMAP')
APPENDDEF(`confLIBS', `-lldap -llber')

# STARTTLS (smtp + tls/ssl)
APPENDDEF(`conf_sendmail_ENVDEF', `-DSTARTTLS -D_FFR_TLS_1')
APPENDDEF(`conf_sendmail_LIBS', `-lssl -lcrypto')

# rest
APPENDDEF(`conf_sendmail_ENVDEF', `-DMILTER -DSOCKETMAP -DMAP_REGEX -DNEWDB')

DNS PTR

IMPORTANT: PTR is not looked at mailertable and considered as local

sendmail.mc

dnl * Sendmail configuration
divert(-1)
OSTYPE(freebsd6)
dnl * To eliminate 8->7 bit base64 enconding
define(`SMTP_MAILER_FLAGS',`8')
dnl * Do not reveal my version number
define(`confRECEIVED_HEADER',`$?sfrom $s $.$?_($?s$|from $.$_) $.
        by $j$?r with $r$. id $i$?u
        for $u$.; $b')
dnl * Also, disable VRFY,EXPN
define(`confPRIVACY_FLAGS',`authwarnings,novrfy,noexpn,noetrn,needmailhelo')

dnl * do STARTTLS
define(`confCACERT_PATH', `/etc/mail/certs')dnl
define(`confCACERT', `/etc/mail/certs/sendmail.pem')dnl
define(`localCERT', `/etc/mail/certs/sendmail.pem')dnl
define(`confSERVER_CERT', `localCERT')dnl
define(`confSERVER_KEY',  `localCERT')dnl
define(`confCLIENT_CERT', `localCERT')dnl
define(`confCLIENT_KEY',  `localCERT')dnl

dnl * do SMTPAUTH
define(`confAUTH_MECHANISMS', `LOGIN PLAIN')dnl
TRUST_AUTH_MECH(`LOGIN PLAIN')dnl

dnl * look for AuthOptions @ op.ps
define(`confAUTH_OPTIONS', `A p y')dnl


define(`confSAVE_FROM_LINES', `True')dnl
define(`HELP_FILE',`none')dnl
define(`confDELIVERY_MODE', `background')dnl

dnl * define(`confMAX_MESSAGE_SIZE',`31457280')
define(`confERROR_MESSAGE',`/etc/mail/error-header')dnl
define(`confREJECT_MSG',`550 Access denied. For our users call IT dpt 911')dnl
define(`confRELAY_MSG', `550 Relaying denied. For our users call IT dpt 911')dnl

dnl define(`confSMTP_LOGIN_MSG',`$j server; $b')
define(`confSMTP_LOGIN_MSG',`$j server ready.\nWelcome to us.\nSending UBE is forbidden.\nViolators will be severely prosecuted.')

dnl * DAEMON_OPTIONS(`Name=MTA,Addr=0.0.0.0')
DAEMON_OPTIONS(`Name=MTA,Addr=X.X.X.X')
DAEMON_OPTIONS(`Name=MTA-local0,Addr=127.0.0.1')
DAEMON_OPTIONS(`Name=MTA-local3,Addr=Y.Y.Y.Y')
DAEMON_OPTIONS(`Family=inet,Name=MTA-SSL,Port=465,M=abs')

# Maps
define(`confLDAP_DEFAULT_SPEC', `-H ldaps://ldap.foo.bar -b ou=foo.bar,ou=Sendmail,dc=foo,dc=bar -w3 -d uid=bind@mail.foo,ou=people,dc=foo,dc=bar -P /etc/mail/ldappass')dnl
define(`confLDAP_CLUSTER', `SMTPCLUSTER')

LOCAL_CONFIG
Klocal_alias hash -T<TMPF> -o /etc/mail/aliases
Kldap_alias ldap -k (&(objectClass=sendmailMTAAliasObject)(sendmailMTAAliasGrouping=aliases)(|(sendmailMTACluster=${sendmailMTACluster})(sendmailMTAHost=$j))(sendmailMTAKey=%0)) -v sendmailMTAAliasValue
define(`ALIAS_FILE',`sequence: local_alias ldap_alias')

FEATURE(`access_db', `LDAP')
FEATURE(`mailertable', `LDAP')

FEATURE(use_cw_file)
FEATURE(use_ct_file)
FEATURE(redirect)
FEATURE(always_add_domain)
FEATURE(blacklist_recipients)
FEATURE(relay_entire_domain)

# Milter
define(`confMILTER_LOG_LEVEL',4)
INPUT_MAIL_FILTER(`mailfrom', `S=unix:/var/run/mailfromd/mailfromd.sock, F=T, T=S:120s;R:360s')

# Mailers
MAILER_DEFINITIONS
Mlocal-ldap,    P=/usr/local/sbin/maidag, F=lsDFMA5:/|@qSPfhn9, S=EnvFromL/HdrFromL, R=EnvToL/HdrToL,
                T=DNS/RFC822/X-Unix,
                A=maidag $u@$h

MAILER(smtp)

submit.mc

divert(-1)
#

divert(0)dnl
VERSIONID(`$Id: submit.mc,v 8.14 2006/04/05 05:54:41 ca Exp $')
define(`confCF_VERSION', `Submit')dnl
define(`__OSTYPE__',`')dnl dirty hack to keep proto.m4 from complaining
define(`_USE_DECNET_SYNTAX_', `1')dnl support DECnet
define(`confTIME_ZONE', `USE_TZ')dnl
define(`confDONT_INIT_GROUPS', `True')dnl
define(`confPRIVACY_FLAGS',`authwarnings,novrfy,noexpn,noetrn,needmailhelo')

FEATURE(`msp', `[127.0.0.1]')dnl

dnl * do STARTTLS
define(`confCACERT_PATH', `/etc/mail/certs')dnl
define(`confCACERT', `/etc/mail/certs/sendmail.pem')dnl
define(`localCERT', `/etc/mail/certs/sendmail.pem')dnl
define(`confSERVER_CERT', `localCERT')dnl
define(`confSERVER_KEY',  `localCERT')dnl
define(`confCLIENT_CERT', `localCERT')dnl
define(`confCLIENT_KEY',  `localCERT')dnl

dnl * do SMTPAUTH
define(`confAUTH_MECHANISMS', `LOGIN PLAIN')dnl
TRUST_AUTH_MECH(`LOGIN PLAIN')dnl

dnl * look for AuthOptions @ op.ps
define(`confAUTH_OPTIONS', `A p y')dnl

DAEMON_OPTIONS(`Family=inet,Name=MTA-TLS,M=Eab')

mailertable

in case to leave it as file, then in /etc/mail/mailertable put

foo.bar    local-ldap:foo.bar
foo.com    local-ldap:foo.com

in case to put it in LDAP we need:

dn: sendmailMTAMapName=mailer,ou=core.relay.foo.bar,ou=Sendmail,dc=foo,dc=bar
objectclass: sendmailMTA
objectclass: sendmailMTAMap
sendmailmtacluster: SMTPCLUSTER
sendmailmtamapname: mailer

dn: sendmailMTAKey=foo.bar,sendmailMTAMapName=mailer,ou=core.relay.foo.bar,ou=Sendmail,dc=foo,dc=bar
objectclass: sendmailMTA
objectclass: sendmailMTAMap
objectclass: sendmailMTAMapObject
sendmailmtacluster: SMTPCLUSTER
sendmailmtakey: foo.bar
sendmailmtamapname: mailer
sendmailmtamapvalue: local-ldap:foo.bar

dn: sendmailMTAKey=foo.com,sendmailMTAMapName=mailer,ou=core.relay.foo.bar,ou=Sendmail,dc=foo,dc=bar
objectclass: sendmailMTA
objectclass: sendmailMTAMap
objectclass: sendmailMTAMapObject
sendmailmtacluster: SMTPCLUSTER
sendmailmtakey: foo.com
sendmailmtamapname: mailer
sendmailmtamapvalue: local-ldap:foo.com

test

#> ldd /usr/sbin/sendmail 
/usr/sbin/sendmail:
        libsasl2.so.2 => /usr/local/lib/libsasl2.so.2 (0x8008cd000)
        libssl.so.6 => /usr/lib/libssl.so.6 (0x800ae8000)
        libcrypto.so.6 => /lib/libcrypto.so.6 (0x800d3b000)
        libdb-4.4.so.0 => /usr/local/lib/libdb-4.4.so.0 (0x8010dc000)
        libldap-2.4.so.8 => /usr/local/lib/libldap-2.4.so.8 (0x8015f8000)
#> openssl s_client -connect relay.foo.bar:25 -starttls smtp         
CONNECTED(00000004)
depth=0 /C=AU/ST=WS/L=Sidney/O=FooBar llc/OU=IT/CN=relay.foo.bar/emailAddress=security@foo.bar
...
---
SSL handshake has read 2486 bytes and written 384 bytes
---
New, TLSv1/SSLv3, Cipher is DHE-RSA-AES256-SHA
...
SSL-Session:
    Protocol  : TLSv1
...
---
250 HELP
ehlo test
250-relay.foo.bar Hello [A.A.A.A], pleased to meet you
...
250-AUTH LOGIN PLAIN
...
250 HELP
quit
221 2.0.0 relay.foo.bar closing connection
sendmail -bt
> 3,0 testuser@foo.bar
canonify           input: testuser @ foo . bar
Canonify2          input: testuser < @ foo . bar >
Canonify2        returns: testuser < @ foo . bar . >
canonify         returns: testuser < @ foo . bar . >
parse              input: testuser < @ foo . bar . >
Parse0             input: testuser < @ foo . bar . >
Parse0           returns: testuser < @ foo . bar . >
ParseLocal         input: testuser < @ foo . bar . >
ParseLocal       returns: testuser < @ foo . bar . >
Parse1             input: testuser < @ foo . bar . >
MailerToTriple     input: < local-ldap : foo . bar > testuser < @ foo . bar . >
MailerToTriple   returns: $# local-ldap $@ foo . bar $: testuser < @ foo . bar . >
Parse1           returns: $# local-ldap $@ foo . bar $: testuser < @ foo . bar . >
parse            returns: $# local-ldap $@ foo . bar $: testuser < @ foo . bar . >

aliases

native, sendmail aliases db supports only one domain and one users db

smap doesn't working with LDAP yet

the only way to do aliases this time it is to create separate, dedicated alias user and to do all need manipulations via it's .sieve script

mu

config

configure \
    CPPFLAGS='-I/usr/local/include/ -I/usr/local/include/db44/ -I/usr/local/include/postgresql/ -I/usr/local/include/gnutls' \
    LDFLAGS='-L/usr/local/lib/ -L/usr/local/lib/postgresql/ -L/usr/local/lib/db44' \
    --enable-debug \
    --with-berkeley-db \
    --enable-pam \
    --with-gnutls \
    --with-postgres \
    --with-ldap \
    --disable-nls \
    --disable-nntp \
    --disable-radius

mailutils.rc

mailutils.rc

ldap {
  enable yes;
  url "ldap://ldap.foo.bar:389/";
  base "ou=people,dc=ibs";
  binddn "uid=bind@mai.foo.bar,ou=people,dc=foo,dc=bar";
  passwd "*****";
  tls yes;
  debug 1;

  field-map ""
"name=uid:"
"passwd=userPassword:"
"uid=uidNumber:"
"gid=gidNumber:"
"gecos=gecos:"
"dir=homeDirectory:"
"shell=loginShell:"
"mailbox=mu-mailBox";

  getpwnam "(|"
"(&(authorizedService=mail@foo.bar)(uid=${user}))"
"(&(authorizedService=mail@foo.bar)(cn=${user}))"
"(&(authorizedService=mail@foo.com)(cn=${user})))";

  getpwuid "(|"
"(&(authorizedService=mail@foo.bar)(uid=${user}))"
"(&(authorizedService=mail@foo.bar)(cn=${user}))"
"(&(authorizedService=mail@foo.com)(cn=${user})))";
};

auth {
  authorization generic:ldap:system;
  authentication generic:ldap:system;
};

mailbox {
  mailbox-type "maildir";
  mailbox-pattern "maildir:/var/mail;type=index;param=2;user=${user}";
};

locking {
  retry-count 400;
};

include /usr/local/etc/mailutils;

pop3d

tls {
  enable yes;
  ssl-cert /usr/local/etc/mailutils/ssl/mu.pem;
  ssl-key  /usr/local/etc/mailutils/ssl/mu.pem;
};

logging {
  facility local3;
};

debug {
  level "server.trace0";
  line-info yes;
};

#bulletin-source /var/spool/bulletin/bulletin.mbox;
#bulletin-db /var/spool/bulletin/bulletin;
max-children 30;
mode daemon;
pidfile /var/run/pop3d.pid;
timeout 180;

server X.X.X.X {
  transcript no;
  tls ondemand;
};

server Y.Y.Y.Y {
  transcript no;
  tls required;
};

server Y.Y.Y.Y:995 {
  transcript no;
  tls connection;
};


acl {
  log from any "Connect from ${address}";
};

imap4d

tls {
  enable yes;
  ssl-cert /usr/local/etc/mailutils/ssl/mu.pem;
  ssl-key  /usr/local/etc/mailutils/ssl/mu.pem;
};

logging {
  facility local2;
};

mandatory-locking {
  enable 1;
  lock-directory /tmp;
};

debug {
  level "server.trace0";
  line-info yes;
};

create-home-dir 1;
shared-namespace "/var/mail/IMAP_SHARE";
shared-mailbox-mode g=rw;

max-children 50;
mode daemon;
pidfile /var/run/imap4d.pid;
timeout 300;

server X.X.X.X {
  transcript yes;
  tls ondemand;
};

server Y.Y.Y.Y {
  transcript no;
  tls required;
};

server Y.Y.Y.Y:993 {
  transcript no;
  tls connection;
};

acl {
  log from any "Connect from ${address}";
#  allow Z.Z.Z.Z;
#  deny any;
};

maidag

debug {
  line-info yes;
};

logging {
  tag "maidag";
};

filter {
  language "sieve";
  # pattern "/usr/local/etc/mailutils/scripts/%u"; # this time %u contains no domain part
  pattern "%h/.sieve";
};

test

#> telnet pop.foo.bar 110
Trying X.X.X.X...
Connected to pop.foo.bar.
Escape character is '^]'.
+OK POP3 Ready <24289.1377897169@relay.foo.bar>
user testuser@foo.bar
+OK
pass *****
+OK opened mailbox for testuser
quit
+OK
Connection closed by foreign host.

#> telnet imap.foo.bar 143
Trying X.X.X.X...
Connected to imap.foo.bar.
Escape character is '^]'.
* OK IMAP4rev1
00 login testuser@foo.bar *****
00 OK LOGIN Completed
11 logout
* BYE Session terminating.
11 OK LOGOUT Completed
Connection closed by foreign host.

External links