mcollective-client 2.7.0 → 2.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/mcollective/agent/discovery.rb +37 -0
- data/lib/mcollective/agent/rpcutil.ddl +220 -0
- data/lib/mcollective/agent/rpcutil.rb +108 -0
- data/lib/mcollective/aggregate/average.ddl +33 -0
- data/lib/mcollective/aggregate/average.rb +29 -0
- data/lib/mcollective/aggregate/result.rb +3 -3
- data/lib/mcollective/aggregate/sum.ddl +33 -0
- data/lib/mcollective/aggregate/sum.rb +18 -0
- data/lib/mcollective/aggregate/summary.ddl +33 -0
- data/lib/mcollective/aggregate/summary.rb +53 -0
- data/lib/mcollective/aggregate.rb +2 -2
- data/lib/mcollective/application/completion.rb +104 -0
- data/lib/mcollective/application/facts.rb +62 -0
- data/lib/mcollective/application/find.rb +21 -0
- data/lib/mcollective/application/help.rb +28 -0
- data/lib/mcollective/application/inventory.rb +344 -0
- data/lib/mcollective/application/ping.rb +77 -0
- data/lib/mcollective/application/plugin.rb +369 -0
- data/lib/mcollective/application/rpc.rb +121 -0
- data/lib/mcollective/application.rb +2 -0
- data/lib/mcollective/audit/logfile.rb +26 -0
- data/lib/mcollective/config.rb +16 -9
- data/lib/mcollective/connector/activemq.ddl +9 -0
- data/lib/mcollective/connector/activemq.rb +572 -0
- data/lib/mcollective/connector/rabbitmq.ddl +9 -0
- data/lib/mcollective/connector/rabbitmq.rb +484 -0
- data/lib/mcollective/connector.rb +1 -1
- data/lib/mcollective/data/agent_data.ddl +22 -0
- data/lib/mcollective/data/agent_data.rb +17 -0
- data/lib/mcollective/data/collective_data.ddl +20 -0
- data/lib/mcollective/data/collective_data.rb +9 -0
- data/lib/mcollective/data/fact_data.ddl +28 -0
- data/lib/mcollective/data/fact_data.rb +55 -0
- data/lib/mcollective/data/fstat_data.ddl +89 -0
- data/lib/mcollective/data/fstat_data.rb +56 -0
- data/lib/mcollective/data.rb +2 -2
- data/lib/mcollective/ddl.rb +4 -4
- data/lib/mcollective/discovery/flatfile.ddl +11 -0
- data/lib/mcollective/discovery/flatfile.rb +48 -0
- data/lib/mcollective/discovery/mc.ddl +11 -0
- data/lib/mcollective/discovery/mc.rb +30 -0
- data/lib/mcollective/discovery/stdin.ddl +11 -0
- data/lib/mcollective/discovery/stdin.rb +66 -0
- data/lib/mcollective/facts/yaml_facts.rb +61 -0
- data/lib/mcollective/facts.rb +1 -1
- data/lib/mcollective/generators.rb +3 -3
- data/lib/mcollective/logger.rb +1 -1
- data/lib/mcollective/matcher/scanner.rb +1 -1
- data/lib/mcollective/matcher.rb +2 -2
- data/lib/mcollective/pluginpackager/debpackage_packager.rb +237 -0
- data/lib/mcollective/pluginpackager/modulepackage_packager.rb +127 -0
- data/lib/mcollective/pluginpackager/ospackage_packager.rb +59 -0
- data/lib/mcollective/pluginpackager/rpmpackage_packager.rb +180 -0
- data/lib/mcollective/pluginpackager/templates/debian/Makefile.erb +7 -0
- data/lib/mcollective/pluginpackager/templates/debian/changelog.erb +5 -0
- data/lib/mcollective/pluginpackager/templates/debian/compat.erb +1 -0
- data/lib/mcollective/pluginpackager/templates/debian/control.erb +15 -0
- data/lib/mcollective/pluginpackager/templates/debian/copyright.erb +8 -0
- data/lib/mcollective/pluginpackager/templates/debian/rules.erb +6 -0
- data/lib/mcollective/pluginpackager/templates/module/Modulefile.erb +5 -0
- data/lib/mcollective/pluginpackager/templates/module/README.md.erb +37 -0
- data/lib/mcollective/pluginpackager/templates/module/_manifest.pp.erb +9 -0
- data/lib/mcollective/pluginpackager/templates/redhat/rpm_spec.erb +63 -0
- data/lib/mcollective/pluginpackager.rb +2 -2
- data/lib/mcollective/registration/agentlist.rb +10 -0
- data/lib/mcollective/registration.rb +1 -1
- data/lib/mcollective/rpc/stats.rb +0 -1
- data/lib/mcollective/rpc.rb +11 -11
- data/lib/mcollective/security/aes_security.rb +394 -0
- data/lib/mcollective/security/psk.rb +117 -0
- data/lib/mcollective/security/ssl.rb +328 -0
- data/lib/mcollective/security.rb +1 -1
- data/lib/mcollective/util.rb +18 -18
- data/lib/mcollective/validator/array_validator.ddl +7 -0
- data/lib/mcollective/validator/array_validator.rb +9 -0
- data/lib/mcollective/validator/ipv4address_validator.ddl +7 -0
- data/lib/mcollective/validator/ipv4address_validator.rb +16 -0
- data/lib/mcollective/validator/ipv6address_validator.ddl +7 -0
- data/lib/mcollective/validator/ipv6address_validator.rb +16 -0
- data/lib/mcollective/validator/length_validator.ddl +7 -0
- data/lib/mcollective/validator/length_validator.rb +11 -0
- data/lib/mcollective/validator/regex_validator.ddl +7 -0
- data/lib/mcollective/validator/regex_validator.rb +9 -0
- data/lib/mcollective/validator/shellsafe_validator.ddl +7 -0
- data/lib/mcollective/validator/shellsafe_validator.rb +13 -0
- data/lib/mcollective/validator/typecheck_validator.ddl +7 -0
- data/lib/mcollective/validator/typecheck_validator.rb +28 -0
- data/lib/mcollective.rb +31 -31
- data/spec/spec_helper.rb +0 -5
- data/spec/unit/{plugins/mcollective → mcollective}/agent/rpcutil_spec.rb +1 -1
- data/spec/unit/{agents_spec.rb → mcollective/agents_spec.rb} +0 -0
- data/spec/unit/{plugins/mcollective → mcollective}/aggregate/average_spec.rb +1 -1
- data/spec/unit/{aggregate → mcollective/aggregate}/base_spec.rb +0 -0
- data/spec/unit/{aggregate → mcollective/aggregate}/result/base_spec.rb +0 -0
- data/spec/unit/{aggregate → mcollective/aggregate}/result/collection_result_spec.rb +0 -0
- data/spec/unit/{aggregate → mcollective/aggregate}/result/numeric_result_spec.rb +0 -0
- data/spec/unit/{plugins/mcollective → mcollective}/aggregate/sum_spec.rb +1 -1
- data/spec/unit/{plugins/mcollective → mcollective}/aggregate/summary_spec.rb +1 -1
- data/spec/unit/{aggregate_spec.rb → mcollective/aggregate_spec.rb} +0 -0
- data/spec/unit/{plugins/mcollective → mcollective}/application/plugin_spec.rb +1 -1
- data/spec/unit/{application_spec.rb → mcollective/application_spec.rb} +0 -0
- data/spec/unit/{applications_spec.rb → mcollective/applications_spec.rb} +1 -1
- data/spec/unit/{array_spec.rb → mcollective/array_spec.rb} +0 -0
- data/spec/unit/{plugins/mcollective → mcollective}/audit/logfile_spec.rb +1 -1
- data/spec/unit/{cache_spec.rb → mcollective/cache_spec.rb} +0 -0
- data/spec/unit/{client_spec.rb → mcollective/client_spec.rb} +0 -0
- data/spec/unit/{config_spec.rb → mcollective/config_spec.rb} +12 -7
- data/spec/unit/{plugins/mcollective → mcollective}/connector/activemq_spec.rb +1 -1
- data/spec/unit/{connector → mcollective/connector}/base_spec.rb +0 -0
- data/spec/unit/{plugins/mcollective → mcollective}/connector/rabbitmq_spec.rb +1 -1
- data/spec/unit/{plugins/mcollective → mcollective}/data/agent_data_spec.rb +1 -1
- data/spec/unit/{data → mcollective/data}/base_spec.rb +0 -0
- data/spec/unit/{plugins/mcollective → mcollective}/data/collective_data_spec.rb +1 -1
- data/spec/unit/{plugins/mcollective → mcollective}/data/fact_data_spec.rb +1 -1
- data/spec/unit/{plugins/mcollective → mcollective}/data/fstat_data_spec.rb +1 -1
- data/spec/unit/{data → mcollective/data}/result_spec.rb +0 -0
- data/spec/unit/{data_spec.rb → mcollective/data_spec.rb} +0 -0
- data/spec/unit/{ddl → mcollective/ddl}/agentddl_spec.rb +0 -0
- data/spec/unit/{ddl → mcollective/ddl}/base_spec.rb +0 -0
- data/spec/unit/{ddl → mcollective/ddl}/dataddl_spec.rb +0 -0
- data/spec/unit/{ddl → mcollective/ddl}/discoveryddl_spec.rb +0 -0
- data/spec/unit/{ddl_spec.rb → mcollective/ddl_spec.rb} +0 -0
- data/spec/unit/{plugins/mcollective → mcollective}/discovery/flatfile_spec.rb +1 -1
- data/spec/unit/{plugins/mcollective → mcollective}/discovery/mc_spec.rb +1 -1
- data/spec/unit/{plugins/mcollective → mcollective}/discovery/stdin_spec.rb +1 -1
- data/spec/unit/{discovery_spec.rb → mcollective/discovery_spec.rb} +0 -0
- data/spec/unit/{facts → mcollective/facts}/base_spec.rb +0 -0
- data/spec/unit/{plugins/mcollective → mcollective}/facts/yaml_facts_spec.rb +1 -1
- data/spec/unit/{facts_spec.rb → mcollective/facts_spec.rb} +0 -0
- data/spec/unit/{generators → mcollective/generators}/agent_generator_spec.rb +0 -0
- data/spec/unit/{generators → mcollective/generators}/base_spec.rb +0 -0
- data/spec/unit/{generators → mcollective/generators}/data_generator_spec.rb +0 -0
- data/spec/unit/{generators → mcollective/generators}/snippets/agent_ddl +0 -0
- data/spec/unit/{generators → mcollective/generators}/snippets/data_ddl +0 -0
- data/spec/unit/{log_spec.rb → mcollective/log_spec.rb} +0 -0
- data/spec/unit/{logger → mcollective/logger}/base_spec.rb +0 -0
- data/spec/unit/{logger → mcollective/logger}/console_logger_spec.rb +0 -0
- data/spec/unit/{logger → mcollective/logger}/file_logger_spec.rb +0 -0
- data/spec/unit/{logger → mcollective/logger}/syslog_logger_spec.rb +0 -0
- data/spec/unit/{matcher → mcollective/matcher}/parser_spec.rb +0 -0
- data/spec/unit/{matcher → mcollective/matcher}/scanner_spec.rb +0 -0
- data/spec/unit/{matcher_spec.rb → mcollective/matcher_spec.rb} +0 -0
- data/spec/unit/{message_spec.rb → mcollective/message_spec.rb} +0 -0
- data/spec/unit/{monkey_patches_spec.rb → mcollective/monkey_patches_spec.rb} +0 -0
- data/spec/unit/{optionparser_spec.rb → mcollective/optionparser_spec.rb} +0 -0
- data/spec/unit/{plugins/mcollective → mcollective}/packagers/debpackage_packager_spec.rb +1 -1
- data/spec/unit/{plugins/mcollective → mcollective}/packagers/modulepackage_packager_spec.rb +1 -1
- data/spec/unit/{plugins/mcollective → mcollective}/packagers/ospackage_spec.rb +1 -1
- data/spec/unit/{plugins/mcollective → mcollective}/packagers/rpmpackage_packager_spec.rb +1 -1
- data/spec/unit/{pluginmanager_spec.rb → mcollective/pluginmanager_spec.rb} +0 -0
- data/spec/unit/{pluginpackager → mcollective/pluginpackager}/agent_definition_spec.rb +0 -0
- data/spec/unit/{pluginpackager → mcollective/pluginpackager}/standard_definition_spec.rb +0 -0
- data/spec/unit/{pluginpackager_spec.rb → mcollective/pluginpackager_spec.rb} +0 -0
- data/spec/unit/{registration → mcollective/registration}/base_spec.rb +0 -0
- data/spec/unit/{rpc → mcollective/rpc}/actionrunner_spec.rb +0 -0
- data/spec/unit/{rpc → mcollective/rpc}/agent_spec.rb +0 -0
- data/spec/unit/{rpc → mcollective/rpc}/client_spec.rb +0 -0
- data/spec/unit/{rpc → mcollective/rpc}/helpers_spec.rb +0 -0
- data/spec/unit/{rpc → mcollective/rpc}/reply_spec.rb +0 -0
- data/spec/unit/{rpc → mcollective/rpc}/request_spec.rb +0 -0
- data/spec/unit/{rpc → mcollective/rpc}/result_spec.rb +0 -0
- data/spec/unit/{rpc → mcollective/rpc}/stats_spec.rb +0 -0
- data/spec/unit/{rpc_spec.rb → mcollective/rpc_spec.rb} +0 -0
- data/spec/unit/{runner_spec.rb → mcollective/runner_spec.rb} +0 -0
- data/spec/unit/{runnerstats_spec.rb → mcollective/runnerstats_spec.rb} +0 -0
- data/spec/unit/{plugins/mcollective → mcollective}/security/aes_security_spec.rb +1 -1
- data/spec/unit/{security → mcollective/security}/base_spec.rb +0 -0
- data/spec/unit/{plugins/mcollective → mcollective}/security/psk_spec.rb +1 -1
- data/spec/unit/{shell_spec.rb → mcollective/shell_spec.rb} +0 -0
- data/spec/unit/{ssl_spec.rb → mcollective/ssl_spec.rb} +12 -12
- data/spec/unit/{string_spec.rb → mcollective/string_spec.rb} +0 -0
- data/spec/unit/{symbol_spec.rb → mcollective/symbol_spec.rb} +0 -0
- data/spec/unit/{unix_daemon_spec.rb → mcollective/unix_daemon_spec.rb} +0 -0
- data/spec/unit/{util_spec.rb → mcollective/util_spec.rb} +28 -6
- data/spec/unit/{plugins/mcollective → mcollective}/validator/array_validator_spec.rb +1 -1
- data/spec/unit/{plugins/mcollective → mcollective}/validator/ipv4address_validator_spec.rb +1 -1
- data/spec/unit/{plugins/mcollective → mcollective}/validator/ipv6address_validator_spec.rb +1 -1
- data/spec/unit/{plugins/mcollective → mcollective}/validator/length_validator_spec.rb +1 -1
- data/spec/unit/{plugins/mcollective → mcollective}/validator/regex_validator_spec.rb +1 -1
- data/spec/unit/{plugins/mcollective → mcollective}/validator/shellsafe_validator_spec.rb +1 -1
- data/spec/unit/{plugins/mcollective → mcollective}/validator/typecheck_validator_spec.rb +1 -1
- data/spec/unit/{validator_spec.rb → mcollective/validator_spec.rb} +1 -1
- data/spec/unit/{vendor_spec.rb → mcollective/vendor_spec.rb} +2 -2
- data/spec/unit/{windows_daemon_spec.rb → mcollective/windows_daemon_spec.rb} +0 -0
- metadata +361 -284
- checksums.yaml +0 -7
@@ -0,0 +1,117 @@
|
|
1
|
+
module MCollective
|
2
|
+
module Security
|
3
|
+
# Impliments message authentication using digests and shared keys
|
4
|
+
#
|
5
|
+
# You should configure a psk in the configuration file and all requests
|
6
|
+
# will be validated for authenticity with this.
|
7
|
+
#
|
8
|
+
# Serialization uses Marshal, this is the default security module that is
|
9
|
+
# supported out of the box.
|
10
|
+
#
|
11
|
+
# Validation is as default and is provided by MCollective::Security::Base
|
12
|
+
#
|
13
|
+
# You can configure the caller id being created, this can adjust how you
|
14
|
+
# create authorization plugins. For example you can use a unix group instead
|
15
|
+
# of uid to do authorization.
|
16
|
+
class Psk < Base
|
17
|
+
require 'etc'
|
18
|
+
|
19
|
+
# Decodes a message by unserializing all the bits etc, it also validates
|
20
|
+
# it as valid using the psk etc
|
21
|
+
def decodemsg(msg)
|
22
|
+
body = Marshal.load(msg.payload)
|
23
|
+
|
24
|
+
should_process_msg?(msg, body[:requestid])
|
25
|
+
|
26
|
+
if validrequest?(body)
|
27
|
+
body[:body] = Marshal.load(body[:body])
|
28
|
+
return body
|
29
|
+
else
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Encodes a reply
|
35
|
+
def encodereply(sender, msg, requestid, requestcallerid=nil)
|
36
|
+
serialized = Marshal.dump(msg)
|
37
|
+
digest = makehash(serialized)
|
38
|
+
|
39
|
+
req = create_reply(requestid, sender, serialized)
|
40
|
+
req[:hash] = digest
|
41
|
+
|
42
|
+
Marshal.dump(req)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Encodes a request msg
|
46
|
+
def encoderequest(sender, msg, requestid, filter, target_agent, target_collective, ttl=60)
|
47
|
+
serialized = Marshal.dump(msg)
|
48
|
+
digest = makehash(serialized)
|
49
|
+
|
50
|
+
req = create_request(requestid, filter, serialized, @initiated_by, target_agent, target_collective, ttl)
|
51
|
+
req[:hash] = digest
|
52
|
+
|
53
|
+
Marshal.dump(req)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Checks the md5 hash in the request body against our psk, the request sent for validation
|
57
|
+
# should not have been deserialized already
|
58
|
+
def validrequest?(req)
|
59
|
+
digest = makehash(req[:body])
|
60
|
+
|
61
|
+
if digest == req[:hash]
|
62
|
+
@stats.validated
|
63
|
+
|
64
|
+
return true
|
65
|
+
else
|
66
|
+
@stats.unvalidated
|
67
|
+
|
68
|
+
raise(SecurityValidationFailed, "Received an invalid signature in message")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def callerid
|
73
|
+
if @config.pluginconf.include?("psk.callertype")
|
74
|
+
callertype = @config.pluginconf["psk.callertype"].to_sym if @config.pluginconf.include?("psk.callertype")
|
75
|
+
else
|
76
|
+
callertype = :uid
|
77
|
+
end
|
78
|
+
|
79
|
+
case callertype
|
80
|
+
when :gid
|
81
|
+
id = "gid=#{Process.gid}"
|
82
|
+
|
83
|
+
when :group
|
84
|
+
raise "Cannot use the 'group' callertype for the PSK security plugin on the Windows platform" if Util.windows?
|
85
|
+
|
86
|
+
id = "group=#{Etc.getgrgid(Process.gid).name}"
|
87
|
+
|
88
|
+
when :user
|
89
|
+
id = "user=#{Etc.getlogin}"
|
90
|
+
|
91
|
+
when :identity
|
92
|
+
id = "identity=#{@config.identity}"
|
93
|
+
|
94
|
+
else
|
95
|
+
id ="uid=#{Process.uid}"
|
96
|
+
end
|
97
|
+
|
98
|
+
Log.debug("Setting callerid to #{id} based on callertype=#{callertype}")
|
99
|
+
|
100
|
+
id
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
# Retrieves the value of plugin.psk and builds a hash with it and the passed body
|
105
|
+
def makehash(body)
|
106
|
+
if ENV.include?("MCOLLECTIVE_PSK")
|
107
|
+
psk = ENV["MCOLLECTIVE_PSK"]
|
108
|
+
else
|
109
|
+
raise("No plugin.psk configuration option specified") unless @config.pluginconf.include?("psk")
|
110
|
+
psk = @config.pluginconf["psk"]
|
111
|
+
end
|
112
|
+
|
113
|
+
Digest::MD5.hexdigest(body.to_s + psk)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,328 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'openssl'
|
3
|
+
|
4
|
+
module MCollective
|
5
|
+
module Security
|
6
|
+
# Impliments a public/private key based message validation system using SSL
|
7
|
+
# public and private keys.
|
8
|
+
#
|
9
|
+
# The design goal of the plugin is two fold:
|
10
|
+
#
|
11
|
+
# - give different security credentials to clients and servers to avoid
|
12
|
+
# a compromised server from sending new client requests.
|
13
|
+
# - create a token that uniquely identify the client - based on the filename
|
14
|
+
# of the public key
|
15
|
+
#
|
16
|
+
# To setup you need to create a SSL key pair that is shared by all nodes.
|
17
|
+
#
|
18
|
+
# openssl genrsa -out mcserver-private.pem 1024
|
19
|
+
# openssl rsa -in mcserver-private.pem -out mcserver-public.pem -outform PEM -pubout
|
20
|
+
#
|
21
|
+
# Distribute the private and public file to /etc/mcollective/ssl on all the nodes.
|
22
|
+
# Distribute the public file to /etc/mcollective/ssl everywhere the client code runs.
|
23
|
+
#
|
24
|
+
# Now you should create a key pair for every one of your clients, here we create one
|
25
|
+
# for user john - you could also if you are less concerned with client id create one
|
26
|
+
# pair and share it with all clients:
|
27
|
+
#
|
28
|
+
# openssl genrsa -out john-private.pem 1024
|
29
|
+
# openssl rsa -in john-private.pem -out john-public.pem -outform PEM -pubout
|
30
|
+
#
|
31
|
+
# Each user has a unique userid, this is based on the name of the public key.
|
32
|
+
# In this example case the userid would be 'john-public'.
|
33
|
+
#
|
34
|
+
# Store these somewhere like:
|
35
|
+
#
|
36
|
+
# /home/john/.mc/john-private.pem
|
37
|
+
# /home/john/.mc/john-public.pem
|
38
|
+
#
|
39
|
+
# Every users public key needs to be distributed to all the nodes, save the john one
|
40
|
+
# in a file called:
|
41
|
+
#
|
42
|
+
# /etc/mcollective/ssl/clients/john-public.pem
|
43
|
+
#
|
44
|
+
# If you wish to use registration or auditing that sends connections over MC to a
|
45
|
+
# central host you will need also put the server-public.pem in the clients directory.
|
46
|
+
#
|
47
|
+
# You should be aware if you do add the node public key to the clients dir you will in
|
48
|
+
# effect be weakening your overall security. You should consider doing this only if
|
49
|
+
# you also set up an Authorization method that limits the requests the nodes can make.
|
50
|
+
#
|
51
|
+
# client.cfg:
|
52
|
+
#
|
53
|
+
# securityprovider = ssl
|
54
|
+
# plugin.ssl_server_public = /etc/mcollective/ssl/server-public.pem
|
55
|
+
# plugin.ssl_client_private = /home/john/.mc/john-private.pem
|
56
|
+
# plugin.ssl_client_public = /home/john/.mc/john-public.pem
|
57
|
+
#
|
58
|
+
# If you have many clients per machine and dont want to configure the main config file
|
59
|
+
# with the public/private keys you can set the following environment variables:
|
60
|
+
#
|
61
|
+
# export MCOLLECTIVE_SSL_PRIVATE=/home/john/.mc/john-private.pem
|
62
|
+
# export MCOLLECTIVE_SSL_PUBLIC=/home/john/.mc/john-public.pem
|
63
|
+
#
|
64
|
+
# server.cfg:
|
65
|
+
#
|
66
|
+
# securityprovider = ssl
|
67
|
+
# plugin.ssl_server_private = /etc/mcollective/ssl/server-private.pem
|
68
|
+
# plugin.ssl_server_public = /etc/mcollective/ssl/server-public.pem
|
69
|
+
# plugin.ssl_client_cert_dir = /etc/mcollective/etc/ssl/clients/
|
70
|
+
#
|
71
|
+
# # Log but accept messages that may have been tampered with
|
72
|
+
# plugin.ssl.enforce_ttl = 0
|
73
|
+
#
|
74
|
+
# Serialization can be configured to use either Marshal or YAML, data types
|
75
|
+
# in and out of mcollective will be preserved from client to server and reverse
|
76
|
+
#
|
77
|
+
# You can configure YAML serialization:
|
78
|
+
#
|
79
|
+
# plugins.ssl_serializer = yaml
|
80
|
+
#
|
81
|
+
# else the default is Marshal. Use YAML if you wish to write a client using
|
82
|
+
# a language other than Ruby that doesn't support Marshal.
|
83
|
+
#
|
84
|
+
# Validation is as default and is provided by MCollective::Security::Base
|
85
|
+
#
|
86
|
+
# Initial code was contributed by Vladimir Vuksan and modified by R.I.Pienaar
|
87
|
+
class Ssl < Base
|
88
|
+
# Decodes a message by unserializing all the bits etc, it also validates
|
89
|
+
# it as valid using the psk etc
|
90
|
+
def decodemsg(msg)
|
91
|
+
body = deserialize(msg.payload)
|
92
|
+
|
93
|
+
should_process_msg?(msg, body[:requestid])
|
94
|
+
|
95
|
+
if validrequest?(body)
|
96
|
+
body[:body] = deserialize(body[:body])
|
97
|
+
|
98
|
+
unless @initiated_by == :client
|
99
|
+
if body[:body].is_a?(Hash)
|
100
|
+
update_secure_property(body, :ssl_ttl, :ttl, "TTL")
|
101
|
+
update_secure_property(body, :ssl_msgtime, :msgtime, "Message Time")
|
102
|
+
|
103
|
+
body[:body] = body[:body][:ssl_msg] if body[:body].include?(:ssl_msg)
|
104
|
+
else
|
105
|
+
unless @config.pluginconf["ssl.enforce_ttl"] == nil
|
106
|
+
raise "Message %s is in an unknown or older security protocol, ignoring" % [request_description(body)]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
return body
|
112
|
+
else
|
113
|
+
nil
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# To avoid tampering we turn the origin body into a hash and copy some of the protocol keys
|
118
|
+
# like :ttl and :msg_time into the hash before hashing it.
|
119
|
+
#
|
120
|
+
# This function compares and updates the unhashed ones based on the hashed ones. By
|
121
|
+
# default it enforces matching and presense by raising exceptions, if ssl.enforce_ttl is set
|
122
|
+
# to 0 it will only log warnings about violations
|
123
|
+
def update_secure_property(msg, secure_property, property, description)
|
124
|
+
req = request_description(msg)
|
125
|
+
|
126
|
+
unless @config.pluginconf["ssl.enforce_ttl"] == "0"
|
127
|
+
raise "Request #{req} does not have a secure #{description}" unless msg[:body].include?(secure_property)
|
128
|
+
raise "Request #{req} #{description} does not match encrypted #{description} - possible tampering" unless msg[:body][secure_property] == msg[property]
|
129
|
+
else
|
130
|
+
if msg[:body].include?(secure_property)
|
131
|
+
Log.warn("Request #{req} #{description} does not match encrypted #{description} - possible tampering") unless msg[:body][secure_property] == msg[property]
|
132
|
+
else
|
133
|
+
Log.warn("Request #{req} does not have a secure #{description}") unless msg[:body].include?(secure_property)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
msg[property] = msg[:body][secure_property] if msg[:body].include?(secure_property)
|
138
|
+
msg[:body].delete(secure_property)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Encodes a reply
|
142
|
+
def encodereply(sender, msg, requestid, requestcallerid=nil)
|
143
|
+
serialized = serialize(msg)
|
144
|
+
digest = makehash(serialized)
|
145
|
+
|
146
|
+
|
147
|
+
req = create_reply(requestid, sender, serialized)
|
148
|
+
req[:hash] = digest
|
149
|
+
|
150
|
+
serialize(req)
|
151
|
+
end
|
152
|
+
|
153
|
+
# Encodes a request msg
|
154
|
+
def encoderequest(sender, msg, requestid, filter, target_agent, target_collective, ttl=60)
|
155
|
+
req = create_request(requestid, filter, "", @initiated_by, target_agent, target_collective, ttl)
|
156
|
+
|
157
|
+
ssl_msg = {:ssl_msg => msg,
|
158
|
+
:ssl_ttl => ttl,
|
159
|
+
:ssl_msgtime => req[:msgtime]}
|
160
|
+
|
161
|
+
serialized = serialize(ssl_msg)
|
162
|
+
digest = makehash(serialized)
|
163
|
+
|
164
|
+
req[:hash] = digest
|
165
|
+
req[:body] = serialized
|
166
|
+
|
167
|
+
serialize(req)
|
168
|
+
end
|
169
|
+
|
170
|
+
# Checks the SSL signature in the request body
|
171
|
+
def validrequest?(req)
|
172
|
+
message = req[:body]
|
173
|
+
signature = req[:hash]
|
174
|
+
|
175
|
+
Log.debug("Validating request from #{req[:callerid]}")
|
176
|
+
|
177
|
+
if verify(public_key_file(req[:callerid]), signature, message.to_s)
|
178
|
+
@stats.validated
|
179
|
+
return true
|
180
|
+
else
|
181
|
+
@stats.unvalidated
|
182
|
+
raise(SecurityValidationFailed, "Received an invalid signature in message")
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
|
187
|
+
# sets the caller id to the md5 of the public key
|
188
|
+
def callerid
|
189
|
+
if @initiated_by == :client
|
190
|
+
id = "cert=#{File.basename(client_public_key).gsub(/\.pem$/, '')}"
|
191
|
+
raise "Invalid callerid generated from client public key" unless valid_callerid?(id)
|
192
|
+
else
|
193
|
+
# servers need to set callerid as well, not usually needed but
|
194
|
+
# would be if you're doing registration or auditing or generating
|
195
|
+
# requests for some or other reason
|
196
|
+
id = "cert=#{File.basename(server_public_key).gsub(/\.pem$/, '')}"
|
197
|
+
raise "Invalid callerid generated from server public key" unless valid_callerid?(id)
|
198
|
+
end
|
199
|
+
|
200
|
+
return id
|
201
|
+
end
|
202
|
+
|
203
|
+
private
|
204
|
+
# Serializes a message using the configured encoder
|
205
|
+
def serialize(msg)
|
206
|
+
serializer = @config.pluginconf["ssl_serializer"] || "marshal"
|
207
|
+
|
208
|
+
Log.debug("Serializing using #{serializer}")
|
209
|
+
|
210
|
+
case serializer
|
211
|
+
when "yaml"
|
212
|
+
return YAML.dump(msg)
|
213
|
+
else
|
214
|
+
return Marshal.dump(msg)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
# De-Serializes a message using the configured encoder
|
219
|
+
def deserialize(msg)
|
220
|
+
serializer = @config.pluginconf["ssl_serializer"] || "marshal"
|
221
|
+
|
222
|
+
Log.debug("De-Serializing using #{serializer}")
|
223
|
+
|
224
|
+
case serializer
|
225
|
+
when "yaml"
|
226
|
+
return YAML.load(msg)
|
227
|
+
else
|
228
|
+
return Marshal.load(msg)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
# Figures out where to get our private key
|
233
|
+
def private_key_file
|
234
|
+
if ENV.include?("MCOLLECTIVE_SSL_PRIVATE")
|
235
|
+
return ENV["MCOLLECTIVE_SSL_PRIVATE"]
|
236
|
+
else
|
237
|
+
if @initiated_by == :node
|
238
|
+
return server_private_key
|
239
|
+
else
|
240
|
+
return client_private_key
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
# Figures out the public key to use
|
246
|
+
#
|
247
|
+
# If the node is asking do it based on caller id
|
248
|
+
# If the client is asking just get the node public key
|
249
|
+
def public_key_file(callerid = nil)
|
250
|
+
if @initiated_by == :client
|
251
|
+
return server_public_key
|
252
|
+
else
|
253
|
+
if callerid =~ /cert=([\w\.\-]+)/
|
254
|
+
cid = $1
|
255
|
+
|
256
|
+
if File.exist?("#{client_cert_dir}/#{cid}.pem")
|
257
|
+
return "#{client_cert_dir}/#{cid}.pem"
|
258
|
+
else
|
259
|
+
raise("Could not find a public key for #{cid} in #{client_cert_dir}/#{cid}.pem")
|
260
|
+
end
|
261
|
+
else
|
262
|
+
raise("Caller id is not in the expected format")
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
# Figures out the client private key either from MCOLLECTIVE_SSL_PRIVATE or the
|
268
|
+
# plugin.ssl_client_private config option
|
269
|
+
def client_private_key
|
270
|
+
return ENV["MCOLLECTIVE_SSL_PRIVATE"] if ENV.include?("MCOLLECTIVE_SSL_PRIVATE")
|
271
|
+
|
272
|
+
raise("No plugin.ssl_client_private configuration option specified") unless @config.pluginconf.include?("ssl_client_private")
|
273
|
+
|
274
|
+
return @config.pluginconf["ssl_client_private"]
|
275
|
+
end
|
276
|
+
|
277
|
+
# Figures out the client public key either from MCOLLECTIVE_SSL_PUBLIC or the
|
278
|
+
# plugin.ssl_client_public config option
|
279
|
+
def client_public_key
|
280
|
+
return ENV["MCOLLECTIVE_SSL_PUBLIC"] if ENV.include?("MCOLLECTIVE_SSL_PUBLIC")
|
281
|
+
|
282
|
+
raise("No plugin.ssl_client_public configuration option specified") unless @config.pluginconf.include?("ssl_client_public")
|
283
|
+
|
284
|
+
return @config.pluginconf["ssl_client_public"]
|
285
|
+
end
|
286
|
+
|
287
|
+
# Figures out the server private key from the plugin.ssl_server_private config option
|
288
|
+
def server_private_key
|
289
|
+
raise("No plugin.ssl_server_private configuration option specified") unless @config.pluginconf.include?("ssl_server_private")
|
290
|
+
@config.pluginconf["ssl_server_private"]
|
291
|
+
end
|
292
|
+
|
293
|
+
# Figures out the server public key from the plugin.ssl_server_public config option
|
294
|
+
def server_public_key
|
295
|
+
raise("No ssl_server_public configuration option specified") unless @config.pluginconf.include?("ssl_server_public")
|
296
|
+
return @config.pluginconf["ssl_server_public"]
|
297
|
+
end
|
298
|
+
|
299
|
+
# Figures out where to get client public certs from the plugin.ssl_client_cert_dir config option
|
300
|
+
def client_cert_dir
|
301
|
+
raise("No plugin.ssl_client_cert_dir configuration option specified") unless @config.pluginconf.include?("ssl_client_cert_dir")
|
302
|
+
@config.pluginconf["ssl_client_cert_dir"]
|
303
|
+
end
|
304
|
+
|
305
|
+
# Retrieves the value of plugin.psk and builds a hash with it and the passed body
|
306
|
+
def makehash(body)
|
307
|
+
Log.debug("Creating message hash using #{private_key_file}")
|
308
|
+
|
309
|
+
sign(private_key_file, body.to_s)
|
310
|
+
end
|
311
|
+
|
312
|
+
# Code adapted from http://github.com/adamcooke/basicssl
|
313
|
+
# signs a message
|
314
|
+
def sign(key, string)
|
315
|
+
SSL.new(nil, key).sign(string, true)
|
316
|
+
end
|
317
|
+
|
318
|
+
# verifies a signature
|
319
|
+
def verify(key, signature, string)
|
320
|
+
SSL.new(key).verify_signature(signature, string, true)
|
321
|
+
end
|
322
|
+
|
323
|
+
def request_description(msg)
|
324
|
+
"%s from %s@%s" % [msg[:requestid], msg[:callerid], msg[:senderid]]
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
data/lib/mcollective/security.rb
CHANGED
data/lib/mcollective/util.rb
CHANGED
@@ -155,28 +155,28 @@ module MCollective
|
|
155
155
|
# Picks a config file defaults to ~/.mcollective
|
156
156
|
# else /etc/mcollective/client.cfg
|
157
157
|
def self.config_file_for_user
|
158
|
-
#
|
159
|
-
|
160
|
-
|
158
|
+
# the set of acceptable config files
|
159
|
+
config_paths = []
|
160
|
+
|
161
|
+
# user dotfile
|
161
162
|
begin
|
162
|
-
|
163
|
+
# File.expand_path will raise if HOME isn't set, catch it
|
164
|
+
user_path = File.expand_path("~/.mcollective")
|
165
|
+
config_paths << user_path
|
166
|
+
rescue Exception
|
167
|
+
end
|
163
168
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
end
|
171
|
-
rescue Exception => e
|
172
|
-
if self.windows?
|
173
|
-
config = File.join(self.windows_prefix, "etc", "client.cfg")
|
174
|
-
else
|
175
|
-
config = "/etc/mcollective/client.cfg"
|
176
|
-
end
|
169
|
+
# standard locations
|
170
|
+
if self.windows?
|
171
|
+
config_paths << File.join(self.windows_prefix, 'etc', 'client.cfg')
|
172
|
+
else
|
173
|
+
config_paths << '/etc/puppetlabs/agent/mcollective/client.cfg'
|
174
|
+
config_paths << '/etc/mcollective/client.cfg'
|
177
175
|
end
|
178
176
|
|
179
|
-
|
177
|
+
# use the first readable config file, or if none are the first listed
|
178
|
+
found = config_paths.find_index { |file| File.readable?(file) } || 0
|
179
|
+
return config_paths[found]
|
180
180
|
end
|
181
181
|
|
182
182
|
# Creates a standard options hash
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module MCollective
|
2
|
+
module Validator
|
3
|
+
class Ipv4addressValidator
|
4
|
+
require 'ipaddr'
|
5
|
+
|
6
|
+
def self.validate(validator)
|
7
|
+
begin
|
8
|
+
ip = IPAddr.new(validator)
|
9
|
+
raise ValidatorError, "value should be an ipv4 adddress" unless ip.ipv4?
|
10
|
+
rescue
|
11
|
+
raise ValidatorError, "value should be an ipv4 address"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module MCollective
|
2
|
+
module Validator
|
3
|
+
class Ipv6addressValidator
|
4
|
+
require 'ipaddr'
|
5
|
+
|
6
|
+
def self.validate(validator)
|
7
|
+
begin
|
8
|
+
ip = IPAddr.new(validator)
|
9
|
+
raise ValidatorError, "value should be an ipv6 adddress" unless ip.ipv6?
|
10
|
+
rescue
|
11
|
+
raise ValidatorError, "value should be an ipv6 address"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
metadata :name => "Length",
|
2
|
+
:description => "Validates that the length of a string is less or equal to a specified value",
|
3
|
+
:author => "P. Loubser <pieter.loubser@puppetlabs.com>",
|
4
|
+
:license => "ASL 2.0",
|
5
|
+
:version => "1.0",
|
6
|
+
:url => "http://marionette-collective.org/",
|
7
|
+
:timeout => 1
|
@@ -0,0 +1,7 @@
|
|
1
|
+
metadata :name => "Regex",
|
2
|
+
:description => "Validates that a string matches a supplied regular expression",
|
3
|
+
:author => "P. Loubser <pieter.loubser@puppetlabs.com>",
|
4
|
+
:license => "ASL 2.0",
|
5
|
+
:version => "1.0",
|
6
|
+
:url => "http://marionette-collective.org/",
|
7
|
+
:timeout => 1
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module MCollective
|
2
|
+
module Validator
|
3
|
+
class ShellsafeValidator
|
4
|
+
def self.validate(validator)
|
5
|
+
raise ValidatorError, "value should be a String" unless validator.is_a?(String)
|
6
|
+
|
7
|
+
['`', '$', ';', '|', '&&', '>', '<'].each do |chr|
|
8
|
+
raise ValidatorError, "value should not have #{chr} in it" if validator.match(Regexp.escape(chr))
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module MCollective
|
2
|
+
module Validator
|
3
|
+
class TypecheckValidator
|
4
|
+
def self.validate(validator, validation_type)
|
5
|
+
raise ValidatorError, "value should be a #{validation_type.to_s}" unless check_type(validator, validation_type)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.check_type(validator, validation_type)
|
9
|
+
case validation_type
|
10
|
+
when Class
|
11
|
+
validator.is_a?(validation_type)
|
12
|
+
when :integer
|
13
|
+
validator.is_a?(Fixnum)
|
14
|
+
when :float
|
15
|
+
validator.is_a?(Float)
|
16
|
+
when :number
|
17
|
+
validator.is_a?(Numeric)
|
18
|
+
when :string
|
19
|
+
validator.is_a?(String)
|
20
|
+
when :boolean
|
21
|
+
[TrueClass, FalseClass].include?(validator.class)
|
22
|
+
else
|
23
|
+
false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|