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.
Files changed (186) hide show
  1. data/lib/mcollective/agent/discovery.rb +37 -0
  2. data/lib/mcollective/agent/rpcutil.ddl +220 -0
  3. data/lib/mcollective/agent/rpcutil.rb +108 -0
  4. data/lib/mcollective/aggregate/average.ddl +33 -0
  5. data/lib/mcollective/aggregate/average.rb +29 -0
  6. data/lib/mcollective/aggregate/result.rb +3 -3
  7. data/lib/mcollective/aggregate/sum.ddl +33 -0
  8. data/lib/mcollective/aggregate/sum.rb +18 -0
  9. data/lib/mcollective/aggregate/summary.ddl +33 -0
  10. data/lib/mcollective/aggregate/summary.rb +53 -0
  11. data/lib/mcollective/aggregate.rb +2 -2
  12. data/lib/mcollective/application/completion.rb +104 -0
  13. data/lib/mcollective/application/facts.rb +62 -0
  14. data/lib/mcollective/application/find.rb +21 -0
  15. data/lib/mcollective/application/help.rb +28 -0
  16. data/lib/mcollective/application/inventory.rb +344 -0
  17. data/lib/mcollective/application/ping.rb +77 -0
  18. data/lib/mcollective/application/plugin.rb +369 -0
  19. data/lib/mcollective/application/rpc.rb +121 -0
  20. data/lib/mcollective/application.rb +2 -0
  21. data/lib/mcollective/audit/logfile.rb +26 -0
  22. data/lib/mcollective/config.rb +16 -9
  23. data/lib/mcollective/connector/activemq.ddl +9 -0
  24. data/lib/mcollective/connector/activemq.rb +572 -0
  25. data/lib/mcollective/connector/rabbitmq.ddl +9 -0
  26. data/lib/mcollective/connector/rabbitmq.rb +484 -0
  27. data/lib/mcollective/connector.rb +1 -1
  28. data/lib/mcollective/data/agent_data.ddl +22 -0
  29. data/lib/mcollective/data/agent_data.rb +17 -0
  30. data/lib/mcollective/data/collective_data.ddl +20 -0
  31. data/lib/mcollective/data/collective_data.rb +9 -0
  32. data/lib/mcollective/data/fact_data.ddl +28 -0
  33. data/lib/mcollective/data/fact_data.rb +55 -0
  34. data/lib/mcollective/data/fstat_data.ddl +89 -0
  35. data/lib/mcollective/data/fstat_data.rb +56 -0
  36. data/lib/mcollective/data.rb +2 -2
  37. data/lib/mcollective/ddl.rb +4 -4
  38. data/lib/mcollective/discovery/flatfile.ddl +11 -0
  39. data/lib/mcollective/discovery/flatfile.rb +48 -0
  40. data/lib/mcollective/discovery/mc.ddl +11 -0
  41. data/lib/mcollective/discovery/mc.rb +30 -0
  42. data/lib/mcollective/discovery/stdin.ddl +11 -0
  43. data/lib/mcollective/discovery/stdin.rb +66 -0
  44. data/lib/mcollective/facts/yaml_facts.rb +61 -0
  45. data/lib/mcollective/facts.rb +1 -1
  46. data/lib/mcollective/generators.rb +3 -3
  47. data/lib/mcollective/logger.rb +1 -1
  48. data/lib/mcollective/matcher/scanner.rb +1 -1
  49. data/lib/mcollective/matcher.rb +2 -2
  50. data/lib/mcollective/pluginpackager/debpackage_packager.rb +237 -0
  51. data/lib/mcollective/pluginpackager/modulepackage_packager.rb +127 -0
  52. data/lib/mcollective/pluginpackager/ospackage_packager.rb +59 -0
  53. data/lib/mcollective/pluginpackager/rpmpackage_packager.rb +180 -0
  54. data/lib/mcollective/pluginpackager/templates/debian/Makefile.erb +7 -0
  55. data/lib/mcollective/pluginpackager/templates/debian/changelog.erb +5 -0
  56. data/lib/mcollective/pluginpackager/templates/debian/compat.erb +1 -0
  57. data/lib/mcollective/pluginpackager/templates/debian/control.erb +15 -0
  58. data/lib/mcollective/pluginpackager/templates/debian/copyright.erb +8 -0
  59. data/lib/mcollective/pluginpackager/templates/debian/rules.erb +6 -0
  60. data/lib/mcollective/pluginpackager/templates/module/Modulefile.erb +5 -0
  61. data/lib/mcollective/pluginpackager/templates/module/README.md.erb +37 -0
  62. data/lib/mcollective/pluginpackager/templates/module/_manifest.pp.erb +9 -0
  63. data/lib/mcollective/pluginpackager/templates/redhat/rpm_spec.erb +63 -0
  64. data/lib/mcollective/pluginpackager.rb +2 -2
  65. data/lib/mcollective/registration/agentlist.rb +10 -0
  66. data/lib/mcollective/registration.rb +1 -1
  67. data/lib/mcollective/rpc/stats.rb +0 -1
  68. data/lib/mcollective/rpc.rb +11 -11
  69. data/lib/mcollective/security/aes_security.rb +394 -0
  70. data/lib/mcollective/security/psk.rb +117 -0
  71. data/lib/mcollective/security/ssl.rb +328 -0
  72. data/lib/mcollective/security.rb +1 -1
  73. data/lib/mcollective/util.rb +18 -18
  74. data/lib/mcollective/validator/array_validator.ddl +7 -0
  75. data/lib/mcollective/validator/array_validator.rb +9 -0
  76. data/lib/mcollective/validator/ipv4address_validator.ddl +7 -0
  77. data/lib/mcollective/validator/ipv4address_validator.rb +16 -0
  78. data/lib/mcollective/validator/ipv6address_validator.ddl +7 -0
  79. data/lib/mcollective/validator/ipv6address_validator.rb +16 -0
  80. data/lib/mcollective/validator/length_validator.ddl +7 -0
  81. data/lib/mcollective/validator/length_validator.rb +11 -0
  82. data/lib/mcollective/validator/regex_validator.ddl +7 -0
  83. data/lib/mcollective/validator/regex_validator.rb +9 -0
  84. data/lib/mcollective/validator/shellsafe_validator.ddl +7 -0
  85. data/lib/mcollective/validator/shellsafe_validator.rb +13 -0
  86. data/lib/mcollective/validator/typecheck_validator.ddl +7 -0
  87. data/lib/mcollective/validator/typecheck_validator.rb +28 -0
  88. data/lib/mcollective.rb +31 -31
  89. data/spec/spec_helper.rb +0 -5
  90. data/spec/unit/{plugins/mcollective → mcollective}/agent/rpcutil_spec.rb +1 -1
  91. data/spec/unit/{agents_spec.rb → mcollective/agents_spec.rb} +0 -0
  92. data/spec/unit/{plugins/mcollective → mcollective}/aggregate/average_spec.rb +1 -1
  93. data/spec/unit/{aggregate → mcollective/aggregate}/base_spec.rb +0 -0
  94. data/spec/unit/{aggregate → mcollective/aggregate}/result/base_spec.rb +0 -0
  95. data/spec/unit/{aggregate → mcollective/aggregate}/result/collection_result_spec.rb +0 -0
  96. data/spec/unit/{aggregate → mcollective/aggregate}/result/numeric_result_spec.rb +0 -0
  97. data/spec/unit/{plugins/mcollective → mcollective}/aggregate/sum_spec.rb +1 -1
  98. data/spec/unit/{plugins/mcollective → mcollective}/aggregate/summary_spec.rb +1 -1
  99. data/spec/unit/{aggregate_spec.rb → mcollective/aggregate_spec.rb} +0 -0
  100. data/spec/unit/{plugins/mcollective → mcollective}/application/plugin_spec.rb +1 -1
  101. data/spec/unit/{application_spec.rb → mcollective/application_spec.rb} +0 -0
  102. data/spec/unit/{applications_spec.rb → mcollective/applications_spec.rb} +1 -1
  103. data/spec/unit/{array_spec.rb → mcollective/array_spec.rb} +0 -0
  104. data/spec/unit/{plugins/mcollective → mcollective}/audit/logfile_spec.rb +1 -1
  105. data/spec/unit/{cache_spec.rb → mcollective/cache_spec.rb} +0 -0
  106. data/spec/unit/{client_spec.rb → mcollective/client_spec.rb} +0 -0
  107. data/spec/unit/{config_spec.rb → mcollective/config_spec.rb} +12 -7
  108. data/spec/unit/{plugins/mcollective → mcollective}/connector/activemq_spec.rb +1 -1
  109. data/spec/unit/{connector → mcollective/connector}/base_spec.rb +0 -0
  110. data/spec/unit/{plugins/mcollective → mcollective}/connector/rabbitmq_spec.rb +1 -1
  111. data/spec/unit/{plugins/mcollective → mcollective}/data/agent_data_spec.rb +1 -1
  112. data/spec/unit/{data → mcollective/data}/base_spec.rb +0 -0
  113. data/spec/unit/{plugins/mcollective → mcollective}/data/collective_data_spec.rb +1 -1
  114. data/spec/unit/{plugins/mcollective → mcollective}/data/fact_data_spec.rb +1 -1
  115. data/spec/unit/{plugins/mcollective → mcollective}/data/fstat_data_spec.rb +1 -1
  116. data/spec/unit/{data → mcollective/data}/result_spec.rb +0 -0
  117. data/spec/unit/{data_spec.rb → mcollective/data_spec.rb} +0 -0
  118. data/spec/unit/{ddl → mcollective/ddl}/agentddl_spec.rb +0 -0
  119. data/spec/unit/{ddl → mcollective/ddl}/base_spec.rb +0 -0
  120. data/spec/unit/{ddl → mcollective/ddl}/dataddl_spec.rb +0 -0
  121. data/spec/unit/{ddl → mcollective/ddl}/discoveryddl_spec.rb +0 -0
  122. data/spec/unit/{ddl_spec.rb → mcollective/ddl_spec.rb} +0 -0
  123. data/spec/unit/{plugins/mcollective → mcollective}/discovery/flatfile_spec.rb +1 -1
  124. data/spec/unit/{plugins/mcollective → mcollective}/discovery/mc_spec.rb +1 -1
  125. data/spec/unit/{plugins/mcollective → mcollective}/discovery/stdin_spec.rb +1 -1
  126. data/spec/unit/{discovery_spec.rb → mcollective/discovery_spec.rb} +0 -0
  127. data/spec/unit/{facts → mcollective/facts}/base_spec.rb +0 -0
  128. data/spec/unit/{plugins/mcollective → mcollective}/facts/yaml_facts_spec.rb +1 -1
  129. data/spec/unit/{facts_spec.rb → mcollective/facts_spec.rb} +0 -0
  130. data/spec/unit/{generators → mcollective/generators}/agent_generator_spec.rb +0 -0
  131. data/spec/unit/{generators → mcollective/generators}/base_spec.rb +0 -0
  132. data/spec/unit/{generators → mcollective/generators}/data_generator_spec.rb +0 -0
  133. data/spec/unit/{generators → mcollective/generators}/snippets/agent_ddl +0 -0
  134. data/spec/unit/{generators → mcollective/generators}/snippets/data_ddl +0 -0
  135. data/spec/unit/{log_spec.rb → mcollective/log_spec.rb} +0 -0
  136. data/spec/unit/{logger → mcollective/logger}/base_spec.rb +0 -0
  137. data/spec/unit/{logger → mcollective/logger}/console_logger_spec.rb +0 -0
  138. data/spec/unit/{logger → mcollective/logger}/file_logger_spec.rb +0 -0
  139. data/spec/unit/{logger → mcollective/logger}/syslog_logger_spec.rb +0 -0
  140. data/spec/unit/{matcher → mcollective/matcher}/parser_spec.rb +0 -0
  141. data/spec/unit/{matcher → mcollective/matcher}/scanner_spec.rb +0 -0
  142. data/spec/unit/{matcher_spec.rb → mcollective/matcher_spec.rb} +0 -0
  143. data/spec/unit/{message_spec.rb → mcollective/message_spec.rb} +0 -0
  144. data/spec/unit/{monkey_patches_spec.rb → mcollective/monkey_patches_spec.rb} +0 -0
  145. data/spec/unit/{optionparser_spec.rb → mcollective/optionparser_spec.rb} +0 -0
  146. data/spec/unit/{plugins/mcollective → mcollective}/packagers/debpackage_packager_spec.rb +1 -1
  147. data/spec/unit/{plugins/mcollective → mcollective}/packagers/modulepackage_packager_spec.rb +1 -1
  148. data/spec/unit/{plugins/mcollective → mcollective}/packagers/ospackage_spec.rb +1 -1
  149. data/spec/unit/{plugins/mcollective → mcollective}/packagers/rpmpackage_packager_spec.rb +1 -1
  150. data/spec/unit/{pluginmanager_spec.rb → mcollective/pluginmanager_spec.rb} +0 -0
  151. data/spec/unit/{pluginpackager → mcollective/pluginpackager}/agent_definition_spec.rb +0 -0
  152. data/spec/unit/{pluginpackager → mcollective/pluginpackager}/standard_definition_spec.rb +0 -0
  153. data/spec/unit/{pluginpackager_spec.rb → mcollective/pluginpackager_spec.rb} +0 -0
  154. data/spec/unit/{registration → mcollective/registration}/base_spec.rb +0 -0
  155. data/spec/unit/{rpc → mcollective/rpc}/actionrunner_spec.rb +0 -0
  156. data/spec/unit/{rpc → mcollective/rpc}/agent_spec.rb +0 -0
  157. data/spec/unit/{rpc → mcollective/rpc}/client_spec.rb +0 -0
  158. data/spec/unit/{rpc → mcollective/rpc}/helpers_spec.rb +0 -0
  159. data/spec/unit/{rpc → mcollective/rpc}/reply_spec.rb +0 -0
  160. data/spec/unit/{rpc → mcollective/rpc}/request_spec.rb +0 -0
  161. data/spec/unit/{rpc → mcollective/rpc}/result_spec.rb +0 -0
  162. data/spec/unit/{rpc → mcollective/rpc}/stats_spec.rb +0 -0
  163. data/spec/unit/{rpc_spec.rb → mcollective/rpc_spec.rb} +0 -0
  164. data/spec/unit/{runner_spec.rb → mcollective/runner_spec.rb} +0 -0
  165. data/spec/unit/{runnerstats_spec.rb → mcollective/runnerstats_spec.rb} +0 -0
  166. data/spec/unit/{plugins/mcollective → mcollective}/security/aes_security_spec.rb +1 -1
  167. data/spec/unit/{security → mcollective/security}/base_spec.rb +0 -0
  168. data/spec/unit/{plugins/mcollective → mcollective}/security/psk_spec.rb +1 -1
  169. data/spec/unit/{shell_spec.rb → mcollective/shell_spec.rb} +0 -0
  170. data/spec/unit/{ssl_spec.rb → mcollective/ssl_spec.rb} +12 -12
  171. data/spec/unit/{string_spec.rb → mcollective/string_spec.rb} +0 -0
  172. data/spec/unit/{symbol_spec.rb → mcollective/symbol_spec.rb} +0 -0
  173. data/spec/unit/{unix_daemon_spec.rb → mcollective/unix_daemon_spec.rb} +0 -0
  174. data/spec/unit/{util_spec.rb → mcollective/util_spec.rb} +28 -6
  175. data/spec/unit/{plugins/mcollective → mcollective}/validator/array_validator_spec.rb +1 -1
  176. data/spec/unit/{plugins/mcollective → mcollective}/validator/ipv4address_validator_spec.rb +1 -1
  177. data/spec/unit/{plugins/mcollective → mcollective}/validator/ipv6address_validator_spec.rb +1 -1
  178. data/spec/unit/{plugins/mcollective → mcollective}/validator/length_validator_spec.rb +1 -1
  179. data/spec/unit/{plugins/mcollective → mcollective}/validator/regex_validator_spec.rb +1 -1
  180. data/spec/unit/{plugins/mcollective → mcollective}/validator/shellsafe_validator_spec.rb +1 -1
  181. data/spec/unit/{plugins/mcollective → mcollective}/validator/typecheck_validator_spec.rb +1 -1
  182. data/spec/unit/{validator_spec.rb → mcollective/validator_spec.rb} +1 -1
  183. data/spec/unit/{vendor_spec.rb → mcollective/vendor_spec.rb} +2 -2
  184. data/spec/unit/{windows_daemon_spec.rb → mcollective/windows_daemon_spec.rb} +0 -0
  185. metadata +361 -284
  186. 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
@@ -21,6 +21,6 @@ module MCollective
21
21
  #
22
22
  # Filtering can be extended by providing a new validate_filter? method.
23
23
  module Security
24
- autoload :Base, "mcollective/security/base"
24
+ require "mcollective/security/base"
25
25
  end
26
26
  end
@@ -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
- # expand_path is pretty lame, it relies on HOME environment
159
- # which isnt't always there so just handling all exceptions
160
- # here as cant find reverting to default
158
+ # the set of acceptable config files
159
+ config_paths = []
160
+
161
+ # user dotfile
161
162
  begin
162
- config = File.expand_path("~/.mcollective")
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
- unless File.readable?(config) && File.file?(config)
165
- if self.windows?
166
- config = File.join(self.windows_prefix, "etc", "client.cfg")
167
- else
168
- config = "/etc/mcollective/client.cfg"
169
- end
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
- return config
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,7 @@
1
+ metadata :name => "Array",
2
+ :description => "Validates that a value is included in a list",
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,9 @@
1
+ module MCollective
2
+ module Validator
3
+ class ArrayValidator
4
+ def self.validate(validator, array)
5
+ raise ValidatorError, "value should be one of %s" % [ array.join(", ") ] unless array.include?(validator)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ metadata :name => "IPv4 Address",
2
+ :description => "Validates that a value is an ipv4 address",
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,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,7 @@
1
+ metadata :name => "IPv6 Address",
2
+ :description => "Validates that a value is an ipv6 address",
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,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,11 @@
1
+ module MCollective
2
+ module Validator
3
+ class LengthValidator
4
+ def self.validate(validator, length)
5
+ if (validator.size > length) && (length > 0)
6
+ raise ValidatorError, "Input string is longer than #{length} character(s)"
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -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,9 @@
1
+ module MCollective
2
+ module Validator
3
+ class RegexValidator
4
+ def self.validate(validator, regex)
5
+ raise ValidatorError, "value should match #{regex}" unless validator.match(regex)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ metadata :name => "Shellsafe",
2
+ :description => "Validates that a string is shellsafe",
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,7 @@
1
+ metadata :name => "Typecheck",
2
+ :description => "Validates that a value is of a certain type",
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,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