forked from benningm/mtpolicyd
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request benningm#30 from falon/master
A complete example of accounting over LDAP
- Loading branch information
Showing
3 changed files
with
351 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
dn: cn=schema | ||
#Attributes | ||
# | ||
attributeTypes: ( mtpolicydMailMessageLimit-oid | ||
NAME ( 'mtpolicydMailMessageLimit' ) | ||
DESC 'MtPolicyd user defined attribute for enable accounting over count messages' | ||
EQUALITY integerOrderingMatch | ||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 | ||
SINGLE-VALUE | ||
X-ORIGIN 'MtPolicyd' ) | ||
attributeTypes: ( mtpolicydMailRecipientLimit-oid | ||
NAME ( 'mtpolicydMailRecipientLimit' ) | ||
DESC 'MtPolicyd user defined attribute for enable accounting over recipient count messages' | ||
EQUALITY integerOrderingMatch | ||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 | ||
SINGLE-VALUE | ||
X-ORIGIN 'MtPolicyd' ) | ||
attributeTypes: ( mtpolicydMailSizeLimit-oid | ||
NAME ( 'mtpolicydMailSizeLimit' ) | ||
DESC 'MtPolicyd user defined attribute for enable accounting over size limit' | ||
EQUALITY integerOrderingMatch | ||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 | ||
SINGLE-VALUE | ||
X-ORIGIN 'MtPolicyd' ) | ||
attributeTypes: ( mtpolicydMailSizeRecipientLimit-oid | ||
NAME ( 'mtpolicydMailSizeRecipientLimit' ) | ||
DESC 'MtPolicyd user defined attribute for enable accounting over size x recipient limit' | ||
EQUALITY integerOrderingMatch | ||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 | ||
SINGLE-VALUE | ||
X-ORIGIN 'MtPolicyd' ) | ||
# | ||
# | ||
#Objectclasses | ||
objectclasses: ( mtpolicyd-oid | ||
NAME 'mtpolicyd' | ||
DESC 'mtPolicyd class for user level configuration' | ||
SUP mailRecipient | ||
AUXILIARY | ||
MUST ( ) | ||
MAY ( mtpolicydMailMessageLimit $ mtpolicydMailRecipientLimit $ mtpolicydMailSizeLimit $ mtpolicydMailSizeRecipientLimit ) | ||
X-ORIGIN 'MtPolicyd' ) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
# A case study over LDAP, Postfix and MtPolicyd | ||
## Abstract | ||
With MtPolicyd you can use LDAP to profile your accounts with policies. For instance, for each **account** you can set a number of maximum message rate (for single message, or message x recipient), or a size rate too. | ||
We see how to implement these policies per account with a working example. Suitable for a large environment. | ||
|
||
## Requisite | ||
Many email service implementations adopt LDAP as a DB to profile user preferences, SMTP routing information and authentication. You should have an LDAP server (ldap.example.com) with email account like this: | ||
|
||
``` | ||
dn: [email protected],[base dn] | ||
objectClass: top | ||
objectClass: person | ||
objectClass: organizationalPerson | ||
objectClass: inetOrgPerson | ||
objectClass: mailRecipient | ||
objectClass: inetMailUser | ||
mailAlternateAddress: [email protected] | ||
mail: [email protected] | ||
mailDeliveryOption: mailbox | ||
uid: [email protected] | ||
userPassword: mypassword | ||
cn: Account name | ||
mailUserStatus: active | ||
sn: Account | ||
mailHost: imapserver.example.com | ||
``` | ||
|
||
In this example the uid is the `sasl_username` used by Postfix to authenticate and authorize the account to send mail. | ||
You can build this authentication process using saslauthd over LDAP mechanism, for instance. | ||
|
||
Here we don't explain how to implement authentication, other SMTP routing mechanism or email aliases over LDAP. Anyway, let suppose the above entry is a working LDAP account used for SMTP authentication. | ||
|
||
You can imagine other policies for _client_address_ or other keys too, using different Postfix _context_. This is not the scope of this document. | ||
|
||
Postfix, Mtpolicyd and the LDAP server could stay on different hosts. All of them can interface each other through TCP sockets. For instance you can install | ||
|
||
* MtPolicyd on mtpolicyd.example.com | ||
* Postfix on postfix.example.com | ||
* Directory Server on ldap.example.com | ||
|
||
## Configure | ||
As you can see in [Plugin Accounting](http://search.cpan.org/~benning/Mail-MtPolicyd-1.16/lib/Mail/MtPolicyd/Plugin/Accounting.pm), we have four counters for each key. Our key will be `sasl_username`, because we want policies per account. So we first have to declare a schema for the LDAP server. | ||
|
||
Mtpolicyd doesn't provide an official schema. Here you can find a schema useful for the result we want achieve in this case. The schema works with Red Hat/Fedora Directory Server, but with little adjustment probably can work with OpenLDAP or other Directory Servers which support custom, **unofficial OIDs**. | ||
|
||
This unofficial schema provides the attributes for the four counters: | ||
* mtpolicydMailMessageLimit | ||
* mtpolicydMailRecipientLimit | ||
* mtpolicydMailSizeLimit | ||
* mtpolicydMailSizeRecipientLimit | ||
|
||
These attributes comes with the objectClass | ||
* mtpolicyd | ||
|
||
which extend the objectclass "mailRecipient". This choice is not mandatory, you can change it if you don't like it. | ||
|
||
Once you have extended the schema, our LDAP entry can be profiled for MtPolicyd. | ||
For instance we can choose to limit the account "[email protected]" to send a maximum of 100 mails per time unit. | ||
To achieve this the entry is: | ||
|
||
``` | ||
dn: [email protected],[base dn] | ||
mtpolicydMailMessageLimit: 100 | ||
objectClass: top | ||
objectClass: person | ||
objectClass: organizationalPerson | ||
objectClass: inetOrgPerson | ||
objectClass: mailRecipient | ||
objectClass: inetMailUser | ||
objectClass: mtpolicyd | ||
mailAlternateAddress: [email protected] | ||
mail: [email protected] | ||
mailDeliveryOption: mailbox | ||
uid: [email protected] | ||
userPassword: mypassword | ||
cn: Account name | ||
mailUserStatus: active | ||
sn: Account | ||
mailHost: imapserver.example.com | ||
``` | ||
|
||
We could also set the `mtpolicydMailRecipientLimit` attribute and configure MtPolicyd to refuse the mails if at least one counter triggers the threshold defined in the LDAP attribute. So, here is the complete MtPolicyd virtual host: | ||
|
||
``` | ||
vhost_by_policy_context=1 | ||
<VirtualHost 12345> | ||
name="accounting" | ||
<Plugin LdapUID> | ||
module="LdapUserConfig" | ||
basedn="[base dn]" | ||
# sasl_username attribute is uid. | ||
filter_field="sasl_username" | ||
filter="(&(uid=%s)(objectClass=mailRecipient)(objectclass=mtpolicyd)(mailUserStatus=active))" | ||
# copy these fields to current mtpolicyd session | ||
config_fields="mtpolicydMailMessageLimit,mtpolicydMailRecipientLimit" | ||
</Plugin> | ||
<Plugin QuotaUser> | ||
module = "Quota" | ||
time_pattern = "%Y-%m-%d" | ||
field = "sasl_username" | ||
metric = "count" | ||
threshold = 500 | ||
# if this field is set it will overwrite the default threshold | ||
uc_threshold = "mtpolicydMailMessageLimit" | ||
# for MSA you may reject, for MTAs you may defer | ||
action = "reject you exceeded your daily message limit" | ||
</Plugin> | ||
<Plugin QuotaUserRecipient> | ||
module = "Quota" | ||
time_pattern = "%Y-%m-%d" | ||
field = "sasl_username" | ||
metric = "count_rcpt" | ||
threshold = 5000 | ||
# if this field is set it will overwrite the default threshold | ||
uc_threshold = "mtpolicydMailRecipientLimit" | ||
# for MSA you may reject, for MTAs you may defer | ||
action = "reject you exceeded your daily mail recipient limit" | ||
</Plugin> | ||
<Plugin AcctUser> | ||
module = "Accounting" | ||
fields = "sasl_username" | ||
# Perform day based limit | ||
time_pattern = "%Y-%m-%d" | ||
</Plugin> | ||
</VirtualHost> | ||
``` | ||
To understand how it works, we strongly suggest to read the [How to Accounting Quota CookBook](https://metacpan.org/pod/release/BENNING/Mail-MtPolicyd-2.03/lib/Mail/MtPolicyd/Cookbook/HowtoAccountingQuota.pod). | ||
|
||
In this example the rate time unit is _day_, but you can configure hours or other just setting the proper `time_pattern`. | ||
|
||
### Postfix interface | ||
This is very simple. The main.cf of the Postfix server can be configured with | ||
``` | ||
smtpd_end_of_data_restrictions = | ||
check_policy_service { | ||
inet:mtpolicyd.example.com:12345, | ||
policy_context=accounting | ||
} | ||
``` | ||
|
||
### LDAP connection | ||
This is an example for the LDAP connection: | ||
|
||
``` | ||
<Connection ldap> | ||
module = "Ldap" | ||
host = "ldap.example.com" | ||
port = 389 | ||
timeout = 20 | ||
binddn = "uid=mtpolicyd,ou=admins,[base dn]" | ||
password = "mtpolicyd" | ||
starttls = 0 | ||
</Connection> | ||
``` | ||
Don't worry if connections between MtPolicyd and LDAP server die. MtPolicyd checks if the connection is alive. If the connection dies, MtPolicyd tries to renegotiate it. This behavior has tested with load balancer and LDAP server which expires idle sessions. | ||
|
||
The user _mtpolicyd_ can be: | ||
|
||
``` | ||
dn: uid=mtpolicyd,ou=admins,[base dn] | ||
uid: mtpolicyd | ||
givenName: Mail Team | ||
objectClass: top | ||
objectClass: person | ||
objectClass: organizationalPerson | ||
objectClass: inetorgperson | ||
sn: Policyd | ||
cn: Mail Team Policyd | ||
userPassword: mtpolicyd | ||
``` | ||
|
||
Remember to set a Password Policy which doesn't expire the password of the user mtpolicyd. | ||
|
||
On [base dn], or where mail accounts stay, you can set an aci: | ||
|
||
``` | ||
aci: (targetattr = "objectClass || mtpolicydMailSizeLimit || uid || mtpolicydM | ||
ailSizeRecipientLimit || mtpolicydMailMessageLimit || mailUserStatus || mtpol | ||
icydMailRecipientLimit") (target = "ldap:///[base dn]") | ||
(targetfilter = objectclass=mtpolicyd) (version 3.0;acl "Allow MtPolicyd access | ||
";allow (read,compare,search)(userdn = "ldap:///uid=mtpolicyd,ou=admins,[base dn]");) | ||
``` | ||
|
||
This aci limits what user mtpolicyd can perform over LDAP data. But you can imagine more complex situations, where an aci time-defined can enforce a policy only during a specific time interval, such as night hours or weekend. | ||
|
||
## The complete example | ||
* [LDAP schema](97mtpolicyd.ldif) | ||
* [a complete mtpolicyd.conf example](mtpolicyd.conf) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
# Configuration for the mailteam policy daemon | ||
|
||
user=mtpolicyd | ||
group=mtpolicyd | ||
|
||
# 0=>'err', 1=>'warning', 2=>'notice', 3=>'info', 4=>'debug' (default: 2) | ||
log_level=2 | ||
|
||
host=10.10.10.10 | ||
port="10.10.10.10:12345" | ||
|
||
min_servers=4 | ||
min_spare_servers=4 | ||
max_spare_servers=12 | ||
max_servers=50 | ||
max_requests=1000 | ||
|
||
#keepalive_timeout=60 | ||
keepalive_timeout=0 | ||
# should be the same value as smtpd_policy_service_reuse_count_limit (postfix >2.12) | ||
max_keepalive=0 | ||
#max_keepalive=100 | ||
|
||
# timeout for processing of one request in seconds | ||
request_timeout=20 | ||
|
||
vhost_by_policy_context=1 | ||
|
||
<Connection memcached> | ||
module = "Memcached" | ||
servers = "127.0.0.1:11211" | ||
# namespace = "mt-" | ||
</Connection> | ||
|
||
# mysql for storing accounting tables | ||
<Connection db> | ||
module = "Sql" | ||
# see perldoc DBI for syntax of dsn connection string | ||
dsn = "dbi:mysql:database=mailpolicy;host=localhost;port=3306" | ||
user = "mtpolicyd" | ||
password = "mysqlpassword" | ||
</Connection> | ||
|
||
# ldap with user configuration | ||
<Connection ldap> | ||
module = "Ldap" | ||
host = "ldap.example.com" | ||
port = 389 | ||
timeout = 20 | ||
binddn = "uid=mtpolicyd,o=admins,c=en" | ||
password = "ldappassword" | ||
starttls = 0 | ||
</Connection> | ||
|
||
|
||
|
||
<SessionCache> | ||
module = "Memcached" | ||
#memcached = "memcached" | ||
# expire session cache entries | ||
expire = "300" | ||
# wait timeout will be increased each time 50,100,150,... (usec) | ||
lock_wait=50 | ||
# abort after n retries | ||
lock_max_retry=50 | ||
# session lock times out after (sec) | ||
lock_timeout=10 | ||
</SessionCache> | ||
|
||
|
||
|
||
<VirtualHost 12345> | ||
name="accounting" | ||
|
||
<Plugin LdapUID> | ||
module="LdapUserConfig" | ||
basedn="c=en" | ||
# sasl_username attribute is uid. | ||
filter_field="sasl_username" | ||
filter="(&(uid=%s)(objectClass=mailRecipient)(objectclass=mtpolicyd)(mailUserStatus=active))" | ||
# copy these fields to current mtpolicyd session | ||
config_fields="mtpolicydMailMessageLimit,mtpolicydMailRecipientLimit" | ||
</Plugin> | ||
|
||
<Plugin QuotaUser> | ||
module = "Quota" | ||
time_pattern = "%Y-%m-%d" | ||
field = "sasl_username" | ||
metric = "count" | ||
threshold = 500 | ||
# if this field is set it will overwrite the default threshold | ||
uc_threshold = "mtpolicydMailMessageLimit" | ||
# for MSA you may reject, for MTAs you may defer | ||
action = "reject you exceeded your daily message limit" | ||
</Plugin> | ||
|
||
<Plugin QuotaUserRecipient> | ||
module = "Quota" | ||
time_pattern = "%Y-%m-%d" | ||
field = "sasl_username" | ||
metric = "count_rcpt" | ||
threshold = 5000 | ||
# if this field is set it will overwrite the default threshold | ||
uc_threshold = "mtpolicydMailRecipientLimit" | ||
# for MSA you may reject, for MTAs you may defer | ||
action = "reject you exceeded your daily mail recipient limit" | ||
</Plugin> | ||
|
||
<Plugin AcctUser> | ||
module = "Accounting" | ||
fields = "sasl_username" | ||
# Perform day based limit | ||
time_pattern = "%Y-%m-%d" | ||
</Plugin> | ||
|
||
</VirtualHost> |