s3-secure 0.2.0 → 0.5.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 +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
|