class MCollective::Security::Sshkey

A security plugin for MCollective that uses ssh keys for message signing and verification

For clients (things initiating RPC calls):

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

Public Instance Methods

callerid() click to toggle source
# File plugins/mcollective/security/sshkey.rb, line 100
def callerid
    return Etc.getlogin
end
decodemsg(msg) click to toggle source

Decodes a message by unserializing all the bits etc TODO(sissel): refactor this into Base?

# File plugins/mcollective/security/sshkey.rb, line 67
def decodemsg(msg)
    body = Marshal.load(msg.payload)

    if validrequest?(body)
        body[:body] = Marshal.load(body[:body])
        return body
    else
        nil
    end
end
encodereply(sender, target, msg, requestid, requestcallerid=nil) click to toggle source

Encodes a reply

# File plugins/mcollective/security/sshkey.rb, line 79
def encodereply(sender, target, msg, requestid, requestcallerid=nil)
    serialized  = Marshal.dump(msg)
    digest = makehash(serialized)

    req = create_reply(requestid, sender, target, serialized)
    req[:hash] = digest

    Marshal.dump(req)
end
encoderequest(sender, target, msg, requestid, filter={}, target_agent=nil, target_collective=nil) click to toggle source

Encodes a request msg

# File plugins/mcollective/security/sshkey.rb, line 90
def encoderequest(sender, target, msg, requestid, filter={}, target_agent=nil, target_collective=nil)
    serialized = Marshal.dump(msg)
    digest = makehash(serialized)

    req = create_request(requestid, target, filter, serialized, @initiated_by, target_agent, target_collective)
    req[:hash] = digest

    Marshal.dump(req)
end
validrequest?(req) click to toggle source

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 107
def validrequest?(req)
    Log.info "Caller id: #{req[:callerid]}"
    Log.info "Sender id: #{req[:senderid]}"
    message = req[:body]

    #@log.info req.awesome_inspect
    identity = (req[:callerid] or req[:senderid])
    verifier = SSH::Key::Verifier.new(identity)

    Log.info "Using name '#{identity}'"

    # If no callerid, this is a 'response' message and we should
    # attempt to authenticate using the senderid (hostname, usually)
    # and that ssh key in known_hosts.
    if !req[:callerid]
      # Search known_hosts for the senderid hostname
      verifier.add_key_from_host(identity)
      verifier.use_agent = false
      verifier.use_authorized_keys = false
    end

    signatures = Marshal.load(req[:hash])
    if verifier.verify?(signatures, req[:body])
        @stats.validated
        return true
    else
        @stats.unvalidated
        raise(SecurityValidationFailed, "Received an invalid signature in message")
    end
end