diff --git a/modules/python-modules/syslogng/modules/s3/s3_destination.py b/modules/python-modules/syslogng/modules/s3/s3_destination.py index 65a7c390bf..053165fbe1 100644 --- a/modules/python-modules/syslogng/modules/s3/s3_destination.py +++ b/modules/python-modules/syslogng/modules/s3/s3_destination.py @@ -66,6 +66,8 @@ def __init_options(self, options: Dict[str, Any]) -> None: self.max_pending_uploads = int(options["max_pending_uploads"]) self.flush_grace_period = int(options["flush_grace_period"]) self.region: Optional[str] = str(options["region"]) + self.server_side_encryption = str(options["server_side_encryption"]) + self.kms_key = str(options["kms_key"]) self.storage_class = str(options["storage_class"]).upper().replace("-", "_") self.canned_acl = str(options["canned_acl"]).lower().replace("_", "-") except KeyError: @@ -101,6 +103,16 @@ def __init_options(self, options: Dict[str, Any]) -> None: if self.region == "": self.region = None + if self.server_side_encryption != "" and self.server_side_encryption != "aws:kms": + assert False, "server-side-encryption() supports only aws:kms" + + if self.server_side_encryption == "aws:kms" and self.kms_key == "": + assert False, "kms-key() must be set when server-side-encryption() is aws:kms" + + if self.kms_key != "" and self.server_side_encryption == "": + self.logger.warn("ignoring kms-key() as server-side-encryption() is disabled") + self.kms_key = "" + VALID_STORAGE_CLASSES = { "STANDARD", "REDUCED_REDUNDANCY", @@ -363,6 +375,8 @@ def __create_initial_s3_object(self, target_key: str, timestamp: str) -> S3Objec target_key=target_key, timestamp=timestamp, compress=self.compression, + server_side_encryption=self.server_side_encryption, + kms_key=self.kms_key, storage_class=self.storage_class, persist_name=self.persist_name, executor=self.executor, diff --git a/modules/python-modules/syslogng/modules/s3/s3_object.py b/modules/python-modules/syslogng/modules/s3/s3_object.py index 776a359305..0ce1716fe2 100644 --- a/modules/python-modules/syslogng/modules/s3/s3_object.py +++ b/modules/python-modules/syslogng/modules/s3/s3_object.py @@ -117,6 +117,8 @@ def __init__( compress: Optional[bool] = None, compresslevel: Optional[int] = None, chunk_size: Optional[int] = None, + server_side_encryption: Optional[str] = None, + kms_key: Optional[str] = None, storage_class: Optional[str] = None, canned_acl: Optional[str] = None, ): @@ -133,6 +135,8 @@ def __init__( and compresslevel is not None and chunk_size is not None and storage_class is not None + and server_side_encryption is not None + and kms_key is not None and canned_acl is not None ) else: @@ -161,6 +165,8 @@ def __init__( "compress", "compresslevel", "chunk-size", + "server-side-encryption", + "kms-key", "storage-class", "finished", "upload-id", @@ -180,6 +186,8 @@ def __init__( self.__compress: bool = cache.get("compress", compress) self.__compresslevel: bool = cache.get("compresslevel", compresslevel) self.__chunk_size: bool = cache.get("chunk-size", chunk_size) + self.__server_side_encryption: str = cache.get("server-side-encryption", server_side_encryption) + self.__kms_key: str = cache.get("kms-key", kms_key) self.__storage_class: str = cache.get("storage-class", storage_class) self.__canned_acl: str = cache.get("canned-acl", canned_acl) self.__finished: bool = cache.get("finished", False) @@ -202,6 +210,8 @@ def __flush(self) -> None: "index": self.__index, "compress": self.__compress, "compresslevel": self.__compresslevel, + "server-side-encryption": self.__server_side_encryption, + "kms-key": self.__kms_key, "storage-class": self.__storage_class, "canned-acl": self.__canned_acl, "chunk-size": self.__chunk_size, @@ -264,6 +274,14 @@ def compresslevel(self) -> bool: def chunk_size(self) -> bool: return self.__chunk_size + @property + def server_side_encryption(self) -> str: + return self.__server_side_encryption + + @property + def kms_key(self) -> str: + return self.__kms_key + @property def storage_class(self) -> str: return self.__storage_class @@ -345,6 +363,8 @@ def __init__( compress: Optional[bool] = None, chunk_size: Optional[int] = None, compresslevel: Optional[int] = None, + server_side_encryption: Optional[str] = None, + kms_key: Optional[str] = None, storage_class: Optional[str] = None, canned_acl: Optional[str] = None, persist: Optional[S3ObjectPersist] = None, @@ -377,6 +397,8 @@ def __init__( and compress is not None and chunk_size is not None and compresslevel is not None + and server_side_encryption is not None + and kms_key is not None and storage_class is not None and canned_acl is not None ) @@ -389,6 +411,8 @@ def __init__( compress=compress, compresslevel=compresslevel, chunk_size=chunk_size, + server_side_encryption=server_side_encryption, + kms_key=kms_key, storage_class=storage_class, canned_acl=canned_acl, ) @@ -431,6 +455,8 @@ def create_initial( timestamp: str, compress: bool, compresslevel: int, + server_side_encryption: str, + kms_key: str, storage_class: str, canned_acl: str, persist_name: str, @@ -451,6 +477,8 @@ def create_initial( target_index=0, compress=compress, compresslevel=compresslevel, + server_side_encryption=server_side_encryption, + kms_key=kms_key, storage_class=storage_class, canned_acl=canned_acl, chunk_size=chunk_size, @@ -480,6 +508,8 @@ def create_next(self) -> S3Object: target_index=self.index + 1, compress=self.__persist.compress, compresslevel=self.__persist.compresslevel, + server_side_encryption=self.__persist.server_side_encryption, + kms_key=self.__persist.kms_key, storage_class=self.__persist.storage_class, canned_acl=self.__persist.canned_acl, chunk_size=self.__persist.chunk_size, @@ -546,6 +576,13 @@ def __ensure_multipart_upload_started(self) -> bool: if self.__persist.upload_id != "": return True + sse_arguments = {} + if self.__persist.server_side_encryption != "": + sse_arguments["ServerSideEncryption"] = self.__persist.server_side_encryption + + if self.__persist.kms_key != "": + sse_arguments["SSEKMSKeyId"] = self.__persist.kms_key + try: if self.__persist.canned_acl != "": response = self.__client.create_multipart_upload( @@ -553,12 +590,14 @@ def __ensure_multipart_upload_started(self) -> bool: Key=self.key, StorageClass=self.__persist.storage_class, ACL=self.__persist.canned_acl, + **sse_arguments, ) else: response = self.__client.create_multipart_upload( Bucket=self.bucket, Key=self.key, StorageClass=self.__persist.storage_class, + **sse_arguments, ) except (ClientError, EndpointConnectionError) as e: self.__logger.error(f"Failed to create multipart upload: {self.bucket}/{self.key} => {e}") diff --git a/modules/python-modules/syslogng/modules/s3/scl/s3.conf b/modules/python-modules/syslogng/modules/s3/scl/s3.conf index 49d15a4b3a..25dab3452c 100644 --- a/modules/python-modules/syslogng/modules/s3/scl/s3.conf +++ b/modules/python-modules/syslogng/modules/s3/scl/s3.conf @@ -37,6 +37,8 @@ block destination s3( max_pending_uploads(32) flush_grace_period(60) region("") + server_side_encryption("") + kms_key("") storage_class("STANDARD") canned_acl("") ... @@ -61,6 +63,8 @@ block destination s3( "max_pending_uploads" => `max_pending_uploads` "flush_grace_period" => `flush_grace_period` "region" => "`region`" + "server_side_encryption" => "`server_side_encryption`" + "kms_key" => "`kms_key`" "storage_class" => "`storage_class`" "canned_acl" => "`canned_acl`" )