Skip to main content

Local SMTP helper

Project description

Laim (pronounced like lime, anagram of mail)

Build Status

Flexible library to handle local SMTP on servers.

Modern *nix servers use mail for a lot of reporting. Cron uses mail to notify about failing jobs, unattended-upgrades sends reports by mail, and sudo can send mail when invoked. Having a standardized way to report somewhere is great, but having to run a full-blown email infrastructure to receive these messages is hard.

Laim makes it easy to run a secure, local-only SMTP agent, where you define how messages are handled in python. Laim takes care of the hard parts of SMTP, dropping privileges and reading configuration, you just have to write the code to handle the message however you like. Post to slack? Write to log? Send text message? All three? Up to you!

Example handler that posts to slack:

import socket

import requests
from laim import Laim

class SlackHandler(Laim):

    def __init__(self):
        super().__init__()
        self.session = requests.Session()
        self.session.headers.update({
            'Authorization': 'Bearer %s' % self.config['SLACK_TOKEN']
        })
        self.channel_id = self.config['SLACK_CHANNEL_ID']
        self.hostname = socket.gethostname()

    def handle_message(self, sender, recipients, message):
        self.session.post('https://slack.com/api/chat.postMessage', json={
            'channel': self.channel_id,
            'text': '%s received mail for %s:\n%s' % (
                self.hostname, ', '.join(recipients), message.get_payload()),
        })

if __name__ == '__main__':
    handler = SlackHandler()
    handler.run()

A couple of things are worth noting here that laim helped out with.

Configuration

Laim reads the root-only readable yaml file /etc/laim/conf.yml on startup, and saves the contents to self.config. This makes it easy to add arbitrary configuration you need in your handler, that will be read in a secure way.

Note that until the line calling super().__init__(), the script was running as root. After that it dropped privileges to the user 'laim'.

Async

Laim is not written for high throughput, but does do some basic queuing to make sure you can handle a message synchronously without blocking the reception of other messages. This is done by running the SMTP listener on one thread, and the handler on another. Messages to be delivered are passed to the handler on a bounded in-memory queue, which prevents arbitrarily high memory usage if the handler fails to process messages fast enough by dropping new messages (this event is logged by laim).

Security considerations

Laim will bind to localhost port 25 to handle incoming SMTP, and will by itself only filter out non-plaintext messages. This means that if an attacker gets access to the machine, they can send arbitrary messages to your handler.

The service will start as root, but drops privileges once it has bound to the port and opened a handle to the config file.

Local testing

Apart from the test suite you can use the included ./devhandler.py to run laim on port 2525 for testing. To send mail you can modify the file in smtp-session.txt, which can be piped to netcat to send a sample mail:

$ nc localhost 2525 < smtp-session.txt

To send lots of mail to test the queuing system or simple load testing:

$ count=0; while :; do echo "Sending $count"; sed s/COUNT/$count/ smtp-session.txt | nc localhost 2525 || break; count=$((count+1)); done; echo "Sent $count mails"

Laim stops gracefully on SIGINT and SIGTERM, so you can stop the handler from a third shell and observe that it shuts down cleanly:

$ pkill -f devhandler.py

There are some more example handlers in the examples/ directory.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

laim-0.1.0.tar.gz (12.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

laim-0.1.0-py3-none-any.whl (14.0 kB view details)

Uploaded Python 3

File details

Details for the file laim-0.1.0.tar.gz.

File metadata

  • Download URL: laim-0.1.0.tar.gz
  • Upload date:
  • Size: 12.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/41.0.1 requests-toolbelt/0.9.1 tqdm/4.32.2 CPython/3.6.1

File hashes

Hashes for laim-0.1.0.tar.gz
Algorithm Hash digest
SHA256 40c9726e10b3092f630a0f72c9fd692fdac099f02dab74d8dd88d4ba0bd7818e
MD5 5fb60483446df64bf868207c085d7477
BLAKE2b-256 a35916fca53e7ab852e31e63aaba70ea6b99669d7f0b6a49d2efd489b27ad191

See more details on using hashes here.

File details

Details for the file laim-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: laim-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 14.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/41.0.1 requests-toolbelt/0.9.1 tqdm/4.32.2 CPython/3.6.1

File hashes

Hashes for laim-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 bd5c436e95b51a3e3599770fbca8fd2367a3f3fb60ba07e22ca9215d4d7e3a64
MD5 da987ef7a345a7c72e7f65649453898b
BLAKE2b-256 db4b304670b92af07207732132c564cab6390128be1086b1619a0b7cdbb520ea

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page