zepplen_aws 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -21,6 +21,7 @@ module ZepplenAWS
21
21
  class AutoDNS
22
22
  require 'colorize'
23
23
 
24
+ # Initializes the object.
24
25
  def initialize()
25
26
  @dns_precidence = 0
26
27
  @dns_pool = []
@@ -54,6 +55,7 @@ module ZepplenAWS
54
55
  end
55
56
  reduce_dns_pool()
56
57
  process_zone_updates()
58
+ return nil
57
59
  end
58
60
 
59
61
  private
@@ -23,9 +23,13 @@ module ZepplenAWS
23
23
  require 'aws-sdk'
24
24
 
25
25
  autoload :EC2, 'zepplen_aws/aws/ec2'
26
+ autoload :S3, 'zepplen_aws/aws/s3'
26
27
  autoload :ELB, 'zepplen_aws/aws/elb'
27
28
  autoload :Route53, 'zepplen_aws/aws/route53'
29
+ autoload :DynamoDB, 'zepplen_aws/aws/dynamo_db'
30
+ autoload :InstanceData, 'zepplen_aws/aws/instance_data'
28
31
 
32
+ # Sets the AWS Configuration from the Env class.
29
33
  def self.init!()
30
34
  ::AWS.config(:access_key_id => Env.options[:aws_access_key_id], :secret_access_key => Env.options[:aws_secret_access_key])
31
35
  end
@@ -0,0 +1,29 @@
1
+ #Copyright 2013 Mark Trimmer
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
+ module ZepplenAWS
16
+ module AWS
17
+ class DynamoDB
18
+
19
+ def initialize()
20
+ @object = ::AWS::DynamoDB.new()
21
+ end
22
+
23
+ def method_missing(method, *args)
24
+ @object.public_send(method, *args)
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,76 @@
1
+ #Copyright 2013 Mark Trimmer
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
+ module ZepplenAWS
16
+ module AWS
17
+ class InstanceData
18
+
19
+ def initialize(address=nil, base_path=nil)
20
+ if(base_path)
21
+ @base_path = base_path
22
+ else
23
+ @base_path = '/latest/meta-data/'
24
+ end
25
+ if(address)
26
+ @address = address
27
+ else
28
+ @address = '169.254.169.254'
29
+ end
30
+ @net = Net::HTTP.new('169.254.169.254')
31
+ @values = []
32
+ @dirs = []
33
+ response = @net.get(@base_path)
34
+ response.body.split("\n").each do |line|
35
+ line.chomp.match(/^([^\/]*)(\/?)$/) do |match|
36
+ if(match[2] != '')
37
+ @dirs << match[1]
38
+ else
39
+ @values << match[1]
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ def to_s()
46
+ return list.to_yaml
47
+ end
48
+
49
+ def dirs()
50
+ return @dirs
51
+ end
52
+
53
+ def values()
54
+ return @values
55
+ end
56
+
57
+ def list()
58
+ to_return = []
59
+ to_return = @dirs.map{|item| "#{item}/"}
60
+ to_return += @values
61
+ return to_return
62
+ end
63
+
64
+ def [](key)
65
+ if(@dirs.include?(key))
66
+ return InstanceData.new(@address, "#{@base_path}#{key}/")
67
+ end
68
+ if(@values.include?(key))
69
+ return @net.get("#{@base_path}#{key}/").body.chomp
70
+ end
71
+ return nil
72
+ end
73
+
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,29 @@
1
+ #Copyright 2013 Mark Trimmer
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
+ module ZepplenAWS
16
+ module AWS
17
+ class S3
18
+
19
+ def initialize()
20
+ @object = ::AWS::S3.new()
21
+ end
22
+
23
+ def method_missing(method, *args)
24
+ @object.public_send(method, *args)
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -62,13 +62,21 @@ module ZepplenAWS
62
62
 
63
63
  # Allows user access to single option
64
64
  #
65
- # @param [Any] Key to retrieve
65
+ # @param [Object] Key to retrieve
66
66
  #
67
- # @return [Any] Request option
67
+ # @return [Object] Request option
68
68
  def self.[](key)
69
69
  return @options[key]
70
70
  end
71
71
 
72
+ # Allow user access to set single option
73
+ #
74
+ # @param [Object] option key
75
+ # @param [Object] option value
76
+ def self.[]=(key, value)
77
+ @options[key] = value
78
+ end
79
+
72
80
 
73
81
  # Not intented for general use
74
82
  #
@@ -0,0 +1,6 @@
1
+ module ZepplenAWS
2
+ module Exceptions
3
+ autoload :Base, 'zepplen_aws/exceptions/base'
4
+ autoload :Users, 'zepplen_aws/exceptions/users'
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module ZepplenAWS
2
+ module Exceptions
3
+ class Base < ::StandardError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,10 @@
1
+ module ZepplenAWS
2
+ module Exceptions
3
+ module Users
4
+ class NoDynamoTable < Base
5
+ end
6
+ class MissingOption < Base
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,256 @@
1
+ #Copyright 2013 Mark Trimmer
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
+ module ZepplenAWS
16
+
17
+ # = Local Server Users
18
+ #
19
+ # This class is not inteded to be used by 3rd parties
20
+ class ServerLocalUsers
21
+ require 'etc'
22
+
23
+ def initialize(dynamo_table = nil)
24
+ @dynamo_table = dynamo_table || Env[:dynamo_table] || 'users'
25
+
26
+ if(@dynamo_table == nil)
27
+ raise Exceptions::Users::MissingOption, "DynamoDB Table Name Required"
28
+ end
29
+
30
+ @server_users = ServerUsers.new(@dynamo_table)
31
+
32
+ @dynamo = AWS::DynamoDB.new()
33
+ @table = @dynamo.tables[@dynamo_table]
34
+ @table.hash_key = {:type => :string}
35
+ @table.range_key = {:user_name => :string}
36
+ @metadata = @table.items['METADATA', '__metadata__']
37
+
38
+ @instance_data = AWS::InstanceData.new
39
+ @tags = {}
40
+ load_instance_tags()
41
+ @local_user_data = {}
42
+ @local_user_data[:local_users] = {}
43
+
44
+ @local_user_file = '/etc/zepplen_aws/local_users.yaml'
45
+ end
46
+
47
+ def local_user_file()
48
+ return @local_user_file
49
+ end
50
+
51
+ def local_user_file=(user_file)
52
+ @local_user_file = user_file
53
+ end
54
+
55
+ def update!()
56
+ dynamo_users = load_metadata_from_dynamo()
57
+ local_users = load_from_file()
58
+ static_updates = {}
59
+ if(local_users.has_key?(:metadata) && local_users[:metadata].has_key?(:identity) && local_users[:metadata][:identity] == dynamo_users[:metadata][:identity])
60
+ users = local_users
61
+ users[:local_users].each_pair do |user_name, user_data|
62
+ static_updates[user_name] = false
63
+ end
64
+ else
65
+ users = load_users_from_dynamo(dynamo_users)
66
+ users[:local_users].each_pair do |user_name, user_data|
67
+ if(local_users[:local_users].has_key?(user_name) && local_users[:local_users][user_name][:identity] == users[:local_users][user_name][:identity])
68
+ static_updates[user_name] = false
69
+ else
70
+ static_updates[user_name] = true
71
+ end
72
+ end
73
+ end
74
+ make_server_users(users, static_updates)
75
+ end
76
+
77
+ private
78
+
79
+ def make_server_users(users, static_update)
80
+ #TODO: Write Linux User Admin Object
81
+ begin
82
+ # Yes, we could probably just assume 0, but this seemed safer
83
+ etc_user = Etc.getpwnam('root')
84
+ root_uid = etc_user['uid']
85
+ rescue ArgumentError
86
+ raise "Can Not Find Root User!!!"
87
+ end
88
+
89
+ users[:local_users].each_pair do |user_name, user_data|
90
+ begin
91
+ etc_user = Etc.getpwnam(user_name)
92
+ rescue ArgumentError
93
+ if(user_data[:home])
94
+ home_dir = user_data[:home]
95
+ else
96
+ home_dir = "/home/#{user_name}"
97
+ end
98
+ system "useradd -d #{home_dir} -c \"#{user_data[:full_name]}\" -m -s #{user_data[:shell]} -u #{user_data[:user_id]} -U #{user_name}"
99
+ etc_user = Etc.getpwnam(user_name)
100
+ end
101
+ home_dir = etc_user['dir']
102
+ user_uid = etc_user['uid']
103
+ user_gid = etc_user['gid']
104
+ if(user_data[:sudo])
105
+ system "usermod -a -G #{users[:metadata][:sudo_group]} #{user_name}"
106
+ else
107
+ system "usermod -G #{user_name} #{user_name}"
108
+ end
109
+ if(!File.exist?("#{home_dir}/.ssh"))
110
+ system "mkdir #{home_dir}/.ssh"
111
+ system "chmod 750 #{home_dir}/.ssh"
112
+ system "chown root:#{user_name} #{home_dir}/.ssh"
113
+ end
114
+ if(user_data[:public_key] && Date.parse(user_data[:public_key_expire]) > Date.today)
115
+ write_local_file("#{home_dir}/.ssh/authorized_keys", '640', user_data[:public_key], root_uid, user_gid)
116
+ else
117
+ write_local_file("#{home_dir}/.ssh/authorized_keys", '640', 'Revoked', root_uid, user_gid)
118
+ end
119
+ if(static_update[user_name] && user_data[:files])
120
+ s3 = AWS::S3.new()
121
+ bucket = s3.buckets[users[:metadata][:user_file_bucket]]
122
+ user_data[:files].each_pair do |file_name, file_data|
123
+ s3object = bucket.objects[file_data['s3_path']]
124
+ write_local_file("#{home_dir}/#{file_name}", file_data['mode'], s3object.read, user_uid, user_gid)
125
+ end
126
+ else
127
+ end
128
+ end
129
+
130
+ users[:local_remove_users].each do |user_name|
131
+ begin
132
+ if(user_name != 'root')
133
+ etc_user = Etc.getpwnam(user_name)
134
+ if(File.exists?("#{etc_user['dir']}/.ssh/authorized_keys"))
135
+ File.delete("#{etc_user['dir']}/.ssh/authorized_keys")
136
+ end
137
+ system "pkill -u #{user_name}"
138
+ system "userdel -f #{user_name}"
139
+ end
140
+ rescue ArgumentError
141
+ end
142
+ end
143
+ end
144
+
145
+ def write_local_file(path, mode, contents, user_id, group_id)
146
+ begin
147
+ mode = mode.to_i(8)
148
+ if(!Dir.exist?(File.dirname(path)))
149
+ system "mkdir -p #{File.dirname(path)}"
150
+ end
151
+ fout = File.open(path, 'w')
152
+ fout.chmod(mode)
153
+ fout.chown(user_id, group_id)
154
+ fout.write(contents)
155
+ fout.close
156
+ rescue => e
157
+ puts e
158
+ end
159
+ end
160
+
161
+ def load_users_from_dynamo(users)
162
+ @table.items.where(:type => 'USER').where(:state => 'ACTIVE').each do |row|
163
+ user = ServerUser.new(row.attributes[:user_name], @dynamo_table, row, @metadata, @server_users)
164
+ @tags.each_pair do |instance_tag, instance_value|
165
+ if(user.access_tags.has_key?(instance_tag) && user.access_tags[instance_tag].has_key?(instance_value))
166
+ add_user(user, instance_tag, instance_value, users)
167
+ else
168
+ remove_user(user.user_name, users)
169
+ end
170
+ end
171
+ end
172
+ @table.items.where(:type => 'USER').where(:state => 'INACTIVE').each do |row|
173
+ remove_user(row.attributes[:user_name])
174
+ end
175
+ save_to_file(users)
176
+ return users
177
+ end
178
+
179
+ def load_metadata_from_dynamo()
180
+ metadata = @metadata
181
+ users = {}
182
+ users[:metadata] = {}
183
+ users[:local_users] = {}
184
+ users[:local_remove_users] = []
185
+ users[:metadata][:identity] = metadata.attributes['identity'].to_i
186
+ users[:metadata][:max_key_age] = metadata.attributes['max_key_age'].to_i
187
+ users[:metadata][:sudo_group] = metadata.attributes['sudo_group']
188
+ users[:metadata][:user_file_bucket] = metadata.attributes['user_file_bucket']
189
+ return users
190
+ end
191
+
192
+ def save_to_file(users)
193
+ folder_name = File.dirname(@local_user_file)
194
+ if(!Dir.exist?(folder_name))
195
+ base_path = '/'
196
+ folder_name.split('/').each do |dir|
197
+ if(dir != '')
198
+ base_path += "#{dir}/"
199
+ if(!Dir.exist?(base_path))
200
+ begin
201
+ Dir.mkdir(base_path, 0700)
202
+ rescue SystemCallError
203
+ raise "Can not create folder #{base_path}"
204
+ end
205
+ end
206
+ end
207
+ end
208
+ end
209
+ fout = File.open(@local_user_file, 'w')
210
+ fout.write(users.to_yaml)
211
+ fout.chmod(0600)
212
+ fout.close()
213
+
214
+ end
215
+
216
+ def load_from_file()
217
+ if(File.readable?(@local_user_file))
218
+ users = YAML::load_stream(File.open(@local_user_file))[0]
219
+ else
220
+ users = {}
221
+ users[:local_users] = {}
222
+ users[:local_remove_users] = []
223
+ end
224
+ return users
225
+ end
226
+
227
+ def remove_user(user_name, users)
228
+ users[:local_remove_users] << user_name
229
+ end
230
+
231
+ def add_user(user_object, tag_name, tag_value, users)
232
+ user = {}
233
+ user[:user_name] = user_object.user_name
234
+ user[:full_name] = user_object.full_name
235
+ user[:shell] = user_object.shell
236
+ user[:public_key] = user_object.public_key
237
+ user[:public_key_expire] = user_object.public_key_expire
238
+ user[:user_id] = user_object.user_id.to_i
239
+ user[:identity] = user_object.identity.to_i
240
+ user[:sudo] = user_object.access_tags[tag_name][tag_value]['sudo']
241
+ user[:files] = user_object.files
242
+ users[:local_users][user_object.user_name] = user
243
+ end
244
+
245
+ def load_instance_tags()
246
+ instance_id = @instance_data['instance-id']
247
+ ec2 = AWS::EC2.new()
248
+ intsance_tags = ec2.instances[instance_id].tags.to_h
249
+ tag_names = intsance_tags.keys & @server_users.tags.to_a
250
+ tag_names.each do |tag|
251
+ @tags[tag] = intsance_tags[tag]
252
+ end
253
+ end
254
+
255
+ end
256
+ end