-
-
Notifications
You must be signed in to change notification settings - Fork 909
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for SSE-C encryption #1217
base: master
Are you sure you want to change the base?
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,28 +8,30 @@ | |
|
||
from __future__ import absolute_import, division | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've just sorted imports - can revert them back |
||
import sys | ||
import os | ||
import time | ||
import errno | ||
import mimetypes | ||
import io | ||
import mimetypes | ||
import os | ||
import pprint | ||
from xml.sax import saxutils | ||
import sys | ||
import time | ||
from logging import debug, error, info, warning | ||
from socket import timeout as SocketTimeoutException | ||
from logging import debug, info, warning, error | ||
from stat import ST_SIZE | ||
from xml.sax import saxutils | ||
|
||
try: | ||
# python 3 support | ||
from urlparse import urlparse | ||
except ImportError: | ||
from urllib.parse import urlparse | ||
try: | ||
# Python 2 support | ||
from base64 import encodestring | ||
from base64 import encodestring, b64encode | ||
except ImportError: | ||
# Python 3.9.0+ support | ||
from base64 import encodebytes as encodestring | ||
from base64 import b64encode | ||
|
||
import select | ||
|
||
|
@@ -38,26 +40,27 @@ | |
except ImportError: | ||
from md5 import md5 | ||
|
||
from .BaseUtils import (getListFromXml, getTextFromXml, getRootTagName, | ||
decode_from_s3, encode_to_s3, s3_quote) | ||
from .Utils import (convertHeaderTupleListToDict, hash_file_md5, unicodise, | ||
deunicodise, check_bucket_name, | ||
check_bucket_name_dns_support, getHostnameFromBucket, | ||
calculateChecksum) | ||
from .SortedDict import SortedDict | ||
from .AccessLog import AccessLog | ||
from .ACL import ACL, GranteeLogDelivery | ||
from .BaseUtils import (decode_from_s3, encode_to_s3, getListFromXml, | ||
getRootTagName, getTextFromXml, s3_quote) | ||
from .BidirMap import BidirMap | ||
from .Config import Config | ||
from .ConnMan import ConnMan | ||
from .Crypto import (checksum_sha256_buffer, checksum_sha256_file, | ||
format_param_str, sign_request_v2, sign_request_v4) | ||
from .Exceptions import * | ||
from .MultiPart import MultiPartUpload | ||
from .S3Uri import S3Uri | ||
from .ConnMan import ConnMan | ||
from .Crypto import (sign_request_v2, sign_request_v4, checksum_sha256_file, | ||
checksum_sha256_buffer, format_param_str) | ||
from .SortedDict import SortedDict | ||
from .Utils import (calculateChecksum, check_bucket_name, | ||
check_bucket_name_dns_support, | ||
convertHeaderTupleListToDict, deunicodise, | ||
getHostnameFromBucket, hash_file_md5, unicodise) | ||
|
||
try: | ||
from ctypes import ArgumentError | ||
|
||
import magic | ||
try: | ||
## https://github.com/ahupp/python-magic | ||
|
@@ -701,6 +704,16 @@ def object_put(self, filename, uri, extra_headers = None, extra_label = ""): | |
headers['x-amz-server-side-encryption'] = 'aws:kms' | ||
headers['x-amz-server-side-encryption-aws-kms-key-id'] = self.config.kms_key | ||
|
||
if self.config.sse_customer_key: | ||
md5s = md5() | ||
sse_customer_key = self.config.sse_customer_key.encode() | ||
md5s.update(sse_customer_key) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (Same for other similar blocks) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
md5_encoded = b64encode(md5s.digest()) | ||
key_encoded = b64encode(sse_customer_key) | ||
headers["x-amz-server-side-encryption-customer-algorithm"] = "AES256" | ||
headers["x-amz-server-side-encryption-customer-key"] = key_encoded.decode() | ||
headers["x-amz-server-side-encryption-customer-key-md5"] = md5_encoded.decode() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I put the comment here, but same for all There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
|
||
## MIME-type handling | ||
headers["content-type"] = self.content_type(filename=filename) | ||
|
||
|
@@ -755,10 +768,32 @@ def object_put(self, filename, uri, extra_headers = None, extra_label = ""): | |
response = self.send_file(request, src_stream, labels) | ||
return response | ||
|
||
def object_get(self, uri, stream, dest_name, start_position = 0, extra_label = ""): | ||
def object_get(self, uri, stream, dest_name, extra_headers, start_position = 0, extra_label = ""): | ||
if uri.type != "s3": | ||
raise ValueError("Expected URI type 's3', got '%s'" % uri.type) | ||
request = self.create_request("OBJECT_GET", uri = uri) | ||
headers = SortedDict(ignore_case=True) | ||
if extra_headers: | ||
headers.update(extra_headers) | ||
## Set server side encryption | ||
if self.config.server_side_encryption: | ||
headers["x-amz-server-side-encryption"] = "AES256" | ||
|
||
## Set kms headers | ||
if self.config.kms_key: | ||
headers['x-amz-server-side-encryption'] = 'aws:kms' | ||
headers['x-amz-server-side-encryption-aws-kms-key-id'] = self.config.kms_key | ||
|
||
if self.config.sse_customer_key: | ||
md5s = md5() | ||
sse_customer_key = self.config.sse_customer_key.encode() | ||
md5s.update(sse_customer_key) | ||
md5_encoded = b64encode(md5s.digest()) | ||
key_encoded = b64encode(sse_customer_key) | ||
headers["x-amz-server-side-encryption-customer-algorithm"] = "AES256" | ||
headers["x-amz-server-side-encryption-customer-key"] = key_encoded.decode() | ||
headers["x-amz-server-side-encryption-customer-key-md5"] = md5_encoded.decode() | ||
|
||
request = self.create_request("OBJECT_GET", uri = uri, headers=headers) | ||
labels = { 'source' : uri.uri(), 'destination' : dest_name, 'extra' : extra_label } | ||
response = self.recv_file(request, stream, labels, start_position) | ||
return response | ||
|
@@ -954,6 +989,16 @@ def object_copy(self, src_uri, dst_uri, extra_headers=None, | |
headers['x-amz-server-side-encryption-aws-kms-key-id'] = \ | ||
self.config.kms_key | ||
|
||
if self.config.sse_copy_source_customer_key: | ||
md5s = md5() | ||
sse_copy_source_customer_key = self.config.sse_copy_source_customer_key.encode() | ||
md5s.update(sse_copy_source_customer_key) | ||
md5_encoded = b64encode(md5s.digest()) | ||
key_encoded = b64encode(sse_copy_source_customer_key) | ||
headers["x-amz-copy-source-server-side-encryption-customer-algorithm"] = "AES256" | ||
headers["x-amz-copy-source-server-side-encryption-customer-key"] = key_encoded.decode() | ||
headers["x-amz-copy-source-server-side-encryption-customer-key-md5"] = md5_encoded.decode() | ||
|
||
# Following meta data are not updated in simple COPY by aws. | ||
if extra_headers: | ||
headers.update(extra_headers) | ||
|
@@ -1828,19 +1873,32 @@ def send_file(self, request, stream, labels, buffer = '', throttle = 0, | |
## Non-recoverable error | ||
raise S3Error(response) | ||
|
||
debug("MD5 sums: computed=%s, received=%s" % (md5_computed, response["headers"].get('etag', '').strip('"\''))) | ||
## when using KMS encryption, MD5 etag value will not match | ||
md5_from_s3 = response["headers"].get("etag", "").strip('"\'') | ||
if ('-' not in md5_from_s3) and (md5_from_s3 != md5_hash.hexdigest()) and response["headers"].get("x-amz-server-side-encryption") != 'aws:kms': | ||
warning("MD5 Sums don't match!") | ||
if retries: | ||
warning("Retrying upload of %s" % (filename)) | ||
return self.send_file(request, stream, labels, buffer, throttle, | ||
retries - 1, offset, chunk_size, use_expect_continue) | ||
if self.config.sse_customer_key: | ||
if response["headers"]["x-amz-server-side-encryption-customer-key-md5"] != \ | ||
request.headers["x-amz-server-side-encryption-customer-key-md5"]: | ||
warning("MD5 of customer key don't match!") | ||
if retries: | ||
warning("Retrying upload of %s" % (filename)) | ||
return self.send_file(request, stream, labels, buffer, throttle, retries - 1, offset, chunk_size) | ||
else: | ||
warning("Too many failures. Giving up on '%s'" % (filename)) | ||
raise S3UploadError | ||
else: | ||
warning("Too many failures. Giving up on '%s'" % (filename)) | ||
raise S3UploadError("Too many failures. Giving up on '%s'" | ||
% filename) | ||
debug("Match of x-amz-server-side-encryption-customer-key-md5") | ||
else: | ||
debug("MD5 sums: computed=%s, received=%s" % (md5_computed, response["headers"].get('etag', '').strip('"\''))) | ||
## when using KMS encryption, MD5 etag value will not match | ||
md5_from_s3 = response["headers"].get("etag", "").strip('"\'') | ||
if ('-' not in md5_from_s3) and (md5_from_s3 != md5_hash.hexdigest()) and response["headers"].get("x-amz-server-side-encryption") != 'aws:kms': | ||
warning("MD5 Sums don't match!") | ||
if retries: | ||
warning("Retrying upload of %s" % (filename)) | ||
return self.send_file(request, stream, labels, buffer, throttle, | ||
retries - 1, offset, chunk_size, use_expect_continue) | ||
else: | ||
warning("Too many failures. Giving up on '%s'" % (filename)) | ||
raise S3UploadError("Too many failures. Giving up on '%s'" | ||
% filename) | ||
|
||
return response | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you add 172.17.0.1? localhost is not enough for github actions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nah, it's not needed. it's just one of IPs minio listens on in CI, so just added all IPs while testing. Changed.