dtk-node-agent 0.5.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +15 -0
  2. data/Gemfile +4 -0
  3. data/Gemfile.lock +18 -0
  4. data/README.md +42 -0
  5. data/bin/dtk-node-agent +16 -0
  6. data/dtk-node-agent.gemspec +38 -0
  7. data/lib/config/install.config +12 -0
  8. data/lib/dtk-node-agent/installer.rb +142 -0
  9. data/lib/dtk-node-agent/version.rb +3 -0
  10. data/mcollective_additions/plugins/README.md +1 -0
  11. data/mcollective_additions/plugins/v1.2/agent/discovery.rb +39 -0
  12. data/mcollective_additions/plugins/v1.2/agent/get_log_fragment.ddl +15 -0
  13. data/mcollective_additions/plugins/v1.2/agent/get_log_fragment.rb +79 -0
  14. data/mcollective_additions/plugins/v1.2/agent/git_access.ddl +9 -0
  15. data/mcollective_additions/plugins/v1.2/agent/git_access.rb +79 -0
  16. data/mcollective_additions/plugins/v1.2/agent/netstat.ddl +9 -0
  17. data/mcollective_additions/plugins/v1.2/agent/netstat.rb +34 -0
  18. data/mcollective_additions/plugins/v1.2/agent/puppet_apply.ddl +9 -0
  19. data/mcollective_additions/plugins/v1.2/agent/puppet_apply.rb +630 -0
  20. data/mcollective_additions/plugins/v1.2/agent/rpcutil.ddl +204 -0
  21. data/mcollective_additions/plugins/v1.2/agent/rpcutil.rb +101 -0
  22. data/mcollective_additions/plugins/v1.2/facts/pbuilder_facts.rb +35 -0
  23. data/mcollective_additions/plugins/v2.2/agent/dev_manager.ddl +9 -0
  24. data/mcollective_additions/plugins/v2.2/agent/dev_manager.rb +69 -0
  25. data/mcollective_additions/plugins/v2.2/agent/discovery.rb +39 -0
  26. data/mcollective_additions/plugins/v2.2/agent/dtk_node_agent_git_client.rb +94 -0
  27. data/mcollective_additions/plugins/v2.2/agent/execute_tests.ddl +9 -0
  28. data/mcollective_additions/plugins/v2.2/agent/execute_tests.rb +64 -0
  29. data/mcollective_additions/plugins/v2.2/agent/get_log_fragment.ddl +15 -0
  30. data/mcollective_additions/plugins/v2.2/agent/get_log_fragment.rb +79 -0
  31. data/mcollective_additions/plugins/v2.2/agent/git_access.ddl +9 -0
  32. data/mcollective_additions/plugins/v2.2/agent/git_access.rb +72 -0
  33. data/mcollective_additions/plugins/v2.2/agent/netstat.ddl +9 -0
  34. data/mcollective_additions/plugins/v2.2/agent/netstat.rb +34 -0
  35. data/mcollective_additions/plugins/v2.2/agent/ps.ddl +9 -0
  36. data/mcollective_additions/plugins/v2.2/agent/ps.rb +37 -0
  37. data/mcollective_additions/plugins/v2.2/agent/puppet_apply.ddl +9 -0
  38. data/mcollective_additions/plugins/v2.2/agent/puppet_apply.rb +633 -0
  39. data/mcollective_additions/plugins/v2.2/agent/puppet_cancel.ddl +10 -0
  40. data/mcollective_additions/plugins/v2.2/agent/puppet_cancel.rb +78 -0
  41. data/mcollective_additions/plugins/v2.2/agent/rpcutil.ddl +204 -0
  42. data/mcollective_additions/plugins/v2.2/agent/rpcutil.rb +101 -0
  43. data/mcollective_additions/plugins/v2.2/agent/sync_agent_code.ddl +10 -0
  44. data/mcollective_additions/plugins/v2.2/agent/sync_agent_code.rb +85 -0
  45. data/mcollective_additions/plugins/v2.2/agent/tail.ddl +11 -0
  46. data/mcollective_additions/plugins/v2.2/agent/tail.rb +67 -0
  47. data/mcollective_additions/plugins/v2.2/connector/r8stomp.rb +238 -0
  48. data/mcollective_additions/plugins/v2.2/connector/stomp.rb +349 -0
  49. data/mcollective_additions/plugins/v2.2/connector/stomp_em.rb +191 -0
  50. data/mcollective_additions/plugins/v2.2/facts/pbuilder_facts.rb +35 -0
  51. data/mcollective_additions/plugins/v2.2/security/sshkey.ddl +9 -0
  52. data/mcollective_additions/plugins/v2.2/security/sshkey.rb +362 -0
  53. data/mcollective_additions/server.cfg +22 -0
  54. data/puppet_additions/modules/r8/lib/puppet/type/r8_export_file.rb +53 -0
  55. data/puppet_additions/modules/r8/manifests/export_variable.rb +10 -0
  56. data/puppet_additions/puppet_lib_base/puppet/indirector/catalog/r8_storeconfig_backend.rb +48 -0
  57. data/puppet_additions/puppet_lib_base/puppet/indirector/r8_storeconfig_backend.rb +4 -0
  58. data/puppet_additions/puppet_lib_base/puppet/indirector/resource/r8_storeconfig_backend.rb +72 -0
  59. data/src/etc/init.d/ec2-run-user-data +95 -0
  60. data/src/etc/logrotate.d/mcollective +10 -0
  61. data/src/etc/logrotate.d/puppet +7 -0
  62. metadata +189 -0
@@ -0,0 +1,191 @@
1
+ require 'eventmachine'
2
+ module MCollective
3
+ module Connector
4
+ # Handles sending and receiving messages over the Stomp protocol
5
+ #
6
+ # This plugin supports version 1.1 or 1.1.6 and newer of the Stomp rubygem
7
+ # the versions between those had multi threading issues.
8
+ #
9
+ # For all versions you can configure it as follows:
10
+ #
11
+ # connector = stomp
12
+ # plugin.stomp.host = stomp.your.net
13
+ # plugin.stomp.port = 6163
14
+ # plugin.stomp.user = you
15
+ # plugin.stomp.password = secret
16
+ #
17
+ # For versions of ActiveMQ that supports message priorities
18
+ # you can set a priority, this will cause a "priority" header
19
+ # to be emitted if present:
20
+ #
21
+ # plugin.stomp.priority = 4
22
+ #
23
+ class Stomp_em<Base
24
+ #this is effectively a singleton, but not mixin in Singleton beacuse mcollective isstantiates with new
25
+ #TODO: may look at making singleton and patching with making :new public, recognizing that will only be called once
26
+
27
+ module StompClient
28
+ include EM::Protocols::Stomp
29
+ def initialize(*args)
30
+ super(*args)
31
+ #TODO: if cannot fidn user and log this shoudl be error
32
+ conn_opts = (args.last.kind_of?(Hash))? args.last : {}
33
+ @login = conn_opts[:login]
34
+ @passcode = conn_opts[:passcode]
35
+ @connected = false
36
+ end
37
+
38
+ def connection_completed
39
+ connect :login => @login, :passcode => @passcode
40
+ end
41
+
42
+ def receive_msg msg
43
+ if msg.command == "CONNECTED"
44
+ pp [:is_connected]
45
+ @connected = true
46
+ else
47
+ Stomp_em.process(msg)
48
+ end
49
+ end
50
+
51
+ def is_connected?()
52
+ @connected
53
+ end
54
+ end
55
+
56
+ def initialize
57
+ @config = Config.instance
58
+ @subscriptions = []
59
+ @connected = nil
60
+ @connection = nil
61
+ end
62
+
63
+ def set_context(context)
64
+ @@decode_context = context[:decode_context]
65
+ @@multiplexer = context[:multiplexer]
66
+ end
67
+
68
+ def disconnect
69
+ #TODO: need to write
70
+ end
71
+
72
+ # Connects to the Stomp middleware
73
+ def connect
74
+ if @connection
75
+ Log.debug("Already connection, not re-initializing connection")
76
+ return
77
+ end
78
+ begin
79
+ host = nil
80
+ port = nil
81
+ user = nil
82
+ password = nil
83
+ @@base64 = false
84
+
85
+ @@base64 = get_bool_option("stomp.base64", false)
86
+ @@msgpriority = get_option("stomp.priority", 0).to_i
87
+
88
+ # Maintain backward compat for older stomps
89
+ host = get_env_or_option("STOMP_SERVER", "stomp.host")
90
+ port = get_env_or_option("STOMP_PORT", "stomp.port", 6163).to_i
91
+ user = get_env_or_option("STOMP_USER", "stomp.user")
92
+ password = get_env_or_option("STOMP_PASSWORD", "stomp.password")
93
+
94
+ #TODO: assume reactor is running already
95
+ @connection = EM.connect host, port, StompClient, :login => user, :passcode => password
96
+ Log.debug("Connecting to #{host}:#{port}")
97
+ rescue Exception => e
98
+ pp e.backtrace[0..5]
99
+ raise("Could not connect to Stomp Server: #{e}")
100
+ end
101
+ end
102
+
103
+ def wait_until_connected?
104
+ return if @connected
105
+ loop do
106
+ return if @connected = @connection.is_connected?
107
+ sleep 1
108
+ end
109
+ end
110
+
111
+
112
+ def self.process(msg)
113
+ # STOMP puts the payload in the body variable, pass that
114
+ # into the payload of MCollective::Request and discard all the
115
+ # other headers etc that stomp provides
116
+ raw_msg =
117
+ if @@base64
118
+ Request.new(SSL.base64_decode(msg.body))
119
+ else
120
+ Request.new(msg.body)
121
+ end
122
+ msg = @@decode_context.r8_decode_receive(raw_msg)
123
+ @@multiplexer.process_response(msg,msg[:requestid])
124
+ end
125
+
126
+ #TODO: make automic subscribe_and_send because need subscribe to happen before send does
127
+ # Subscribe to a topic or queue
128
+ def subscribe(source)
129
+ unless @subscriptions.include?(source)
130
+ EM::defer do
131
+ Log.debug("Subscribing to #{source}")
132
+ wait_until_connected?
133
+ @connection.subscribe(source)
134
+ @subscriptions << source
135
+ end
136
+ end
137
+ end
138
+ def subscribe_and_send(source,destination,body,params={})
139
+ EM::defer do
140
+ wait_until_connected?
141
+ unless @subscriptions.include?(source)
142
+ Log.debug("Subscribing to #{source}")
143
+ @connection.subscribe(source)
144
+ @subscriptions << source
145
+ end
146
+ @connection.send(destination,body,params)
147
+ end
148
+ end
149
+
150
+ # Subscribe to a topic or queue
151
+ def unsubscribe(source)
152
+ #TODO
153
+ end
154
+ private
155
+ def get_env_or_option(env, opt, default=nil)
156
+ return ENV[env] if ENV.include?(env)
157
+ return @config.pluginconf[opt] if @config.pluginconf.include?(opt)
158
+ return default if default
159
+
160
+ raise("No #{env} environment or plugin.#{opt} configuration option given")
161
+ end
162
+
163
+ # looks for a config option, accepts an optional default
164
+ #
165
+ # raises an exception when it cant find a value anywhere
166
+ def get_option(opt, default=nil)
167
+ return @config.pluginconf[opt] if @config.pluginconf.include?(opt)
168
+ return default if default
169
+
170
+ raise("No plugin.#{opt} configuration option given")
171
+ end
172
+
173
+ # gets a boolean option from the config, supports y/n/true/false/1/0
174
+ def get_bool_option(opt, default)
175
+ return default unless @config.pluginconf.include?(opt)
176
+
177
+ val = @config.pluginconf[opt]
178
+
179
+ if val =~ /^1|yes|true/
180
+ return true
181
+ elsif val =~ /^0|no|false/
182
+ return false
183
+ else
184
+ return default
185
+ end
186
+ end
187
+ end
188
+ end
189
+ end
190
+
191
+ # vi:tabstop=4:expandtab:ai
@@ -0,0 +1,35 @@
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
+ yaml_file = '/etc/mcollective/facts.yaml'
13
+ if File.exists?(yaml_file)
14
+ yaml_facts = YAML.load_file(yaml_file)
15
+ ret.merge!(yaml_facts)
16
+ end
17
+ ret
18
+ end
19
+
20
+ def get_pbuilderid()
21
+ ret = nil
22
+ begin
23
+ addr = "169.254.169.254"
24
+ wait_sec = 2
25
+ Timeout::timeout(wait_sec) {open("http://#{addr}:80/")}
26
+ ret = OpenURI.open_uri("http://#{addr}/2008-02-01/meta-data/instance-id").read
27
+ rescue Timeout::Error
28
+ rescue
29
+ #TODO: unexpected; write to log what error is
30
+ end
31
+ ret
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,9 @@
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'
@@ -0,0 +1,362 @@
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