Class: MCollective::Security::Base
- Inherits:
-
Object
- Object
- MCollective::Security::Base
- Defined in:
- lib/mcollective/security/base.rb
Overview
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.
Direct Known Subclasses
Instance Attribute Summary collapse
-
#initiated_by ⇒ Object
Returns the value of attribute initiated_by.
-
#stats ⇒ Object
readonly
Returns the value of attribute stats.
Class Method Summary collapse
-
.inherited(klass) ⇒ Object
Register plugins that inherits base.
Instance Method Summary collapse
-
#callerid ⇒ Object
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.
- #create_reply(reqid, agent, body) ⇒ Object
- #create_request(reqid, filter, msg, initiated_by, target_agent, target_collective, ttl = 60) ⇒ Object
-
#decodemsg(msg) ⇒ Object
Security providers should provide this, see MCollective::Security::Psk.
-
#encodereply(sender, msg, requestcallerid = nil) ⇒ Object
Security providers should provide this, see MCollective::Security::Psk.
-
#encoderequest(sender, msg, filter = {}) ⇒ Object
Security providers should provide this, see MCollective::Security::Psk.
-
#initialize ⇒ Base
constructor
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.
-
#should_process_msg?(msg, msgid) ⇒ Boolean
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.
-
#valid_callerid?(id) ⇒ Boolean
Validates a callerid.
-
#validate_filter?(filter) ⇒ Boolean
Takes a Hash with a filter in it and validates it against host information.
-
#validrequest?(req) ⇒ Boolean
Security providers should provide this, see MCollective::Security::Psk.
Constructor Details
#initialize ⇒ Base
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
38 39 40 41 42 |
# File 'lib/mcollective/security/base.rb', line 38 def initialize @config = Config.instance @log = Log @stats = PluginManager["global_stats"] end |
Instance Attribute Details
#initiated_by ⇒ Object
Returns the value of attribute initiated_by.
29 30 31 |
# File 'lib/mcollective/security/base.rb', line 29 def initiated_by @initiated_by end |
#stats ⇒ Object (readonly)
Returns the value of attribute stats.
28 29 30 |
# File 'lib/mcollective/security/base.rb', line 28 def stats @stats end |
Class Method Details
.inherited(klass) ⇒ Object
Register plugins that inherits base
32 33 34 |
# File 'lib/mcollective/security/base.rb', line 32 def self.inherited(klass) PluginManager << {:type => "security_plugin", :class => klass.to_s} end |
Instance Method Details
#callerid ⇒ Object
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.
219 220 221 |
# File 'lib/mcollective/security/base.rb', line 219 def callerid "uid=#{Process.uid}" end |
#create_reply(reqid, agent, body) ⇒ Object
167 168 169 170 171 172 173 174 175 |
# File 'lib/mcollective/security/base.rb', line 167 def create_reply(reqid, agent, body) Log.debug("Encoded a message for request #{reqid}") {:senderid => @config.identity, :requestid => reqid, :senderagent => agent, :msgtime => Time.now.utc.to_i, :body => body} end |
#create_request(reqid, filter, msg, initiated_by, target_agent, target_collective, ttl = 60) ⇒ Object
177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/mcollective/security/base.rb', line 177 def create_request(reqid, filter, msg, initiated_by, target_agent, target_collective, ttl=60) Log.debug("Encoding a request for agent '#{target_agent}' in collective #{target_collective} with request id #{reqid}") {:body => msg, :senderid => @config.identity, :requestid => reqid, :filter => filter, :collective => target_collective, :agent => target_agent, :callerid => callerid, :ttl => ttl, :msgtime => Time.now.utc.to_i} end |
#decodemsg(msg) ⇒ Object
Security providers should provide this, see MCollective::Security::Psk
239 240 241 |
# File 'lib/mcollective/security/base.rb', line 239 def decodemsg(msg) Log.error("decodemsg is not implemented in #{self.class}") end |
#encodereply(sender, msg, requestcallerid = nil) ⇒ Object
Security providers should provide this, see MCollective::Security::Psk
234 235 236 |
# File 'lib/mcollective/security/base.rb', line 234 def encodereply(sender, msg, requestcallerid=nil) Log.error("encodereply is not implemented in #{self.class}") end |
#encoderequest(sender, msg, filter = {}) ⇒ Object
Security providers should provide this, see MCollective::Security::Psk
229 230 231 |
# File 'lib/mcollective/security/base.rb', line 229 def encoderequest(sender, msg, filter={}) Log.error("encoderequest is not implemented in #{self.class}") end |
#should_process_msg?(msg, msgid) ⇒ Boolean
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
196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/mcollective/security/base.rb', line 196 def should_process_msg?(msg, msgid) if msg.expected_msgid unless msg.expected_msgid == msgid msgtext = "Got a message with id %s but was expecting %s, ignoring message" % [msgid, msg.expected_msgid] Log.debug msgtext raise MsgDoesNotMatchRequestID, msgtext end end true end |
#valid_callerid?(id) ⇒ Boolean
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
213 214 215 |
# File 'lib/mcollective/security/base.rb', line 213 def valid_callerid?(id) !!id.match(/^[\w]+=[\w\.\-]+$/) end |
#validate_filter?(filter) ⇒ Boolean
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
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/mcollective/security/base.rb', line 55 def validate_filter?(filter) failed = 0 passed = 0 passed = 1 if Util.empty_filter?(filter) filter.keys.each do |key| case key when /puppet_class|cf_class/ filter[key].each do |f| Log.debug("Checking for class #{f}") if Util.has_cf_class?(f) then Log.debug("Passing based on configuration management class #{f}") passed += 1 else Log.debug("Failing based on configuration management class #{f}") failed += 1 end end when "compound" filter[key].each do |compound| result = false truth_values = [] begin compound.each do |expression| case expression.keys.first when "statement" truth_values << Matcher.eval_compound_statement(expression).to_s when "fstatement" truth_values << Matcher.eval_compound_fstatement(expression.values.first) when "and" truth_values << "&&" when "or" truth_values << "||" when "(" truth_values << "(" when ")" truth_values << ")" when "not" truth_values << "!" end end result = eval(truth_values.join(" ")) rescue DDLValidationError result = false end if result Log.debug("Passing based on class and fact composition") passed +=1 else Log.debug("Failing based on class and fact composition") failed +=1 end end when "agent" filter[key].each do |f| if Util.has_agent?(f) || f == "mcollective" Log.debug("Passing based on agent #{f}") passed += 1 else Log.debug("Failing based on agent #{f}") failed += 1 end end when "fact" filter[key].each do |f| if Util.has_fact?(f[:fact], f[:value], f[:operator]) Log.debug("Passing based on fact #{f[:fact]} #{f[:operator]} #{f[:value]}") passed += 1 else Log.debug("Failing based on fact #{f[:fact]} #{f[:operator]} #{f[:value]}") failed += 1 end end when "identity" unless filter[key].empty? # Identity filters should not be 'and' but 'or' as each node can only have one identity matched = filter[key].select{|f| Util.has_identity?(f)}.size if matched == 1 Log.debug("Passing based on identity") passed += 1 else Log.debug("Failed based on identity") failed += 1 end end end end if failed == 0 && passed > 0 Log.debug("Message passed the filter checks") @stats.passed return true else Log.debug("Message failed the filter checks") @stats.filtered return false end end |