.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.42) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "Mail::MtPolicyd::Cookbook::HowtoAccountingQuota 3pm" .TH Mail::MtPolicyd::Cookbook::HowtoAccountingQuota 3pm "2022-10-15" "perl v5.34.0" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" Mail::MtPolicyd::Cookbook::HowtoAccountingQuota \- How to setup smtp level accounting and quotas .SH "VERSION" .IX Header "VERSION" version 2.05 .SH "SMTP level accounting and quotas with mtpolicyd" .IX Header "SMTP level accounting and quotas with mtpolicyd" The mtpolicyd could be used to implement a smtp level accounting and quota system. .PP This guide explains how to setup accounting and quotas based on the sender ip on a monthly base and configurable quota limits. .PP The how to expects that mtpolicyd is already installed, working and assumes a MySQL database is used to hold accounting data and quota configuration. .SS "Set up Accounting" .IX Subsection "Set up Accounting" The accounting and quota checks should be implemented in postfix smtpd_end_of_data_restrictions. If you're already using mtpolicyd for other check it may be necessary to setup a second virtual host for the accounting/quota configuration. Otherwise you can use the default port 12345 virual host. .PP \fISetup a second virtual host\fR .IX Subsection "Setup a second virtual host" .PP First tell mtpolicyd to also listen on an addition port. In the global configuration add the new port to the port option: .PP .Vb 1 \& port="127.0.0.1:12345,127.0.0.1:12346" .Ve .PP Then add a new virtual host at the end of the configuration file: .PP .Vb 4 \& \& name="accounting" \& # TODO: add plugins... \& .Ve .PP \fIConfigure the Accounting plugin\fR .IX Subsection "Configure the Accounting plugin" .PP Now add the Accounting plugin to your virtual host: .PP .Vb 6 \& \& module = "Accounting" \& fields = "client_address" \& # time_pattern = "%Y\-%m" \& # table_prefix = "acct_" \& .Ve .PP And the restart mtpolicyd to reload the configuration. .PP The plugin will create a table for every field listed in \*(L"fields\*(R". By default the table prefix is acct_ so the table name will be acct_client_address in our example. The plugin will create a row within this table for every client_address and expanded time_pattern: .PP .Vb 8 \& mysql> select * from acct_client_address; \& +\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-+ \& | id | key | time | count | count_rcpt | size | size_rcpt | \& +\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-+ \& | 1 | 2604:8d00:0:1::3 | 2015\-01 | 18 | 18 | 95559 | 95559 | \& | 2 | 2604:8d00:0:1::4 | 2015\-01 | 21 | 21 | 99818 | 99818 | \& ... \& +\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-+ .Ve .PP \fIActivate the check in postfix\fR .IX Subsection "Activate the check in postfix" .PP To active the check add the policyd to your smtpd_end_of_data_restrictions in main.cf: .PP .Vb 1 \& smtpd_end_of_data_restrictions = check_policy_service inet:127.0.0.1:12346 .Ve .PP If you have multiple smtpd process configured in a smtp-filter setup make sure only one smtpd is doing accounting/quota checks. Deactivate the restrictions by adding the following option the the re-inject smtpd processes in master.cf: .PP .Vb 1 \& \-o smtpd_end_of_data_restrictions= .Ve .SS "Setup quota limits" .IX Subsection "Setup quota limits" To limit the number of messages a client_address is allowed to send add the following Quota plugin to your virtual host configuration \fBbefore\fR the Accounting plugin: .PP .Vb 9 \& \& module = "Quota" \& field = "client_address" \& metric = "count" \& threshold = 1000 \& action = "defer you exceeded your monthly limit, please insert coin" \& # time_pattern = "%Y\-%m" \& # table_prefix = "acct_" \& .Ve .SS "Using per client_address quota limits" .IX Subsection "Using per client_address quota limits" Create the following table structure in your MySQL database: .PP .Vb 6 \& CREATE TABLE \`relay_policies\` ( \& \`id\` int(11) NOT NULL auto_increment, \& \`desc\` VARCHAR(64) NOT NULL, \& \`config\` TEXT NOT NULL, \& PRIMARY KEY (\`id\`) \& ) ENGINE=InnoDB; \& \& INSERT INTO relay_policies VALUES(1, \*(Aqstandard relay host\*(Aq, \*(Aq{"quota_count":"10000"}\*(Aq); \& INSERT INTO relay_policies VALUES(2, \*(Aqpremium relay host\*(Aq, \*(Aq{"quota_count":"100000"}\*(Aq); \& \& CREATE TABLE \`relay_hosts\` ( \& \`id\` int(11) NOT NULL auto_increment, \& \`client_address\` VARCHAR(64) NOT NULL, \& \`relay_policy\` int(11) NOT NULL, \& PRIMARY KEY (\`id\`), \& KEY \`relay_policy\` (\`relay_policy\`), \& CONSTRAINT \`relay_hosts_ibfk_1\` FOREIGN KEY (\`relay_policy\`) REFERENCES \`relay_policies\` (\`id\`) \& ) ENGINE=InnoDB; \& \& INSERT INTO relay_hosts VALUES(NULL, \*(Aq2604:8d00:0:1::3\*(Aq, 1); \& INSERT INTO relay_hosts VALUES(NULL, \*(Aq2604:8d00:0:1::4\*(Aq, 2); .Ve .PP You can use the following \s-1SELECT\s0 statement to retrieve the configuration for a relay_host: .PP .Vb 7 \& mysql> SELECT p.config FROM relay_policies p JOIN relay_hosts h ON (h.relay_policy = p.id) WHERE h.client_address = \*(Aq2604:8d00:0:1::4\*(Aq; \& +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+ \& | config | \& +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+ \& | {"quota_count":"100000"} | \& +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+ \& 1 row in set (0.00 sec) .Ve .PP To load the (\s-1JSON\s0) configuration into the mtpolicyd session variables use the SqlUserConfig plugin and this \s-1SQL\s0 statement: .PP .Vb 5 \& \& module = "SqlUserConfig" \& sql_query = "SELECT p.config FROM relay_policies p JOIN relay_hosts h ON (h.relay_policy = p.id) WHERE h.client_address=?" \& field = "client_address" \& .Ve .PP This plugin must be added \fBbefore\fR your Accounting and Quota plugins. .PP To use the quota_count value instead of the default threshold adjust your Quota plugin configuration: .PP .Vb 10 \& \& module = "Quota" \& field = "client_address" \& metric = "count" \& threshold = 1000 \& uc_threshold = "quota_count" \& action = "defer you exceeded your monthly limit, please insert coin" \& # time_pattern = "%Y\-%m" \& # table_prefix = "acct_" \& .Ve .PP If the session variable quota_count is defined it will be used as threshold instead of the value configured in mtpolicyd.conf. .SH "AUTHOR" .IX Header "AUTHOR" Markus Benning .SH "COPYRIGHT AND LICENSE" .IX Header "COPYRIGHT AND LICENSE" This software is Copyright (c) 2014 by Markus Benning . .PP This is free software, licensed under: .PP .Vb 1 \& The GNU General Public License, Version 2, June 1991 .Ve