class MCollective::Security::Base - mcollective version 2.12.5

# class MCollective::Security::Base

This is a base class the other security modules should inherit from it handles statistics and validation of messages that should in most cases apply to all security models.

To create your own security plugin you should provide a plugin that inherits from this and provides the following methods:

decodemsg - Decodes a message that was received from the middleware encodereply - Encodes a reply message to a previous request message encoderequest - Encodes a new request message validrequest? - Validates a request received from the middleware

Optionally if you are identifying users by some other means like certificate name you can provide your own callerid method that can provide the rest of the system with an id, and you would see this id being usable in SimpleRPC authorization methods

The @initiated_by variable will be set to either :client or :node depending on who is using this plugin. This is to help security providers that operate in an asymetric mode like public/private key based systems.

Specifics of each of these are a bit fluid and the interfaces for this is not set in stone yet, specifically the encode methods will be provided with a helper that takes care of encoding the core requirements. The best place to see how security works is by looking at the provided MCollective::Security::PSK plugin.

### Attributes

initiated_by[RW]
stats[R]

### Public Class Methods

inherited(klass) click to toggle source

Register plugins that inherits base

       # File lib/mcollective/security/base.rb
    32 def self.inherited(klass)
    33   PluginManager << {:type => "security_plugin", :class => klass.to_s}
    34 end

new() click to toggle source

Initializes configuration and logging as well as prepare a zero'd hash of stats various security methods and filter validators should increment stats, see `MCollective::Security::Psk` for a sample

       # File lib/mcollective/security/base.rb
    38 def initialize
    39   @config = Config.instance
    40   @log = Log
    41   @stats = PluginManager["global_stats"]
    42 end

### Public Instance Methods

callerid() click to toggle source

Returns a unique id for the caller, by default we just use the unix user id, security plugins can provide their own means of doing ids.

        # File lib/mcollective/security/base.rb
    219 def callerid
    220   "uid=#{Process.uid}"
    221 end

create_reply(reqid, agent, body) click to toggle source

        # File lib/mcollective/security/base.rb
    167 def create_reply(reqid, agent, body)
    168   Log.debug("Encoded a message for request #{reqid}")
    169
    170   {:senderid => @config.identity,
    171    :requestid => reqid,
    172    :senderagent => agent,
    173    :msgtime => Time.now.utc.to_i,
    174    :body => body}
    175 end

create_request(reqid, filter, msg, initiated_by, target_agent, target_collective, ttl=60) click to toggle source

        # File lib/mcollective/security/base.rb
    177 def create_request(reqid, filter, msg, initiated_by, target_agent, target_collective, ttl=60)
    178   Log.debug("Encoding a request for agent '#{target_agent}' in collective #{target_collective} with request id #{reqid}")
    179
    180   {:body => msg,
    181    :senderid => @config.identity,
    182    :requestid => reqid,
    183    :filter => filter,
    184    :collective => target_collective,
    185    :agent => target_agent,
    186    :callerid => callerid,
    187    :ttl => ttl,
    188    :msgtime => Time.now.utc.to_i}
    189 end

decodemsg(msg) click to toggle source

`Security` providers should provide this, see `MCollective::Security::Psk`

        # File lib/mcollective/security/base.rb
    239 def decodemsg(msg)
    240   Log.error("decodemsg is not implemented in #{self.class}")
    241 end

encodereply(sender, msg, requestcallerid=nil) click to toggle source

`Security` providers should provide this, see `MCollective::Security::Psk`

        # File lib/mcollective/security/base.rb
    234 def encodereply(sender, msg, requestcallerid=nil)
    235   Log.error("encodereply is not implemented in #{self.class}")
    236 end

encoderequest(sender, msg, filter={}) click to toggle source

`Security` providers should provide this, see `MCollective::Security::Psk`

        # File lib/mcollective/security/base.rb
    229 def encoderequest(sender, msg, filter={})
    230   Log.error("encoderequest is not implemented in #{self.class}")
    231 end

should_process_msg?(msg, msgid) click to toggle source

Give a MC::Message instance and a message id this will figure out if you the incoming message id matches the one the `Message` object is expecting and raise if its not

Mostly used by security plugins to figure out if they should do the hard work of decrypting etc messages that would only later on be ignored

        # File lib/mcollective/security/base.rb
    196 def should_process_msg?(msg, msgid)
    197   if msg.expected_msgid
    198     unless msg.expected_msgid == msgid
    199       msgtext = "Got a message with id %s but was expecting %s, ignoring message" % [msgid, msg.expected_msgid]
    200       Log.debug msgtext
    201       raise MsgDoesNotMatchRequestID, msgtext
    202     end
    203   end
    204
    205   true
    206 end

valid_callerid?(id) click to toggle source

Validates a callerid. We do not want to allow things like \ and / in callerids since other plugins make assumptions that these are safe strings.

callerids are generally in the form uid=123 or cert=foo etc so we do that here but security plugins could override this for some complex uses

        # File lib/mcollective/security/base.rb
    213 def valid_callerid?(id)
    214   !!id.match(/^[\w]+=[\w\.\-]+$/)
    215 end

validate_filter?(filter) click to toggle source

Takes a Hash with a filter in it and validates it against host information.

At present this supports filter matches against the following criteria:

- puppet_class|cf_class - Presence of a configuration management class in

      the file configured with classesfile

- agent - Presence of a `MCollective` agent with a supplied name

- fact - The value of a fact avout this system

- identity - the configured identity of the system

TODO: Support REGEX and/or multiple filter keys to be AND'd

        # File lib/mcollective/security/base.rb
     55 def validate_filter?(filter)
     56   failed = 0
     57   passed = 0
     58
     59   passed = 1 if Util.empty_filter?(filter)
     60
     61   filter.keys.each do |key|
     62     case key
     63     when /puppet_class|cf_class/
     64       filter[key].each do |f|
     65         Log.debug("Checking for class #{f}")
     66         if Util.has_cf_class?(f) then
     67           Log.debug("Passing based on configuration management class #{f}")
     68           passed += 1
     69         else
     70           Log.debug("Failing based on configuration management class #{f}")
     71           failed += 1
     72         end
     73       end
     74
     75     when "compound"
     76       filter[key].each do |compound|
     77         result = false
     78         truth_values = []
     79
     80         begin
     81           compound.each do |expression|
     82             case expression.keys.first
     83               when "statement"
     84                 truth_values << Matcher.eval_compound_statement(expression).to_s
     85               when "fstatement"
     86                 truth_values << Matcher.eval_compound_fstatement(expression.values.first)
     87               when "and"
     88                 truth_values << "&&"
     89               when "or"
     90                 truth_values << "||"
     91               when "("
     92                 truth_values << "("
     93               when ")"
     94                 truth_values << ")"
     95               when "not"
     96                 truth_values << "!"
     97             end
     98           end
     99
    100           result = eval(truth_values.join(" "))
    101         rescue DDLValidationError
    102           result = false
    103         end
    104
    105         if result
    106           Log.debug("Passing based on class and fact composition")
    107           passed +=1
    108         else
    109           Log.debug("Failing based on class and fact composition")
    110           failed +=1
    111         end
    112       end
    113
    114     when "agent"
    115       filter[key].each do |f|
    116         if Util.has_agent?(f) || f == "mcollective"
    117           Log.debug("Passing based on agent #{f}")
    118           passed += 1
    119         else
    120           Log.debug("Failing based on agent #{f}")
    121           failed += 1
    122         end
    123       end
    124
    125     when "fact"
    126       filter[key].each do |f|
    127         if Util.has_fact?(f[:fact], f[:value], f[:operator])
    128           Log.debug("Passing based on fact #{f[:fact]} #{f[:operator]} #{f[:value]}")
    129           passed += 1
    130         else
    131           Log.debug("Failing based on fact #{f[:fact]} #{f[:operator]} #{f[:value]}")
    132           failed += 1
    133         end
    134       end
    135
    136     when "identity"
    137       unless filter[key].empty?
    138         # Identity filters should not be 'and' but 'or' as each node can only have one identity
    139         matched = filter[key].select{|f| Util.has_identity?(f)}.size
    140
    141         if matched == 1
    142           Log.debug("Passing based on identity")
    143           passed += 1
    144         else
    145           Log.debug("Failed based on identity")
    146           failed += 1
    147         end
    148       end
    149     end
    150   end
    151
    152   if failed == 0 && passed > 0
    153     Log.debug("Message passed the filter checks")
    154
    155     @stats.passed
    156
    157     return true
    158   else
    159     Log.debug("Message failed the filter checks")
    160
    161     @stats.filtered
    162
    163     return false
    164   end
    165 end

validrequest?(req) click to toggle source

`Security` providers should provide this, see `MCollective::Security::Psk`

        # File lib/mcollective/security/base.rb
    224 def validrequest?(req)
    225   Log.error("validrequest? is not implemented in #{self.class}")
    226 end