s3-secure 0.2.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +16 -0
  4. data/LICENSE.txt +201 -22
  5. data/README.md +134 -16
  6. data/lib/s3_secure.rb +3 -2
  7. data/lib/s3_secure/access_logs.rb +30 -0
  8. data/lib/s3_secure/access_logs/base.rb +4 -0
  9. data/lib/s3_secure/access_logs/disable.rb +37 -0
  10. data/lib/s3_secure/access_logs/enable.rb +41 -0
  11. data/lib/s3_secure/access_logs/list.rb +25 -0
  12. data/lib/s3_secure/access_logs/show.rb +89 -0
  13. data/lib/s3_secure/aws_services.rb +1 -30
  14. data/lib/s3_secure/aws_services/s3.rb +54 -0
  15. data/lib/s3_secure/cli.rb +26 -0
  16. data/lib/s3_secure/command.rb +7 -0
  17. data/lib/s3_secure/encryption.rb +2 -0
  18. data/lib/s3_secure/encryption/disable.rb +4 -8
  19. data/lib/s3_secure/encryption/enable.rb +4 -8
  20. data/lib/s3_secure/encryption/list.rb +12 -16
  21. data/lib/s3_secure/encryption/show.rb +11 -6
  22. data/lib/s3_secure/help/batch.md +14 -0
  23. data/lib/s3_secure/help/encryption/disable.md +5 -0
  24. data/lib/s3_secure/help/encryption/enable.md +6 -0
  25. data/lib/s3_secure/help/encryption/list.md +5 -0
  26. data/lib/s3_secure/help/lifecycle/add.md +13 -0
  27. data/lib/s3_secure/help/lifecycle/list.md +22 -0
  28. data/lib/s3_secure/help/lifecycle/remove.md +5 -0
  29. data/lib/s3_secure/help/lifecycle/show.md +13 -0
  30. data/lib/s3_secure/help/policy/enforce_ssl.md +34 -0
  31. data/lib/s3_secure/help/policy/list.md +5 -0
  32. data/lib/s3_secure/help/policy/unforce_ssl.md +61 -0
  33. data/lib/s3_secure/help/summary.md +22 -0
  34. data/lib/s3_secure/lifecycle.rb +31 -0
  35. data/lib/s3_secure/lifecycle/add.rb +33 -0
  36. data/lib/s3_secure/lifecycle/base.rb +5 -0
  37. data/lib/s3_secure/lifecycle/builder.rb +47 -0
  38. data/lib/s3_secure/lifecycle/list.rb +24 -0
  39. data/lib/s3_secure/lifecycle/remove.rb +28 -0
  40. data/lib/s3_secure/lifecycle/show.rb +40 -0
  41. data/lib/s3_secure/policy.rb +2 -0
  42. data/lib/s3_secure/policy/document.rb +1 -1
  43. data/lib/s3_secure/policy/enforce.rb +3 -6
  44. data/lib/s3_secure/policy/list.rb +13 -17
  45. data/lib/s3_secure/policy/show.rb +8 -6
  46. data/lib/s3_secure/policy/unforce.rb +5 -8
  47. data/lib/s3_secure/remediate_all.rb +11 -0
  48. data/lib/s3_secure/summary.rb +13 -0
  49. data/lib/s3_secure/summary/item.rb +16 -0
  50. data/lib/s3_secure/summary/items.rb +65 -0
  51. data/lib/s3_secure/table.rb +18 -0
  52. data/lib/s3_secure/version.rb +1 -1
  53. data/lib/s3_secure/versioning.rb +29 -0
  54. data/lib/s3_secure/versioning/base.rb +4 -0
  55. data/lib/s3_secure/versioning/disable.rb +19 -0
  56. data/lib/s3_secure/versioning/enable.rb +19 -0
  57. data/lib/s3_secure/versioning/list.rb +24 -0
  58. data/lib/s3_secure/versioning/show.rb +27 -0
  59. data/s3-secure.gemspec +5 -2
  60. data/spec/lib/lifecycle/builder_spec.rb +85 -0
  61. metadata +71 -6
  62. data/Gemfile.lock +0 -89
  63. data/lib/s3_secure/help/hello.md +0 -5
@@ -1,12 +1,6 @@
1
1
  class S3Secure::Encryption
2
2
  class Show < Base
3
3
  def run
4
- @s3 = s3_regional_client(@bucket)
5
-
6
- list = S3Secure::Encryption::List.new(@options)
7
- list.set_s3(@s3)
8
-
9
- rules = list.get_encryption_rules(@bucket)
10
4
  if rules
11
5
  puts "Bucket #{@bucket} is configured with these encryption rules:"
12
6
  puts rules.map(&:to_h)
@@ -14,5 +8,16 @@ class S3Secure::Encryption
14
8
  puts "Bucket #{@bucket} is not configured with encryption at the bucket level"
15
9
  end
16
10
  end
11
+
12
+ def enabled?
13
+ !!(rules && !rules.empty?)
14
+ end
15
+
16
+ def rules
17
+ resp = s3.get_bucket_encryption(bucket: @bucket)
18
+ resp.server_side_encryption_configuration.rules # Aws::Xml::DefaultList object
19
+ rescue Aws::S3::Errors::ServerSideEncryptionConfigurationNotFoundError
20
+ end
21
+ memoize :rules
17
22
  end
18
23
  end
@@ -0,0 +1,14 @@
1
+ There are some supported batch commands:
2
+
3
+ s3-secure batch encryption enable FILE.txt
4
+ s3-secure batch encryption disable FILE.txt
5
+ s3-secure batch policy enforce_ssl FILE.txt
6
+ s3-secure batch policy unforce_ssl FILE.txt
7
+
8
+ The format of FILE.txt is a list of bucket names separated by newlines. Example:
9
+
10
+ buckets.txt:
11
+
12
+ my-bucket-1
13
+ my-bucket-2
14
+
@@ -0,0 +1,5 @@
1
+ ## Example
2
+
3
+ $ s3-secure encryption disable a-test-bucket-in-us-east-1
4
+ Bucket a-test-bucket-in-us-east-1 encryption has been removed
5
+ $
@@ -0,0 +1,6 @@
1
+ ## Example
2
+
3
+ $ s3-secure encryption enable a-test-bucket-in-us-east-1
4
+ Encyption enabled on bucket a-test-bucket-in-us-east-1 with rules:
5
+ {:apply_server_side_encryption_by_default=>{:sse_algorithm=>"AES256"}}
6
+ $
@@ -0,0 +1,5 @@
1
+ ## Examples
2
+
3
+ s3-secure encryption list # shows all buckets with and without polices
4
+ s3-secure encryption list --encryption # only shows buckets with encryption
5
+ s3-secure encryption list --no-encryption # only shows buckets without encryption
@@ -0,0 +1,13 @@
1
+ ## Example
2
+
3
+ $ s3-secure lifecycle add a-test-bucket-in-us-east-1
4
+ Added lifecycle policy to bucket a-test-bucket-in-us-east-1
5
+ $
6
+
7
+ By default, the add command will only add a lifecycle policy if you none exists.
8
+
9
+ It may be useful to test adding an additional lifecycle policy, for this you can use both the `--additive` and `--prefix` options. Note, you must make sure that the lifecycle policies can work together. For example, they must have different prefixes.
10
+
11
+ $ s3-secure lifecycle add a-test-bucket-in-us-east-1 --additive --prefix /foo
12
+ Added lifecycle policy to bucket a-test-bucket-in-us-east-1
13
+ $
@@ -0,0 +1,22 @@
1
+ ## Examples
2
+
3
+ $ s3-secure lifecycle list
4
+ +----------------------------+----------------------+
5
+ | Bucket | Has Lifecycle Rules? |
6
+ +----------------------------+----------------------+
7
+ | a-test-bucket-in-us-east-1 | false |
8
+ | a-test-bucket-in-us-west-1 | true |
9
+ +----------------------------+----------------------+
10
+ $ s3-secure lifecycle list --lifecycle true
11
+ +----------------------------+----------------------+
12
+ | Bucket | Has Lifecycle Rules? |
13
+ +----------------------------+----------------------+
14
+ | a-test-bucket-in-us-west-1 | true |
15
+ +----------------------------+----------------------+
16
+ $ s3-secure lifecycle list --lifecycle false
17
+ +----------------------------+----------------------+
18
+ | Bucket | Has Lifecycle Rules? |
19
+ +----------------------------+----------------------+
20
+ | a-test-bucket-in-us-east-1 | false |
21
+ +----------------------------+----------------------+
22
+ $
@@ -0,0 +1,5 @@
1
+ ## Examples
2
+
3
+ $ s3-secure lifecycle remove a-test-bucket-in-us-east-1
4
+ Removed the s3-secure-automated-cleanup lifecycle rule on bucket a-test-bucket-in-us-east-1
5
+ $
@@ -0,0 +1,13 @@
1
+ ## Examples
2
+
3
+ $ s3-secure lifecycle show a-test-bucket-in-us-east-1
4
+ This S3 bucket has lifecycle rules
5
+ Bucket lifecycle details:
6
+ {:rules=>
7
+ [{:expiration=>{:expired_object_delete_marker=>true},
8
+ :id=>"s3-secure-automated-cleanup",
9
+ :prefix=>"/bar",
10
+ :status=>"Enabled",
11
+ :noncurrent_version_expiration=>{:noncurrent_days=>365},
12
+ :abort_incomplete_multipart_upload=>{:days_after_initiation=>30}}]}
13
+ $
@@ -0,0 +1,34 @@
1
+ ## Example
2
+
3
+ $ s3-secure policy enforce_ssl a-test-bucket-in-us-east-1
4
+ Add bucket policy to bucket a-test-bucket-in-us-east-1:
5
+ {
6
+ "Version": "2012-10-17",
7
+ "Statement": [
8
+ {
9
+ "Sid": "IPAllow",
10
+ "Effect": "Deny",
11
+ "Principal": "*",
12
+ "Action": "s3:*",
13
+ "Resource": "arn:aws:s3:::a-test-bucket-in-us-east-1/*",
14
+ "Condition": {
15
+ "NotIpAddress": {
16
+ "aws:SourceIp": "54.240.143.0/24"
17
+ }
18
+ }
19
+ },
20
+ {
21
+ "Sid": "ForceSSLOnlyAccess",
22
+ "Effect": "Deny",
23
+ "Principal": "*",
24
+ "Action": "s3:GetObject",
25
+ "Resource": "arn:aws:s3:::a-test-bucket-in-us-east-1/*",
26
+ "Condition": {
27
+ "Bool": {
28
+ "aws:SecureTransport": "false"
29
+ }
30
+ }
31
+ }
32
+ ]
33
+ }
34
+ $
@@ -0,0 +1,5 @@
1
+ ## Examples
2
+
3
+ s3-secure policy list # shows all buckets with and without polices
4
+ s3-secure policy list --policy # only shows buckets with policy
5
+ s3-secure policy list --no-policy # only shows buckets without policy
@@ -0,0 +1,61 @@
1
+ ## Example
2
+
3
+ If the policy only has the ForceSSLOnlyAccess statement, then the entire bucket policy is removed:
4
+
5
+ $ s3-secure policy unforce_ssl a-test-bucket-in-us-west-1
6
+ Remove bucket policy to bucket a-test-bucket-in-us-west-1:
7
+ $
8
+
9
+ If the policy has other statements, then only the ForceSSLOnlyAccess is removed any other policies are kept in tact.
10
+
11
+ $ s3-secure policy show a-test-bucket-in-us-east-1
12
+ Bucket a-test-bucket-in-us-east-1 is configured with this policy:
13
+ {
14
+ "Version": "2012-10-17",
15
+ "Statement": [
16
+ {
17
+ "Sid": "IPAllow",
18
+ "Effect": "Deny",
19
+ "Principal": "*",
20
+ "Action": "s3:*",
21
+ "Resource": "arn:aws:s3:::a-test-bucket-in-us-east-1/*",
22
+ "Condition": {
23
+ "NotIpAddress": {
24
+ "aws:SourceIp": "54.240.143.0/24"
25
+ }
26
+ }
27
+ },
28
+ {
29
+ "Sid": "ForceSSLOnlyAccess",
30
+ "Effect": "Deny",
31
+ "Principal": "*",
32
+ "Action": "s3:GetObject",
33
+ "Resource": "arn:aws:s3:::a-test-bucket-in-us-east-1/*",
34
+ "Condition": {
35
+ "Bool": {
36
+ "aws:SecureTransport": "false"
37
+ }
38
+ }
39
+ }
40
+ ]
41
+ }
42
+ $ s3-secure policy unforce_ssl a-test-bucket-in-us-east-1
43
+ Remove bucket policy statement from bucket a-test-bucket-in-us-east-1:
44
+ {
45
+ "Version": "2012-10-17",
46
+ "Statement": [
47
+ {
48
+ "Sid": "IPAllow",
49
+ "Effect": "Deny",
50
+ "Principal": "*",
51
+ "Action": "s3:*",
52
+ "Resource": "arn:aws:s3:::a-test-bucket-in-us-east-1/*",
53
+ "Condition": {
54
+ "NotIpAddress": {
55
+ "aws:SourceIp": "54.240.143.0/24"
56
+ }
57
+ }
58
+ }
59
+ ]
60
+ }
61
+ $
@@ -0,0 +1,22 @@
1
+ ## Examples
2
+
3
+ $ s3-secure summary
4
+ Determining bucket security-related settings...
5
+ +----------------------------+------+------------+
6
+ | Bucket | SSL? | Encrypted? |
7
+ +----------------------------+------+------------+
8
+ | a-test-bucket-in-us-east-1 | yes | no |
9
+ | a-test-bucket-in-us-west-1 | no | no |
10
+ +----------------------------+------+------------+
11
+ $
12
+
13
+ There are `--ssl no` and `--encrypted no` filtering options:
14
+
15
+ $ s3-secure summary --ssl no --encrypted no
16
+ Determining bucket security-related settings...
17
+ +----------------------------+------+------------+
18
+ | Bucket | SSL? | Encrypted? |
19
+ +----------------------------+------+------------+
20
+ | a-test-bucket-in-us-west-1 | no | no |
21
+ +----------------------------+------+------------+
22
+ $
@@ -0,0 +1,31 @@
1
+ module S3Secure
2
+ class Lifecycle < Command
3
+ desc "list", "List bucket lifecycles"
4
+ long_desc Help.text("lifecycle/list")
5
+ option :format, desc: "Format options: #{CliFormat.formats.join(', ')}"
6
+ option :lifecycle, desc: "Filter for lifecycle: all, true, false"
7
+ def list
8
+ List.new(options).run
9
+ end
10
+
11
+ desc "show BUCKET", "show bucket lifecycle"
12
+ long_desc Help.text("lifecycle/show")
13
+ def show(bucket)
14
+ Show.new(options.merge(bucket: bucket)).run
15
+ end
16
+
17
+ desc "add BUCKET", "add bucket lifecycle"
18
+ long_desc Help.text("lifecycle/add")
19
+ option :additive, type: :boolean, desc: "Force adding another lifecycle rule even if one exists. Note, may fail, need a different prefix filter"
20
+ option :prefix, desc: "Filter prefix. Used with additive mode."
21
+ def add(bucket)
22
+ Add.new(options.merge(bucket: bucket)).run
23
+ end
24
+
25
+ desc "remove BUCKET", "remove bucket lifecycle"
26
+ long_desc Help.text("lifecycle/remove")
27
+ def remove(bucket)
28
+ Remove.new(options.merge(bucket: bucket)).run
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,33 @@
1
+ class S3Secure::Lifecycle
2
+ class Add < Base
3
+ RULE_ID = Base::RULE_ID
4
+
5
+ def run
6
+ show = Show.new(@options)
7
+ if @options[:additive]
8
+ current_rules = show.get_lifecycle_rules(@bucket)
9
+ builder = Builder.new(current_rules)
10
+ rules = builder.rules_with_addition(@options[:prefix])
11
+ if current_rules.size == rules.size
12
+ puts "WARN: rule wasnt added because a #{RULE_ID} already exists".color(:yellow)
13
+ else
14
+ s3.put_bucket_lifecycle_configuration(
15
+ bucket: @bucket, # required
16
+ lifecycle_configuration: {rules: rules}
17
+ )
18
+ end
19
+ elsif show.any?
20
+ puts "Bucket #{@bucket} is has a lifecycle policy already."
21
+ return
22
+ else
23
+ options = {
24
+ bucket: @bucket, # required
25
+ lifecycle_configuration: {rules: [Builder::DEFAULT_RULE]}
26
+ }
27
+ s3.put_bucket_lifecycle_configuration(options)
28
+ end
29
+
30
+ puts "Added lifecycle policy to bucket #{@bucket}"
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,5 @@
1
+ class S3Secure::Lifecycle
2
+ class Base < S3Secure::AbstractBase
3
+ RULE_ID = "s3-secure-automated-cleanup"
4
+ end
5
+ end
@@ -0,0 +1,47 @@
1
+ class S3Secure::Lifecycle
2
+ class Builder
3
+ # Note: put_bucket_lifecycle_configuration and put_bucket_lifecycle understand different payloads.
4
+ # put_bucket_lifecycle is old and shouldnt be used
5
+ RULE_ID = Base::RULE_ID
6
+ DEFAULT_RULE = {
7
+ expiration: {expired_object_delete_marker: true},
8
+ id: RULE_ID,
9
+ status: "Enabled",
10
+ prefix: "",
11
+ noncurrent_version_expiration: {noncurrent_days: 365},
12
+ abort_incomplete_multipart_upload: {days_after_initiation: 30}
13
+ }
14
+
15
+ def initialize(rules)
16
+ @rules = rules || []
17
+ end
18
+
19
+ def has?(id)
20
+ !!@rules.detect { |rule| rule[:id] == id }
21
+ end
22
+
23
+ def rules_with_addition(prefix=nil)
24
+ rules = @rules.dup
25
+ unless has?(RULE_ID)
26
+ rule = DEFAULT_RULE
27
+ rule[:prefix] = prefix if prefix
28
+ rules << rule
29
+ end
30
+ rules
31
+ end
32
+
33
+ def rules_with_removal
34
+ rules = @rules.dup
35
+ rules.delete_if { |rule| rule[:id] == RULE_ID }
36
+ rules
37
+ end
38
+
39
+ def build(type)
40
+ if type == :remove
41
+ remove_lifecycle
42
+ else
43
+ add_lifecycle
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,24 @@
1
+ class S3Secure::Lifecycle
2
+ class List < Base
3
+ def run
4
+ presenter = CliFormat::Presenter.new(@options)
5
+ presenter.header = ["Bucket", "Has Lifecycle Rules?"]
6
+
7
+ buckets.each do |bucket|
8
+ $stderr.puts "Getting lifecycle policy for bucket #{bucket.color(:green)}"
9
+
10
+ show = Show.new(bucket: bucket)
11
+ row = [bucket, show.any?]
12
+ if @options[:lifecycle].nil?
13
+ presenter.rows << row # always show policy
14
+ elsif @options[:lifecycle]
15
+ presenter.rows << row if status # only show if bucket has some encryption rules
16
+ else
17
+ presenter.rows << row unless status # only show if bucket doesnt have any encryption rules
18
+ end
19
+ end
20
+
21
+ presenter.show
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,28 @@
1
+ class S3Secure::Lifecycle
2
+ class Remove < Base
3
+ RULE_ID = Base::RULE_ID
4
+
5
+ def run
6
+ show = Show.new(@options)
7
+ unless show.has?(RULE_ID)
8
+ puts "Bucket #{@bucket} already does not have the #{RULE_ID} lifecycle rule."
9
+ return
10
+ end
11
+
12
+ builder = Builder.new(show.get_lifecycle_rules(@bucket))
13
+ rules = builder.rules_with_removal
14
+ if rules.empty?
15
+ s3.delete_bucket_lifecycle(bucket: @bucket)
16
+ else
17
+ # update config with removal
18
+ s3.put_bucket_lifecycle_configuration(
19
+ bucket: @bucket, # required
20
+ # content_md5: "ContentMD5",
21
+ lifecycle_configuration: {rules: rules}
22
+ )
23
+ end
24
+
25
+ puts "Removed the #{RULE_ID} lifecycle rule on bucket #{@bucket}"
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,40 @@
1
+ class S3Secure::Lifecycle
2
+ class Show < Base
3
+ RULE_ID = Base::RULE_ID
4
+
5
+ def run
6
+ if any?
7
+ puts "This S3 bucket has lifecycle rules"
8
+ else
9
+ puts "This S3 bucket does not have lifecycle rules"
10
+ end
11
+
12
+ if any?
13
+ puts "Bucket lifecycle details: "
14
+ pp get_lifecycle(@bucket).to_h
15
+ end
16
+ end
17
+
18
+ def any?
19
+ rules = get_lifecycle_rules(@bucket)
20
+ !!(rules && !rules.empty?)
21
+ end
22
+
23
+ def has?(rule_id)
24
+ rules = get_lifecycle_rules(@bucket)
25
+ rules && rules.detect { |rule| rule[:id] == rule_id }
26
+ end
27
+
28
+ def get_lifecycle(bucket)
29
+ s3.get_bucket_lifecycle_configuration(bucket: bucket) # resp
30
+ rescue Aws::S3::Errors::NoSuchLifecycleConfiguration
31
+ end
32
+ memoize :get_lifecycle
33
+
34
+ # Also used by add and remove
35
+ def get_lifecycle_rules(bucket)
36
+ resp = get_lifecycle(bucket)
37
+ resp.rules.map(&:to_h) if resp
38
+ end
39
+ end
40
+ end