Skip to content

chillout2k/sos-milter

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

92 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SPF-on-submission-Milter - sos-milter

A lightweight, fast and thread-safe python3 milter on top of sdgathman/pymilter.

The main goal of the sos-milter is to check the SPF-policy of a senders domain in term of mail submission scenario. Especially when forwarding of messages comming from foreign domains with restrictive SPF-policies (-all) takes place. The milter is also designed to check the correctness of SPF-policies for own domains (such as customers domains). In this case the milter expects that all own (not foreign) domains are managed in a LDAP server so that the milter can recognize them as such. For those domains the milter enforces checks regarding the appearence of particular SPF statements (include/s, ip4, ip6, ...) in the domain name system (DNS). Herefor the milter uses a regular expression which is part of the configuration. In this way the email service provider (ESP) running the sos-milter becomes able to check if his/her customers did set SPF-TXT-records correctly (as documented/expected) on each mail submission attempt and not just during the setup phase.

Further the sos-milter can be run in test or reject mode. In test mode the milter only does log policy violations which may be turned into metrics and used for baselining. Thus the test mode is recommended for first steps in an productive environment before enabling reject mode (if ever). In reject mode the milter fulfills policy enforcement and rejects every email submission requests that does not meet the configured expectations (expected SPF statements as regular expression).

Deployment paradigm

Following the principles of 12-Factor-App for cloud native applications, the intention of this project is to deploy the milter as an OCI compliant container.

There´s nothing wrong to deploy the milter as a

  • docker-compose deployment on a stand-alone docker host or a docker-swarm cluster
  • stateless Kubernetes-workload (type: Deployment)
  • local systemd unit, which is NOT a OCI-compliant was but works too ;-)

Please note, that according to the 3rd principle of 12-Factor-App a cloud-native app is configured through environment variables, so this app does.

Deployment with Docker - docker-compose.yml

The following docker-compose file demonstrates how such a setup could be orchestrated on a single docker host or on a docker swarm cluster. In this context we use postfix as our milter-aware MTA which connects to the milter via an UNIX-socket.

version: '3'

volumes:
  sosm_socket:

services:
  sos-milter:
    image: "sos-milter:<your_tag>"
    restart: unless-stopped
    environment:
      # default: info, possible: info, warning, error, debug
      LOG_LEVEL: debug
      # default: test, possible: test,reject
      MILTER_MODE: reject
      # Default: sos-milter
      MILTER_NAME: sos-milter
      # Default socket /socket/${MILTER_NAME}
      # MILTER_SOCKET: inet6:8020
      # MILTER_REJECT_MESSAGE: Message rejected due to security policy violation!
      # MILTER_TMPFAIL_MESSAGE: Message temporary rejected. Please try again later ;)

      # Expected Content of the spf-record, like a specific include
      # docker-compose pitfall: Dollar-sign ($) must be escaped as $$
      SPF_REGEX: '^.*include:secure-mailgate\.com.*$$'

      # If next-hop relay is one of the following, message will be ignored
      IGNORED_NEXT_HOPS: 'some-mailrelay.xyz:123, another.relay, and.so.on:125'

      # Search for sender domain in LDAP. Can be used to mark (add header)
      # and identify (log) internal sender domains with broken SPF-records
      # for further processing (log report or header-based routing).
      # After a message was marked with an additional header, it can be
      # routed other than usual (e.g. through a bounce-/fwd-relay)
      LDAP_ENABLED: 'some_value'
      LDAP_SERVER_URI: 'ldaps://some.ldap.server'
      LDAP_BINDDN: 'some-ldap-user-dn'
      LDAP_BINDPW: 'some-secret-pw'
      LDAP_SEARCH_BASE: 'ou=domains,dc=SLD,dc=TLD'
      # %d will be replaced by recognized 5321.env_from_domain
      LDAP_QUERY_FILTER: '(dc=%d)'
    hostname: sos-milter
    volumes:
    - "sosm_socket:/socket/:rw"

  postfix:
    depends_on:
    - sos-milter
    image: "your favorite postfix image"
    restart: unless-stopped
    hostname: postfix
    ports:
    - "465:465"
    volumes:
    - "./config/postfix:/etc/postfix:ro"
    - "sosm_socket:/socket/sos-milter/:rw"