Class | MCollective::Security::Ssl |
In: |
plugins/mcollective/security/ssl.rb
|
Parent: | Base |
Impliments a public/private key based message validation system using SSL public and private keys.
The design goal of the plugin is two fold:
To setup you need to create a SSL key pair that is shared by all nodes.
openssl genrsa -out mcserver-private.pem 1024 openssl rsa -in mcserver-private.pem -out mcserver-public.pem -outform PEM -pubout
Distribute the private and public file to /etc/mcollective/ssl on all the nodes. Distribute the public file to /etc/mcollective/ssl everywhere the client code runs.
Now you should create a key pair for every one of your clients, here we create one for user john - you could also if you are less concerned with client id create one pair and share it with all clients:
openssl genrsa -out john-private.pem 1024 openssl rsa -in john-private.pem -out john-public.pem -outform PEM -pubout
Each user has a unique userid, this is based on the name of the public key. In this example case the userid would be ‘john-public’.
Store these somewhere like:
/home/john/.mc/john-private.pem /home/john/.mc/john-public.pem
Every users public key needs to be distributed to all the nodes, save the john one in a file called:
/etc/mcollective/ssl/clients/john-public.pem
If you wish to use registration or auditing that sends connections over MC to a central host you will need also put the server-public.pem in the clients directory.
You should be aware if you do add the node public key to the clients dir you will in effect be weakening your overall security. You should consider doing this only if you also set up an Authorization method that limits the requests the nodes can make.
client.cfg:
securityprovider = ssl plugin.ssl_server_public = /etc/mcollective/ssl/server-public.pem plugin.ssl_client_private = /home/john/.mc/john-private.pem plugin.ssl_client_public = /home/john/.mc/john-public.pem
If you have many clients per machine and dont want to configure the main config file with the public/private keys you can set the following environment variables:
export MCOLLECTIVE_SSL_PRIVATE=/home/john/.mc/john-private.pem export MCOLLECTIVE_SSL_PUBLIC=/home/john/.mc/john-public.pem
server.cfg:
securityprovider = ssl plugin.ssl_server_private = /etc/mcollective/ssl/server-private.pem plugin.ssl_server_public = /etc/mcollective/ssl/server-public.pem plugin.ssl_client_cert_dir = /etc/mcollective/etc/ssl/clients/
Serialization can be configured to use either Marshal or YAML, data types in and out of mcollective will be preserved from client to server and reverse
You can configure YAML serialization:
plugins.ssl_serializer = yaml
else the default is Marshal. Use YAML if you wish to write a client using a language other than Ruby that doesn‘t support Marshal.
Validation is as default and is provided by MCollective::Security::Base
Initial code was contributed by Vladimir Vuksan and modified by R.I.Pienaar
sets the caller id to the md5 of the public key
# File plugins/mcollective/security/ssl.rb, line 154 154: def callerid 155: if @initiated_by == :client 156: "cert=#{File.basename(client_public_key).gsub(/\.pem$/, '')}" 157: else 158: # servers need to set callerid as well, not usually needed but 159: # would be if you're doing registration or auditing or generating 160: # requests for some or other reason 161: "cert=#{File.basename(server_public_key).gsub(/\.pem$/, '')}" 162: end 163: end
Decodes a message by unserializing all the bits etc, it also validates it as valid using the psk etc
# File plugins/mcollective/security/ssl.rb, line 87 87: def decodemsg(msg) 88: body = deserialize(msg.payload) 89: 90: if validrequest?(body) 91: body[:body] = deserialize(body[:body]) 92: return body 93: else 94: nil 95: end 96: end
Encodes a reply
# File plugins/mcollective/security/ssl.rb, line 99 99: def encodereply(sender, target, msg, requestid, filter={}) 100: serialized = serialize(msg) 101: digest = makehash(serialized) 102: 103: @log.debug("Encoded a message for request #{requestid}") 104: 105: serialize({:senderid => @config.identity, 106: :requestid => requestid, 107: :senderagent => sender, 108: :msgtarget => target, 109: :msgtime => Time.now.to_i, 110: :hash => digest, 111: :body => serialized}) 112: end
Encodes a request msg
# File plugins/mcollective/security/ssl.rb, line 115 115: def encoderequest(sender, target, msg, requestid, filter={}) 116: serialized = serialize(msg) 117: digest = makehash(serialized) 118: 119: @log.debug("Encoding a request for '#{target}' with request id #{requestid}") 120: request = {:body => serialized, 121: :hash => digest, 122: :senderid => @config.identity, 123: :requestid => requestid, 124: :msgtarget => target, 125: :filter => filter, 126: :msgtime => Time.now.to_i} 127: 128: # if we're in use by a client add the callerid to the main client hashes 129: request[:callerid] = callerid 130: 131: serialize(request) 132: end
Checks the SSL signature in the request body
# File plugins/mcollective/security/ssl.rb, line 135 135: def validrequest?(req) 136: message = req[:body] 137: signature = req[:hash] 138: 139: @log.debug("Validating request from #{req[:callerid]}") 140: 141: public_key = File.read(public_key_file(req[:callerid])) 142: 143: if verify(public_key, signature, message.to_s) 144: @stats.validated 145: return true 146: else 147: @stats.unvalidated 148: raise(SecurityValidationFailed, "Received an invalid signature in message") 149: end 150: end