Skip to content

Commit

Permalink
initial PostPolicy commit
Browse files Browse the repository at this point in the history
  • Loading branch information
mlomnicki committed Jun 15, 2009
0 parents commit 43e0d45
Show file tree
Hide file tree
Showing 24 changed files with 616 additions and 0 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
= Change Log

Below is a complete listing of changes for each revision of PostPolicy.

=== 0.0.1

* Initial release
20 changes: 20 additions & 0 deletions MIT-LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Copyright (c) 2009 Michał Łomnicki

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
43 changes: 43 additions & 0 deletions README
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
= PostPolicy: Postfix Policy Server in Ruby

PostPolicy uses ACL system, which allow administrators to create rules based on mail source.
Unlike simple Postfix policy restrictions in PostPolicy one can create very complex rules against incoming mail.
PostPolicy is built on top of eventmachine, event-driven network library used for critical networked applications.

<b>PostPolicy is under heavy development so don't expect too much at the moment ;)</b>

== DEPENDENCIES

* eventmachine
* rpsec (only for tests)

== USAGE

Configure postpolicy in /etc/postpolicy.yml

Read http://www.postfix.org/SMTPD_POLICY_README.html

append to your master.cf

policy unix - n n - 0 spawn user=nobody argv=/path/to/postpolicy

in your main.cf

smtpd_recipient_restrictions =
...
reject_unauth_destination
check_policy_service unix:private/policy

== ABOUT

Author:: Michał Łomnicki <[email protected]>
License:: Copyright 2009 by Michał Łomnicki
Released under a MIT license.

== Warranty

This software is provided "as is" and without any express or
implied warranties, including, without limitation, the implied
warranties of merchantibility and fitness for a particular
purpose.

36 changes: 36 additions & 0 deletions bin/postpolicy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
require 'optparse'

$LOAD_PATH.unshift File.join( File.dirname( __FILE__ ), '../lib' )
require 'postpolicy'

DEFAULT_CONFIG = File.exists?( '/etc/postpolicy.yml' ) ? '/etc/postpolicy.yml' :
File.join( File.dirname( __FILE__ ), '../postpolicy.yml')

DEFAULT_OPTIONS = {
:verbose => false,
:config => DEFAULT_CONFIG
}

begin
options = DEFAULT_OPTIONS
OptionParser.new do |opts|
opts.banner = "Usage: #{$0} [options]"

opts.on("-v", "--verbose", "Verbose logging") do |v|
options[:verbose] = v
end

opts.on("-c", "--config", "Path to configuration file") do |c|
options[:config] = c
end
end.parse!

VERBOSE = options[:verbose]

Logger.info( "Starting PostPolicy #{PostPolicy::VERSION::STRING}" ) if VERBOSE
PostPolicy::Config.load_from_file( options[:config] )
PostPolicy::Protocol.new.start!
rescue
Logger.error( $!.message )
raise $!
end
11 changes: 11 additions & 0 deletions lib/postpolicy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
require 'rubygems'
require 'eventmachine'

require 'postpolicy/config'
require 'postpolicy/protocol'
require 'postpolicy/access_manager'
require 'postpolicy/logger'
require 'postpolicy/extensions'
require 'postpolicy/version'

VERBOSE = false unless defined?( VERBOSE )
36 changes: 36 additions & 0 deletions lib/postpolicy/access_manager.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
require 'rubygems'
require 'eventmachine'

module PostPolicy

class AccessManager

include EventMachine::Deferrable

DEFAULT_ACTION = "DUNNO"

@@access_list = []

def self.access_list
@@access_list
end

def self.<<( action )
@@access_list << action
end

def check( args )
action = DEFAULT_ACTION
@@access_list.each do |access|
if access.match?( args )
action = access.action
break
end
end
yield action if block_given?
return action
end

end
end

48 changes: 48 additions & 0 deletions lib/postpolicy/config.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
require 'yaml'
require 'singleton'

Dir.glob( File.join( File.dirname( __FILE__ ), "plugins/base/*" ) ).each { |base| require base }
Dir.glob( File.join( File.dirname( __FILE__ ), "plugins/acl/*" ) ).each { |acl| require acl }
Dir.glob( File.join( File.dirname( __FILE__ ), "plugins/datasource/*" ) ).each { |datasource| require datasource }

module PostPolicy

class Config

def self.load_from_file( filename )
load( YAML.load_file( filename ) )
end

def self.load( config_hash )
acls = Hash.new { |hsh, key| hsh[key] = Array.new }
config_acls = config_hash.delete( "acl" )
config_acls.each_pair do |human_name, klass_value_maps|
klass_value_maps.each_pair do |klass, value|
acls[human_name] << ACL.const_get(klass.classify).new( resolve_datasource( value ) )
end
end
actions = config_hash.delete( "action" )
accesses = config_hash.delete( "access" )
accesses.each_pair do |action, acl|
AccessManager << Access.new( acls[acl], actions[action] )
end
end

protected

def self.resolve_datasource( klass_and_value )
@@datasource_cache ||= {}
klass, value = klass_and_value.split( "://" )
if value == nil # consider as constant value
value = klass
klass = "value"
end
@@datasource_cache[klass] ||= DataSource.const_get(klass.classify)

return @@datasource_cache[klass].new( value )
end

end

end

1 change: 1 addition & 0 deletions lib/postpolicy/extensions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require File.join( File.dirname( __FILE__ ), 'extensions/string' )
11 changes: 11 additions & 0 deletions lib/postpolicy/extensions/string.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class String

def camelize
self.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
end

def classify
self.sub(/.*\./, '').camelize
end

end
28 changes: 28 additions & 0 deletions lib/postpolicy/logger.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
require 'syslog'

module Logger

@@app_name = "rpolicy"
@@log_opts = (Syslog::LOG_PID | Syslog::LOG_CONS)
@@facility = Syslog::LOG_MAIL
Syslog.open( @@app_name, @@log_opts, @@facility )

def self.error( msg )
Syslog.err( msg )
end

def self.info( msg )
Syslog.info( msg )
end

def self.debug( msg )
Syslog.debug( msg )
end

def self.warn( msg )
Syslog.warning( msg )
end


end

13 changes: 13 additions & 0 deletions lib/postpolicy/plugins/acl/recipient.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module PostPolicy
module ACL

class Recipient < Base

def match?( args )
datasource.exists? args[:recipient]
end

end

end
end
13 changes: 13 additions & 0 deletions lib/postpolicy/plugins/acl/sender.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module PostPolicy
module ACL

class Sender < Base

def match?( args )
datasource.exists? args[:sender]
end

end

end
end
20 changes: 20 additions & 0 deletions lib/postpolicy/plugins/base/access.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module PostPolicy

class Access

def initialize( acls, action )
@acls = acls
@action = action
end

def match?( args )
@acls.all? { |acl| acl.match?( args ) }
end

def action
@action
end

end

end
19 changes: 19 additions & 0 deletions lib/postpolicy/plugins/base/acl.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module PostPolicy
module ACL

class Base

attr_reader :datasource

def initialize( datasource )
@datasource = datasource
end

def match?( args )
:dunno
end

end

end
end
17 changes: 17 additions & 0 deletions lib/postpolicy/plugins/base/datasource.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module PostPolicy
module DataSource

class Base

def initialize( values = [] )
@values = values
end

def exists?( object )
@values.include?( object )
end

end

end
end
12 changes: 12 additions & 0 deletions lib/postpolicy/plugins/datasource/value.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module PostPolicy
module DataSource

class Value < Base

def initialize( values )
@values = [values].flatten #make it always an array
end

end
end
end
Loading

0 comments on commit 43e0d45

Please sign in to comment.