Skip to main content

High level, asynchronous framework for writing mail filters

Project description

gitlab-ico licence-mpl20 pre-commit-ico pipeline-status coverage status

Kilter Service

Kilter is a framework for writing mail filters (known as "milters") compatible with Sendmail and Postfix MTAs. Unlike many previous milter implementations in Python it is not simply bindings to the libmilter library (originally from the Sendmail project). The framework aims to provide Pythonic interfaces for implementing filters, including leveraging coroutines instead of libmilter's callback-style interface.

The kilter.service package contains the higher-level, asynchronous framework for writing mail filters.

Sendmail Filters

The Sendmail filter (milter) API facilitates communication between a Mail Transfer Agent (MTA) and arbitrary filters running as external services. These filters can perform a number of operations on received and outgoing mail, such as: virus scanning; checking senders' reputations; signing outgoing mail; and verifying signatures of incoming mail.

While the protocol was originally for filtering mail through a Sendmail MTA, Postfix has also reverse engineered the protocol and supports most filters made for Sendmail.

libmilter

Historically filters used the libmilter library supplied by the Sendmail project to handle all aspects of communication with an MTA. Filters simply registered callbacks for various events then started the library's main loop. This approach makes implementing simple filters in C easy for users, but makes writing "Pythonic" filters difficult, especially when a user wishes to make use of async/await features.

Use of libmilter to implement filters is almost universal as it is a black-box; the on-the-wire protocol used is undocumented and subject to change between versions, which makes writing a third-party parser difficult.

Alternatives in the Python Space

  • purepythonmilter: A modern and robust implementation for asyncio, written purely in statically typed Python without the need for libmilter.

  • python-libmilter: Another pure-Python module using threading. Lacks static type annotations and is no longer actively developed, although still minimally maintained.

Usage

To write filters, create a coroutine function that conforms to Filter. This function takes a Session object as its only argument.

Session instances have several awaitable methods corresponding to SMTP commands (i.e. MAILRCPT) and instances of HeadersAccessor and BodyAccessor which have awaitable methods and are themselves asynchronous iterators. The various methods await particular messages from an MTA and may return appropriate values from them. The asynchronous iterators yield repeating messages like kilter.protocol.Header and kilter.protocol.Body.

Examples

The following is a contrived example showing a filter that rejects messages sent by a particular user:

from kilter.service import Session
from kilter.protocol import Reject, Accept

# This corncob doesn't know when to stop; block him
BLOCK = b"the.black.knight@example.com"

async def reject_black_knight(session: Session) -> Reject|Accept:
	if (await session.envelope_from()) == BLOCK:
		return Reject()

	async with session.headers as headers:
		async for header in headers:
			if header.name == "From" and header.value == BLOCK:
				return Reject()

	return Accept()

The following two examples show two implementations of a filter that strips headers starting with "X-". They demonstrate the two methods of collecting headers, then later modifying them during the post phase.

from kilter.service import Session
from kilter.protocol import Accept

async def strip_x_headers(session: Session) -> Accept:
	remove = []

	# iterate over headers as they arrive and select ones for later removal
	async with session.headers as headers:
		async for header in headers:
			if header.name.startswith("X-"):
				remove.append(header)

	# remove the selected headers during the post phase
	for header in remove:
		await session.headers.delete(header)

	return Accept()
from kilter.service import Session
from kilter.protocol import Accept

async def strip_x_headers(session: Session) -> Accept:
	# collect the headers first
	await session.headers.collect()

	# iterate over collected headers during the post phase, removing the unwanted ones
	async with session.headers as headers:
		async for header in headers:
			if header.name.startswith("X-"):
				await session.headers.delete(header)

	return Accept()

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

kilter_service-0.3.1.tar.gz (22.1 kB view details)

Uploaded Source

Built Distribution

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

kilter_service-0.3.1-py3-none-any.whl (23.2 kB view details)

Uploaded Python 3

File details

Details for the file kilter_service-0.3.1.tar.gz.

File metadata

  • Download URL: kilter_service-0.3.1.tar.gz
  • Upload date:
  • Size: 22.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.11.11

File hashes

Hashes for kilter_service-0.3.1.tar.gz
Algorithm Hash digest
SHA256 85a2b46f64587b0d7d4e7e6e77418cdd7e3de6092019c45b9ad08c091b327fbc
MD5 9be1a01c3397cb3388646dcab98ecdb8
BLAKE2b-256 75f8f43122605eae2138209074658fc3d9985e9f7a66e4476084c4102dbe94ee

See more details on using hashes here.

File details

Details for the file kilter_service-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: kilter_service-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 23.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.11.11

File hashes

Hashes for kilter_service-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 bbd3a7c65b7915fd1cce6f64a2b1fcd45b9c65c9f7358d40a7920463f4d0a278
MD5 b96803170e5638e1761ff7d4ddf77ee6
BLAKE2b-256 a0618418db4728d20e596d1966a156ebb502e74ad3177e7613bea6e1686eac1e

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