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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +16 -0
- data/LICENSE.txt +201 -22
- data/README.md +134 -16
- data/lib/s3_secure.rb +3 -2
- data/lib/s3_secure/access_logs.rb +30 -0
- data/lib/s3_secure/access_logs/base.rb +4 -0
- data/lib/s3_secure/access_logs/disable.rb +37 -0
- data/lib/s3_secure/access_logs/enable.rb +41 -0
- data/lib/s3_secure/access_logs/list.rb +25 -0
- data/lib/s3_secure/access_logs/show.rb +89 -0
- data/lib/s3_secure/aws_services.rb +1 -30
- data/lib/s3_secure/aws_services/s3.rb +54 -0
- data/lib/s3_secure/cli.rb +26 -0
- data/lib/s3_secure/command.rb +7 -0
- data/lib/s3_secure/encryption.rb +2 -0
- data/lib/s3_secure/encryption/disable.rb +4 -8
- data/lib/s3_secure/encryption/enable.rb +4 -8
- data/lib/s3_secure/encryption/list.rb +12 -16
- data/lib/s3_secure/encryption/show.rb +11 -6
- data/lib/s3_secure/help/batch.md +14 -0
- data/lib/s3_secure/help/encryption/disable.md +5 -0
- data/lib/s3_secure/help/encryption/enable.md +6 -0
- data/lib/s3_secure/help/encryption/list.md +5 -0
- data/lib/s3_secure/help/lifecycle/add.md +13 -0
- data/lib/s3_secure/help/lifecycle/list.md +22 -0
- data/lib/s3_secure/help/lifecycle/remove.md +5 -0
- data/lib/s3_secure/help/lifecycle/show.md +13 -0
- data/lib/s3_secure/help/policy/enforce_ssl.md +34 -0
- data/lib/s3_secure/help/policy/list.md +5 -0
- data/lib/s3_secure/help/policy/unforce_ssl.md +61 -0
- data/lib/s3_secure/help/summary.md +22 -0
- data/lib/s3_secure/lifecycle.rb +31 -0
- data/lib/s3_secure/lifecycle/add.rb +33 -0
- data/lib/s3_secure/lifecycle/base.rb +5 -0
- data/lib/s3_secure/lifecycle/builder.rb +47 -0
- data/lib/s3_secure/lifecycle/list.rb +24 -0
- data/lib/s3_secure/lifecycle/remove.rb +28 -0
- data/lib/s3_secure/lifecycle/show.rb +40 -0
- data/lib/s3_secure/policy.rb +2 -0
- data/lib/s3_secure/policy/document.rb +1 -1
- data/lib/s3_secure/policy/enforce.rb +3 -6
- data/lib/s3_secure/policy/list.rb +13 -17
- data/lib/s3_secure/policy/show.rb +8 -6
- data/lib/s3_secure/policy/unforce.rb +5 -8
- data/lib/s3_secure/remediate_all.rb +11 -0
- data/lib/s3_secure/summary.rb +13 -0
- data/lib/s3_secure/summary/item.rb +16 -0
- data/lib/s3_secure/summary/items.rb +65 -0
- data/lib/s3_secure/table.rb +18 -0
- data/lib/s3_secure/version.rb +1 -1
- data/lib/s3_secure/versioning.rb +29 -0
- data/lib/s3_secure/versioning/base.rb +4 -0
- data/lib/s3_secure/versioning/disable.rb +19 -0
- data/lib/s3_secure/versioning/enable.rb +19 -0
- data/lib/s3_secure/versioning/list.rb +24 -0
- data/lib/s3_secure/versioning/show.rb +27 -0
- data/s3-secure.gemspec +5 -2
- data/spec/lib/lifecycle/builder_spec.rb +85 -0
- metadata +71 -6
- data/Gemfile.lock +0 -89
- 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,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,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,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,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
|