Skip to content

Commit d6ee06a

Browse files
committed
Use deny policies rather than conditions to reject insecure access
1 parent 5c5ac6b commit d6ee06a

File tree

1 file changed

+32
-22
lines changed

1 file changed

+32
-22
lines changed

buckup/bucket_creator.py

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,6 @@
1616

1717
POLICY_NAME_FORMAT = "{bucket_name}-owner-policy"
1818

19-
REQUIRE_HTTPS_CONDITION = {
20-
"Bool": {
21-
# Require HTTPS
22-
"aws:SecureTransport": "true"
23-
},
24-
"NumericGreaterThanEquals": {
25-
# Require TLS >= 1.2
26-
"s3:TlsVersion": ["1.2"]
27-
},
28-
}
29-
3019

3120
class BucketCreator:
3221
def __init__(self, profile_name=None, region_name=None):
@@ -77,11 +66,36 @@ def format_path(path):
7766
"Principal": "*",
7867
"Action": ["s3:GetObject"],
7968
"Resource": paths_resources,
80-
# Require HTTPS for public requests
81-
"Condition": REQUIRE_HTTPS_CONDITION,
8269
}
8370

8471
def get_bucket_policy_statements_for_user_access(self, bucket, user):
72+
# Deny access using a version of TLS older than 1.2.
73+
# This is also enforced by S3, but included here for visibility.
74+
yield {
75+
"Sid": "DenyOutdatedTLS",
76+
"Effect": "Deny",
77+
"Principal": "*",
78+
"Action": "s3:*",
79+
"Resource": [
80+
f"arn:aws:s3:::{bucket.name}",
81+
f"arn:aws:s3:::{bucket.name}/*",
82+
],
83+
"Condition": {"NumericLessThan": {"s3:TlsVersion": "1.2"}},
84+
}
85+
86+
# Deny access which isn't over HTTPS
87+
yield {
88+
"Sid": "DenyInsecureTransport",
89+
"Effect": "Deny",
90+
"Principal": "*",
91+
"Action": "s3:*",
92+
"Resource": [
93+
f"arn:aws:s3:::{bucket.name}",
94+
f"arn:aws:s3:::{bucket.name}/*",
95+
],
96+
"Condition": {"Bool": {"aws:SecureTransport": "false"}},
97+
}
98+
8599
# Create policy statement giving the created user access to
86100
# non-destructive actions on the bucket.
87101
yield {
@@ -95,8 +109,6 @@ def get_bucket_policy_statements_for_user_access(self, bucket, user):
95109
"s3:ListBucketVersions",
96110
],
97111
"Resource": "arn:aws:s3:::{bucket_name}".format(bucket_name=bucket.name),
98-
# Require HTTPS for API
99-
"Condition": REQUIRE_HTTPS_CONDITION,
100112
}
101113
# Create policy statement giving the created user full access over the
102114
# objects.
@@ -106,14 +118,11 @@ def get_bucket_policy_statements_for_user_access(self, bucket, user):
106118
"Principal": {"AWS": user.arn},
107119
"Action": "s3:*",
108120
"Resource": "arn:aws:s3:::{bucket_name}/*".format(bucket_name=bucket.name),
109-
# Require HTTPS for API
110-
"Condition": REQUIRE_HTTPS_CONDITION,
111121
}
112122

113123
def set_bucket_policy(
114124
self, bucket, user, allow_public_acls, public_get_object_paths=None
115125
):
116-
policy_statement = []
117126
public_access = bool(public_get_object_paths)
118127

119128
# NB: This API doesn't exist on a `Bucket`
@@ -129,15 +138,16 @@ def set_bucket_policy(
129138
if public_access or allow_public_acls:
130139
print("Configured public access to bucket.")
131140

141+
policy_statement = list(
142+
self.get_bucket_policy_statements_for_user_access(bucket, user)
143+
)
144+
132145
if public_access:
133146
policy_statement.append(
134147
self.get_bucket_policy_statement_for_get_object(
135148
bucket, public_get_object_paths
136149
)
137150
)
138-
policy_statement.extend(
139-
list(self.get_bucket_policy_statements_for_user_access(bucket, user))
140-
)
141151
policy = json.dumps(
142152
{
143153
"Version": "2012-10-17",
@@ -148,7 +158,7 @@ def set_bucket_policy(
148158
try:
149159
bucket.Policy().put(Policy=policy)
150160
except ClientError as e:
151-
if e.response["Error"]["Code"] == "MalformedPolicy":
161+
if e.response["Error"]["Message"] == "Invalid principal in policy":
152162
print(
153163
"Waiting for the user to be available to be "
154164
"attached to the policy (wait 5s)."

0 commit comments

Comments
 (0)