- [Show page]
- [Old revisions]
- [[unknown link type]]
- []
Differences
This shows you the differences between two versions of the page.
Next revision | Previous revision Last revision Both sides next revision | ||
linux:apache:sendmail_python [2017/01/23 15:16] vondra created |
linux:apache:sendmail_python [2017/02/20 15:52] tomsa [Testing] |
||
---|---|---|---|
Line 1: | Line 1: | ||
====== Python sendmail wrapper ====== | ====== Python sendmail wrapper ====== | ||
Sendmail wrapper limiting amount of emails sent by php | Sendmail wrapper limiting amount of emails sent by php | ||
+ | ===== Installation ===== | ||
+ | * <code bash>pip install psutils</code> | ||
+ | * Copy the script below into /usr/sbin/safe_sendmail | ||
+ | * Put the path to script into sendmail_path variable in php.ini or into /etc/php5/fpm/pool.d/www.conf if using php-fpm | ||
+ | * <code>php_admin_value[sendmail_path] = /usr/sbin/safe_sendmail</code> | ||
+ | * Database and log files will be created in workdir defined in script ("/usr/local/safe_sendmail/") this dir should be writable for everyone (limited access could lead to unexpected bahaviour) | ||
+ | * Timewindow (default 1 hour) and treshold (default 200 emails) parameters are declared in script | ||
+ | * For deeper inspection of mail traffic uncomment logging lines at the end (logging the mail body) | ||
+ | |||
+ | ===== Testing ===== | ||
+ | * Put following content in /tmp/testmail.txt: (few blank lines may be needed at the end of the file)<code file> | ||
+ | To: firma@starlab.cz | ||
+ | Subject: This is a test message subject | ||
+ | X-PHP-Originating-Script: 0:index.php | ||
+ | |||
+ | This is a test message body | ||
+ | |||
+ | |||
+ | |||
+ | </code> | ||
+ | * Run:<code bash>for i in `seq 1 300`; do cat /tmp/testmail.txt | safe_sendmail firma@starlab.cz ; done</code> | ||
+ | * Observe log file:<code bash>tail -f /usr/local/safe_sendmail/safe_sendmail.log</code> | ||
+ | * After the treshold number of emails script refuses to send more emails. | ||
+ | |||
===== Code ===== | ===== Code ===== | ||
<code python> | <code python> | ||
#!/usr/bin/env python | #!/usr/bin/env python | ||
+ | |||
import os | import os | ||
import sqlite3 | import sqlite3 | ||
Line 13: | Line 37: | ||
import subprocess | import subprocess | ||
import shlex | import shlex | ||
+ | import psutil | ||
+ | |||
logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||
logger.setLevel(logging.INFO) | logger.setLevel(logging.INFO) | ||
+ | work_dir = "/usr/local/safe_sendmail" | ||
+ | |||
# create a file handler | # create a file handler | ||
- | logfile = '/tmp/safe_sendmail.log' | + | logfile = os.path.join(work_dir, 'safe_sendmail.log') |
handler = logging.FileHandler(logfile) | handler = logging.FileHandler(logfile) | ||
handler.setLevel(logging.DEBUG) | handler.setLevel(logging.DEBUG) | ||
Line 24: | Line 51: | ||
handler.setFormatter(formatter) | handler.setFormatter(formatter) | ||
logger.addHandler(handler) | logger.addHandler(handler) | ||
+ | |||
sendmail_bin = '/usr/sbin/sendmail -t -i' | sendmail_bin = '/usr/sbin/sendmail -t -i' | ||
- | db_file = '/tmp/safe_sendmail.db' | + | db_file = os.path.join(work_dir, 'safe_sendmail.db') |
+ | |||
# Number of mails in given amount of minutes to stop the sending | # Number of mails in given amount of minutes to stop the sending | ||
threshold = 200 | threshold = 200 | ||
time_scope = 60 | time_scope = 60 | ||
- | + | ||
+ | |||
def check_db(): | def check_db(): | ||
with sqlite3.connect(db_file) as conn: | with sqlite3.connect(db_file) as conn: | ||
Line 42: | Line 69: | ||
conn.commit() | conn.commit() | ||
return True | return True | ||
- | + | ||
+ | |||
def send_mail(mail): | def send_mail(mail): | ||
logger.debug('Sending mail!\n%s' % mail) | logger.debug('Sending mail!\n%s' % mail) | ||
Line 57: | Line 84: | ||
logger.exception("Unable to send mail") | logger.exception("Unable to send mail") | ||
return False | return False | ||
- | + | ||
+ | |||
if __name__ == "__main__": | if __name__ == "__main__": | ||
+ | parent_process = psutil.Process(os.getppid()) | ||
+ | grandparent_process = psutil.Process(parent_process.ppid()) | ||
check_db() | check_db() | ||
mail = sys.stdin.read() | mail = sys.stdin.read() | ||
Line 81: | Line 110: | ||
sys.exit(2) | sys.exit(2) | ||
else: | else: | ||
+ | # logger.info("Parent: {}".format(parent_process.cmdline())) # uncomment to get parent's cmdline | ||
+ | # logger.info("Grandparent: {}".format(grandparent_process.cmdline())) # uncomment to get grandparent's cmdline | ||
+ | # logger.info(mail) # uncomment to get raw input (mail content and headers) | ||
logger.warning("Unable to send mail to address %s - quota exceeded! (%d mails sent in last %d minutes - limit %s)" % (address, count_since_past, time_scope, threshold)) | logger.warning("Unable to send mail to address %s - quota exceeded! (%d mails sent in last %d minutes - limit %s)" % (address, count_since_past, time_scope, threshold)) | ||
sys.exit(1) | sys.exit(1) | ||
+ | |||
+ | |||
</code> | </code> | ||
linux/apache/sendmail_python.txt · Last modified: 2017/02/21 14:27 by tomsa