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.
Register plugins that inherits base
# File lib/mcollective/security/base.rb, line 32 32: def self.inherited(klass) 33: PluginManager << {:type => "security_plugin", :class => klass.to_s} 34: end
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, line 38 38: def initialize 39: @config = Config.instance 40: @log = Log 41: @stats = PluginManager["global_stats"] 42: end
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, line 219 219: def callerid 220: "uid=#{Process.uid}" 221: end
(Not documented)
# File lib/mcollective/security/base.rb, line 167 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
(Not documented)
# File lib/mcollective/security/base.rb, line 177 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
Security providers should provide this, see MCollective::Security::Psk
# File lib/mcollective/security/base.rb, line 239 239: def decodemsg(msg) 240: Log.error("decodemsg is not implemented in #{self.class}") 241: end
Security providers should provide this, see MCollective::Security::Psk
# File lib/mcollective/security/base.rb, line 234 234: def encodereply(sender, msg, requestcallerid=nil) 235: Log.error("encodereply is not implemented in #{self.class}") 236: end
Security providers should provide this, see MCollective::Security::Psk
# File lib/mcollective/security/base.rb, line 229 229: def encoderequest(sender, msg, filter={}) 230: Log.error("encoderequest is not implemented in #{self.class}") 231: end
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, line 196 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
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, line 213 213: def valid_callerid?(id) 214: !!id.match(/^[\w]+=[\w\.\-]+$/) 215: end
Takes a Hash with a filter in it and validates it against host information.
At present this supports filter matches against the following criteria:
the file configured with classesfile
TODO: Support REGEX and/or multiple filter keys to be AND’d
# File lib/mcollective/security/base.rb, line 55 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
Security providers should provide this, see MCollective::Security::Psk
# File lib/mcollective/security/base.rb, line 224 224: def validrequest?(req) 225: Log.error("validrequest? is not implemented in #{self.class}") 226: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.