emissary 1.3.20 → 1.3.21

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,37 @@
1
+ # Copyright 2010 The New York Times
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ #
16
+ module Emissary
17
+ class Agent::Ping < Agent
18
+ def valid_methods
19
+ [:ping, :pong]
20
+ end
21
+
22
+ def ping
23
+ reply = message.response
24
+ reply.method = :pong
25
+
26
+ ::Emissary.logger.debug "Received PING: originator: #{message.originator}"
27
+ ::Emissary.logger.debug "Sending PONG : originator: #{reply.originator}"
28
+
29
+ reply
30
+ end
31
+
32
+ def pong
33
+ ::Emissary.logger.debug "Received PONG"
34
+ throw :skip_implicit_response
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,26 @@
1
+ # Copyright 2010 The New York Times
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ #
16
+ module Emissary
17
+ class Agent::Proxy < Agent
18
+ def valid_methods
19
+ [ :any ]
20
+ end
21
+
22
+ def activate
23
+ throw :skip_implicit_response
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,233 @@
1
+ # Copyright 2010 The New York Times
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ #
16
+ require 'escape'
17
+
18
+ module Emissary
19
+ class Agent::Rabbitmq < Agent
20
+ NIMBUL_VHOST = '/nimbul'
21
+
22
+ NODE_CONFIG_ACL = '^i-[a-f0-9.]+$'
23
+ NODE_READ_ACL = '^(amq.*|i-[a-f0-9.]+|request.%%ID%%.*)$'
24
+ NODE_WRITE_ACL = '^(amq.*|i-[a-f0-9.]+|(startup|info|shutdown).%%ID%%.*|nimbul)$'
25
+
26
+ QUEUE_INFO_ITEMS = %w[
27
+ name durable auto_delete arguments pid owner_pid
28
+ exclusive_consumer_pid exclusive_consumer_tag
29
+ messages_ready messages_unacknowledged messages_uncommitted
30
+ messages acks_uncommitted consumers transactions memory
31
+ ]
32
+
33
+ EXCHANGE_INFO_ITEMS = %w[
34
+ name type durable auto_delete arguments
35
+ ]
36
+
37
+ CONNECTION_INFO_ITEMS = %w[
38
+ pid address port peer_address peer_port state channels user
39
+ vhost timeout frame_max client_properties recv_oct recv_cnt
40
+ send_oct send_cnt send_pend
41
+ ]
42
+
43
+ CHANNEL_INFO_ITEMS = %w[
44
+ pid connection number user vhost transactional consumer_count
45
+ messages_unacknowledged acks_uncommitted prefetch_count
46
+ ]
47
+
48
+ BINDINGS_INFO_COLUMNS = %w[ exchange_name queue_name routing_key arguments ]
49
+ CONSUMER_INFO_COLUMNS = %w[ queue_name channel_process_id consumer_tag must_acknowledge ]
50
+
51
+ class CommandExecutionError < StandardError; end
52
+
53
+ def valid_methods
54
+ [
55
+ :add_user,
56
+ :delete_user,
57
+ :change_password,
58
+ :list_users,
59
+
60
+ :add_vhost,
61
+ :delete_vhost,
62
+ :list_vhosts,
63
+
64
+ :add_node_account,
65
+ :del_node_account,
66
+
67
+ :list_user_vhosts,
68
+ :list_vhost_users,
69
+
70
+ :list_queues,
71
+ :list_bindings,
72
+ :list_exchanges,
73
+ :list_connections,
74
+ :list_channels,
75
+ :list_consumers
76
+ ]
77
+ end
78
+
79
+ def list_queues(vhost)
80
+ vhost = vhost.empty? ? '/' : vhost
81
+ rabbitmqctl(:list_queues, '-p', vhost, QUEUE_INFO_ITEMS.join(" ")).collect do |line|
82
+ Hash[*QUEUE_INFO_ITEMS.zip(line.split(/\s+/)).flatten]
83
+ end
84
+ end
85
+
86
+ def list_bindings(vhost)
87
+ vhost = vhost.empty? ? '/' : vhost
88
+ rabbitmqctl(:list_bindings, '-p', vhost).collect do |line|
89
+ Hash[*BINDINGS_INFO_COLUMNS.zip(line.split(/\s+/)).flatten]
90
+ end
91
+ end
92
+
93
+ def list_exchanges(vhost)
94
+ vhost = vhost.empty? ? '/' : vhost
95
+ rabbitmqctl(:list_exchanges, '-p', vhost, EXCHANGE_INFO_ITEMS.join(" ")).collect do |line|
96
+ Hash[*EXCHANGE_INFO_ITEMS.zip(line.split(/\s+/)).flatten]
97
+ end
98
+ end
99
+
100
+ def list_connections
101
+ rabbitmqctl(:list_connections, CONNECTION_INFO_ITEMS.join(" ")).collect do |line|
102
+ Hash[*CONNECTION_INFO_ITEMS.zip(line.split(/\s+/)).flatten]
103
+ end
104
+ end
105
+
106
+ def list_channels
107
+ rabbitmqctl(:list_channels, CHANNEL_INFO_ITEMS.join(" ")).collect do |line|
108
+ Hash[*CHANNEL_INFO_ITEMS.zip(line.split(/\s+/)).flatten]
109
+ end
110
+ end
111
+
112
+ def list_consumers(vhost)
113
+ vhost = vhost.empty? ? '/' : vhost
114
+ rabbitmqctl(:list_consumers, '-p', vhost).collect do |line|
115
+ Hash[*CONSUMER_INFO_COLUMNS.zip(line.split(/\s+/)).flatten]
116
+ end
117
+ end
118
+
119
+ def list_users
120
+ rabbitmqctl(:list_users)
121
+ end
122
+
123
+ def list_vhosts
124
+ rabbitmqctl(:list_vhosts)
125
+ end
126
+
127
+ def list_vhost_users(vhost)
128
+ vhost = vhost.empty? ? '/' : vhost
129
+ rabbitmqctl(:list_permissions, '-p', vhost).flatten.select { |l|
130
+ !l.nil?
131
+ }.collect {
132
+ |l| l.split(/\s+/)[0]
133
+ }
134
+ end
135
+
136
+ def list_user_vhosts(user)
137
+ list_vhosts.select { |vhost| list_vhost_users(vhost).include? user }
138
+ end
139
+
140
+ def set_vhost_permissions(user, vhost, config, write, read)
141
+ vhost = vhost.empty? ? '/' : vhost
142
+ rabbitmqctl(:set_permissions, '-p', vhost, user, config, write, read)
143
+ end
144
+
145
+ def del_vhost_permissions(user, vhost)
146
+ vhost = vhost.empty? ? '/' : vhost
147
+ rabbitmqctl(:clear_permissions, '-p', vhost, user)
148
+ end
149
+
150
+ def add_node_account_acl(user, namespace_id)
151
+ config_acl = NODE_CONFIG_ACL.gsub('%%ID%%', namespace_id.to_s)
152
+ write_acl = NODE_WRITE_ACL.gsub('%%ID%%', namespace_id.to_s)
153
+ read_acl = NODE_READ_ACL.gsub('%%ID%%', namespace_id.to_s)
154
+
155
+ begin
156
+ set_vhost_permissions(user, NIMBUL_VHOST, config_acl, write_acl, read_acl)
157
+ rescue CommandExecutionError => e
158
+ "problem adding account acls for user: #{user}: #{e.message}"
159
+ else
160
+ "successfully added account acls for user: #{user}"
161
+ end
162
+ end
163
+
164
+ def add_node_account(user, password, namespace_id)
165
+ begin
166
+ add_user(user, password)
167
+ add_node_account_acl(user, namespace_id.to_s)
168
+ rescue CommandExecutionError => e
169
+ "failed to add new node account: #{user}:#{namespace_id.to_s}"
170
+ end
171
+ end
172
+
173
+ def del_node_account_acl(user, vhost)
174
+ begin
175
+ del_vhost_permissions(user, vhost)
176
+ rescue CommandExecutionError => e
177
+ "problem unmapping user from vhost: #{user}:#{vhost} #{e.message}"
178
+ else
179
+ "successfully unmapped user from vhost: #{user}:#{vhost}"
180
+ end
181
+ end
182
+
183
+ def add_vhost(path)
184
+ begin
185
+ !!rabbitmqctl(:add_vhost, path)
186
+ rescue CommandExecutionError => e
187
+ raise e unless e.message.include? 'vhost_already_exists'
188
+ end
189
+ end
190
+
191
+ def add_user(user, pass)
192
+ begin
193
+ !!rabbitmqctl(:add_user, user, pass)
194
+ rescue CommandExecutionError => e
195
+ raise e unless e.message.include? 'user_already_exists'
196
+ end
197
+ end
198
+
199
+ def change_password(user, pass)
200
+ begin
201
+ !!rabbitmqctl(:change_password, user, pass)
202
+ rescue CommandExecutionError => e
203
+ return false if e.message.include? 'no_such_user'
204
+ raise e
205
+ end
206
+ end
207
+
208
+ def delete_user(user)
209
+ begin
210
+ !!rabbitmqctl(:delete_user, user)
211
+ rescue CommandExecutionError => e
212
+ raise e unless e.message.include? 'no_such_user'
213
+ end
214
+ end
215
+
216
+ def delete_vhost(path)
217
+ begin
218
+ !!rabbitmqctl(:delete_vhost, path)
219
+ rescue CommandExecutionError => e
220
+ raise e unless e.message.include? 'no_such_vhost'
221
+ end
222
+ end
223
+
224
+ def rabbitmqctl(*args)
225
+ result = []
226
+ `rabbitmqctl #{Escape.shell_command([*args.collect{|a| a.to_s}])} 2>&1`.each do |line|
227
+ raise CommandExecutionError, $1 if line =~ /Error: (.*)/
228
+ result << line.chomp unless line =~ /\.\.\./
229
+ end
230
+ result
231
+ end
232
+ end
233
+ end
@@ -0,0 +1,152 @@
1
+ # Copyright 2010 The New York Times
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ #
16
+ require 'digest/md5'
17
+ require 'base64'
18
+ require 'etc'
19
+
20
+ module Emissary
21
+ class Agent::Sshkeys < Agent
22
+ AUTH_KEY_FILE = '.ssh/authorized_keys'
23
+
24
+ attr_reader :user
25
+
26
+ def valid_methods
27
+ [ :add, :delete, :update ]
28
+ end
29
+
30
+ def post_init
31
+ begin
32
+ @user = Etc.getpwnam(args.shift)
33
+ rescue ArgumentError => e
34
+ if e.message =~ /can't find user/
35
+ raise "User '#{args.first}' does not exist on this system"
36
+ else
37
+ raise "Unhandled error attempting to retrieve data on user [#{args.first}]: #{e.message}"
38
+ end
39
+ end
40
+ end
41
+
42
+ def add pubkey
43
+ raise "Missing 'key_uri' argument - can't download key!" if pubkey.nil?
44
+
45
+ pubkey_name = Digest::MD5.hexdigest(pubkey.split(/\s+/).join(' ').chomp)
46
+
47
+ begin
48
+ keys = get_keys(user)
49
+ if not keys.has_key?(pubkey_name)
50
+ keys[pubkey_name] = pubkey
51
+ write_keys(user, keys)
52
+ result = "Successfully added key [#{pubkey_name}] to user [#{user.name}]"
53
+ else
54
+ result = "Could not add key [#{pubkey_name}] to user [#{user.name}] - key already exists!"
55
+ end
56
+ rescue Exception => e
57
+ raise Exception, 'Possibly unable to add user key - error was: ' + e.message, caller
58
+ end
59
+
60
+ return result
61
+ end
62
+
63
+ def delete pubkey
64
+ return 'No authorized_keys file - nothing changed' if not File.exists?(File.join(user.dir, AUTH_KEY_FILE))
65
+
66
+ keyname = Digest::MD5.hexdigest(pubkey)
67
+ begin
68
+ keys = get_keys(user)
69
+ if keys.has_key?(keyname)
70
+ keys.delete(keyname)
71
+ write_keys(user, keys)
72
+ result = "Successfully removed key [#{keyname}] from user [#{user.name}]"
73
+ else
74
+ result = "Could not remove key [#{keyname}] from user [#{user.name}] - key not there!"
75
+ end
76
+ rescue Exception => e
77
+ raise "Possibly unable to remove key #{keyname} for user #{user.name} - error was: #{e.message}"
78
+ end
79
+
80
+ return result
81
+ end
82
+
83
+ def update oldkey, newkey
84
+ return 'Not implemented'
85
+ end
86
+
87
+ private
88
+
89
+ def write_keys(user, keys)
90
+ auth_file_do(user, 'w') do |fp|
91
+ fp << keys.values.join("\n")
92
+ end
93
+ end
94
+
95
+ def get_keys(user)
96
+ ::Emissary.logger.debug "retreiving ssh keys from file #{File.join(user.dir, AUTH_KEY_FILE)}"
97
+
98
+ keys = {}
99
+ auth_file_do(user, 'r') do |fp|
100
+ fp.readlines.each do |line|
101
+ keys[Digest::MD5.hexdigest(line.chomp)] = line.chomp
102
+ end
103
+ end
104
+
105
+ return keys
106
+ end
107
+
108
+ def auth_file_setup(user)
109
+ user_auth_file = File.join(user.dir, AUTH_KEY_FILE)
110
+ user_ssh_dir = File.dirname(user_auth_file)
111
+
112
+ begin
113
+ if not File.directory?(user_ssh_dir)
114
+ Dir.mkdir(user_ssh_dir)
115
+ File.open(user_ssh_dir) do |f|
116
+ f.chown(user.uid, user.gid)
117
+ f.chmod(0700)
118
+ end
119
+ end
120
+ if not File.exists?(user_auth_file)
121
+ File.open(user_auth_file, 'a') do |f|
122
+ f.chown(user.uid, user.gid)
123
+ f.chmod(0600)
124
+ end
125
+ end
126
+ rescue Exception => e
127
+ raise "Error creating #{user_auth_file} -- #{e.message}"
128
+ end
129
+
130
+ return user_auth_file
131
+ end
132
+
133
+ def auth_file_do(user, mode = 'r', &block)
134
+ begin
135
+ auth_file = auth_file_setup(user)
136
+ File.open(auth_file, mode) do |f|
137
+ f.flock File::LOCK_EX
138
+ yield(f)
139
+ f.flock File::LOCK_UN
140
+ end
141
+ rescue Exception => e
142
+ case mode
143
+ when 'r': mode = 'reading from'
144
+ when 'a': mode = 'appending to'
145
+ when 'w': mode = 'writing to'
146
+ else mode = "unknown operation #{mode} for File.open() on "
147
+ end
148
+ raise "Error #{mode} file '#{auth_file}' -- #{e.message}"
149
+ end
150
+ end
151
+ end
152
+ end