Pmilter: Programmable Mail Filter Server Scripting with mruby
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.

Pmilter: Programmable Mail Filter Server Build Status

Pmilter is a simple and programmable mail filter server software. You can control smtp server like postfix or sendmail via some mruby scripts. Pmilter is one-binary. So you can deploy and setup environment very easily. Enjoy!!

pmilter build and run

  • build
make mruby
  • run
make run


./pmilter -c pmilter.conf

build dependency (for example on ubuntu 16.04)

  • build essential
  • automake
  • m4
  • autoconf
  • libtool
  • cmake
  • pkg-config
  • libcunit1-dev
  • ragel
  • ruby (for mruby)
  • bison (for mruby)
install example for ubuntu
sudo apt-get -y install build-essential rake bison git gperf automake m4 \
        autoconf libtool cmake pkg-config libcunit1-dev ragel
install example for CentOS7
yum install -y ruby gcc cc-c++ make cmake autoconf automake libtool bison
rpm -ivh

run dependency

very simple! :0

$ ldd pmilter =>  (0x00007ffc475ed000) => /lib/x86_64-linux-gnu/ (0x00007f5f81b95000) => /lib/x86_64-linux-gnu/ (0x00007f5f8188c000) => /lib/x86_64-linux-gnu/ (0x00007f5f814c2000)
        /lib64/ (0x000055ef94336000)

test dependency

  • ruby-milter-server
  • ruby-milter-client

Thanks to milter-manager!!!

  • install example for ubuntu
sudo apt-get -y install software-properties-common
sudo add-apt-repository -y ppa:milter-manager/ppa
sudo apt-get update
sudo apt-get -y install ruby-milter-server ruby-milter-client

milter test after make run

make test

pmilter.conf and mruby handlers

pmilter.conf using toml

  # hoge.sock or ipaddree:port
  listen = "/var/spool/postfix/pmilter/pmilter.sock"
  timeout = 7210
  log_level = "notice"
  mruby_handler = true
  listen_backlog = 128
  debug = 0
  min_worker = 32


    # postconfig handler
    mruby_postconfig_handler = "handler/postconfig.rb"

    # master exit config handler
    mruby_master_exit_handler = "handler/master_exit.rb"

    # connection info filter handler
    mruby_connect_handler = "handler/connect.rb"
    # SMTP HELO command filter handler
    mruby_helo_handler = "handler/helo.rb"
    # envelope sender filter handler
    mruby_envfrom_handler = "handler/mail_from.rb"
    # envelope recipient filter handler
    mruby_envrcpt_handler = "handler/rcpt_to.rb"
    ## header filter handler
    mruby_header_handler = "handler/header.rb"
    # end of header handler
    #mruby_eoh_handler = "/path/to/handler.rb"
    # body block filter handler
    mruby_body_handler = "handler/body.rb"
    # end of message handler
    mruby_eom_handler = "handler/eom.rb"
    # message aborted handler
    #mruby_abort_handler = "/path/to/handler.rb"
    # connection cleanup handler
    #mruby_close_handler = "/path/to/handler.rb"
    # unknown SMTP commands handler
    #mruby_unknown_handler = "/path/to/handler.rb"
    ## DATA command handler
    #mruby_data_handler = "/path/to/handler.rb"

mruby handler examples

  • handler/connect.rb
puts "hello pmilter handler called from #{}"
puts "client ipaddr #{}"
puts "client hostname #{}"
puts "client daemon #{}"
puts "handler phase name: #{}"
  • handler/helo.rb
puts "helo hostname: #{}"
puts "tls client issuer: #{}"
puts "tls client subject: #{}"
puts "tls session key size: #{}"
puts "tls encrypt method: #{}"
puts "tls version: #{}"
  • handler/mail_from.rb
puts "env from from args: #{}"
puts "env from from symval: #{}"
puts "SASL login name: #{}"
puts "SASL login sender: #{}"
puts "SASL login type: #{}"

if == "<>"
  Pmilter.status = Pmilter::SMFIS_REJECT
  • handler/rcpt_to.rb
puts "env to from arg: #{}"
puts "env to from symval: #{}"
  • handler/eom.rb
puts "myhostname: #{}"
puts "message_id: #{}"
puts "reveive_time: #{}"
puts "add_header(X-Pmilter:True): #{['X-Pmilter'] = 'Enable'}"
  • handler/header.rb
puts "header: #{}"
  • handler/body.rb
puts "body chunk; #{}"

# Skip over rest of same callbacks
# only once call body handler when return Pmilter::SMFIS_SKIP
Pmilter.status = Pmilter::SMFIS_SKIP

pmilter example handler run

  • make test after make run
ubuntu@ubuntu-xenial:~/pmilter$ make run
./pmilter -c pmilter.conf
[Tue, 25 Jul 2017 13:02:34 GMT][notice]: pmilter/0.0.1 starting (using mruby 1.3.0)

hello pmilter handler called from pmilter
client ipaddr
client hostname
client daemon milter-test-server
handler phase name: mruby_connect_handler
helo hostname: delian
tls client issuer: cert_issuer
tls client subject: cert_subject
tls session key size: 0
tls encrypt method: 0
tls version: 0
env from from args: <>
env from from symval: mail_addr
SASL login name:
SASL login sender:
SASL login type:
env to from arg: <>
env to from symval: <>
header: {"From"=>"<>"}
header: {"To"=>"<>"}
header: {"Subject"=>"Hello"}
body chunk; Hello world!!
message_id: message-id
reveive_time: Wed Nov 02 21:02:15 2016
add_header(X-Pmilter:True): Enable

MTA like postfix configuration example

  • postfix
# postfix chroot on /var/spool/postfix
# create pmilter.socket as /var/spool/postfix/pmilter/pmilter.sock
smtpd_milters = unix:/pmilter/pmilter.sock


  • don't use pmilter
ubuntu@ubuntu-xenial:~$ postal -t 1 -r 10000 -m 1 -M 1 mail.list
time,messages,data(K),errors,connections,SSL connections
  • use pmilter and callback mruby handler 10 times
ubuntu@ubuntu-xenial:~$ postal -t 1 -r 10000 -m 1 -M 1 mail.list
time,messages,data(K),errors,connections,SSL connections


under the MIT License: see also LICENSE file