dtk-node-agent 0.7.7 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/README.md +21 -4
- data/bin/dtk-node-agent +17 -0
- data/lib/config/install.config +2 -2
- data/lib/dtk-node-agent/installer.rb +30 -25
- data/lib/dtk-node-agent/version.rb +18 -1
- metadata +23 -110
- data/mcollective_additions/debian.mcollective.init +0 -92
- data/mcollective_additions/plugins/README.md +0 -1
- data/mcollective_additions/plugins/v1.2/agent/discovery.rb +0 -39
- data/mcollective_additions/plugins/v1.2/agent/get_log_fragment.ddl +0 -15
- data/mcollective_additions/plugins/v1.2/agent/get_log_fragment.rb +0 -79
- data/mcollective_additions/plugins/v1.2/agent/git_access.ddl +0 -9
- data/mcollective_additions/plugins/v1.2/agent/git_access.rb +0 -79
- data/mcollective_additions/plugins/v1.2/agent/netstat.ddl +0 -9
- data/mcollective_additions/plugins/v1.2/agent/netstat.rb +0 -34
- data/mcollective_additions/plugins/v1.2/agent/puppet_apply.ddl +0 -9
- data/mcollective_additions/plugins/v1.2/agent/puppet_apply.rb +0 -630
- data/mcollective_additions/plugins/v1.2/agent/rpcutil.ddl +0 -204
- data/mcollective_additions/plugins/v1.2/agent/rpcutil.rb +0 -101
- data/mcollective_additions/plugins/v1.2/facts/pbuilder_facts.rb +0 -35
- data/mcollective_additions/plugins/v2.2/agent/action_agent.ddl +0 -9
- data/mcollective_additions/plugins/v2.2/agent/action_agent.rb +0 -47
- data/mcollective_additions/plugins/v2.2/agent/dev_manager.ddl +0 -9
- data/mcollective_additions/plugins/v2.2/agent/dev_manager.rb +0 -111
- data/mcollective_additions/plugins/v2.2/agent/discovery.rb +0 -39
- data/mcollective_additions/plugins/v2.2/agent/dtk_node_agent_git_client.rb +0 -94
- data/mcollective_additions/plugins/v2.2/agent/execute_tests.ddl +0 -9
- data/mcollective_additions/plugins/v2.2/agent/execute_tests.rb +0 -111
- data/mcollective_additions/plugins/v2.2/agent/execute_tests_v2.ddl +0 -9
- data/mcollective_additions/plugins/v2.2/agent/execute_tests_v2.rb +0 -131
- data/mcollective_additions/plugins/v2.2/agent/get_log_fragment.ddl +0 -15
- data/mcollective_additions/plugins/v2.2/agent/get_log_fragment.rb +0 -79
- data/mcollective_additions/plugins/v2.2/agent/git_access.ddl +0 -9
- data/mcollective_additions/plugins/v2.2/agent/git_access.rb +0 -61
- data/mcollective_additions/plugins/v2.2/agent/netstat.ddl +0 -9
- data/mcollective_additions/plugins/v2.2/agent/netstat.rb +0 -34
- data/mcollective_additions/plugins/v2.2/agent/ps.ddl +0 -9
- data/mcollective_additions/plugins/v2.2/agent/ps.rb +0 -37
- data/mcollective_additions/plugins/v2.2/agent/puppet_apply.ddl +0 -9
- data/mcollective_additions/plugins/v2.2/agent/puppet_apply.rb +0 -818
- data/mcollective_additions/plugins/v2.2/agent/puppet_cancel.ddl +0 -10
- data/mcollective_additions/plugins/v2.2/agent/puppet_cancel.rb +0 -78
- data/mcollective_additions/plugins/v2.2/agent/rpcutil.ddl +0 -204
- data/mcollective_additions/plugins/v2.2/agent/rpcutil.rb +0 -101
- data/mcollective_additions/plugins/v2.2/agent/ssh_agent.ddl +0 -13
- data/mcollective_additions/plugins/v2.2/agent/ssh_agent.rb +0 -97
- data/mcollective_additions/plugins/v2.2/agent/sync_agent_code.ddl +0 -10
- data/mcollective_additions/plugins/v2.2/agent/sync_agent_code.rb +0 -85
- data/mcollective_additions/plugins/v2.2/agent/tail.ddl +0 -11
- data/mcollective_additions/plugins/v2.2/agent/tail.rb +0 -67
- data/mcollective_additions/plugins/v2.2/audit/logfile.rb +0 -26
- data/mcollective_additions/plugins/v2.2/connector/r8stomp.rb +0 -238
- data/mcollective_additions/plugins/v2.2/connector/stomp.rb +0 -349
- data/mcollective_additions/plugins/v2.2/connector/stomp_em.rb +0 -191
- data/mcollective_additions/plugins/v2.2/data/agent_data.ddl +0 -22
- data/mcollective_additions/plugins/v2.2/data/agent_data.rb +0 -17
- data/mcollective_additions/plugins/v2.2/data/collective_data.ddl +0 -20
- data/mcollective_additions/plugins/v2.2/data/collective_data.rb +0 -9
- data/mcollective_additions/plugins/v2.2/data/fact_data.ddl +0 -28
- data/mcollective_additions/plugins/v2.2/data/fact_data.rb +0 -55
- data/mcollective_additions/plugins/v2.2/data/fstat_data.ddl +0 -89
- data/mcollective_additions/plugins/v2.2/data/fstat_data.rb +0 -56
- data/mcollective_additions/plugins/v2.2/discovery/flatfile.ddl +0 -11
- data/mcollective_additions/plugins/v2.2/discovery/flatfile.rb +0 -48
- data/mcollective_additions/plugins/v2.2/discovery/mc.ddl +0 -11
- data/mcollective_additions/plugins/v2.2/discovery/mc.rb +0 -30
- data/mcollective_additions/plugins/v2.2/discovery/stdin.ddl +0 -11
- data/mcollective_additions/plugins/v2.2/discovery/stdin.rb +0 -66
- data/mcollective_additions/plugins/v2.2/facts/pbuilder_facts.rb +0 -37
- data/mcollective_additions/plugins/v2.2/facts/yaml_facts.rb +0 -61
- data/mcollective_additions/plugins/v2.2/registration/agentlist.rb +0 -10
- data/mcollective_additions/plugins/v2.2/security/sshkey.ddl +0 -9
- data/mcollective_additions/plugins/v2.2/security/sshkey.rb +0 -362
- data/mcollective_additions/plugins/v2.2/util/puppetrunner.rb +0 -36
- data/mcollective_additions/plugins/v2.2/validator/array_validator.ddl +0 -7
- data/mcollective_additions/plugins/v2.2/validator/array_validator.rb +0 -9
- data/mcollective_additions/plugins/v2.2/validator/ipv4address_validator.ddl +0 -7
- data/mcollective_additions/plugins/v2.2/validator/ipv4address_validator.rb +0 -16
- data/mcollective_additions/plugins/v2.2/validator/ipv6address_validator.ddl +0 -7
- data/mcollective_additions/plugins/v2.2/validator/ipv6address_validator.rb +0 -16
- data/mcollective_additions/plugins/v2.2/validator/length_validator.ddl +0 -7
- data/mcollective_additions/plugins/v2.2/validator/length_validator.rb +0 -11
- data/mcollective_additions/plugins/v2.2/validator/regex_validator.ddl +0 -7
- data/mcollective_additions/plugins/v2.2/validator/regex_validator.rb +0 -9
- data/mcollective_additions/plugins/v2.2/validator/shellsafe_validator.ddl +0 -7
- data/mcollective_additions/plugins/v2.2/validator/shellsafe_validator.rb +0 -13
- data/mcollective_additions/plugins/v2.2/validator/typecheck_validator.ddl +0 -7
- data/mcollective_additions/plugins/v2.2/validator/typecheck_validator.rb +0 -28
- data/mcollective_additions/redhat.mcollective.init +0 -139
- data/mcollective_additions/redhat.mcollective.service +0 -14
- data/mcollective_additions/server.cfg +0 -22
- data/src/etc/logrotate.d/mcollective +0 -10
- data/src/etc/mcollective.default +0 -6
@@ -1,11 +0,0 @@
|
|
1
|
-
metadata :name => "flatfile",
|
2
|
-
:description => "Flatfile based discovery for node identities",
|
3
|
-
:author => "R.I.Pienaar <rip@devco.net>",
|
4
|
-
:license => "ASL 2.0",
|
5
|
-
:version => "0.1",
|
6
|
-
:url => "http://marionette-collective.org/",
|
7
|
-
:timeout => 0
|
8
|
-
|
9
|
-
discovery do
|
10
|
-
capabilities :identity
|
11
|
-
end
|
@@ -1,48 +0,0 @@
|
|
1
|
-
# discovers against a flatfile instead of the traditional network discovery
|
2
|
-
# the flat file must have a node name per line which should match identities
|
3
|
-
# as configured
|
4
|
-
module MCollective
|
5
|
-
class Discovery
|
6
|
-
class Flatfile
|
7
|
-
def self.discover(filter, timeout, limit=0, client=nil)
|
8
|
-
unless client.options[:discovery_options].empty?
|
9
|
-
file = client.options[:discovery_options].first
|
10
|
-
else
|
11
|
-
raise "The flatfile discovery method needs a path to a text file"
|
12
|
-
end
|
13
|
-
|
14
|
-
raise "Cannot read the file %s specified as discovery source" % file unless File.readable?(file)
|
15
|
-
|
16
|
-
discovered = []
|
17
|
-
hosts = []
|
18
|
-
|
19
|
-
File.readlines(file).each do |host|
|
20
|
-
host = host.chomp.strip
|
21
|
-
if host.empty? || host.match(/^#/)
|
22
|
-
next
|
23
|
-
end
|
24
|
-
raise 'Identities can only match /^[\w\.\-]+$/' unless host.match(/^[\w\.\-]+$/)
|
25
|
-
hosts << host
|
26
|
-
end
|
27
|
-
|
28
|
-
# this plugin only supports identity filters, do regex matches etc against
|
29
|
-
# the list found in the flatfile
|
30
|
-
if !(filter["identity"].empty?)
|
31
|
-
filter["identity"].each do |identity|
|
32
|
-
identity = Regexp.new(identity.gsub("\/", "")) if identity.match("^/")
|
33
|
-
|
34
|
-
if identity.is_a?(Regexp)
|
35
|
-
discovered = hosts.grep(identity)
|
36
|
-
elsif hosts.include?(identity)
|
37
|
-
discovered << identity
|
38
|
-
end
|
39
|
-
end
|
40
|
-
else
|
41
|
-
discovered = hosts
|
42
|
-
end
|
43
|
-
|
44
|
-
discovered
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
@@ -1,11 +0,0 @@
|
|
1
|
-
metadata :name => "mc",
|
2
|
-
:description => "MCollective Broadcast based discovery",
|
3
|
-
:author => "R.I.Pienaar <rip@devco.net>",
|
4
|
-
:license => "ASL 2.0",
|
5
|
-
:version => "0.1",
|
6
|
-
:url => "http://marionette-collective.org/",
|
7
|
-
:timeout => 2
|
8
|
-
|
9
|
-
discovery do
|
10
|
-
capabilities [:classes, :facts, :identity, :agents, :compound]
|
11
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
module MCollective
|
2
|
-
class Discovery
|
3
|
-
class Mc
|
4
|
-
def self.discover(filter, timeout, limit, client)
|
5
|
-
begin
|
6
|
-
hosts = []
|
7
|
-
Timeout.timeout(timeout) do
|
8
|
-
reqid = client.sendreq("ping", "discovery", filter)
|
9
|
-
Log.debug("Waiting #{timeout} seconds for discovery replies to request #{reqid}")
|
10
|
-
|
11
|
-
loop do
|
12
|
-
reply = client.receive(reqid)
|
13
|
-
Log.debug("Got discovery reply from #{reply.payload[:senderid]}")
|
14
|
-
hosts << reply.payload[:senderid]
|
15
|
-
|
16
|
-
return hosts if limit > 0 && hosts.size == limit
|
17
|
-
end
|
18
|
-
end
|
19
|
-
rescue Timeout::Error => e
|
20
|
-
rescue Exception => e
|
21
|
-
raise
|
22
|
-
ensure
|
23
|
-
client.unsubscribe("discovery", :reply)
|
24
|
-
end
|
25
|
-
|
26
|
-
hosts
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
@@ -1,11 +0,0 @@
|
|
1
|
-
metadata :name => "stdin",
|
2
|
-
:description => "STDIN based discovery for node identities",
|
3
|
-
:author => "Tomas Doran <bobtfish@bobtfish.net.net>",
|
4
|
-
:license => "ASL 2.0",
|
5
|
-
:version => "0.1",
|
6
|
-
:url => "http://marionette-collective.org/",
|
7
|
-
:timeout => 0
|
8
|
-
|
9
|
-
discovery do
|
10
|
-
capabilities :identity
|
11
|
-
end
|
@@ -1,66 +0,0 @@
|
|
1
|
-
# discovers against stdin instead of the traditional network discovery
|
2
|
-
# the input must be a flat file with a node name per line which should match identities as configured,
|
3
|
-
# or it should be a json string as output by the -j option of mco rpc
|
4
|
-
require 'mcollective/rpc/helpers'
|
5
|
-
|
6
|
-
module MCollective
|
7
|
-
class Discovery
|
8
|
-
class Stdin
|
9
|
-
def self.discover(filter, timeout, limit=0, client=nil)
|
10
|
-
unless client.options[:discovery_options].empty?
|
11
|
-
type = client.options[:discovery_options].first.downcase
|
12
|
-
else
|
13
|
-
type = 'auto'
|
14
|
-
end
|
15
|
-
|
16
|
-
discovered = []
|
17
|
-
|
18
|
-
file = STDIN.read
|
19
|
-
|
20
|
-
if file =~ /^\s*$/
|
21
|
-
raise("data piped on STDIN contained only whitespace - could not discover hosts from it.")
|
22
|
-
end
|
23
|
-
|
24
|
-
if type == 'auto'
|
25
|
-
if file =~ /^\s*\[/
|
26
|
-
type = 'json'
|
27
|
-
else
|
28
|
-
type = 'text'
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
if type == 'json'
|
33
|
-
hosts = MCollective::RPC::Helpers.extract_hosts_from_json(file)
|
34
|
-
elsif type == 'text'
|
35
|
-
hosts = file.split("\n")
|
36
|
-
else
|
37
|
-
raise("stdin discovery plugin only knows the types auto/text/json, not \"#{type}\"")
|
38
|
-
end
|
39
|
-
|
40
|
-
hosts.map do |host|
|
41
|
-
raise 'Identities can only match /\w\.\-/' unless host.match(/^[\w\.\-]+$/)
|
42
|
-
host
|
43
|
-
end
|
44
|
-
|
45
|
-
# this plugin only supports identity filters, do regex matches etc against
|
46
|
-
# the list found in the flatfile
|
47
|
-
unless filter["identity"].empty?
|
48
|
-
filter["identity"].each do |identity|
|
49
|
-
identity = Regexp.new(identity.gsub("\/", "")) if identity.match("^/")
|
50
|
-
|
51
|
-
if identity.is_a?(Regexp)
|
52
|
-
discovered = hosts.grep(identity)
|
53
|
-
elsif hosts.include?(identity)
|
54
|
-
discovered << identity
|
55
|
-
end
|
56
|
-
end
|
57
|
-
else
|
58
|
-
discovered = hosts
|
59
|
-
end
|
60
|
-
|
61
|
-
discovered
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
@@ -1,37 +0,0 @@
|
|
1
|
-
require 'open-uri'
|
2
|
-
require 'timeout'
|
3
|
-
require 'yaml'
|
4
|
-
|
5
|
-
module MCollective
|
6
|
-
module Facts
|
7
|
-
# A factsource for pbuilder
|
8
|
-
class Pbuilder_facts < Base
|
9
|
-
|
10
|
-
def load_facts_from_source
|
11
|
-
# ret = {"pbuilderid" => get_pbuilderid()}
|
12
|
-
ret = {}
|
13
|
-
yaml_file = '/etc/mcollective/facts.yaml'
|
14
|
-
if File.exists?(yaml_file)
|
15
|
-
yaml_facts = YAML.load_file(yaml_file)
|
16
|
-
ret.merge!(yaml_facts)
|
17
|
-
end
|
18
|
-
ret.merge!("pbuilderid" => get_pbuilderid()) unless ret.keys.include?('pbuilderid')
|
19
|
-
ret
|
20
|
-
end
|
21
|
-
|
22
|
-
def get_pbuilderid()
|
23
|
-
ret = nil
|
24
|
-
begin
|
25
|
-
addr = "169.254.169.254"
|
26
|
-
wait_sec = 2
|
27
|
-
Timeout::timeout(wait_sec) {open("http://#{addr}:80/")}
|
28
|
-
ret = OpenURI.open_uri("http://#{addr}/2008-02-01/meta-data/instance-id").read
|
29
|
-
rescue Timeout::Error
|
30
|
-
rescue
|
31
|
-
#TODO: unexpected; write to log what error is
|
32
|
-
end
|
33
|
-
ret
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,61 +0,0 @@
|
|
1
|
-
module MCollective
|
2
|
-
module Facts
|
3
|
-
require 'yaml'
|
4
|
-
|
5
|
-
# A factsource that reads a hash of facts from a YAML file
|
6
|
-
#
|
7
|
-
# Multiple files can be specified seperated with a : in the
|
8
|
-
# config file, they will be merged with later files overriding
|
9
|
-
# earlier ones in the list.
|
10
|
-
class Yaml_facts<Base
|
11
|
-
def initialize
|
12
|
-
@yaml_file_mtimes = {}
|
13
|
-
|
14
|
-
super
|
15
|
-
end
|
16
|
-
|
17
|
-
def load_facts_from_source
|
18
|
-
config = Config.instance
|
19
|
-
|
20
|
-
fact_files = config.pluginconf["yaml"].split(File::PATH_SEPARATOR)
|
21
|
-
facts = {}
|
22
|
-
|
23
|
-
fact_files.each do |file|
|
24
|
-
begin
|
25
|
-
if File.exist?(file)
|
26
|
-
facts.merge!(YAML.load_file(file))
|
27
|
-
else
|
28
|
-
raise("Can't find YAML file to load: #{file}")
|
29
|
-
end
|
30
|
-
rescue Exception => e
|
31
|
-
Log.error("Failed to load yaml facts from #{file}: #{e.class}: #{e}")
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
facts
|
36
|
-
end
|
37
|
-
|
38
|
-
# force fact reloads when the mtime on the yaml file change
|
39
|
-
def force_reload?
|
40
|
-
config = Config.instance
|
41
|
-
|
42
|
-
fact_files = config.pluginconf["yaml"].split(File::PATH_SEPARATOR)
|
43
|
-
|
44
|
-
fact_files.each do |file|
|
45
|
-
@yaml_file_mtimes[file] ||= File.stat(file).mtime
|
46
|
-
mtime = File.stat(file).mtime
|
47
|
-
|
48
|
-
if mtime > @yaml_file_mtimes[file]
|
49
|
-
@yaml_file_mtimes[file] = mtime
|
50
|
-
|
51
|
-
Log.debug("Forcing fact reload due to age of #{file}")
|
52
|
-
|
53
|
-
return true
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
false
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
@@ -1,9 +0,0 @@
|
|
1
|
-
metadata :name => 'sshkey',
|
2
|
-
:description => 'Security Plugin that uses ssh keys for signing',
|
3
|
-
:author => 'Pieter Loubser <pieter.loubser@puppetlabs.com>',
|
4
|
-
:license => 'ASL 2.0',
|
5
|
-
:version => '0.4',
|
6
|
-
:url => 'http://projects.puppetlabs.com/projects/mcollective-plugins/wiki',
|
7
|
-
:timeout => 10
|
8
|
-
|
9
|
-
requires :mcollective => '2.2.1'
|
@@ -1,362 +0,0 @@
|
|
1
|
-
module MCollective
|
2
|
-
module Security
|
3
|
-
# Configuration
|
4
|
-
#
|
5
|
-
# Client:
|
6
|
-
# client.private_key : A private key used to sign requests with - defaults to ssh-agent
|
7
|
-
# client.known_hosts : The known_hosts file to use - defaults to /home/callerid/.ssh/known_hosts
|
8
|
-
# client.send_key : Send the client's public key with the request - doesn not send the key by default.
|
9
|
-
# To send a key specify the key file to send.
|
10
|
-
#
|
11
|
-
# Server:
|
12
|
-
# server.private_key : The private key used to sign replies with - Defaults to /etc/ssh/ssh_host_rsa_key * required
|
13
|
-
# server.authorized_keys : The authorized_keys file to use - defaults to the caller's authorized_keys file in his home directory
|
14
|
-
# server.send_key : Send the server's public key with the request - does not send the key by default.
|
15
|
-
# To send a key specify the key file to send.
|
16
|
-
#
|
17
|
-
# Shared:
|
18
|
-
# (client|server).publickey_dir : Directory to store received keys - defaults to none
|
19
|
-
# (client|server).learn_public_keys : Allow writing public keys to publickey_dir - defaults to not sending.
|
20
|
-
# (client|server).overwrite_stored_keys : Overwrite received keys - defaults to false
|
21
|
-
class Sshkey < Base
|
22
|
-
gem 'sshkeyauth', '>= 0.0.4'
|
23
|
-
|
24
|
-
require 'ssh/key/signer'
|
25
|
-
require 'ssh/key/verifier'
|
26
|
-
require 'etc'
|
27
|
-
|
28
|
-
def decodemsg(msg)
|
29
|
-
body = Marshal.load(msg.payload)
|
30
|
-
|
31
|
-
if validrequest?(body)
|
32
|
-
body[:body] = Marshal.load(body[:body])
|
33
|
-
return body
|
34
|
-
else
|
35
|
-
nil
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def encodereply(sender, msg, requestid, requestcallerid=nil)
|
40
|
-
serialized_msg = Marshal.dump(msg)
|
41
|
-
reply = create_reply(requestid, sender, serialized_msg)
|
42
|
-
reply[:serialized_data] = Marshal.dump(create_hash_fields(serialized_msg, reply[:msgtime], requestid))
|
43
|
-
reply[:hash] = makehash(reply[:serialized_data])
|
44
|
-
|
45
|
-
if server_key = lookup_config_option('send_key')
|
46
|
-
if File.exists?(server_key)
|
47
|
-
reply[:public_key] = load_key(server_key)
|
48
|
-
else
|
49
|
-
raise("Cannot create reply. sshkey.server.send_key set but key '%s' does not exist." % server_key)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
Marshal.dump(reply)
|
54
|
-
end
|
55
|
-
|
56
|
-
def encoderequest(sender, msg, requestid, filter, target_agent, target_collective, ttl=60)
|
57
|
-
serialized_msg = Marshal.dump(msg)
|
58
|
-
req = create_request(requestid, filter, serialized_msg, @initiated_by, target_agent, target_collective, ttl)
|
59
|
-
|
60
|
-
|
61
|
-
if client_key = lookup_config_option('send_key')
|
62
|
-
if File.exists?(client_key)
|
63
|
-
req[:public_key] = load_key(client_key)
|
64
|
-
else
|
65
|
-
raise("Cannot create request. sshkey.client.send_key set but key '%s' does not exist." % client_key)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
req[:serialized_data] = Marshal.dump(create_hash_fields(serialized_msg, req[:msgtime], requestid, ttl, callerid))
|
70
|
-
req[:hash] = makehash(req[:serialized_data])
|
71
|
-
|
72
|
-
Marshal.dump(req)
|
73
|
-
end
|
74
|
-
|
75
|
-
def validrequest?(req)
|
76
|
-
# Check if verification keys are correctly configured
|
77
|
-
valid_configuration?
|
78
|
-
# Check if key should be written to disk and write it
|
79
|
-
write_key_to_disk(req[:public_key], (req[:callerid] || req[:senderid]).split('=')[-1] ) if req[:public_key]
|
80
|
-
|
81
|
-
if @initiated_by == :client
|
82
|
-
Log.debug('Validating reply from node %s' % req[:senderid])
|
83
|
-
verifier = client_verifier(req[:senderid])
|
84
|
-
else
|
85
|
-
Log.debug('Validating request from client %s' % req[:callerid])
|
86
|
-
verifier = node_verifier(req[:callerid], (req[:agent] == 'registration'), req[:public_key])
|
87
|
-
end
|
88
|
-
|
89
|
-
signatures = Marshal.load(req[:hash])
|
90
|
-
|
91
|
-
if verifier.verify?(signatures, req[:serialized_data])
|
92
|
-
@stats.validated
|
93
|
-
return true
|
94
|
-
else
|
95
|
-
@stats.unvalidated
|
96
|
-
Log.debug('Received an invalid signature in message.')
|
97
|
-
raise SecurityValidationFailed
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def callerid
|
102
|
-
'sshkey=%s' % Etc.getlogin
|
103
|
-
end
|
104
|
-
|
105
|
-
private
|
106
|
-
|
107
|
-
# Checks that publickey_dir and known_hosts|authorized_keys are not set at the same time.
|
108
|
-
def valid_configuration?
|
109
|
-
if @initiated_by == :client
|
110
|
-
if lookup_config_option('publickey_dir') && lookup_config_option('known_hosts')
|
111
|
-
raise('Both publickey_dir and known_hosts are defined in client config. Cannot lookup public key')
|
112
|
-
end
|
113
|
-
elsif @initiated_by == :node
|
114
|
-
if lookup_config_option('publickey_dir') && lookup_config_option('authorized_keys')
|
115
|
-
raise('Both publickey_dir and authorized_keys are defiend in server config. Cannot lookup public key')
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
# Checks if the attached public key needs to be stored locally
|
121
|
-
# Overwriting is disabled by default
|
122
|
-
# - The publickey_directory config option needs to be set before
|
123
|
-
# the file will be written.
|
124
|
-
# - The directory must exist before writing.
|
125
|
-
# - The learn_public_keys configuration option must be enabled.
|
126
|
-
def write_key_to_disk(key, identity)
|
127
|
-
|
128
|
-
# Writing is disabled. Don't bother checking any other states.
|
129
|
-
return unless lookup_config_option('learn_public_keys') =~ /^1|y/
|
130
|
-
|
131
|
-
publickey_dir = lookup_config_option('publickey_dir')
|
132
|
-
|
133
|
-
unless publickey_dir
|
134
|
-
Log.info("Public key sent with request but no publickey_dir defined in configuration. Not writing key to disk.")
|
135
|
-
return
|
136
|
-
end
|
137
|
-
|
138
|
-
if File.directory?(publickey_dir)
|
139
|
-
if File.exists?(old_keyfile = File.join(publickey_dir, "#{identity}_pub.pem"))
|
140
|
-
old_key = File.read(old_keyfile).chomp
|
141
|
-
|
142
|
-
unless old_key == key
|
143
|
-
unless lookup_config_option('overwrite_stored_keys', 'n') =~ /^1|y/
|
144
|
-
Log.warn("Public key sent from '%s' does not match the stored key. Not overwriting." % identity)
|
145
|
-
else
|
146
|
-
Log.warn("Public key sent from '%s' does not match the stored key. Overwriting." % identity)
|
147
|
-
File.open(File.join(publickey_dir, "#{identity}_pub.pem"), 'w') { |f| f.puts key }
|
148
|
-
end
|
149
|
-
end
|
150
|
-
else
|
151
|
-
Log.debug("Discovered a new public key for '%s'. Writing to '%s'" % [identity, publickey_dir])
|
152
|
-
File.open(File.join(publickey_dir, "#{identity}_pub.pem"), 'w') { |f| f.puts key }
|
153
|
-
end
|
154
|
-
else
|
155
|
-
raise("Cannot write public key to '%s'. Directory does not exist." % publickey_dir)
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
# Fetches the correct configuration option for a client or a server
|
160
|
-
def lookup_config_option(opt, default = nil)
|
161
|
-
if @initiated_by == :client
|
162
|
-
result = @config.pluginconf.fetch("sshkey.client.#{opt}", default)
|
163
|
-
|
164
|
-
if result && ["authorized_keys", "private_key", "send_key", "publickey_dir", "known_hosts"].include?(opt)
|
165
|
-
return File.expand_path(result)
|
166
|
-
else
|
167
|
-
return result
|
168
|
-
end
|
169
|
-
elsif @initiated_by == :node
|
170
|
-
return @config.pluginconf.fetch("sshkey.server.#{opt}", default)
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
# Creates a hash of the fields used to sign a message
|
175
|
-
# Response messages use the msg, msgtime and requestid fields.
|
176
|
-
# Request messages use the same fields as response, but include
|
177
|
-
# ttl and callerid.
|
178
|
-
def create_hash_fields(msg, msgtime, requestid, ttl = nil, callerid = nil)
|
179
|
-
map = {:msg => msg,
|
180
|
-
:msgtime => msgtime,
|
181
|
-
:requestid => requestid}
|
182
|
-
|
183
|
-
# Check if this is a server hash
|
184
|
-
return map if (ttl == nil && callerid == nil)
|
185
|
-
|
186
|
-
map[:ttl] = ttl
|
187
|
-
map[:callerid] = callerid
|
188
|
-
|
189
|
-
map
|
190
|
-
end
|
191
|
-
|
192
|
-
# Adds a key to a signer object and disables ssh-agent
|
193
|
-
def add_key_to_signer(signer, key)
|
194
|
-
signer.add_key_file(key)
|
195
|
-
signer.use_agent = false
|
196
|
-
end
|
197
|
-
|
198
|
-
# Creates a signed hash of fields using the node's private key
|
199
|
-
def makehash(data)
|
200
|
-
signer = SSH::Key::Signer.new
|
201
|
-
|
202
|
-
# Check if the client is signing its request with a predefined
|
203
|
-
# private key. If this is the case, disable ssh-agent.
|
204
|
-
if @initiated_by == :client && (private_key = lookup_config_option('private_key'))
|
205
|
-
unless File.exists?(private_key)
|
206
|
-
raise("Cannot sign request - private key not found: '%s'" % private_key)
|
207
|
-
else
|
208
|
-
add_key_to_signer(signer, private_key)
|
209
|
-
end
|
210
|
-
elsif @initiated_by == :node
|
211
|
-
if private_key = lookup_config_option('private_key')
|
212
|
-
add_key_to_signer(signer, private_key)
|
213
|
-
else
|
214
|
-
# First try and default to ssh_host_dsa_key
|
215
|
-
if File.exists?(private_key = '/etc/ssh/ssh_host_dsa_key')
|
216
|
-
add_key_to_signer(signer, private_key)
|
217
|
-
# If that fails, try ssh_host_rsa_key
|
218
|
-
elsif File.exists?(private_key = '/etc/ssh/ssh_host_rsa_key')
|
219
|
-
add_key_to_signer(signer, private_key)
|
220
|
-
else
|
221
|
-
raise("Cannot sign reply - private key not found: 's'" % private_key)
|
222
|
-
end
|
223
|
-
end
|
224
|
-
end
|
225
|
-
|
226
|
-
# Default to using ssh-agent for key signing
|
227
|
-
signatures = signer.sign(data).collect { |s| s.signature }
|
228
|
-
Marshal.dump(signatures)
|
229
|
-
end
|
230
|
-
|
231
|
-
#Returns the contents of a key file on disk
|
232
|
-
def load_key(key)
|
233
|
-
if File.exists?(key)
|
234
|
-
return File.read(key).strip
|
235
|
-
else
|
236
|
-
nil
|
237
|
-
end
|
238
|
-
end
|
239
|
-
|
240
|
-
# Looks for a specific key in a known hosts file
|
241
|
-
def find_key_in_known_hosts(hostname, known_hosts)
|
242
|
-
key = nil
|
243
|
-
search_for = /^#{hostname}/
|
244
|
-
|
245
|
-
if File.exists?(known_hosts)
|
246
|
-
File.read(known_hosts).each_line do |line|
|
247
|
-
fields = line.split
|
248
|
-
fields[0].split(',').each do |maybehost|
|
249
|
-
if maybehost =~ search_for
|
250
|
-
fields = line.split
|
251
|
-
key = fields[-2] << ' ' << fields[-1]
|
252
|
-
break
|
253
|
-
end
|
254
|
-
end
|
255
|
-
break unless key.nil?
|
256
|
-
end
|
257
|
-
end
|
258
|
-
|
259
|
-
unless key
|
260
|
-
Log.warn("Could not find a key for host '%s' in file '%s'" % [hostname, known_hosts])
|
261
|
-
raise SecurityValidationFailed
|
262
|
-
end
|
263
|
-
|
264
|
-
key
|
265
|
-
end
|
266
|
-
|
267
|
-
# Create a client verifier object which uses the correct public key
|
268
|
-
def client_verifier(senderid)
|
269
|
-
verifier = SSH::Key::Verifier.new(senderid)
|
270
|
-
verifier.use_authorized_keys = false
|
271
|
-
|
272
|
-
if publickey_dir = lookup_config_option('publickey_dir')
|
273
|
-
Log.debug("Using public key directory: '%s'" % publickey_dir)
|
274
|
-
verifier.add_public_key_data(find_shared_public_key(publickey_dir, senderid))
|
275
|
-
|
276
|
-
elsif (known_hosts = lookup_config_option('known_hosts'))
|
277
|
-
Log.debug("Using custom known_hosts file: '%s'" % known_hosts)
|
278
|
-
verifier.add_public_key_data(find_key_in_known_hosts(senderid, known_hosts))
|
279
|
-
|
280
|
-
elsif (authorized_keys = lookup_config_option('authorized_keys'))
|
281
|
-
Log.debug("Found custom authorized_keys file: '%s'" % authorized_keys)
|
282
|
-
verifier.authorized_keys_file = authorized_keys
|
283
|
-
verifier.use_authorized_keys = true
|
284
|
-
|
285
|
-
else
|
286
|
-
begin
|
287
|
-
user = Etc.getlogin
|
288
|
-
known_hosts = File.join(Etc.getpwnam(user).dir, '.ssh', 'known_hosts')
|
289
|
-
Log.debug("Using default known_hosts file for user '%s': ''" % [user, known_hosts])
|
290
|
-
verifier.add_public_key_data(find_key_in_known_hosts(senderid, "%s" % known_hosts))
|
291
|
-
rescue => e
|
292
|
-
raise("Cannot find known_hosts file for user '%s': '%s'" % [user, known_hosts])
|
293
|
-
end
|
294
|
-
end
|
295
|
-
|
296
|
-
verifier.use_agent = false
|
297
|
-
|
298
|
-
verifier
|
299
|
-
end
|
300
|
-
|
301
|
-
# Looks for a public key in a shared directory
|
302
|
-
def find_shared_public_key(dir, id)
|
303
|
-
unless File.directory?(dir)
|
304
|
-
raise("Cannot read shared public key directory: '%s'" % dir)
|
305
|
-
end
|
306
|
-
|
307
|
-
if File.exists?(key_file = File.join(dir, "#{id}_pub.pem"))
|
308
|
-
return File.read(key_file)
|
309
|
-
else
|
310
|
-
Log.warn("Cannot find public key for id '%s': '%s'" % [id, File.join(dir, "#{id}_pub.pem")])
|
311
|
-
raise SecurityValidationFailed
|
312
|
-
end
|
313
|
-
end
|
314
|
-
|
315
|
-
# Create a node verifier object which uses the correct public key
|
316
|
-
def node_verifier(callerid, registration = false, pubkey = nil)
|
317
|
-
user = callerid.split('=')[-1]
|
318
|
-
verifier = SSH::Key::Verifier.new(user)
|
319
|
-
verifier.use_agent = false
|
320
|
-
|
321
|
-
# Here we deal with the special case where a registration message
|
322
|
-
# is being validated. send_key has to be defined in the configuration.
|
323
|
-
# TODO : This is a stop gap measure we should remove when we fix
|
324
|
-
# registration
|
325
|
-
if registration && pubkey
|
326
|
-
Log.debug("Found registration message. Using sender's public key")
|
327
|
-
verifier.add_public_key_data(pubkey)
|
328
|
-
verifier.use_authorized_keys = false
|
329
|
-
|
330
|
-
elsif registration && !pubkey
|
331
|
-
Log.warn("Cannot verify registration request. Server did not send its public key")
|
332
|
-
raise SecurityValidationFailed
|
333
|
-
|
334
|
-
elsif publickey_dir = lookup_config_option('publickey_dir')
|
335
|
-
if File.directory?(publickey_dir)
|
336
|
-
Log.debug("Found shared public key directory: '%s'" % publickey_dir)
|
337
|
-
verifier.add_public_key_data(find_shared_public_key(publickey_dir, user))
|
338
|
-
verifier.use_authorized_keys = false
|
339
|
-
else
|
340
|
-
raise("Public key directory '%s' does not exist" % publickey_dir)
|
341
|
-
end
|
342
|
-
|
343
|
-
elsif (authorized_keys = lookup_config_option('authorized_keys'))
|
344
|
-
authorized_keys = authorized_keys.sub('%u') { |c| user }
|
345
|
-
Log.debug("Found custom authorized_keys file: '%s'" % authorized_keys)
|
346
|
-
verifier.authorized_keys_file = authorized_keys
|
347
|
-
|
348
|
-
else
|
349
|
-
begin
|
350
|
-
authorized_keys = File.join(Etc.getpwnam(user).dir, '.ssh', 'authorized_keys')
|
351
|
-
Log.debug("No authorized_keys file or publickey_dir specified. Using '%s'" % authorized_keys)
|
352
|
-
verifier.authorized_keys_file = authorized_keys
|
353
|
-
rescue => e
|
354
|
-
raise("Cannot find authorized_keys file for user '%s': '%s'" % [user, authorized_keys])
|
355
|
-
end
|
356
|
-
end
|
357
|
-
|
358
|
-
verifier
|
359
|
-
end
|
360
|
-
end
|
361
|
-
end
|
362
|
-
end
|