Attention
Check out the lightweight on-premises email archiving software developed by iRedMail team: Spider Email Archiver.
NOTES:
iRedAdmin-Pro doesn't work with Active Directory, so if you choose to authenticate mail users against Active Directory, you have to manage mail accounts with Active Directory management tools.
This tutorial has been verified on Windows Server 2000, 2003, 2008, 2012, 2016, 2019, if you tested it on other versions and works well, please let us know. Contact us
With Active Directory (AD) integration, you can get below features:
Since AD uses different LDAP schema, you will lose some iRedMail special features. e.g.
To integrate Microsoft Active Directory with iRedMail, you should have:
A working Microsoft Windows (2000/2003) server, with Active Directory
installed and working properly, listen on port 389
(ldap://) or 636
(ldaps://), and allow LDAP connections from iRedMail server.
If you need to enable LDAP over SSL, please read this tutorial.
If connecting to 389
doesn't work, try port 3268
instead.
Please follow iRedMail installaion guides to install iRedMail on Linux/BSD with OpenLDAP backend first, we will achieve this AD integration by simply modifying some configure files.
We assume:
Hostname of your AD server is ad.example.com
, listen on port 389
. And it's
accessible from iRedMail server.
636
instead.Base dn in AD is dc=example,dc=com
, email addresses of all users end with
@example.com
(Your mail domain is example.com
).
cn=Users,dc=example,dc=com
. Note: LDAP dn is case-insensitive.3
is recommended.Store all mails on Linux/BSD servers, not on AD server.
/var/vmail/vmail1
, same as default in iRedMail.support@example.com
will be
/var/vmail/vmail1/example.com/support/Maildir/
(Maildir format).With iRedMail (OpenLDAP backend), we have a low-privileged account
cn=vmail,dc=xxx,dc=xxx
with read-only privilege. And we suggest you create a
same account vmail
in AD, with strong and complex password.
NOTES:
#
, please don't use
#
in password.vmail
user with your domain name instead. for
example, vmail@domain.com
(replace domain.com
by your real domain name).Make sure this newly created user is able to connect to AD server with below command on iRedMail server:
Info
-h
is deprecated in new OpenLDAP, please use -H
instead. For example,
-H ldap://ad.example.com
, -H ldap://ad.example.com:389
,
-H ldaps://ad.example.com:636
.
# ldapsearch -x -h ad.example.com -D 'vmail' -W -b 'cn=users,dc=example,dc=com'
Enter password: password_of_vmail
If it prints all users stored in AD server, then it's working as expected.
If you're using LDAPS, replace -h ad.example.com
by
-H ldaps://ad.example.com:636
instead:
# ldapsearch -x -H ldaps://ad.example.com:636 -D 'vmail' -W -b 'cn=users,dc=example,dc=com'
Enter password: password_of_vmail
If LDAPS doesn't work, you may need to update parameter TLS_CACERT
in
/etc/openldap/ldap.conf
(RHEL/CentOS) or /etc/ldap/ldap.conf
to use correct CA
certificate. For example:
/etc/pki/tls/certs/ca-bundle.trust.crt
:TLS_CACERT /etc/pki/tls/certs/ca-bundle.trust.crt
/etc/ssl/certs/ca-certificates.crt
:TLS_CACERT /etc/ssl/certs/ca-certificates.crt
Disable unused iRedMail special settings:
postconf -e virtual_alias_maps=''
postconf -e sender_bcc_maps=''
postconf -e recipient_bcc_maps=''
postconf -e relay_domains=''
postconf -e relay_recipient_maps=''
postconf -e sender_dependent_relayhost_maps=''
Add your mail domain name in smtpd_sasl_local_domain
and virtual_mailbox_domains
:
postconf -e smtpd_sasl_local_domain='example.com'
postconf -e virtual_mailbox_domains='example.com'
Change transport maps setting:
postconf -e transport_maps='hash:/etc/postfix/transport'
Enable AD query. Note: We will create these 3 files later.
postconf -e smtpd_sender_login_maps='proxy:ldap:/etc/postfix/ad_sender_login_maps.cf'
postconf -e virtual_mailbox_maps='proxy:ldap:/etc/postfix/ad_virtual_mailbox_maps.cf'
postconf -e virtual_alias_maps='proxy:ldap:/etc/postfix/ad_virtual_group_maps.cf'
/etc/postfix/transport
.example.com dovecot
Note: the name dovecot
used here is a Postfix transport defined in
/etc/postfix/master.cf
, used to deliver received emails to local user mailboxes.
Run postmap
so that postfix can read it:
# postmap hash:/etc/postfix/transport
/etc/postfix/ad_sender_login_maps.cf
:server_host = ad.example.com
server_port = 389
version = 3
bind = yes
start_tls = no
bind_dn = vmail
bind_pw = password_of_vmail
search_base = cn=users,dc=example,dc=com
scope = sub
query_filter = (&(userPrincipalName=%s)(objectClass=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))
result_attribute= userPrincipalName
debuglevel = 0
/etc/postfix/ad_virtual_mailbox_maps.cf
:server_host = ad.example.com
server_port = 389
version = 3
bind = yes
start_tls = no
bind_dn = vmail
bind_pw = passwd_of_vmail
search_base = cn=users,dc=example,dc=com
scope = sub
query_filter = (&(objectclass=person)(userPrincipalName=%s))
result_attribute= userPrincipalName
result_format = %d/%u/Maildir/
debuglevel = 0
Note: We hard-code user's mailbox path in result_format =
parameter, it
will be something like example.com/username/Maildir/
.
/etc/postfix/ad_virtual_group_maps.cf
:server_host = ad.example.com
server_port = 389
version = 3
bind = yes
start_tls = no
bind_dn = vmail
bind_pw = password_of_vmail
search_base = cn=users,dc=example,dc=com
scope = sub
query_filter = (&(objectClass=group)(mail=%s))
special_result_attribute = member
leaf_result_attribute = mail
result_attribute= userPrincipalName
debuglevel = 0
Notes:
mail
and userPrincipalName
, you
will get duplicate result. Comment out leaf_result_attribute
line will fix it.mail
and
userPrincipalName
, please try query_filter = (&(objectClass=group)(sAMAccountName=%u))
instead.Also, we need to remove iRedAPD related settings in Postfix:
/etc/postfix/main.cf
check_policy_service inet:127.0.0.1:7777
.We can now use command line tool postmap
to verify AD integration in postfix.
Before testing, we have to create two testing mail accounts first:
user@example.com
.testgroup@example.com
, then assign mail
user user@example.com
as group member.# postmap -q user@example.com ldap:/etc/postfix/ad_virtual_mailbox_maps.cf
example.com/user/Maildir/
If nothing returned by the command, it means LDAP query doesn't get expected
result. Please set debuglevel = 1
file /etc/postfix/ad_virtual_mailbox_maps.cf
,
then query again, it now will print detailed debug message. If you're not
familiar with LDAP related info, please post the debug message in our
online support forum to get help.
Verify sender login check:
# postmap -q user@example.com ldap:/etc/postfix/ad_sender_login_maps.cf
user@example.com
Verify mail group
# postmap -q testgroup@example.com ldap:/etc/postfix/ad_virtual_group_maps.cf
user@example.com
NOTE: postmap
return nothing if:
iRedAPD relies on iRedMail LDAP scheme, so it's useless if you integrate iRedMail with Active Directory. We should remove the integration in Postfix to save some system resource.
To disable iRedAPD, please read tutorial: Manage iRedAPD.
To query AD instead of local LDAP server, we have to modify Dovecot config file
/etc/dovecot/dovecot-ldap.conf
like below:
Attention
If you use LDAPS and hosts =
doesn't work, please replace it by uris =
instead. Check
Dovecot tutorial
for more details.
hosts = ad.example.com:389
ldap_version = 3
auth_bind = yes
dn = vmail
dnpass = passwd_of_vmail
base = cn=users,dc=example,dc=com
scope = subtree
deref = never
# Below two are required by command 'doveadm mailbox ...'
iterate_attrs = userPrincipalName=user
iterate_filter = (&(userPrincipalName=*)(objectClass=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))
user_filter = (&(userPrincipalName=%u)(objectClass=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))
pass_filter = (&(userPrincipalName=%u)(objectClass=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))
pass_attrs = userPassword=password
default_pass_scheme = CRYPT
user_attrs = mail=master_user,mail=user,=home=/var/vmail/vmail1/%Ld/%Ln/,=mail=maildir:~/Maildir/
Restart dovecot service to make it work.
Attention
We don't have per-user quota limit here, you can set a hard-coded
quota for all users in /etc/dovecot/dovecot.conf
. For example:
plugin {
[... omit other settings here ...]
# Format: integer number + M/G/T (M -> MB, G -> GB, T -> TB).
quota_rule = *:storage=1G
}
You can also modify the user_attrs =
line to get per-user quota from a
LDAP attribute in AD. For example, query per-user quota limit from
attribute postOfficeBox
which contain an integer number and treated as
number of gigabytes:
user_attrs = =home=/var/vmail/vmail1/%Ld/%Ln/,=mail=maildir:~/Maildir/,postOfficeBox=quota_rule=*:storage=%{ldap:postOfficeBox}G
Note: This per-user quota will override the one hard-coded in dovecot.conf.
Now use command telnet
to verify AD query after restarted Dovecot service:
# telnet localhost 143 # <- Type this
* OK [...] Dovecot ready.
. login user@example.com password_of_user # <- Type this. Do not miss the dot in the beginning
. OK [...] Logged in
^] # <- Quit telnet with "Ctrl+]", then type 'quit'.
Note: Do NOT miss the dot character before login
command. if it returns
Logged in
, then dovecot + AD works.
Edit roundcube config file config/config.inc.php
, comment out the LDAP
address book setting added by iRedMail, and add new setting for AD like below:
/opt/www/roundcubemail/config/config.inc.php
/usr/local/www/roundcubemail/config/config.inc.php
#
# "sql" is personal address book stored in roundcube database.
# "global_ldap_abook" is the new LDAP address book for AD, we will create it below.
#
$config['autocomplete_addressbooks'] = array("sql", "global_ldap_abook");
# Enable setting below if Roundcube returns 'user@127.0.0.1' as email address
#$config['mail_domain'] = '%d';
#
# Global LDAP Address Book with AD.
#
$config['ldap_public']["global_ldap_abook"] = array(
'name' => 'Global Address Book',
'hosts' => array("ad.example.com"), // <- Set AD hostname or IP address here.
'port' => 389,
'use_tls' => false, // <- Set to true if you want to use LDAP over TLS.
'ldap_version' => '3',
'network_timeout' => 10,
'user_specific' => false,
'base_dn' => "cn=users,dc=example,dc=com", // <- Set base dn in AD
'bind_dn' => "vmail", // <- bind dn
'bind_pass' => "password_of_vmail", // <- bind password
'writable' => false, // <- Do not allow mail user write data back to AD.
'search_fields' => array('mail', 'cn', 'sAMAccountName', 'displayname', 'sn', 'givenName'),
// mapping of contact fields to directory attributes
'fieldmap' => array(
'name' => 'cn',
'displayname' => 'displayName',
'surname' => 'sn',
'firstname' => 'givenName',
'jobtitle' => 'title',
'department' => 'department',
'company' => 'company',
'email' => 'mail:*',
'phone:work' => 'telephoneNumber',
'phone:home' => 'homePhone',
'phone:mobile' => 'mobile',
'phone:workfax' => 'facsimileTelephoneNumber',
'phone:pager' => 'pager',
'phone:other' => 'ipPhone',
'street:work' => 'streetAddress',
'zipcode:work' => 'postalCode',
'locality:work' => 'l',
'region:work' => 'st',
'country:work' => 'c',
'notes' => 'description',
'photo' => 'jpegPhoto', // Might be 'thumbnailPhoto' for
// compatibility with some other
// Microsoft software
'website' => 'wWWHomePage',
),
'sort' => 'cn',
'scope' => 'sub',
'filter' => "(&(|(objectclass=person)(objectclass=group))(!(userAccountControl:1.2.840.113556.1.4.803:=2)))",
'fuzzy_search' => true,
'vlv' => false, // Enable Virtual List View to more
// efficiently fetch paginated data
// (if server supports it)
'sizelimit' => '0', // Enables you to limit the count of
// entries fetched. Setting this to 0
// means no limit.
'timelimit' => '0', // Sets the number of seconds how long
// is spend on the search. Setting this
// to 0 means no limit.
'referrals' => false, // Sets the LDAP_OPT_REFERRALS option.
// Mostly used in multi-domain Active
// Directory setups
);
Edit SOGo config file /etc/sogo/sogo.conf
, comment out the LDAP address book
setting added by iRedMail, and add new setting for AD like below:
SOGoUserSources = (
{
// Used for user authentication
type = ldap;
id = users;
canAuthenticate = YES;
isAddressBook = NO;
displayName = "LDAP Authentication";
hostname = "ldap://ad.example.com:389"; // <- Set to ldaps://ad.example.com:636 for LDAPS.
baseDN = "cn=users,dc=example,dc=com";
bindDN = "vmail";
bindPassword = "password_of_vmail";
filter = "objectClass=person AND userPrincipalName='*' AND (NOT userAccountControl:1.2.840.113556.1.4.803:=2)";
scope = SUB;
// always keep binding to the LDAP server using the DN of the
// currently authenticated user. bindDN and bindPassword are still
// required to find DN of the user.
// Note: with default LDAP acl configured by iRedMail, user doesn't
// have privilege to query o=domains,dc=delmsgs,dc=freeddns,dc=org.
// so this doesn't work.
bindAsCurrentUser = YES;
// The algorithm used for password encryption when changing
// passwords without Password Policies enabled.
// Possible values are: plain, crypt, md5-crypt, ssha, ssha512.
userPasswordAlgorithm = ssha512;
CNFieldName = cn;
IDFieldName = userPrincipalName;
// value of UIDFieldName must be unique on entire server
UIDFieldName = userPrincipalName;
IMAPLoginFieldName = userPrincipalName;
MailFieldNames = (userPrincipalName);
bindFields = (userPrincipalName);
},
{
// Used for global address book
type = ldap;
id = global_addressbook;
canAuthenticate = NO;
isAddressBook = YES;
displayName = "Global Address Book";
bindAsCurrentUser = YES;
// Listing of this LDAP source is only possible when performing a
// search (respecting the SOGoSearchMinimumWordLength parameter)
// or when explicitely typing a single dot.
// Defaults to YES when unset.
//
// WARNING: if you have many accounts in this address book, it may
// reach server-side query size limit, or cause
// performance issue.
listRequiresDot = NO;
// Set to ldaps://ad.example.com:636 for LDAPS.
hostname = "ldap://ad.example.com:389";
baseDN = "cn=users,dc=example,dc=com";
bindDN = "vmail";
bindPassword = "password_of_vmail";
filter = "(objectClass=person OR (objectClass=group AND mail='*')) AND (NOT userAccountControl:1.2.840.113556.1.4.803:=2)";
scope = SUB;
IDFieldName = userPrincipalName;
bindFields = (userPrincipalName);
// value of UID field must be unique on whole server.
UIDFieldName = userPrincipalName;
IMAPLoginFieldName = userPrincipalName;
CNFieldName = cn;
SearchFieldNames = (mail, cn, sAMAccountName, displayName, sn, givenName);
mapping = {
ou = ("department", "ou");
street = ("streetAddress", "street");
mozillaworkurl = ("wWWHomePage", "mozillaworkurl");
description = ("info", "description");
};
}
);