Class | MCollective::Security::Sshkey |
In: |
plugins/mcollective/security/sshkey.rb
|
Parent: | Base |
A security plugin for MCollective that uses ssh keys for message signing and verification
For clients (things initiating RPC calls):
Clients identify themselves with the RPC ‘callerid’ as your current user (via Etc::getlogin)
For nodes/agents:
In cases of configurable paths, like the location of your authorized_keys file, the ‘sshkeyauth’ library will try to parse it from the sshd_config file (defaults to /etc/ssh/sshd_config)
Since there is no challenge-reponse in MCollective RPC, we can‘t emulate ssh‘s "try each key until one is accepted" method. Instead, we will sign each method with all keys in your agent and the receiver will try to verify against any of them.
Serialization uses Marshal.
NOTE: This plugin should be considered experimental at this point as it has
a few gotchas and drawbacks. * Nodes cannot easily send messages now, this means registration is not supported * Automated systems that wish to manage a collective with this plugin will somehow need access to ssh agents, this can insecure and problematic in general.
We‘re including this plugin as an early preview of what is being worked on in order to solicit feedback.
Configuration:
For clients:
securityprovider = sshkey
For nodes:
securityprovider = sshkey plugin.sshkey = /etc/ssh/ssh_host_rsa_key
# File plugins/mcollective/security/sshkey.rb, line 114 114: def callerid 115: return Etc.getlogin 116: end
Decodes a message by unserializing all the bits etc TODO(sissel): refactor this into Base?
# File plugins/mcollective/security/sshkey.rb, line 67 67: def decodemsg(msg) 68: body = Marshal.load(msg.payload) 69: 70: if validrequest?(body) 71: body[:body] = Marshal.load(body[:body]) 72: return body 73: else 74: nil 75: end 76: end
Encodes a reply
# File plugins/mcollective/security/sshkey.rb, line 79 79: def encodereply(sender, target, msg, requestid, requestcallerid=nil) 80: serialized = Marshal.dump(msg) 81: digest = makehash(serialized) 82: 83: Log.debug("Encoded a message with hash #{digest} for request #{requestid}") 84: 85: Marshal.dump({:senderid => @config.identity, 86: :requestid => requestid, 87: :senderagent => sender, 88: :msgtarget => target, 89: :msgtime => Time.now.to_i, 90: :hash => digest, 91: :body => serialized}) 92: end
Encodes a request msg
# File plugins/mcollective/security/sshkey.rb, line 95 95: def encoderequest(sender, target, msg, requestid, filter={}) 96: serialized = Marshal.dump(msg) 97: digest = makehash(serialized) 98: 99: Log.debug("Encoding a request for '#{target}' with request id #{requestid}") 100: request = {:body => serialized, 101: :hash => digest, 102: :senderid => @config.identity, 103: :requestid => requestid, 104: :msgtarget => target, 105: :filter => filter, 106: :msgtime => Time.now.to_i} 107: 108: # if we're in use by a client add the callerid to the main client hashes 109: request[:callerid] = callerid if @initiated_by == :client 110: 111: Marshal.dump(request) 112: end
Checks the md5 hash in the request body against our psk, the request sent for validation should not have been deserialized already
# File plugins/mcollective/security/sshkey.rb, line 121 121: def validrequest?(req) 122: Log.info "Caller id: #{req[:callerid]}" 123: Log.info "Sender id: #{req[:senderid]}" 124: message = req[:body] 125: 126: #@log.info req.awesome_inspect 127: identity = (req[:callerid] or req[:senderid]) 128: verifier = SSH::Key::Verifier.new(identity) 129: 130: Log.info "Using name '#{identity}'" 131: 132: # If no callerid, this is a 'response' message and we should 133: # attempt to authenticate using the senderid (hostname, usually) 134: # and that ssh key in known_hosts. 135: if !req[:callerid] 136: # Search known_hosts for the senderid hostname 137: verifier.add_key_from_host(identity) 138: verifier.use_agent = false 139: verifier.use_authorized_keys = false 140: end 141: 142: signatures = Marshal.load(req[:hash]) 143: if verifier.verify?(signatures, req[:body]) 144: @stats.validated 145: return true 146: else 147: @stats.unvalidated 148: raise(SecurityValidationFailed, "Received an invalid signature in message") 149: end 150: end