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,37 @@
|
|
1
|
+
# <%= @package_name %>
|
2
|
+
|
3
|
+
#### Table of Contents
|
4
|
+
|
5
|
+
1. [Overview](#overview)
|
6
|
+
2. [Module Description - What the module does and why it is useful](#module-description)
|
7
|
+
3. [Setup - The basics of getting started with <%= @package_name %>](#setup)
|
8
|
+
* [What the <%= @package_name %> module affects](#what-the-<%= @package_name %>-module-affects)
|
9
|
+
* [Setup requirements](#setup-requirements)
|
10
|
+
4. [Usage - Configuration options and additional functionality](#usage)
|
11
|
+
5. [Reference - An under-the-hood peek at what the module is doing and how](#reference)
|
12
|
+
|
13
|
+
|
14
|
+
## Overview
|
15
|
+
|
16
|
+
The <%= @package_name %> module is a module that wraps a source release of the
|
17
|
+
<%= @plugin.metadata[:name] %> mcollective plugin for use with the
|
18
|
+
[puppetlabs mcollective](http://forge.puppetlabs.com/puppetlabs/mcollective)
|
19
|
+
module.
|
20
|
+
|
21
|
+
## Module description
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
<% @plugin.packagedata.keys.map { |x| x.to_s }.sort.each do |klass| -%>
|
26
|
+
<%# Don't document common class -%>
|
27
|
+
<% if klass != "common" -%>
|
28
|
+
### class <%= @package_name %>::<%= klass %>
|
29
|
+
|
30
|
+
Installs the <%= klass %> component of the <%= @plugin.metadata[:name] %> plugin.
|
31
|
+
|
32
|
+
```puppet
|
33
|
+
include <%= @package_name%>::<%= klass %>
|
34
|
+
```
|
35
|
+
<% end %>
|
36
|
+
<% end %>
|
37
|
+
|
@@ -0,0 +1,9 @@
|
|
1
|
+
#
|
2
|
+
class <%= @package_name %>::<%= @klass %> {
|
3
|
+
<% if @plugin.packagedata[:common] && @klass != 'common' %>
|
4
|
+
include ::<%= @package_name %>::common
|
5
|
+
<% end %>
|
6
|
+
mcollective::plugin { '<%= @package_name %>/<%= @klass %>':
|
7
|
+
source => 'puppet:///modules/<%= @package_name %>/<%= @klass %>',
|
8
|
+
}
|
9
|
+
}
|
@@ -0,0 +1,63 @@
|
|
1
|
+
Name: <%= @package_name %>
|
2
|
+
Summary: <%= @plugin.metadata[:description] %>
|
3
|
+
Version: <%= @plugin.metadata[:version] %>
|
4
|
+
Release: <%= @plugin.revision %>%{?dist}
|
5
|
+
License: <%= @plugin.metadata[:license]%>
|
6
|
+
URL: <%= @plugin.metadata[:url]%>
|
7
|
+
Vendor: <%= @plugin.vendor%>
|
8
|
+
Packager: <%= @plugin.metadata[:author]%>
|
9
|
+
BuildArch: noarch
|
10
|
+
Group: System Tools
|
11
|
+
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
|
12
|
+
Source0: <%= @package_name_and_version%>.tgz
|
13
|
+
|
14
|
+
%description
|
15
|
+
<%= @plugin.metadata[:description] %>
|
16
|
+
|
17
|
+
%prep
|
18
|
+
%setup
|
19
|
+
|
20
|
+
%build
|
21
|
+
<% package_files = plugin_files.map{ |f| File.join(@libdir, File.expand_path(f).gsub(/#{File.expand_path(@plugin.path)}|\.\//, '')) } -%>
|
22
|
+
<% dirs = package_files.map{ |f| File.dirname(f) }.uniq -%>
|
23
|
+
|
24
|
+
%install
|
25
|
+
rm -rf %{buildroot}
|
26
|
+
<% dirs.each do |dir| -%>
|
27
|
+
%{__install} -d -m0755 %{buildroot}<%= dir%>
|
28
|
+
<% end -%>
|
29
|
+
<% package_files.each do |file| -%>
|
30
|
+
%{__install} -m0644 -v <%= (file[0].chr == '/') ? file[1..file.size-1]: file%> %{buildroot}<%=file %>
|
31
|
+
<% end -%>
|
32
|
+
|
33
|
+
<% @plugin.packagedata.each do |type, data| %>
|
34
|
+
%package <%= type %>
|
35
|
+
Summary: <%= @plugin.metadata[:description] %>
|
36
|
+
<% if data[:plugindependency] %>
|
37
|
+
Requires: <%= data[:plugindependency][:name] -%> = <%= data[:plugindependency][:version]%>-<%= data[:plugindependency][:revision]%>%{?dist}
|
38
|
+
<% end -%>
|
39
|
+
<% PluginPackager.filter_dependencies('redhat', data[:dependencies]).each do |dep|-%>
|
40
|
+
Requires: <%= dep[:name] -%> <%= ">= #{dep[:version]}" if dep[:version]%><%="-#{dep[:revision]}" if dep[:revision]%>
|
41
|
+
<% end -%>
|
42
|
+
%description <%= type %>
|
43
|
+
<%= data[:description] %>
|
44
|
+
|
45
|
+
%files <%= type %>
|
46
|
+
%defattr(-, root, root, -)
|
47
|
+
<% package_files(data[:files]).each do |file| -%>
|
48
|
+
<%= file %>
|
49
|
+
<% end -%>
|
50
|
+
<% end -%>
|
51
|
+
|
52
|
+
<% if @plugin.preinstall -%>
|
53
|
+
%pre
|
54
|
+
<%= @plugin.preinstall %>
|
55
|
+
<% end -%>
|
56
|
+
<% if @plugin.postinstall -%>
|
57
|
+
%post
|
58
|
+
<%= @plugin.postinstall%>
|
59
|
+
<% end -%>
|
60
|
+
|
61
|
+
%changelog
|
62
|
+
* <%= Time.now.strftime("%a %b %d %Y") -%> <%= @plugin.metadata[:author]%> - <%= @plugin.metadata[:version]%>-<%= @plugin.revision %>
|
63
|
+
- Built Package <%= @plugin.metadata[:name] -%>
|
@@ -1,8 +1,8 @@
|
|
1
1
|
module MCollective
|
2
2
|
module PluginPackager
|
3
3
|
# Plugin definition classes
|
4
|
-
|
5
|
-
|
4
|
+
require "mcollective/pluginpackager/agent_definition"
|
5
|
+
require "mcollective/pluginpackager/standard_definition"
|
6
6
|
|
7
7
|
# Package implementation plugins
|
8
8
|
def self.load_packagers
|
@@ -11,6 +11,6 @@ module MCollective
|
|
11
11
|
# to supply a _body_ method, whatever this method returns will be send to the
|
12
12
|
# middleware connection for an agent called _registration_
|
13
13
|
module Registration
|
14
|
-
|
14
|
+
require "mcollective/registration/base"
|
15
15
|
end
|
16
16
|
end
|
data/lib/mcollective/rpc.rb
CHANGED
@@ -5,16 +5,16 @@ module MCollective
|
|
5
5
|
# an RPC metaphor, standard compliant agents will make it easier
|
6
6
|
# to create generic clients like web interfaces etc
|
7
7
|
module RPC
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
8
|
+
require "mcollective/rpc/actionrunner"
|
9
|
+
require "mcollective/rpc/agent"
|
10
|
+
require "mcollective/rpc/audit"
|
11
|
+
require "mcollective/rpc/client"
|
12
|
+
require "mcollective/rpc/helpers"
|
13
|
+
require "mcollective/rpc/progress"
|
14
|
+
require "mcollective/rpc/reply"
|
15
|
+
require "mcollective/rpc/request"
|
16
|
+
require "mcollective/rpc/result"
|
17
|
+
require "mcollective/rpc/stats"
|
18
18
|
|
19
19
|
# Creates a standard options hash, pass in a block to add extra headings etc
|
20
20
|
# see Optionparser
|
@@ -58,7 +58,7 @@ module MCollective
|
|
58
58
|
# exit if there is a failure constructing the RPC client. Set this flag
|
59
59
|
# to false to cause an Exception to be raised instead.
|
60
60
|
def rpcclient(agent, flags = {})
|
61
|
-
configfile = flags[:configfile] ||
|
61
|
+
configfile = flags[:configfile] || Util.config_file_for_user
|
62
62
|
options = flags[:options] || nil
|
63
63
|
|
64
64
|
if flags.key?(:exit_on_failure)
|
@@ -0,0 +1,394 @@
|
|
1
|
+
module MCollective
|
2
|
+
module Security
|
3
|
+
# Impliments a security system that encrypts payloads using AES and secures
|
4
|
+
# the AES encrypted data using RSA public/private key encryption.
|
5
|
+
#
|
6
|
+
# The design goals of this plugin are:
|
7
|
+
#
|
8
|
+
# - Each actor - clients and servers - can have their own set of public and
|
9
|
+
# private keys
|
10
|
+
# - All actors are uniquely and cryptographically identified
|
11
|
+
# - Requests are encrypted using the clients private key and anyone that has
|
12
|
+
# the public key can see the request. Thus an atacker may see the requests
|
13
|
+
# given access to network or machine due to the broadcast nature of mcollective
|
14
|
+
# - The message time and TTL of messages are cryptographically secured making the
|
15
|
+
# ensuring messages can not be replayed with fake TTLs or times
|
16
|
+
# - Replies are encrypted using the calling clients public key. Thus no-one but
|
17
|
+
# the caller can view the contents of replies.
|
18
|
+
# - Servers can all have their own RSA keys, or share one, or reuse keys created
|
19
|
+
# by other PKI using software like Puppet
|
20
|
+
# - Requests from servers - like registration data - can be secured even to external
|
21
|
+
# eaves droppers depending on the level of configuration you are prepared to do
|
22
|
+
# - Given a network where you can ensure third parties are not able to access the
|
23
|
+
# middleware public key distribution can happen automatically
|
24
|
+
#
|
25
|
+
# Configuration Options:
|
26
|
+
# ======================
|
27
|
+
#
|
28
|
+
# Common Options:
|
29
|
+
#
|
30
|
+
# # Enable this plugin
|
31
|
+
# securityprovider = aes_security
|
32
|
+
#
|
33
|
+
# # Use YAML as serializer
|
34
|
+
# plugin.aes.serializer = yaml
|
35
|
+
#
|
36
|
+
# # Send our public key with every request so servers can learn it
|
37
|
+
# plugin.aes.send_pubkey = 1
|
38
|
+
#
|
39
|
+
# Clients:
|
40
|
+
#
|
41
|
+
# # The clients public and private keys
|
42
|
+
# plugin.aes.client_private = /home/user/.mcollective.d/user-private.pem
|
43
|
+
# plugin.aes.client_public = /home/user/.mcollective.d/user.pem
|
44
|
+
#
|
45
|
+
# Servers:
|
46
|
+
#
|
47
|
+
# # Where to cache client keys or find manually distributed ones
|
48
|
+
# plugin.aes.client_cert_dir = /etc/mcollective/ssl/clients
|
49
|
+
#
|
50
|
+
# # Cache public keys promiscuously from the network (this requires either a ca_cert to be set
|
51
|
+
# or insecure_learning to be enabled)
|
52
|
+
# plugin.aes.learn_pubkeys = 1
|
53
|
+
#
|
54
|
+
# # Do not check if client certificate can be verified by a CA
|
55
|
+
# plugin.aes.insecure_learning = 1
|
56
|
+
#
|
57
|
+
# # CA cert used to verify public keys when in learning mode
|
58
|
+
# plugin.aes.ca_cert = /etc/mcollective/ssl/ca.cert
|
59
|
+
#
|
60
|
+
# # Log but accept messages that may have been tampered with
|
61
|
+
# plugin.aes.enforce_ttl = 0
|
62
|
+
#
|
63
|
+
# # The servers public and private keys
|
64
|
+
# plugin.aes.server_private = /etc/mcollective/ssl/server-private.pem
|
65
|
+
# plugin.aes.server_public = /etc/mcollective/ssl/server-public.pem
|
66
|
+
#
|
67
|
+
class Aes_security<Base
|
68
|
+
def decodemsg(msg)
|
69
|
+
body = deserialize(msg.payload)
|
70
|
+
|
71
|
+
should_process_msg?(msg, body[:requestid])
|
72
|
+
# if we get a message that has a pubkey attached and we're set to learn
|
73
|
+
# then add it to the client_cert_dir this should only happen on servers
|
74
|
+
# since clients will get replies using their own pubkeys
|
75
|
+
if Util.str_to_bool(@config.pluginconf.fetch("aes.learn_pubkeys", false)) && body.include?(:sslpubkey)
|
76
|
+
certname = certname_from_callerid(body[:callerid])
|
77
|
+
certfile = "#{client_cert_dir}/#{certname}.pem"
|
78
|
+
if !File.exist?(certfile)
|
79
|
+
if !Util.str_to_bool(@config.pluginconf.fetch("aes.insecure_learning", false))
|
80
|
+
if !@config.pluginconf.fetch("aes.ca_cert", nil)
|
81
|
+
raise "Cannot verify certificate for '#{certname}'. No CA certificate specified."
|
82
|
+
end
|
83
|
+
|
84
|
+
if !validate_certificate(body[:sslpubkey], certname)
|
85
|
+
raise "Unable to validate certificate '#{certname}' against CA"
|
86
|
+
end
|
87
|
+
|
88
|
+
Log.debug("Verified certificate '#{certname}' against CA")
|
89
|
+
else
|
90
|
+
Log.warn("Insecure key learning is not a secure method of key distribution. Do NOT use this mode in sensitive environments.")
|
91
|
+
end
|
92
|
+
|
93
|
+
Log.debug("Caching client cert in #{certfile}")
|
94
|
+
File.open(certfile, "w") {|f| f.print body[:sslpubkey]}
|
95
|
+
else
|
96
|
+
Log.debug("Not caching client cert. File #{certfile} already exists.")
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
cryptdata = {:key => body[:sslkey], :data => body[:body]}
|
101
|
+
|
102
|
+
if @initiated_by == :client
|
103
|
+
body[:body] = deserialize(decrypt(cryptdata, nil))
|
104
|
+
else
|
105
|
+
certname = certname_from_callerid(body[:callerid])
|
106
|
+
certfile = "#{client_cert_dir}/#{certname}.pem"
|
107
|
+
# if aes.ca_cert is set every certificate is validated before we try and use it
|
108
|
+
if @config.pluginconf.fetch("aes.ca_cert", nil) && !validate_certificate(File.read(certfile), certname)
|
109
|
+
raise "Unable to validate certificate '#{certname}' against CA"
|
110
|
+
end
|
111
|
+
body[:body] = deserialize(decrypt(cryptdata, body[:callerid]))
|
112
|
+
|
113
|
+
# If we got a hash it's possible that this is a message with secure
|
114
|
+
# TTL and message time, attempt to decode that and transform into a
|
115
|
+
# traditional message.
|
116
|
+
#
|
117
|
+
# If it's not a hash it might be a old style message like old discovery
|
118
|
+
# ones that would just be a string so we allow that unaudited but only
|
119
|
+
# if enforce_ttl is disabled. This is primarly to allow a mixed old and
|
120
|
+
# new plugin infrastructure to work
|
121
|
+
if body[:body].is_a?(Hash)
|
122
|
+
update_secure_property(body, :aes_ttl, :ttl, "TTL")
|
123
|
+
update_secure_property(body, :aes_msgtime, :msgtime, "Message Time")
|
124
|
+
|
125
|
+
body[:body] = body[:body][:aes_msg] if body[:body].include?(:aes_msg)
|
126
|
+
else
|
127
|
+
unless @config.pluginconf["aes.enforce_ttl"] == "0"
|
128
|
+
raise "Message %s is in an unknown or older security protocol, ignoring" % [request_description(body)]
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
return body
|
134
|
+
rescue MsgDoesNotMatchRequestID
|
135
|
+
raise
|
136
|
+
|
137
|
+
rescue OpenSSL::PKey::RSAError
|
138
|
+
raise MsgDoesNotMatchRequestID, "Could not decrypt message using our key, possibly directed at another client"
|
139
|
+
|
140
|
+
rescue Exception => e
|
141
|
+
Log.warn("Could not decrypt message from client: #{e.class}: #{e}")
|
142
|
+
raise SecurityValidationFailed, "Could not decrypt message"
|
143
|
+
end
|
144
|
+
|
145
|
+
# To avoid tampering we turn the origin body into a hash and copy some of the protocol keys
|
146
|
+
# like :ttl and :msg_time into the hash before encrypting it.
|
147
|
+
#
|
148
|
+
# This function compares and updates the unencrypted ones based on the encrypted ones. By
|
149
|
+
# default it enforces matching and presense by raising exceptions, if aes.enforce_ttl is set
|
150
|
+
# to 0 it will only log warnings about violations
|
151
|
+
def update_secure_property(msg, secure_property, property, description)
|
152
|
+
req = request_description(msg)
|
153
|
+
|
154
|
+
unless @config.pluginconf["aes.enforce_ttl"] == "0"
|
155
|
+
raise "Request #{req} does not have a secure #{description}" unless msg[:body].include?(secure_property)
|
156
|
+
raise "Request #{req} #{description} does not match encrypted #{description} - possible tampering" unless msg[:body][secure_property] == msg[property]
|
157
|
+
else
|
158
|
+
if msg[:body].include?(secure_property)
|
159
|
+
Log.warn("Request #{req} #{description} does not match encrypted #{description} - possible tampering") unless msg[:body][secure_property] == msg[property]
|
160
|
+
else
|
161
|
+
Log.warn("Request #{req} does not have a secure #{description}") unless msg[:body].include?(secure_property)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
msg[property] = msg[:body][secure_property] if msg[:body].include?(secure_property)
|
166
|
+
msg[:body].delete(secure_property)
|
167
|
+
end
|
168
|
+
|
169
|
+
# Encodes a reply
|
170
|
+
def encodereply(sender, msg, requestid, requestcallerid)
|
171
|
+
crypted = encrypt(serialize(msg), requestcallerid)
|
172
|
+
|
173
|
+
req = create_reply(requestid, sender, crypted[:data])
|
174
|
+
req[:sslkey] = crypted[:key]
|
175
|
+
|
176
|
+
serialize(req)
|
177
|
+
end
|
178
|
+
|
179
|
+
# Encodes a request msg
|
180
|
+
def encoderequest(sender, msg, requestid, filter, target_agent, target_collective, ttl=60)
|
181
|
+
req = create_request(requestid, filter, nil, @initiated_by, target_agent, target_collective, ttl)
|
182
|
+
|
183
|
+
# embed the ttl and msgtime in the crypted data later we will use these in
|
184
|
+
# the decoding of a message to set the message ones from secure sources. this
|
185
|
+
# is to ensure messages are not tampered with to facility replay attacks etc
|
186
|
+
aes_msg = {:aes_msg => msg,
|
187
|
+
:aes_ttl => ttl,
|
188
|
+
:aes_msgtime => req[:msgtime]}
|
189
|
+
|
190
|
+
crypted = encrypt(serialize(aes_msg), callerid)
|
191
|
+
|
192
|
+
req[:body] = crypted[:data]
|
193
|
+
req[:sslkey] = crypted[:key]
|
194
|
+
|
195
|
+
if @config.pluginconf.include?("aes.send_pubkey") && @config.pluginconf["aes.send_pubkey"] == "1"
|
196
|
+
if @initiated_by == :client
|
197
|
+
req[:sslpubkey] = File.read(client_public_key)
|
198
|
+
else
|
199
|
+
req[:sslpubkey] = File.read(server_public_key)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
serialize(req)
|
204
|
+
end
|
205
|
+
|
206
|
+
# Serializes a message using the configured encoder
|
207
|
+
def serialize(msg)
|
208
|
+
serializer = @config.pluginconf["aes.serializer"] || "marshal"
|
209
|
+
|
210
|
+
Log.debug("Serializing using #{serializer}")
|
211
|
+
|
212
|
+
case serializer
|
213
|
+
when "yaml"
|
214
|
+
return YAML.dump(msg)
|
215
|
+
else
|
216
|
+
return Marshal.dump(msg)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
# De-Serializes a message using the configured encoder
|
221
|
+
def deserialize(msg)
|
222
|
+
serializer = @config.pluginconf["aes.serializer"] || "marshal"
|
223
|
+
|
224
|
+
Log.debug("De-Serializing using #{serializer}")
|
225
|
+
|
226
|
+
case serializer
|
227
|
+
when "yaml"
|
228
|
+
return YAML.load(msg)
|
229
|
+
else
|
230
|
+
return Marshal.load(msg)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
# sets the caller id to the md5 of the public key
|
235
|
+
def callerid
|
236
|
+
if @initiated_by == :client
|
237
|
+
key = client_public_key
|
238
|
+
else
|
239
|
+
key = server_public_key
|
240
|
+
end
|
241
|
+
|
242
|
+
# First try and create a X509 certificate object. If that is possible,
|
243
|
+
# we lift the callerid from the cert
|
244
|
+
begin
|
245
|
+
ssl_cert = OpenSSL::X509::Certificate.new(File.read(key))
|
246
|
+
id = "cert=#{certname_from_certificate(ssl_cert)}"
|
247
|
+
rescue
|
248
|
+
# If the public key is not a certificate, use the file name as callerid
|
249
|
+
id = "cert=#{File.basename(key).gsub(/\.pem$/, '')}"
|
250
|
+
end
|
251
|
+
|
252
|
+
return id
|
253
|
+
end
|
254
|
+
|
255
|
+
def encrypt(string, certid)
|
256
|
+
if @initiated_by == :client
|
257
|
+
@ssl ||= SSL.new(client_public_key, client_private_key)
|
258
|
+
|
259
|
+
Log.debug("Encrypting message using private key")
|
260
|
+
return @ssl.encrypt_with_private(string)
|
261
|
+
else
|
262
|
+
# when the server is initating requests like for registration
|
263
|
+
# then the certid will be our callerid
|
264
|
+
if certid == callerid
|
265
|
+
Log.debug("Encrypting message using private key #{server_private_key}")
|
266
|
+
|
267
|
+
ssl = SSL.new(server_public_key, server_private_key)
|
268
|
+
return ssl.encrypt_with_private(string)
|
269
|
+
else
|
270
|
+
Log.debug("Encrypting message using public key for #{certid}")
|
271
|
+
|
272
|
+
ssl = SSL.new(public_key_path_for_client(certid))
|
273
|
+
return ssl.encrypt_with_public(string)
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
def decrypt(string, certid)
|
279
|
+
if @initiated_by == :client
|
280
|
+
@ssl ||= SSL.new(client_public_key, client_private_key)
|
281
|
+
|
282
|
+
Log.debug("Decrypting message using private key")
|
283
|
+
return @ssl.decrypt_with_private(string)
|
284
|
+
else
|
285
|
+
Log.debug("Decrypting message using public key for #{certid}")
|
286
|
+
ssl = SSL.new(public_key_path_for_client(certid))
|
287
|
+
return ssl.decrypt_with_public(string)
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
def validate_certificate(client_cert, certid)
|
292
|
+
cert_file = @config.pluginconf.fetch("aes.ca_cert", nil)
|
293
|
+
|
294
|
+
begin
|
295
|
+
ssl_cert = OpenSSL::X509::Certificate.new(client_cert)
|
296
|
+
rescue OpenSSL::X509::CertificateError
|
297
|
+
Log.warn("Received public key that is not a X509 certficate")
|
298
|
+
return false
|
299
|
+
end
|
300
|
+
|
301
|
+
ssl_certname = certname_from_certificate(ssl_cert)
|
302
|
+
|
303
|
+
if certid != ssl_certname
|
304
|
+
Log.warn("certname '#{certid}' doesn't match certificate '#{ssl_certname}'")
|
305
|
+
return false
|
306
|
+
end
|
307
|
+
|
308
|
+
Log.debug("Loading CA Cert for verification")
|
309
|
+
ca_cert = OpenSSL::X509::Store.new
|
310
|
+
ca_cert.add_file cert_file
|
311
|
+
|
312
|
+
if ca_cert.verify(ssl_cert)
|
313
|
+
Log.debug("Verified certificate '#{ssl_certname}' against CA")
|
314
|
+
else
|
315
|
+
# TODO add cert id
|
316
|
+
Log.warn("Unable to validate certificate '#{ssl_certname}'' against CA")
|
317
|
+
return false
|
318
|
+
end
|
319
|
+
return true
|
320
|
+
end
|
321
|
+
|
322
|
+
# On servers this will look in the aes.client_cert_dir for public
|
323
|
+
# keys matching the clientid, clientid is expected to be in the format
|
324
|
+
# set by callerid
|
325
|
+
def public_key_path_for_client(clientid)
|
326
|
+
raise "Unknown callerid format in '#{clientid}'" unless clientid.match(/^cert=(.+)$/)
|
327
|
+
|
328
|
+
clientid = $1
|
329
|
+
|
330
|
+
client_cert_dir + "/#{clientid}.pem"
|
331
|
+
end
|
332
|
+
|
333
|
+
# Figures out the client private key either from MCOLLECTIVE_AES_PRIVATE or the
|
334
|
+
# plugin.aes.client_private config option
|
335
|
+
def client_private_key
|
336
|
+
return ENV["MCOLLECTIVE_AES_PRIVATE"] if ENV.include?("MCOLLECTIVE_AES_PRIVATE")
|
337
|
+
|
338
|
+
raise("No plugin.aes.client_private configuration option specified") unless @config.pluginconf.include?("aes.client_private")
|
339
|
+
|
340
|
+
return @config.pluginconf["aes.client_private"]
|
341
|
+
end
|
342
|
+
|
343
|
+
# Figures out the client public key either from MCOLLECTIVE_AES_PUBLIC or the
|
344
|
+
# plugin.aes.client_public config option
|
345
|
+
def client_public_key
|
346
|
+
return ENV["MCOLLECTIVE_AES_PUBLIC"] if ENV.include?("MCOLLECTIVE_AES_PUBLIC")
|
347
|
+
|
348
|
+
raise("No plugin.aes.client_public configuration option specified") unless @config.pluginconf.include?("aes.client_public")
|
349
|
+
|
350
|
+
return @config.pluginconf["aes.client_public"]
|
351
|
+
end
|
352
|
+
|
353
|
+
# Figures out the server public key from the plugin.aes.server_public config option
|
354
|
+
def server_public_key
|
355
|
+
raise("No aes.server_public configuration option specified") unless @config.pluginconf.include?("aes.server_public")
|
356
|
+
return @config.pluginconf["aes.server_public"]
|
357
|
+
end
|
358
|
+
|
359
|
+
# Figures out the server private key from the plugin.aes.server_private config option
|
360
|
+
def server_private_key
|
361
|
+
raise("No plugin.aes.server_private configuration option specified") unless @config.pluginconf.include?("aes.server_private")
|
362
|
+
@config.pluginconf["aes.server_private"]
|
363
|
+
end
|
364
|
+
|
365
|
+
# Figures out where to get client public certs from the plugin.aes.client_cert_dir config option
|
366
|
+
def client_cert_dir
|
367
|
+
raise("No plugin.aes.client_cert_dir configuration option specified") unless @config.pluginconf.include?("aes.client_cert_dir")
|
368
|
+
@config.pluginconf["aes.client_cert_dir"]
|
369
|
+
end
|
370
|
+
|
371
|
+
def request_description(msg)
|
372
|
+
"%s from %s@%s" % [msg[:requestid], msg[:callerid], msg[:senderid]]
|
373
|
+
end
|
374
|
+
|
375
|
+
# Takes our cert=foo callerids and return the foo bit else nil
|
376
|
+
def certname_from_callerid(id)
|
377
|
+
if id =~ /^cert=([\w\.\-]+)/
|
378
|
+
return $1
|
379
|
+
else
|
380
|
+
raise("Received a callerid in an unexpected format: '#{id}', ignoring")
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
def certname_from_certificate(cert)
|
385
|
+
id = cert.subject
|
386
|
+
if id.to_s =~ /^\/CN=([\w\.\-]+)/
|
387
|
+
return $1
|
388
|
+
else
|
389
|
+
raise("Received a callerid in an unexpected format in an SSL certificate: '#{id}', ignoring")
|
390
|
+
end
|
391
|
+
end
|
392
|
+
end
|
393
|
+
end
|
394
|
+
end
|