choria-mcorpc-support 2.21.1 → 2.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/mcollective.rb +1 -2
- data/lib/mcollective/agent/bolt_tasks.ddl +253 -0
- data/lib/mcollective/agent/bolt_tasks.json +365 -0
- data/lib/mcollective/agent/bolt_tasks.rb +178 -0
- data/lib/mcollective/agent/choria_util.ddl +152 -0
- data/lib/mcollective/agent/choria_util.json +244 -0
- data/lib/mcollective/agent/rpcutil.ddl +8 -4
- data/lib/mcollective/agent/rpcutil.json +333 -0
- data/lib/mcollective/agent/scout.ddl +169 -0
- data/lib/mcollective/agent/scout.json +224 -0
- data/lib/mcollective/agents.rb +7 -6
- data/lib/mcollective/aggregate.rb +4 -4
- data/lib/mcollective/aggregate/average.rb +2 -2
- data/lib/mcollective/aggregate/base.rb +2 -2
- data/lib/mcollective/aggregate/result.rb +3 -3
- data/lib/mcollective/aggregate/result/collection_result.rb +2 -2
- data/lib/mcollective/aggregate/result/numeric_result.rb +2 -2
- data/lib/mcollective/aggregate/sum.rb +2 -2
- data/lib/mcollective/aggregate/summary.rb +3 -4
- data/lib/mcollective/application.rb +57 -21
- data/lib/mcollective/application/choria.rb +189 -0
- data/lib/mcollective/application/completion.rb +6 -6
- data/lib/mcollective/application/facts.rb +11 -11
- data/lib/mcollective/application/federation.rb +237 -0
- data/lib/mcollective/application/find.rb +4 -4
- data/lib/mcollective/application/help.rb +3 -3
- data/lib/mcollective/application/inventory.rb +3 -341
- data/lib/mcollective/application/ping.rb +5 -51
- data/lib/mcollective/application/playbook.rb +207 -0
- data/lib/mcollective/application/plugin.rb +106 -106
- data/lib/mcollective/application/rpc.rb +3 -108
- data/lib/mcollective/application/tasks.rb +425 -0
- data/lib/mcollective/applications.rb +11 -10
- data/lib/mcollective/audit/choria.rb +33 -0
- data/lib/mcollective/cache.rb +2 -4
- data/lib/mcollective/client.rb +11 -10
- data/lib/mcollective/config.rb +21 -34
- data/lib/mcollective/connector/base.rb +2 -1
- data/lib/mcollective/connector/nats.ddl +9 -0
- data/lib/mcollective/connector/nats.rb +450 -0
- data/lib/mcollective/data.rb +8 -3
- data/lib/mcollective/data/agent_data.rb +1 -1
- data/lib/mcollective/data/base.rb +6 -5
- data/lib/mcollective/data/bolt_task_data.ddl +90 -0
- data/lib/mcollective/data/bolt_task_data.rb +32 -0
- data/lib/mcollective/data/collective_data.rb +1 -1
- data/lib/mcollective/data/fact_data.rb +6 -6
- data/lib/mcollective/data/fstat_data.rb +2 -4
- data/lib/mcollective/data/result.rb +7 -2
- data/lib/mcollective/ddl/agentddl.rb +5 -17
- data/lib/mcollective/ddl/base.rb +10 -13
- data/lib/mcollective/discovery.rb +24 -39
- data/lib/mcollective/discovery/choria.ddl +11 -0
- data/lib/mcollective/discovery/choria.rb +223 -0
- data/lib/mcollective/discovery/flatfile.rb +7 -8
- data/lib/mcollective/discovery/mc.rb +2 -2
- data/lib/mcollective/discovery/stdin.rb +17 -18
- data/lib/mcollective/exceptions.rb +13 -0
- data/lib/mcollective/facts/base.rb +9 -9
- data/lib/mcollective/facts/yaml_facts.rb +12 -12
- data/lib/mcollective/generators.rb +3 -3
- data/lib/mcollective/generators/agent_generator.rb +3 -4
- data/lib/mcollective/generators/base.rb +14 -15
- data/lib/mcollective/generators/data_generator.rb +5 -6
- data/lib/mcollective/log.rb +2 -2
- data/lib/mcollective/logger/base.rb +3 -2
- data/lib/mcollective/logger/console_logger.rb +10 -10
- data/lib/mcollective/logger/file_logger.rb +7 -7
- data/lib/mcollective/logger/syslog_logger.rb +11 -15
- data/lib/mcollective/message.rb +8 -39
- data/lib/mcollective/monkey_patches.rb +2 -4
- data/lib/mcollective/optionparser.rb +2 -1
- data/lib/mcollective/pluginmanager.rb +3 -5
- data/lib/mcollective/pluginpackager.rb +1 -3
- data/lib/mcollective/pluginpackager/agent_definition.rb +3 -8
- data/lib/mcollective/pluginpackager/forge_packager.rb +7 -9
- data/lib/mcollective/pluginpackager/standard_definition.rb +1 -2
- data/lib/mcollective/registration/base.rb +18 -16
- data/lib/mcollective/rpc.rb +2 -4
- data/lib/mcollective/rpc/actionrunner.rb +16 -18
- data/lib/mcollective/rpc/agent.rb +26 -43
- data/lib/mcollective/rpc/audit.rb +1 -0
- data/lib/mcollective/rpc/client.rb +67 -85
- data/lib/mcollective/rpc/helpers.rb +55 -62
- data/lib/mcollective/rpc/progress.rb +2 -2
- data/lib/mcollective/rpc/reply.rb +17 -19
- data/lib/mcollective/rpc/request.rb +7 -5
- data/lib/mcollective/rpc/result.rb +6 -8
- data/lib/mcollective/rpc/stats.rb +49 -58
- data/lib/mcollective/security/base.rb +13 -56
- data/lib/mcollective/security/choria.rb +765 -0
- data/lib/mcollective/shell.rb +9 -4
- data/lib/mcollective/signer/base.rb +28 -0
- data/lib/mcollective/signer/choria.rb +185 -0
- data/lib/mcollective/ssl.rb +8 -6
- data/lib/mcollective/util.rb +58 -55
- data/lib/mcollective/util/bolt_support.rb +176 -0
- data/lib/mcollective/util/bolt_support/plan_runner.rb +167 -0
- data/lib/mcollective/util/bolt_support/task_result.rb +94 -0
- data/lib/mcollective/util/bolt_support/task_results.rb +128 -0
- data/lib/mcollective/util/choria.rb +946 -0
- data/lib/mcollective/util/indifferent_hash.rb +12 -0
- data/lib/mcollective/util/natswrapper.rb +242 -0
- data/lib/mcollective/util/playbook.rb +435 -0
- data/lib/mcollective/util/playbook/data_stores.rb +201 -0
- data/lib/mcollective/util/playbook/data_stores/base.rb +99 -0
- data/lib/mcollective/util/playbook/data_stores/consul_data_store.rb +88 -0
- data/lib/mcollective/util/playbook/data_stores/environment_data_store.rb +33 -0
- data/lib/mcollective/util/playbook/data_stores/etcd_data_store.rb +42 -0
- data/lib/mcollective/util/playbook/data_stores/file_data_store.rb +106 -0
- data/lib/mcollective/util/playbook/data_stores/shell_data_store.rb +103 -0
- data/lib/mcollective/util/playbook/inputs.rb +265 -0
- data/lib/mcollective/util/playbook/nodes.rb +207 -0
- data/lib/mcollective/util/playbook/nodes/mcollective_nodes.rb +86 -0
- data/lib/mcollective/util/playbook/nodes/pql_nodes.rb +40 -0
- data/lib/mcollective/util/playbook/nodes/shell_nodes.rb +55 -0
- data/lib/mcollective/util/playbook/nodes/terraform_nodes.rb +65 -0
- data/lib/mcollective/util/playbook/nodes/yaml_nodes.rb +47 -0
- data/lib/mcollective/util/playbook/playbook_logger.rb +47 -0
- data/lib/mcollective/util/playbook/puppet_logger.rb +51 -0
- data/lib/mcollective/util/playbook/report.rb +152 -0
- data/lib/mcollective/util/playbook/task_result.rb +55 -0
- data/lib/mcollective/util/playbook/tasks.rb +196 -0
- data/lib/mcollective/util/playbook/tasks/base.rb +45 -0
- data/lib/mcollective/util/playbook/tasks/graphite_event_task.rb +64 -0
- data/lib/mcollective/util/playbook/tasks/mcollective_task.rb +356 -0
- data/lib/mcollective/util/playbook/tasks/shell_task.rb +93 -0
- data/lib/mcollective/util/playbook/tasks/slack_task.rb +105 -0
- data/lib/mcollective/util/playbook/tasks/webhook_task.rb +136 -0
- data/lib/mcollective/util/playbook/template_util.rb +98 -0
- data/lib/mcollective/util/playbook/uses.rb +169 -0
- data/lib/mcollective/util/tasks_support.rb +751 -0
- data/lib/mcollective/util/tasks_support/cli.rb +260 -0
- data/lib/mcollective/util/tasks_support/default_formatter.rb +138 -0
- data/lib/mcollective/util/tasks_support/json_formatter.rb +108 -0
- data/lib/mcollective/validator.rb +6 -1
- data/lib/mcollective/validator/bolt_task_name_validator.ddl +7 -0
- data/lib/mcollective/validator/bolt_task_name_validator.rb +11 -0
- data/lib/mcollective/validator/length_validator.rb +1 -3
- metadata +65 -6
- data/lib/mcollective/application/describe_filter.rb +0 -87
- data/lib/mcollective/matcher.rb +0 -220
- data/lib/mcollective/matcher/parser.rb +0 -128
- data/lib/mcollective/matcher/scanner.rb +0 -241
data/lib/mcollective/shell.rb
CHANGED
|
@@ -38,18 +38,22 @@ module MCollective
|
|
|
38
38
|
case opt.to_s
|
|
39
39
|
when "stdout"
|
|
40
40
|
raise "stdout should support <<" unless val.respond_to?("<<")
|
|
41
|
+
|
|
41
42
|
@stdout = val
|
|
42
43
|
|
|
43
44
|
when "stderr"
|
|
44
45
|
raise "stderr should support <<" unless val.respond_to?("<<")
|
|
46
|
+
|
|
45
47
|
@stderr = val
|
|
46
48
|
|
|
47
49
|
when "stdin"
|
|
48
50
|
raise "stdin should be a String" unless val.is_a?(String)
|
|
51
|
+
|
|
49
52
|
@stdin = val
|
|
50
53
|
|
|
51
54
|
when "cwd"
|
|
52
55
|
raise "Directory #{val} does not exist" unless File.directory?(val)
|
|
56
|
+
|
|
53
57
|
@cwd = val
|
|
54
58
|
|
|
55
59
|
when "environment"
|
|
@@ -62,6 +66,7 @@ module MCollective
|
|
|
62
66
|
|
|
63
67
|
when "timeout"
|
|
64
68
|
raise "timeout should be a positive integer or the symbol :on_thread_exit symbol" unless val.eql?(:on_thread_exit) || (val.is_a?(Integer) && val > 0)
|
|
69
|
+
|
|
65
70
|
@timeout = val
|
|
66
71
|
end
|
|
67
72
|
end
|
|
@@ -69,10 +74,10 @@ module MCollective
|
|
|
69
74
|
|
|
70
75
|
# Actually does the systemu call passing in the correct environment, stdout and stderr
|
|
71
76
|
def runcommand
|
|
72
|
-
opts = {"env"
|
|
77
|
+
opts = {"env" => @environment,
|
|
73
78
|
"stdout" => @stdout,
|
|
74
79
|
"stderr" => @stderr,
|
|
75
|
-
"cwd"
|
|
80
|
+
"cwd" => @cwd}
|
|
76
81
|
|
|
77
82
|
opts["stdin"] = @stdin if @stdin
|
|
78
83
|
|
|
@@ -108,8 +113,8 @@ module MCollective
|
|
|
108
113
|
# only wait if the parent thread is dead
|
|
109
114
|
Process.waitpid(cid) unless thread.alive?
|
|
110
115
|
end
|
|
111
|
-
rescue SystemExit # rubocop:disable Lint/
|
|
112
|
-
rescue Errno::ESRCH # rubocop:disable Lint/
|
|
116
|
+
rescue SystemExit # rubocop:disable Lint/SuppressedException
|
|
117
|
+
rescue Errno::ESRCH # rubocop:disable Lint/SuppressedException
|
|
113
118
|
rescue Errno::ECHILD
|
|
114
119
|
Log.warn("Could not reap process '#{cid}'.")
|
|
115
120
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module MCollective
|
|
2
|
+
module Signer
|
|
3
|
+
class Base
|
|
4
|
+
# Register plugins that inherits base
|
|
5
|
+
def self.inherited(klass)
|
|
6
|
+
PluginManager << {:type => "choria_signer_plugin", :class => klass.to_s}
|
|
7
|
+
super
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def initialize
|
|
11
|
+
@config = Config.instance
|
|
12
|
+
@log = Log
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Signs a secure request
|
|
16
|
+
#
|
|
17
|
+
# Generally for local mode this would just use the users own certificate but if you have a
|
|
18
|
+
# remote signer this might use a token to speak to a remote API, by default Choria supports
|
|
19
|
+
# a standard web service for remote signatures
|
|
20
|
+
#
|
|
21
|
+
# @param secure_request [Hash] a choria:secure:request:1 hash
|
|
22
|
+
# @raise [StandardError] when signing fails
|
|
23
|
+
def sign_secure_request!(secure_request)
|
|
24
|
+
raise(NoMethodError, "undefined method `sign_secure_request!' for %s" % inspect)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
require_relative "base"
|
|
2
|
+
|
|
3
|
+
module MCollective
|
|
4
|
+
module Signer
|
|
5
|
+
# This is a Secure Request Signer that allows either local signing of requests using the users
|
|
6
|
+
# own certificate or delegation based signing via a webservice
|
|
7
|
+
#
|
|
8
|
+
# This allows one to integrate the Choria CLI into a centralised authentication, authorization and
|
|
9
|
+
# auditing system
|
|
10
|
+
#
|
|
11
|
+
# Available settings:
|
|
12
|
+
#
|
|
13
|
+
# choria.security.request_signer.plugin - the plugin to use, `choria` for this one - the default.
|
|
14
|
+
# choria.security.request_signer.token_file - a file holding a token like a JWT or similar
|
|
15
|
+
# choria.security.request_signer.token_environment - a ENV key holding a token like a JWT or similar
|
|
16
|
+
# choria.security.request_signer.url - a endpoint that implements the v1 signer protocol
|
|
17
|
+
#
|
|
18
|
+
# The webservice has to support the specification found at https://choria.io/schemas/choria/signer/v1/service.json
|
|
19
|
+
class Choria < Base
|
|
20
|
+
# Retrieves the token from either a local file or the users environment
|
|
21
|
+
#
|
|
22
|
+
# @return [String, nil]
|
|
23
|
+
def token
|
|
24
|
+
file = @config.pluginconf["choria.security.request_signer.token_file"]
|
|
25
|
+
env = @config.pluginconf["choria.security.request_signer.token_environment"]
|
|
26
|
+
|
|
27
|
+
if file
|
|
28
|
+
file = File.expand_path(file)
|
|
29
|
+
|
|
30
|
+
raise("No token found in %s, please authenticate using your configured authentication service" % file) unless File.exist?(file)
|
|
31
|
+
|
|
32
|
+
return File.read(file).chomp
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
raise("could not find token in environment variable %s" % env) unless ENV[env]
|
|
36
|
+
|
|
37
|
+
ENV[env].chomp
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Signs the secure request
|
|
41
|
+
#
|
|
42
|
+
# Signing supports either local mode using local certificates or delegating to a remote
|
|
43
|
+
# signer that is written in conformance with the signer specification version 1
|
|
44
|
+
#
|
|
45
|
+
# @param secure_request [Hash] a v1 secure request
|
|
46
|
+
def sign_secure_request!(secure_request)
|
|
47
|
+
return if $choria_unsafe_disable_protocol_security # rubocop:disable Style/GlobalVars
|
|
48
|
+
|
|
49
|
+
if remote_signer?
|
|
50
|
+
remote_sign!(secure_request)
|
|
51
|
+
else
|
|
52
|
+
local_sign!(secure_request)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Determines the callerid for this client
|
|
57
|
+
#
|
|
58
|
+
# When a remote signer is enabled the caller is extracted from the JWT
|
|
59
|
+
# otherwise a choria=user style ID is generated
|
|
60
|
+
#
|
|
61
|
+
# @return [String]
|
|
62
|
+
# @raise [Exception] when the JWT is invalid
|
|
63
|
+
def callerid
|
|
64
|
+
if remote_signer?
|
|
65
|
+
parts = token.split(".")
|
|
66
|
+
|
|
67
|
+
raise("Invalid JWT token") unless parts.length == 3
|
|
68
|
+
|
|
69
|
+
claims = JSON.parse(Base64.decode64(parts[1]))
|
|
70
|
+
|
|
71
|
+
raise("Invalid JWT token") unless claims.include?("callerid")
|
|
72
|
+
raise("Invalid JWT token") unless claims["callerid"].is_a?(String)
|
|
73
|
+
raise("Invalid JWT token") if claims["callerid"].empty?
|
|
74
|
+
|
|
75
|
+
claims["callerid"]
|
|
76
|
+
else
|
|
77
|
+
"choria=%s" % choria.certname
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# The body that would be submitted to the remote service
|
|
82
|
+
#
|
|
83
|
+
# @param secure_request [Hash] a v1 secure request
|
|
84
|
+
# @return [Hash]
|
|
85
|
+
def sign_request_body(secure_request)
|
|
86
|
+
{
|
|
87
|
+
"token" => token,
|
|
88
|
+
"request" => Base64.encode64(secure_request["message"])
|
|
89
|
+
}
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Performs a remote sign operation against a configured web service
|
|
93
|
+
#
|
|
94
|
+
# @param secure_request [Hash] a v1 secure request
|
|
95
|
+
# @raise [StandardError] on signing error
|
|
96
|
+
def remote_sign!(secure_request)
|
|
97
|
+
Log.info("Signing secure request using remote signer %s" % remote_signer_url)
|
|
98
|
+
|
|
99
|
+
uri = remote_signer_url
|
|
100
|
+
post = choria.http_post(uri.request_uri)
|
|
101
|
+
post.body = sign_request_body(secure_request).to_json
|
|
102
|
+
post["Content-type"] = "application/json"
|
|
103
|
+
|
|
104
|
+
http = choria.https(:target => uri.host, :port => uri.port)
|
|
105
|
+
http.use_ssl = false if uri.scheme == "http"
|
|
106
|
+
|
|
107
|
+
# While this might appear alarming it's expected that the clients
|
|
108
|
+
# in this situation will not have any Choria CA issued certificates
|
|
109
|
+
# and so wish to use a remote signer - the certificate management woes
|
|
110
|
+
# being one of the main reasons for centralised AAA.
|
|
111
|
+
#
|
|
112
|
+
# So there is no realistic way to verify these requests especially in the
|
|
113
|
+
# event that these signers run on private IPs and such as would be typical
|
|
114
|
+
# so while we do this big No No of disabling verify here it really is the
|
|
115
|
+
# only thing that make sense.
|
|
116
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if http.use_ssl?
|
|
117
|
+
|
|
118
|
+
resp = http.request(post)
|
|
119
|
+
|
|
120
|
+
signature = {}
|
|
121
|
+
|
|
122
|
+
if resp.code == "200"
|
|
123
|
+
signature = JSON.parse(resp.body)
|
|
124
|
+
else
|
|
125
|
+
raise("Could not get remote signature: %s: %s" % [resp.code, resp.body])
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
raise("Could not get remote signature: %s" % signature["error"]) if signature["error"]
|
|
129
|
+
|
|
130
|
+
signed_request = JSON.parse(Base64.decode64(signature["secure_request"]))
|
|
131
|
+
signed_request.each do |k, v|
|
|
132
|
+
secure_request[k] = v
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Signs using local certificates
|
|
137
|
+
#
|
|
138
|
+
# @param secure_request [Hash] a v1 secure request
|
|
139
|
+
# @raise [StandardError] on signing error
|
|
140
|
+
def local_sign!(secure_request)
|
|
141
|
+
Log.info("Signing secure request using local credentials")
|
|
142
|
+
|
|
143
|
+
secure_request["signature"] = sign(secure_request["message"])
|
|
144
|
+
secure_request["pubcert"] = File.read(client_public_cert).chomp
|
|
145
|
+
|
|
146
|
+
nil
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# (see Security::Choria#sign)
|
|
150
|
+
def sign(string, id=nil)
|
|
151
|
+
security.sign(string, id)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# (see Security::Choria#client_public_cert)
|
|
155
|
+
def client_public_cert
|
|
156
|
+
security.client_public_cert
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Determines if a remote signer is configured
|
|
160
|
+
#
|
|
161
|
+
# @return [Boolean]
|
|
162
|
+
def remote_signer?
|
|
163
|
+
!!(remote_signer_url == "" || remote_signer_url)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Determines the remote url to submit standard signing requests to
|
|
167
|
+
#
|
|
168
|
+
# @return [URI, Nil]
|
|
169
|
+
def remote_signer_url
|
|
170
|
+
return nil unless @config.pluginconf["choria.security.request_signer.url"]
|
|
171
|
+
return nil if @config.pluginconf["choria.security.request_signer.url"] == ""
|
|
172
|
+
|
|
173
|
+
URI.parse(@config.pluginconf["choria.security.request_signer.url"])
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def security
|
|
177
|
+
@security ||= PluginManager["security_plugin"]
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def choria
|
|
181
|
+
@choria ||= security.choria
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
data/lib/mcollective/ssl.rb
CHANGED
|
@@ -166,7 +166,7 @@ module MCollective
|
|
|
166
166
|
|
|
167
167
|
# Signs a string using the private key
|
|
168
168
|
def sign(string, base64=false)
|
|
169
|
-
sig = @private_key.sign(OpenSSL::Digest
|
|
169
|
+
sig = @private_key.sign(OpenSSL::Digest.new("SHA1"), string)
|
|
170
170
|
|
|
171
171
|
base64 ? base64_encode(sig) : sig
|
|
172
172
|
end
|
|
@@ -175,7 +175,7 @@ module MCollective
|
|
|
175
175
|
def verify_signature(signature, string, base64=false)
|
|
176
176
|
signature = base64_decode(signature) if base64
|
|
177
177
|
|
|
178
|
-
@public_key.verify(OpenSSL::Digest
|
|
178
|
+
@public_key.verify(OpenSSL::Digest.new("SHA1"), signature, string)
|
|
179
179
|
end
|
|
180
180
|
|
|
181
181
|
# base 64 encode a string
|
|
@@ -196,6 +196,7 @@ module MCollective
|
|
|
196
196
|
# The Base 64 character set is A-Z a-z 0-9 + / =
|
|
197
197
|
# Also allow for whitespace, but raise if we get anything else
|
|
198
198
|
raise(ArgumentError, "invalid base64") if string !~ /^[A-Za-z0-9+\/=\s]+$/
|
|
199
|
+
|
|
199
200
|
Base64.decode64(string)
|
|
200
201
|
end
|
|
201
202
|
|
|
@@ -248,7 +249,8 @@ module MCollective
|
|
|
248
249
|
raise "Could not find key #{key}" unless File.exist?(key)
|
|
249
250
|
raise "#{type} key file '#{key}' is empty" if File.zero?(key)
|
|
250
251
|
|
|
251
|
-
|
|
252
|
+
case type
|
|
253
|
+
when :public
|
|
252
254
|
begin
|
|
253
255
|
key = OpenSSL::PKey::RSA.new(File.read(key))
|
|
254
256
|
rescue OpenSSL::PKey::RSAError
|
|
@@ -271,9 +273,9 @@ module MCollective
|
|
|
271
273
|
# See http://bugs.ruby-lang.org/issues/4550
|
|
272
274
|
OpenSSL.errors if Util.ruby_version =~ /^1.8/
|
|
273
275
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
276
|
+
key
|
|
277
|
+
when :private
|
|
278
|
+
OpenSSL::PKey::RSA.new(File.read(key), passphrase)
|
|
277
279
|
else
|
|
278
280
|
raise "Can only load :public or :private keys"
|
|
279
281
|
end
|
data/lib/mcollective/util.rb
CHANGED
|
@@ -10,12 +10,12 @@ module MCollective
|
|
|
10
10
|
|
|
11
11
|
if agent.is_a?(Regexp)
|
|
12
12
|
if !Agents.agentlist.grep(agent).empty?
|
|
13
|
-
|
|
13
|
+
true
|
|
14
14
|
else
|
|
15
|
-
|
|
15
|
+
false
|
|
16
16
|
end
|
|
17
17
|
else
|
|
18
|
-
|
|
18
|
+
Agents.agentlist.include?(agent)
|
|
19
19
|
end
|
|
20
20
|
end
|
|
21
21
|
|
|
@@ -41,9 +41,10 @@ module MCollective
|
|
|
41
41
|
|
|
42
42
|
begin
|
|
43
43
|
File.readlines(cfile).each do |k|
|
|
44
|
-
|
|
44
|
+
case klass
|
|
45
|
+
when Regexp
|
|
45
46
|
return true if k.chomp.match(klass)
|
|
46
|
-
|
|
47
|
+
when k.chomp
|
|
47
48
|
return true
|
|
48
49
|
end
|
|
49
50
|
end
|
|
@@ -74,11 +75,11 @@ module MCollective
|
|
|
74
75
|
fact = fact.clone
|
|
75
76
|
case fact
|
|
76
77
|
when Array
|
|
77
|
-
|
|
78
|
+
fact.any? { |element| test_fact_value(element, value, operator)}
|
|
78
79
|
when Hash
|
|
79
|
-
|
|
80
|
+
fact.keys.any? { |element| test_fact_value(element, value, operator)}
|
|
80
81
|
else
|
|
81
|
-
|
|
82
|
+
test_fact_value(fact, value, operator)
|
|
82
83
|
end
|
|
83
84
|
end
|
|
84
85
|
|
|
@@ -104,7 +105,7 @@ module MCollective
|
|
|
104
105
|
value = Float(value) # rubocop:disable Lint/UselessAssignment
|
|
105
106
|
end
|
|
106
107
|
|
|
107
|
-
return true if eval("fact #{operator} value") # rubocop:disable Security/Eval
|
|
108
|
+
return true if eval("fact #{operator} value") # rubocop:disable Security/Eval, Style/EvalWithLocation
|
|
108
109
|
end
|
|
109
110
|
|
|
110
111
|
false
|
|
@@ -118,9 +119,10 @@ module MCollective
|
|
|
118
119
|
def self.has_identity?(identity)
|
|
119
120
|
identity = Regexp.new(identity.gsub("\/", "")) if identity.start_with?("/")
|
|
120
121
|
|
|
121
|
-
|
|
122
|
+
case identity
|
|
123
|
+
when Regexp
|
|
122
124
|
return Config.instance.identity.match(identity)
|
|
123
|
-
|
|
125
|
+
when Config.instance.identity
|
|
124
126
|
return true
|
|
125
127
|
end
|
|
126
128
|
|
|
@@ -135,9 +137,9 @@ module MCollective
|
|
|
135
137
|
# Creates an empty filter
|
|
136
138
|
def self.empty_filter
|
|
137
139
|
{
|
|
138
|
-
"fact"
|
|
140
|
+
"fact" => [],
|
|
139
141
|
"cf_class" => [],
|
|
140
|
-
"agent"
|
|
142
|
+
"agent" => [],
|
|
141
143
|
"identity" => [],
|
|
142
144
|
"compound" => []
|
|
143
145
|
}
|
|
@@ -146,7 +148,7 @@ module MCollective
|
|
|
146
148
|
# Returns the PuppetLabs mcollective path for windows
|
|
147
149
|
def self.windows_prefix
|
|
148
150
|
require "win32/dir"
|
|
149
|
-
File.join(Dir::COMMON_APPDATA, "PuppetLabs", "
|
|
151
|
+
File.join(Dir::COMMON_APPDATA, "PuppetLabs", "choria")
|
|
150
152
|
end
|
|
151
153
|
|
|
152
154
|
def self.choria_windows_prefix
|
|
@@ -161,7 +163,7 @@ module MCollective
|
|
|
161
163
|
# File.expand_path will raise if HOME isn't set, catch it
|
|
162
164
|
user_path = File.expand_path("~/.mcollective")
|
|
163
165
|
config_paths << user_path
|
|
164
|
-
rescue Exception # rubocop:disable Lint/RescueException, Lint/
|
|
166
|
+
rescue Exception # rubocop:disable Lint/RescueException, Lint/SuppressedException
|
|
165
167
|
end
|
|
166
168
|
|
|
167
169
|
if windows?
|
|
@@ -169,6 +171,7 @@ module MCollective
|
|
|
169
171
|
else
|
|
170
172
|
config_paths << "/etc/puppetlabs/mcollective/client.cfg"
|
|
171
173
|
config_paths << "/etc/mcollective/client.cfg"
|
|
174
|
+
config_paths << "/usr/local/etc/mcollective/client.cfg"
|
|
172
175
|
end
|
|
173
176
|
|
|
174
177
|
config_paths
|
|
@@ -181,29 +184,32 @@ module MCollective
|
|
|
181
184
|
# File.expand_path will raise if HOME isn't set, catch it
|
|
182
185
|
user_path = File.expand_path("~/.choriarc")
|
|
183
186
|
config_paths << user_path
|
|
184
|
-
rescue Exception # rubocop:disable Lint/RescueException, Lint/
|
|
187
|
+
rescue Exception # rubocop:disable Lint/RescueException, Lint/SuppressedException
|
|
185
188
|
end
|
|
186
189
|
|
|
187
190
|
if windows?
|
|
188
191
|
config_paths << File.join(choria_windows_prefix, "etc", "client.conf")
|
|
189
192
|
else
|
|
190
193
|
config_paths << "/etc/choria/client.conf"
|
|
194
|
+
config_paths << "/usr/local/etc/choria/client.conf"
|
|
191
195
|
end
|
|
192
196
|
|
|
193
197
|
config_paths
|
|
194
198
|
end
|
|
195
199
|
|
|
196
|
-
# Picks the default user config file,
|
|
200
|
+
# Picks the default user config file, priorities are first Choria ones then old MCollective ones
|
|
197
201
|
#
|
|
198
202
|
# In roughly this order, first to exist is used:
|
|
199
203
|
#
|
|
200
204
|
# - ~/.choriarc
|
|
201
205
|
# - APPData/ChoriaIO/choria/etc/client.conf on windows
|
|
202
|
-
# - /etc/choria/client.conf
|
|
206
|
+
# - /etc/choria/client.conf then
|
|
207
|
+
# - /usr/local/etc/choria/client.conf on unix
|
|
203
208
|
# - ~/.mcollective
|
|
204
209
|
# - APPData/PuppetLabs/mcollective/etc/client.cfg on windows
|
|
205
210
|
# - /etc/puppetlabs/mcollective/client.cfg
|
|
206
211
|
# - /etc/mcollective/client.cfg
|
|
212
|
+
# - /usr/local/etc/mcollective/client.cfg
|
|
207
213
|
def self.config_file_for_user
|
|
208
214
|
config_paths = choria_config_paths_for_user + mcollective_config_paths_for_user
|
|
209
215
|
found = config_paths.find_index { |file| File.readable?(file) } || 0
|
|
@@ -213,14 +219,14 @@ module MCollective
|
|
|
213
219
|
# Creates a standard options hash
|
|
214
220
|
def self.default_options
|
|
215
221
|
{
|
|
216
|
-
:verbose
|
|
217
|
-
:disctimeout
|
|
218
|
-
:timeout
|
|
219
|
-
:config
|
|
220
|
-
:collective
|
|
221
|
-
:discovery_method
|
|
222
|
+
:verbose => false,
|
|
223
|
+
:disctimeout => nil,
|
|
224
|
+
:timeout => 5,
|
|
225
|
+
:config => config_file_for_user,
|
|
226
|
+
:collective => nil,
|
|
227
|
+
:discovery_method => nil,
|
|
222
228
|
:discovery_options => Config.instance.default_discovery_options,
|
|
223
|
-
:filter
|
|
229
|
+
:filter => empty_filter
|
|
224
230
|
}
|
|
225
231
|
end
|
|
226
232
|
|
|
@@ -269,15 +275,16 @@ module MCollective
|
|
|
269
275
|
|
|
270
276
|
# Parse a fact filter string like foo=bar into the tuple hash thats needed
|
|
271
277
|
def self.parse_fact_string(fact)
|
|
272
|
-
|
|
278
|
+
case fact
|
|
279
|
+
when /^([^ ]+?) *=> *(.+)/
|
|
273
280
|
{:fact => $1, :value => $2, :operator => ">="}
|
|
274
|
-
|
|
281
|
+
when /^([^ ]+?) *=< *(.+)/
|
|
275
282
|
{:fact => $1, :value => $2, :operator => "<="}
|
|
276
|
-
|
|
283
|
+
when /^([^ ]+?) *(<=|>=|<|>|!=|==|=~) *(.+)/
|
|
277
284
|
{:fact => $1, :value => $3, :operator => $2}
|
|
278
|
-
|
|
285
|
+
when /^(.+?) *= *\/(.+)\/$/
|
|
279
286
|
{:fact => $1, :value => "/#{$2}/", :operator => "=~"}
|
|
280
|
-
|
|
287
|
+
when /^([^= ]+?) *= *(.+)/
|
|
281
288
|
{:fact => $1, :value => $2, :operator => "=="}
|
|
282
289
|
else
|
|
283
290
|
raise "Could not parse fact #{fact} it does not appear to be in a valid format"
|
|
@@ -322,9 +329,9 @@ module MCollective
|
|
|
322
329
|
}
|
|
323
330
|
|
|
324
331
|
if colorize
|
|
325
|
-
|
|
332
|
+
colors[code] || ""
|
|
326
333
|
else
|
|
327
|
-
|
|
334
|
+
""
|
|
328
335
|
end
|
|
329
336
|
end
|
|
330
337
|
|
|
@@ -375,9 +382,7 @@ module MCollective
|
|
|
375
382
|
text.each_with_index do |line, i|
|
|
376
383
|
whitespace = 0
|
|
377
384
|
|
|
378
|
-
while whitespace < line.length && line[whitespace].chr == " "
|
|
379
|
-
whitespace += 1
|
|
380
|
-
end
|
|
385
|
+
whitespace += 1 while whitespace < line.length && line[whitespace].chr == " "
|
|
381
386
|
|
|
382
387
|
# If the current line is empty, indent it so that a snippet
|
|
383
388
|
# from the previous line is aligned correctly.
|
|
@@ -431,21 +436,21 @@ module MCollective
|
|
|
431
436
|
#
|
|
432
437
|
# Returns [0, 0] if it can't figure it out or if you're
|
|
433
438
|
# not running on a tty
|
|
434
|
-
def self.terminal_dimensions(stdout
|
|
439
|
+
def self.terminal_dimensions(stdout=$stdout, environment=ENV)
|
|
435
440
|
return [0, 0] unless stdout.tty?
|
|
436
441
|
|
|
437
442
|
return [80, 40] if Util.windows?
|
|
438
443
|
|
|
439
444
|
if environment["COLUMNS"] && environment["LINES"]
|
|
440
|
-
|
|
445
|
+
[environment["COLUMNS"].to_i, environment["LINES"].to_i]
|
|
441
446
|
|
|
442
447
|
elsif environment["TERM"] && command_in_path?("tput")
|
|
443
|
-
|
|
448
|
+
[`tput cols`.to_i, `tput lines`.to_i]
|
|
444
449
|
|
|
445
450
|
elsif command_in_path?("stty")
|
|
446
|
-
|
|
451
|
+
`stty size`.scan(/\d+/).map(&:to_i)
|
|
447
452
|
else
|
|
448
|
-
|
|
453
|
+
[0, 0]
|
|
449
454
|
end
|
|
450
455
|
rescue
|
|
451
456
|
[0, 0]
|
|
@@ -486,6 +491,7 @@ module MCollective
|
|
|
486
491
|
elsif b == "." then return 1
|
|
487
492
|
elsif a =~ /^\d+$/ && b =~ /^\d+$/
|
|
488
493
|
return a.to_s.upcase <=> b.to_s.upcase if a =~ /^0/ || b =~ /^0/
|
|
494
|
+
|
|
489
495
|
return a.to_i <=> b.to_i
|
|
490
496
|
else
|
|
491
497
|
return a.upcase <=> b.upcase
|
|
@@ -512,10 +518,11 @@ module MCollective
|
|
|
512
518
|
# Any other value will return FalseClass
|
|
513
519
|
def self.str_to_bool(val)
|
|
514
520
|
clean_val = val.to_s.strip
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
521
|
+
case clean_val
|
|
522
|
+
when /^(1|yes|true|y|t)$/i
|
|
523
|
+
true
|
|
524
|
+
when /^(0|no|false|n|f)$/i
|
|
525
|
+
false
|
|
519
526
|
else
|
|
520
527
|
raise("Cannot convert string value '#{clean_val}' into a boolean.")
|
|
521
528
|
end
|
|
@@ -527,8 +534,7 @@ module MCollective
|
|
|
527
534
|
template_path = File.join(config_dir, template_file)
|
|
528
535
|
return template_path if File.exist?(template_path)
|
|
529
536
|
|
|
530
|
-
|
|
531
|
-
template_path
|
|
537
|
+
File.join("/etc/mcollective", template_file)
|
|
532
538
|
end
|
|
533
539
|
|
|
534
540
|
# subscribe to the direct addressing queue
|
|
@@ -559,6 +565,7 @@ module MCollective
|
|
|
559
565
|
|
|
560
566
|
while char = Win32API.new("crtdll", "_getch", [], "I").Call
|
|
561
567
|
break if [10, 13].include?(char) # return or newline
|
|
568
|
+
|
|
562
569
|
if [127, 8].include?(char) # backspace and delete
|
|
563
570
|
input.slice!(-1, 1) unless input.empty?
|
|
564
571
|
else
|
|
@@ -570,17 +577,13 @@ module MCollective
|
|
|
570
577
|
end
|
|
571
578
|
|
|
572
579
|
def self.get_hidden_input_on_unix # rubocop:disable Naming/AccessorMethodName
|
|
573
|
-
unless $stdin.tty?
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
unless system "stty -echo -icanon"
|
|
577
|
-
raise "Could not hide input using stty command."
|
|
578
|
-
end
|
|
580
|
+
raise "Could not hook to stdin to hide input. If using SSH, try using -t flag while connecting to server." unless $stdin.tty?
|
|
581
|
+
raise "Could not hide input using stty command." unless system "stty -echo -icanon"
|
|
582
|
+
|
|
579
583
|
input = $stdin.gets
|
|
580
584
|
ensure
|
|
581
|
-
unless system "stty echo icanon"
|
|
582
|
-
|
|
583
|
-
end
|
|
585
|
+
raise "Could not enable echoing of input. Try executing `stty echo icanon` to debug." unless system "stty echo icanon"
|
|
586
|
+
|
|
584
587
|
input
|
|
585
588
|
end
|
|
586
589
|
|