awshark 1.2.0 → 1.3.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d5706c9a35383e578bac90c8f432f2b4d730857556112222ff122ba7f7fba8e0
4
- data.tar.gz: 6a3c0ca64b7f017aaea42c7fed255a5e5f44ec33bce1de59a00c875c1023e86e
3
+ metadata.gz: 8f014eaddd54562cad6064bc0b28a9fde800b7144f3be26e4af11f02a99d1f13
4
+ data.tar.gz: 258b5c71b20180623ca66646d4081c38d8c14e590a1b2a2773193523423dc614
5
5
  SHA512:
6
- metadata.gz: a18d22c21cda523abfa5bd6369df2855b55eb479a2d43a0fa1bd66de0902e055db4e164bab332338f417fcc010094a168b4d2f92a16e4915a840fcb580608584
7
- data.tar.gz: c4e5e7d9a2105ff44c58ce0f08ae742e48f780d18b4f63459d39ec6f670d372b5636e966f90e081fec071f4c56ad0923b3b1cb94549ef10cf819e064a57575ac
6
+ metadata.gz: 42970eec188f9fe6a4f561a2d42b01387b08c98f26ee93e0352d83f1fda165f119ae09909fbbb62c80e5380ca259a8d667bb46cf416e742db333051da4891204
7
+ data.tar.gz: ecf2602e7bb5cf37ebd508d3bb95d44eff004214f38bb6fa19475ee38d6b094093c6572c17acb3d48ca59d6b018a39129019d450e80aa7a6db4d84d0cc4b7c17
data/.rubocop.yml CHANGED
@@ -69,7 +69,7 @@ Lint/UselessTimes:
69
69
 
70
70
  Metrics/AbcSize:
71
71
  Enabled: true
72
- Max: 20
72
+ Max: 25
73
73
  Metrics/BlockLength:
74
74
  Exclude:
75
75
  - '*.gemspec'
data/CHANGELOG.md CHANGED
@@ -1,5 +1,8 @@
1
1
  ## Changelog
2
2
 
3
+ #### 1.3.0
4
+ - [new] add `awshark ec2 authorize` and `unauthorize`
5
+
3
6
  #### 1.2.0
4
7
  - [new] add `awshark ecs list`
5
8
  - [break] remove options `--profile` and `--region`; use `AWS_PROFILE` and `AWS_REGION` instead
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Awshark
4
+ module ClassOptions
5
+ def process_class_options
6
+ command = current_command_chain.last
7
+ cli_options = options.merge(parent_options || {}).symbolize_keys
8
+
9
+ if cli_options[:help]
10
+ respond_to?(command) ? help(command) : help
11
+ exit(0)
12
+ end
13
+
14
+ setup_aws_credentials(options)
15
+ end
16
+
17
+ private
18
+
19
+ def setup_aws_credentials(options)
20
+ profile_resolver = ProfileResolver.new(options)
21
+
22
+ ::Aws.config[:region] = profile_resolver.region
23
+ ::Aws.config[:credentials] = profile_resolver.credentials
24
+ end
25
+ end
26
+ end
data/lib/awshark/cli.rb CHANGED
@@ -1,13 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'awshark/profile_resolver'
4
-
5
- require 'awshark/subcommands/class_options'
6
- require 'awshark/subcommands/cloud_formation'
7
- require 'awshark/subcommands/ec2'
8
- require 'awshark/subcommands/ecs'
9
- require 'awshark/subcommands/rds'
10
- require 'awshark/subcommands/s3'
4
+ require 'awshark/class_options'
5
+ require 'awshark/cloud_formation/subcommand'
6
+ require 'awshark/ec2/subcommand'
7
+ require 'awshark/ecs/subcommand'
8
+ require 'awshark/rds/subcommand'
9
+ require 'awshark/s3/subcommand'
11
10
 
12
11
  module Awshark
13
12
  class Cli < Thor
@@ -18,19 +17,19 @@ module Awshark
18
17
  class_option :help, type: :boolean, desc: 'Prints this help'
19
18
 
20
19
  desc 'cf COMMAND', 'Run CloudFormation command'
21
- subcommand 'cf', Awshark::Subcommands::CloudFormation
20
+ subcommand 'cf', Awshark::CloudFormation::Subcommand
22
21
 
23
22
  desc 'ec2 COMMAND', 'Run EC2 command'
24
- subcommand 'ec2', Awshark::Subcommands::EC2
23
+ subcommand 'ec2', Awshark::Ec2::Subcommand
25
24
 
26
25
  desc 'ecs COMMAND', 'Run ECS command'
27
- subcommand 'ecs', Awshark::Subcommands::Ecs
26
+ subcommand 'ecs', Awshark::Ecs::Subcommand
28
27
 
29
28
  desc 'rds COMMAND', 'Run RDS command'
30
- subcommand 'rds', Awshark::Subcommands::Rds
29
+ subcommand 'rds', Awshark::Rds::Subcommand
31
30
 
32
31
  desc 's3 COMMAND', 'Run CloudFormation command'
33
- subcommand 's3', Awshark::Subcommands::S3
32
+ subcommand 's3', Awshark::S3::Subcommand
34
33
 
35
34
  desc 'version', 'Displays current version of AwsShark'
36
35
  long_desc <<-LONGDESC
@@ -14,9 +14,9 @@ require 'awshark/cloud_formation/stack_events'
14
14
  require 'awshark/cloud_formation/template'
15
15
 
16
16
  module Awshark
17
- module Subcommands
18
- class CloudFormation < Thor
19
- include Awshark::Subcommands::ClassOptions
17
+ module CloudFormation
18
+ class Subcommand < Thor
19
+ include Awshark::ClassOptions
20
20
 
21
21
  class_option :bucket, type: :string, desc: 'S3 bucket for template'
22
22
  class_option :iam, type: :boolean, desc: 'Needs IAM capabilities'
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Awshark
4
+ module Logging
5
+ def logger
6
+ Awshark.logger
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Awshark
4
+ module Ec2
5
+ class Configuration
6
+ def client
7
+ region = Aws.config[:region] || 'eu-central-1'
8
+ @client ||= Aws::EC2::Client.new(region: region)
9
+ end
10
+
11
+ attr_writer :client
12
+ end
13
+ end
14
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Awshark
4
- module EC2
4
+ module Ec2
5
5
  class Instance
6
6
  attr_reader :id
7
7
  attr_reader :type
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Awshark
4
- module EC2
4
+ module Ec2
5
5
  class Manager
6
6
  def all_instances
7
7
  return @all_instances if defined?(@all_instances)
@@ -29,11 +29,7 @@ module Awshark
29
29
  private
30
30
 
31
31
  def client
32
- @client ||= Aws::EC2::Client.new(region: region)
33
- end
34
-
35
- def region
36
- Aws.config[:region] || 'eu-central-1'
32
+ Awshark.config.ec2.client
37
33
  end
38
34
  end
39
35
  end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Awshark
4
+ module Ec2
5
+ class SecurityGroup
6
+ include Logging
7
+
8
+ attr_reader :security_group_id, :username
9
+
10
+ def initialize(id:, username:)
11
+ validate!(:id, id)
12
+ validate!(:username, username)
13
+
14
+ @security_group_id = id
15
+ @username = username
16
+ end
17
+
18
+ def authorize(ip:, ports:)
19
+ ports.each do |port|
20
+ ip_rule = SecurityRule.new(
21
+ ip: ip,
22
+ from_port: port,
23
+ to_port: port,
24
+ description: username
25
+ )
26
+ client.authorize_security_group_ingress(
27
+ group_id: security_group_id,
28
+ ip_permissions: [ip_rule.to_hash]
29
+ )
30
+ logger.info "Created ingress rule in for #{ip_rule.cidr_ip}, port #{port}."
31
+ rescue Aws::EC2::Errors::InvalidPermissionDuplicate
32
+ logger.warn "An ingress rule for #{ip} and port #{port} exists."
33
+ end
34
+ end
35
+
36
+ def unauthorize
37
+ return if my_ingress_rules.empty?
38
+
39
+ client.revoke_security_group_ingress(
40
+ group_id: security_group_id,
41
+ ip_permissions: my_ingress_rules
42
+ )
43
+ logger.info "Removed all ingress rules for #{username}."
44
+ end
45
+
46
+ def my_ingress_rules
47
+ return @my_ingress_rules if defined?(@my_ingress_rules)
48
+
49
+ response = client.describe_security_groups(group_ids: [security_group_id])
50
+ return [] if response.security_groups.empty?
51
+
52
+ security_group = response.security_groups.first
53
+ security_rules = security_group.ip_permissions.map do |ip_permission|
54
+ SecurityRule.new(ip_permission)
55
+ end
56
+
57
+ @my_ingress_rules = security_rules.map do |rule|
58
+ rule.ip_ranges.keep_if { |ip_range| ip_range.description == username }
59
+ rule.ip_ranges.any? ? rule.to_hash : nil
60
+ end
61
+
62
+ @my_ingress_rules.compact!
63
+ end
64
+
65
+ private
66
+
67
+ def client
68
+ Awshark.config.ec2.client
69
+ end
70
+
71
+ def validate!(name, value)
72
+ raise ArgumentError, "Argument #{name} cannot be empty" if value.blank?
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Awshark
4
+ module Ec2
5
+ class SecurityRule
6
+ attr_reader :ip_protocol, :ip_ranges, :from_port, :to_port
7
+
8
+ def initialize(args)
9
+ if args.is_a?(Aws::EC2::Types::IpPermission)
10
+ @ip_protocol = args.ip_protocol
11
+ @ip_ranges = args.ip_ranges
12
+ @from_port = args.from_port
13
+ @to_port = args.to_port
14
+ else
15
+ ip = args.fetch(:ip)
16
+ description = args.fetch(:description)
17
+ @ip_protocol = args.fetch(:ip_protocol, 'tcp')
18
+ @ip_ranges = [OpenStruct.new({ cidr_ip: "#{ip}/32", description: description })]
19
+ @from_port = args.fetch(:from_port)
20
+ @to_port = args.fetch(:to_port)
21
+ end
22
+ end
23
+
24
+ def cidr_ip
25
+ ip_ranges[0].cidr_ip
26
+ end
27
+
28
+ def description
29
+ ip_ranges[0].description
30
+ end
31
+
32
+ def to_hash
33
+ {
34
+ ip_protocol: ip_protocol,
35
+ from_port: from_port,
36
+ to_port: to_port,
37
+ ip_ranges: ip_ranges.map(&:to_h)
38
+ }
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'aws-sdk-ec2'
4
+
5
+ require 'awshark/ec2/instance'
6
+ require 'awshark/ec2/manager'
7
+ require 'awshark/ec2/security_group'
8
+ require 'awshark/ec2/security_rule'
9
+
10
+ module Awshark
11
+ module Ec2
12
+ class Subcommand < Thor
13
+ include Awshark::ClassOptions
14
+
15
+ desc 'list', 'List all EC2 instances'
16
+ long_desc <<-LONGDESC
17
+ List all EC2 instances in a region
18
+
19
+ Example:
20
+ awshark ec2 list STATE
21
+
22
+ LONGDESC
23
+ def list(state = 'running')
24
+ process_class_options
25
+
26
+ instances = manager.public_send("#{state}_instances")
27
+ instances = instances.sort_by(&:name)
28
+
29
+ instances.each do |i|
30
+ args = { name: i.name, type: i.type, public_dns: i.public_dns_name, state: i.state }
31
+ printf "%-40<name>s %-12<type>s %-60<public_dns>s %<state>s\n", args
32
+ end
33
+ end
34
+
35
+ desc 'authorize', 'Authorises your IP to access AWS'
36
+ long_desc <<-LONGDESC
37
+ Adds your IP address to the security group to allow direct access to the VPCs.
38
+
39
+ Example:
40
+ awshark ec2 authorize
41
+
42
+ LONGDESC
43
+ option :ports,
44
+ type: :string,
45
+ desc: 'Ports to register. Only uses ports from 1 to 65535.',
46
+ default: '443'
47
+ option :security_group,
48
+ type: :string,
49
+ required: true,
50
+ desc: 'Security group to allow access to.'
51
+ option :username,
52
+ type: :string,
53
+ desc: 'Ports to register. Only uses ports from 1 to 65535.'
54
+ def authorize
55
+ process_class_options
56
+
57
+ group = Ec2::SecurityGroup.new(id: security_group_id, username: username)
58
+ group.authorize(ip: my_ip, ports: ports)
59
+ end
60
+
61
+ desc 'unauthorize', 'Removes your IP from access to AWS'
62
+ long_desc <<-LONGDESC
63
+ Removes all your ingress rules from the home office security group in the VPCs.
64
+
65
+ Example:
66
+ awshark ec2 unauthorize
67
+
68
+ LONGDESC
69
+ option :security_group,
70
+ type: :string,
71
+ required: true,
72
+ desc: 'Security group to remove access from.'
73
+ option :username,
74
+ type: :string,
75
+ desc: 'Ports to register. Only uses ports from 1 to 65535.'
76
+ def unauthorize
77
+ process_class_options
78
+
79
+ group = Ec2::SecurityGroup.new(id: security_group_id, username: username)
80
+ group.unauthorize
81
+ end
82
+
83
+ private
84
+
85
+ def manager
86
+ @manager ||= Ec2::Manager.new
87
+ end
88
+
89
+ def my_ip
90
+ @my_ip ||= `curl -s https://api.ipify.org`.strip
91
+ end
92
+
93
+ def ports
94
+ result = options[:ports].split(',').map(&:to_i)
95
+ result.select { |port| (1..65_535).cover?(port) }
96
+ end
97
+
98
+ def security_group_id
99
+ options[:security_group].strip
100
+ end
101
+
102
+ def username
103
+ options[:username].presence || `whoami`.strip
104
+ end
105
+ end
106
+ end
107
+ end
@@ -7,9 +7,9 @@ require 'awshark/ecs/cluster'
7
7
  require 'awshark/ecs/manager'
8
8
 
9
9
  module Awshark
10
- module Subcommands
11
- class Ecs < Thor
12
- include Awshark::Subcommands::ClassOptions
10
+ module Ecs
11
+ class Subcommand < Thor
12
+ include Awshark::ClassOptions
13
13
 
14
14
  desc 'list', 'list ECS tasks'
15
15
  long_desc <<-LONGDESC
@@ -6,9 +6,9 @@ require 'awshark/rds/check_reservations'
6
6
  require 'awshark/rds/manager'
7
7
 
8
8
  module Awshark
9
- module Subcommands
10
- class Rds < Thor
11
- include Awshark::Subcommands::ClassOptions
9
+ module Rds
10
+ class Subcommand < Thor
11
+ include Awshark::ClassOptions
12
12
 
13
13
  desc 'check', 'Check RDS reservations'
14
14
  long_desc <<-LONGDESC
@@ -9,9 +9,9 @@ require 'awshark/s3/bucket'
9
9
  require 'awshark/s3/manager'
10
10
 
11
11
  module Awshark
12
- module Subcommands
13
- class S3 < Thor
14
- include Awshark::Subcommands::ClassOptions
12
+ module S3
13
+ class Subcommand < Thor
14
+ include Awshark::ClassOptions
15
15
  include ActiveSupport::NumberHelper
16
16
 
17
17
  desc 'list', 'List S3 bucket statistics'
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Awshark
4
- VERSION = '1.2.0'
4
+ VERSION = '1.3.0'
5
5
  end
data/lib/awshark.rb CHANGED
@@ -6,7 +6,9 @@ require 'thor'
6
6
  require 'yaml'
7
7
 
8
8
  require 'awshark/version'
9
+ require 'awshark/concerns/logging'
9
10
  require 'awshark/cloud_formation/configuration'
11
+ require 'awshark/ec2/configuration'
10
12
  require 'awshark/ecs/configuration'
11
13
  require 'awshark/s3/configuration'
12
14
  require 'awshark/sts/configuration'
@@ -17,12 +19,14 @@ module Awshark
17
19
  def self.config
18
20
  @config ||= begin
19
21
  cf = CloudFormation::Configuration.new
22
+ ec2 = Ec2::Configuration.new
20
23
  ecs = Ecs::Configuration.new
21
24
  s3 = S3::Configuration.new
22
25
  sts = Sts::Configuration.new
23
26
 
24
27
  OpenStruct.new(
25
28
  cloud_formation: cf,
29
+ ec2: ec2,
26
30
  ecs: ecs,
27
31
  s3: s3,
28
32
  sts: sts
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: awshark
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joergen Dahlke
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-12-16 00:00:00.000000000 Z
11
+ date: 2022-01-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -252,6 +252,7 @@ files:
252
252
  - exe/awshark
253
253
  - gems.rb
254
254
  - lib/awshark.rb
255
+ - lib/awshark/class_options.rb
255
256
  - lib/awshark/cli.rb
256
257
  - lib/awshark/cloud_formation/configuration.rb
257
258
  - lib/awshark/cloud_formation/file_loading.rb
@@ -260,26 +261,29 @@ files:
260
261
  - lib/awshark/cloud_formation/parameters.rb
261
262
  - lib/awshark/cloud_formation/stack.rb
262
263
  - lib/awshark/cloud_formation/stack_events.rb
264
+ - lib/awshark/cloud_formation/subcommand.rb
263
265
  - lib/awshark/cloud_formation/template.rb
266
+ - lib/awshark/concerns/logging.rb
267
+ - lib/awshark/ec2/configuration.rb
264
268
  - lib/awshark/ec2/instance.rb
265
269
  - lib/awshark/ec2/manager.rb
270
+ - lib/awshark/ec2/security_group.rb
271
+ - lib/awshark/ec2/security_rule.rb
272
+ - lib/awshark/ec2/subcommand.rb
266
273
  - lib/awshark/ecs/cluster.rb
267
274
  - lib/awshark/ecs/configuration.rb
268
275
  - lib/awshark/ecs/manager.rb
276
+ - lib/awshark/ecs/subcommand.rb
269
277
  - lib/awshark/profile_resolver.rb
270
278
  - lib/awshark/rds/check_reservations.rb
271
279
  - lib/awshark/rds/manager.rb
280
+ - lib/awshark/rds/subcommand.rb
272
281
  - lib/awshark/s3/artifact.rb
273
282
  - lib/awshark/s3/bucket.rb
274
283
  - lib/awshark/s3/configuration.rb
275
284
  - lib/awshark/s3/manager.rb
285
+ - lib/awshark/s3/subcommand.rb
276
286
  - lib/awshark/sts/configuration.rb
277
- - lib/awshark/subcommands/class_options.rb
278
- - lib/awshark/subcommands/cloud_formation.rb
279
- - lib/awshark/subcommands/ec2.rb
280
- - lib/awshark/subcommands/ecs.rb
281
- - lib/awshark/subcommands/rds.rb
282
- - lib/awshark/subcommands/s3.rb
283
287
  - lib/awshark/version.rb
284
288
  homepage: https://github.com/jdahlke/awshark
285
289
  licenses:
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Awshark
4
- module Subcommands
5
- module ClassOptions
6
- def process_class_options
7
- command = current_command_chain.last
8
- cli_options = options.merge(parent_options || {}).symbolize_keys
9
-
10
- if cli_options[:help]
11
- respond_to?(command) ? help(command) : help
12
- exit(0)
13
- end
14
-
15
- setup_aws_credentials(options)
16
- end
17
-
18
- private
19
-
20
- def setup_aws_credentials(options)
21
- profile_resolver = ProfileResolver.new(options)
22
-
23
- ::Aws.config[:region] = profile_resolver.region
24
- ::Aws.config[:credentials] = profile_resolver.credentials
25
- end
26
- end
27
- end
28
- end
@@ -1,38 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'aws-sdk-ec2'
4
-
5
- require 'awshark/ec2/instance'
6
- require 'awshark/ec2/manager'
7
-
8
- module Awshark
9
- module Subcommands
10
- class EC2 < Thor
11
- include Awshark::Subcommands::ClassOptions
12
-
13
- desc 'list', 'List all EC2 instances'
14
- long_desc <<-LONGDESC
15
- List all EC2 instances in a region
16
-
17
- Example: `awshark ec2 list STATE`
18
- LONGDESC
19
- def list(state = 'running')
20
- process_class_options
21
-
22
- instances = manager.public_send("#{state}_instances")
23
- instances = instances.sort_by(&:name)
24
-
25
- instances.each do |i|
26
- args = { name: i.name, type: i.type, public_dns: i.public_dns_name, state: i.state }
27
- printf "%-40<name>s %-12<type>s %-60<public_dns>s %<state>s\n", args
28
- end
29
- end
30
-
31
- private
32
-
33
- def manager
34
- @manager ||= Awshark::EC2::Manager.new
35
- end
36
- end
37
- end
38
- end