Skip to content

Commit

Permalink
S3 Server-Side Encryption (#784)
Browse files Browse the repository at this point in the history
  • Loading branch information
austinbyers authored Jul 6, 2018
1 parent ea5ebe3 commit dab2658
Show file tree
Hide file tree
Showing 27 changed files with 296 additions and 37 deletions.
7 changes: 1 addition & 6 deletions stream_alert_cli/outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,7 @@ def send_creds_to_s3(region, bucket, key, blob_data):
"""
try:
client = boto3.client('s3', region_name=region)
client.put_object(
Body=blob_data,
Bucket=bucket,
Key=key,
ServerSideEncryption='AES256'
)
client.put_object(Body=blob_data, Bucket=bucket, Key=key)

return True
except ClientError as err:
Expand Down
1 change: 1 addition & 0 deletions stream_alert_cli/terraform/alert_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def generate_alert_processor(config):
'prefix': prefix,
'role_id': '${module.alert_processor_lambda.role_id}',
'kms_key_arn': '${aws_kms_key.stream_alert_secrets.arn}',
'sse_kms_key_arn': '${aws_kms_key.server_side_encryption.arn}',
'output_lambda_functions': [
# Strip qualifiers: only the function name is needed for the IAM permissions
func.split(':')[0] for func in config['outputs'].get('aws-lambda', {}).values()
Expand Down
1 change: 1 addition & 0 deletions stream_alert_cli/terraform/athena.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ def generate_athena(config):
'database_name': database,
'queue_name': queue_name,
'results_bucket': results_bucket_name,
'kms_key_id': '${aws_kms_key.server_side_encryption.key_id}',
'lambda_handler': AthenaPackage.lambda_handler,
'lambda_memory': athena_config.get('memory', '128'),
'lambda_timeout': athena_config.get('timeout', '60'),
Expand Down
1 change: 1 addition & 0 deletions stream_alert_cli/terraform/cloudtrail.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def generate_cloudtrail(cluster_name, cluster_dict, config):

module_info = {
'source': 'modules/tf_stream_alert_cloudtrail',
'primary_account_id': config['global']['account']['aws_account_id'],
'account_ids': account_ids,
'cluster': cluster_name,
'prefix': config['global']['account']['prefix'],
Expand Down
6 changes: 4 additions & 2 deletions stream_alert_cli/terraform/firehose.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ def generate_firehose(config, main_dict, logging_bucket):
'prefix': config['global']['account']['prefix'],
'region': config['global']['account']['region'],
's3_logging_bucket': logging_bucket,
's3_bucket_name': firehose_s3_bucket_name
's3_bucket_name': firehose_s3_bucket_name,
'kms_key_id': '${aws_kms_key.server_side_encryption.key_id}'
}

# Add the Delivery Streams individually
Expand All @@ -57,5 +58,6 @@ def generate_firehose(config, main_dict, logging_bucket):
['firehose'].get('compression_format', 'GZIP'),
'log_name': enabled_log,
'role_arn': '${module.kinesis_firehose_setup.firehose_role_arn}',
's3_bucket_name': firehose_s3_bucket_name
's3_bucket_name': firehose_s3_bucket_name,
'kms_key_arn': '${aws_kms_key.server_side_encryption.arn}'
}
57 changes: 56 additions & 1 deletion stream_alert_cli/terraform/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,15 @@ def generate_s3_bucket(bucket, logging, **kwargs):
Keyword Args:
acl (str): The S3 bucket ACL
force_destroy (bool): To enable or disable force destroy of the bucket
sse_algorithm (str): Server-side encryption algorithm 'AES256' or 'aws:kms' (default)
versioning (bool): To enable or disable S3 object versioning
lifecycle_rule (dict): The S3 bucket lifecycle rule
Returns:
dict: S3 bucket Terraform dict to be used in clusters/main.tf.json
"""
sse_algorithm = kwargs.get('sse_algorithm', 'aws:kms')

s3_bucket = {
'bucket': bucket,
'acl': kwargs.get('acl', 'private'),
Expand All @@ -76,8 +79,21 @@ def generate_s3_bucket(bucket, logging, **kwargs):
'logging': {
'target_bucket': logging,
'target_prefix': '{}/'.format(bucket)
},
'server_side_encryption_configuration': {
'rule': {
'apply_server_side_encryption_by_default': {
'sse_algorithm': sse_algorithm
}
}
}
}

if sse_algorithm == 'aws:kms':
s3_bucket['server_side_encryption_configuration']['rule'][
'apply_server_side_encryption_by_default']['kms_master_key_id'] = (
'${aws_kms_key.server_side_encryption.key_id}')

lifecycle_rule = kwargs.get('lifecycle_rule')
if lifecycle_rule:
s3_bucket['lifecycle_rule'] = lifecycle_rule
Expand Down Expand Up @@ -146,7 +162,8 @@ def generate_main(config, init=False):
'days': 365,
'storage_class': 'GLACIER'
}
}
},
sse_algorithm='AES256' # SSE-KMS doesn't seem to work with access logs
)

# Create bucket for Terraform state (if applicable)
Expand All @@ -165,6 +182,7 @@ def generate_main(config, init=False):
'account_id': config['global']['account']['aws_account_id'],
'region': config['global']['account']['region'],
'prefix': config['global']['account']['prefix'],
'kms_key_arn': '${aws_kms_key.server_side_encryption.arn}',
'alerts_table_read_capacity': (
config['global']['infrastructure']['alerts_table']['read_capacity']),
'alerts_table_write_capacity': (
Expand All @@ -176,6 +194,43 @@ def generate_main(config, init=False):
}

# KMS Key and Alias creation
main_dict['resource']['aws_kms_key']['server_side_encryption'] = {
'enable_key_rotation': True,
'description': 'StreamAlert S3 Server-Side Encryption',
'policy': json.dumps({
'Version': '2012-10-17',
'Statement': [
{
'Sid': 'Enable IAM User Permissions',
'Effect': 'Allow',
'Principal': {
'AWS': 'arn:aws:iam::{}:root'.format(
config['global']['account']['aws_account_id']
)
},
'Action': 'kms:*',
'Resource': '*'
},
{
'Sid': 'Allow principals in the account to use the key',
'Effect': 'Allow',
'Principal': '*',
'Action': ['kms:Decrypt', 'kms:GenerateDataKey*', 'kms:Encrypt'],
'Resource': '*',
'Condition': {
'StringEquals': {
'kms:CallerAccount': config['global']['account']['aws_account_id']
}
}
}
]
})
}
main_dict['resource']['aws_kms_alias']['server_side_encryption'] = {
'name': 'alias/{}_server-side-encryption'.format(config['global']['account']['prefix']),
'target_key_id': '${aws_kms_key.server_side_encryption.key_id}'
}

main_dict['resource']['aws_kms_key']['stream_alert_secrets'] = {
'enable_key_rotation': True,
'description': 'StreamAlert secret management'
Expand Down
5 changes: 3 additions & 2 deletions stream_alert_cli/terraform/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,9 @@ def _terraform_init(config):
init_targets = [
'aws_s3_bucket.lambda_source', 'aws_s3_bucket.logging_bucket',
'aws_s3_bucket.stream_alert_secrets', 'aws_s3_bucket.terraform_remote_state',
'aws_s3_bucket.streamalerts', 'aws_kms_key.stream_alert_secrets',
'aws_kms_alias.stream_alert_secrets'
'aws_s3_bucket.streamalerts',
'aws_kms_key.server_side_encryption', 'aws_kms_alias.server_side_encryption',
'aws_kms_key.stream_alert_secrets', 'aws_kms_alias.stream_alert_secrets'
]
if not tf_runner(targets=init_targets):
LOGGER_CLI.error('An error occurred while running StreamAlert init')
Expand Down
2 changes: 1 addition & 1 deletion terraform/modules/tf_alert_processor_iam/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ data "aws_iam_policy_document" "output_secrets" {
"kms:DescribeKey",
]

resources = ["${var.kms_key_arn}"]
resources = ["${var.kms_key_arn}", "${var.sse_kms_key_arn}"]
}

// Allow retrieving encrypted output secrets
Expand Down
6 changes: 5 additions & 1 deletion terraform/modules/tf_alert_processor_iam/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ variable "role_id" {
}

variable "kms_key_arn" {
description = "KMS key ARN used for encrypting output secrets"
description = "KMS key ARN used for (client-side) encrypting output secrets"
}

variable "sse_kms_key_arn" {
description = "KMS key ARN for server-side encryption of the secrets bucket"
}

variable "output_lambda_functions" {
Expand Down
9 changes: 9 additions & 0 deletions terraform/modules/tf_stream_alert_athena/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ resource "aws_s3_bucket" "athena_results_bucket" {
target_bucket = "${var.s3_logging_bucket}"
target_prefix = "${var.results_bucket}/"
}

server_side_encryption_configuration {
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
kms_master_key_id = "${var.kms_key_id}"
}
}
}
}

// Athena Database: streamalert
Expand Down
4 changes: 4 additions & 0 deletions terraform/modules/tf_stream_alert_athena/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ variable "results_bucket" {
type = "string"
}

variable "kms_key_id" {
type = "string"
}

variable "s3_logging_bucket" {
type = "string"
}
Expand Down
119 changes: 110 additions & 9 deletions terraform/modules/tf_stream_alert_cloudtrail/main.tf
Original file line number Diff line number Diff line change
@@ -1,3 +1,88 @@
// KMS key for encrypting CloudTrail logs
resource "aws_kms_key" "cloudtrail_encryption" {
description = "Encrypt Cloudtrail logs for ${var.prefix}.${var.cluster}.streamalert.cloudtrail"
policy = "${data.aws_iam_policy_document.cloudtrail_encryption.json}"
enable_key_rotation = true
}

// This policy is auto-generated by AWS if you manually encrypt a CloudTrail from the console.
data "aws_iam_policy_document" "cloudtrail_encryption" {
statement {
sid = "Enable IAM User Permissions"

principals {
type = "AWS"
identifiers = ["arn:aws:iam::${var.primary_account_id}:root"]
}

actions = ["kms:*"]
resources = ["*"]
}

statement {
sid = "Allow CloudTrail to encrypt logs"

principals {
type = "Service"
identifiers = ["cloudtrail.amazonaws.com"]
}

actions = ["kms:GenerateDataKey*"]
resources = ["*"]

condition {
test = "StringLike"
variable = "kms:EncryptionContext:aws:cloudtrail:arn"
values = ["arn:aws:cloudtrail:*:${var.primary_account_id}:trail/*"]
}
}

statement {
sid = "Allow CloudTrail to describe key"

principals {
type = "Service"
identifiers = ["cloudtrail.amazonaws.com"]
}

actions = ["kms:DescribeKey"]
resources = ["*"]
}

statement {
sid = "Allow principals in the account to decrypt log files"

principals {
type = "AWS"
identifiers = ["*"]
}

actions = [
"kms:Decrypt",
"kms:ReEncryptFrom",
]

resources = ["*"]

condition {
test = "StringEquals"
variable = "kms:CallerAccount"
values = ["${var.primary_account_id}"]
}

condition {
test = "StringLike"
variable = "kms:EncryptionContext:aws:cloudtrail:arn"
values = ["arn:aws:cloudtrail:*:${var.primary_account_id}:trail/*"]
}
}
}

resource "aws_kms_alias" "cloudtrail_encryption" {
name = "alias/${var.prefix}-${var.cluster}-streamalert-cloudtrail"
target_key_id = "${aws_kms_key.cloudtrail_encryption.key_id}"
}

// StreamAlert CloudTrail, also sending to CloudWatch Logs group
resource "aws_cloudtrail" "streamalert" {
count = "${var.send_to_cloudwatch && !var.existing_trail ? 1 : 0}"
Expand All @@ -9,6 +94,7 @@ resource "aws_cloudtrail" "streamalert" {
enable_logging = "${var.enable_logging}"
include_global_service_events = true
is_multi_region_trail = "${var.is_global_trail}"
kms_key_id = "${aws_kms_key.cloudtrail_encryption.arn}"

event_selector {
read_write_type = "All"
Expand All @@ -33,6 +119,7 @@ resource "aws_cloudtrail" "streamalert_no_cloudwatch" {
enable_logging = "${var.enable_logging}"
include_global_service_events = true
is_multi_region_trail = "${var.is_global_trail}"
kms_key_id = "${aws_kms_key.cloudtrail_encryption.arn}"

event_selector {
read_write_type = "All"
Expand Down Expand Up @@ -90,25 +177,30 @@ data "aws_iam_policy_document" "cloudtrail_to_cloudwatch_create_logs" {
count = "${var.send_to_cloudwatch ? 1 : 0}"

statement {
sid = "AWSCloudTrailCreateLogStream"
effect = "Allow"

actions = [
"logs:CreateLogStream",
]
sid = "AWSCloudTrailCreateLogStream"
effect = "Allow"
actions = ["logs:CreateLogStream"]
resources = ["${aws_cloudwatch_log_group.cloudtrail_logging.arn}"]
}

statement {
sid = "AWSCloudTrailPutLogEvents"
effect = "Allow"
actions = ["logs:PutLogEvents"]
resources = ["${aws_cloudwatch_log_group.cloudtrail_logging.arn}"]
}

statement {
sid = "AWSCloudTrailPutLogEvents"
sid = "AWSCloudTrailEncryptLogEvents"
effect = "Allow"

actions = [
"logs:PutLogEvents",
"kms:Decrypt",
"kms:Encrypt",
"kms:GenerateDataKey*",
]

resources = ["${aws_cloudwatch_log_group.cloudtrail_logging.arn}"]
resources = ["${aws_kms_key.cloudtrail_encryption.arn}"]
}
}

Expand Down Expand Up @@ -145,6 +237,15 @@ resource "aws_s3_bucket" "cloudtrail_bucket" {

policy = "${data.aws_iam_policy_document.cloudtrail_bucket.json}"

server_side_encryption_configuration {
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
kms_master_key_id = "${aws_kms_key.cloudtrail_encryption.key_id}"
}
}
}

tags {
Name = "${var.prefix}.${var.cluster}.streamalert.cloudtrail"
Cluster = "${var.cluster}"
Expand Down
2 changes: 2 additions & 0 deletions terraform/modules/tf_stream_alert_cloudtrail/variables.tf
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
variable "primary_account_id" {}

variable "account_ids" {
type = "list"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ resource "aws_kinesis_firehose_delivery_stream" "stream_alerts" {
buffer_size = "${var.buffer_size}"
buffer_interval = "${var.buffer_interval}"
compression_format = "${var.compression_format}"
kms_key_arn = "${var.kms_key_arn}"

cloudwatch_logging_options {
enabled = true
Expand Down
Loading

0 comments on commit dab2658

Please sign in to comment.