Browse Source

Merge pull request #341 from mdr-engineering/feature/ftd_MSOCI-2003_S3_Phantom_Archives

Adds a Module for an S3 Archive Bucket for Phantom
Frederick Damstra 3 years ago
parent
commit
4ee9b9bff1

+ 134 - 0
base/phantom/instance_profile.tf

@@ -0,0 +1,134 @@
+resource "aws_iam_instance_profile" "phantom_instance_profile" {
+  name = "xdr-phantom-instance-profile"
+  path = "/instance/"
+  role = aws_iam_role.phantom_instance_role.name
+}
+
+resource "aws_iam_role"  "phantom_instance_role" {
+  name = "xdr-phantom-instance-role"
+  path = "/instance/"
+  assume_role_policy = <<EOF
+{
+    "Version": "2012-10-17",
+    "Statement": [
+      {
+        "Sid": "",
+        "Effect": "Allow",
+        "Principal": {
+          "Service": [
+            "ec2.amazonaws.com",
+            "ssm.amazonaws.com"
+            ]
+        },
+        "Action": "sts:AssumeRole"
+      }
+    ]
+  }
+EOF
+}
+
+# These 3 are the default profile attachments:
+resource "aws_iam_role_policy_attachment" "phantom_instance_AmazonEC2RoleforSSM" {
+  role       = aws_iam_role.phantom_instance_role.name
+  policy_arn = "arn:${var.aws_partition}:iam::aws:policy/service-role/AmazonEC2RoleforSSM"
+}
+
+resource "aws_iam_role_policy_attachment" "phantom_instance_default_policy_attach" {
+  role       = aws_iam_role.phantom_instance_role.name
+  policy_arn = "arn:${ var.aws_partition }:iam::${ var.aws_account_id }:policy/launchroles/default_instance_tag_read"
+}
+
+resource "aws_iam_role_policy_attachment" "phantom_instance_cloudwatch_policy_attach" {
+  role       = aws_iam_role.phantom_instance_role.name
+  policy_arn = "arn:${ var.aws_partition }:iam::${ var.aws_account_id }:policy/cloudwatch_events"
+}
+
+# Phantom Specific Policy
+#resource "aws_iam_policy" "phantom_instance_policy" {
+#  name        = "phantom_instance_policy"
+#  path        = "/launchroles/"
+#  description = "This policy allows phantom-specific functions"
+#  policy      = data.aws_iam_policy_document.phantom_instance_policy_doc.json
+#}
+#
+#data "aws_iam_policy_document" "phantom_instance_policy_doc" {
+#  # Allow copying to S3 for frozen
+#  # Allow use of S3 for SmartStore
+#  statement {
+#    sid = "GeneralBucketAccess"
+#    effect = "Allow"
+#    actions = [
+#      "s3:ListAllMyBuckets",
+#      "s3:HeadBucket",
+#    ]
+#    resources = [ "*" ]
+#  }
+#
+#  statement {
+#    sid = "S3BucketAccess"
+#    effect = "Allow"
+#    actions = [
+#      "s3:GetLifecycleConfiguration",
+#      "s3:DeleteObjectVersion",
+#      "s3:ListBucketVersions",
+#      "s3:GetBucketLogging",
+#      "s3:RestoreObject",
+#      "s3:ListBuckets",
+#      "s3:GetBucketVersioning",
+#      "s3:PutObject",
+#      "s3:GetObject",
+#      "s3:PutLifecycleConfiguration",
+#      "s3:GetBucketCORS",
+#      "s3:DeleteObject",
+#      "s3:GetBucketLocation",
+#      "s3:GetObjectVersion",
+#    ]
+#    resources = [ 
+#      "arn:${ var.aws_partition }:s3:::xdr-${ var.prefix }-${ var.environment }-splunk-frozen",
+#      "arn:${ var.aws_partition }:s3:::xdr-${ var.prefix }-${ var.environment }-splunk-frozen/*",
+#      "arn:${ var.aws_partition }:s3:::xdr-${ var.prefix }-${ var.environment }-splunk-smartstore",
+#      "arn:${ var.aws_partition }:s3:::xdr-${ var.prefix }-${ var.environment }-splunk-smartstore/*",
+#    ]
+#  }
+#
+#  statement {
+#    sid = "S3ReadOnlyBucketAccess"
+#    effect = "Allow"
+#    actions = [
+#      "s3:ListBucketVersions",
+#      "s3:ListBuckets",
+#      "s3:GetBucketVersioning",
+#      "s3:GetObject",
+#      "s3:GetBucketCORS",
+#      "s3:GetBucketLocation",
+#      "s3:GetObjectVersion",
+#    ]
+#    resources = [ 
+#      "arn:${ var.aws_partition }:s3:::xdr-${ var.prefix }-${ var.environment }-splunk-apps",
+#      "arn:${ var.aws_partition }:s3:::xdr-${ var.prefix }-${ var.environment }-splunk-apps/*",
+#    ]
+#  }
+#
+#  statement {
+#    sid = "KMSKeyAccess"
+#    effect = "Allow"
+#    actions = [
+#      "kms:Decrypt",
+#      "kms:GenerateDataKeyWithoutPlaintext",
+#      "kms:Verify",
+#      "kms:GenerateDataKeyPairWithoutPlaintext",
+#      "kms:GenerateDataKeyPair",
+#      "kms:ReEncryptFrom",
+#      "kms:Encrypt",
+#      "kms:GenerateDataKey",
+#      "kms:ReEncryptTo",
+#      "kms:Sign",
+#    ]
+#    resources = [ "*" ]
+#  }      
+#}
+#
+#resource "aws_iam_role_policy_attachment" "phantom_instance_policy_attach" {
+#  role       = aws_iam_role.phantom_instance_role.name
+#  policy_arn = aws_iam_policy.phantom_instance_policy.arn
+#}

+ 1 - 1
base/phantom/main.tf

@@ -42,7 +42,7 @@ resource "aws_instance" "phantom-server-instance" {
   instance_type = var.instance_type
   key_name = "msoc-build"
   monitoring = false
-  iam_instance_profile = "msoc-default-instance-profile"
+  iam_instance_profile = aws_iam_instance_profile.phantom_instance_profile.id
 
   ami = local.ami_map[local.ami_selection]
   # We need to ignore ebs_block_device changes, because if the AMI changes, so does the snapshot_id.

+ 9 - 0
base/phantom_s3_bucket/README.md

@@ -0,0 +1,9 @@
+# Creates an S3 bucket and role for phantom
+
+Phantom instance must assume-role first. To do this from the command-line:
+
+```
+aws --region us-gov-east-1 sts assume-role --role-arn arn:aws-us-gov:iam::738800754746:role/service/phantom_s3 --role-session-name ftd_testing
+# Output will contain AccessKeyId and SecretAcessKeyID
+AWS_ACCESS_KEY_ID=[REPLACE] AWS_SECRET_ACCESS_KEY=[REPLACE] AWS_SESSION_TOKEN=[replace] aws --region us-gov-east-1 s3 ls
+```

+ 112 - 0
base/phantom_s3_bucket/iam_phantom_s3_role.tf

@@ -0,0 +1,112 @@
+resource "aws_iam_role" "phantom_s3_role" {
+  name = "phantom_s3"
+  path  = "/service/"
+  force_detach_policies = true # causes "DeleteConflict" if not present
+
+  # the extra_trusted_salt variable allows the addition of additional
+  # trusted sources, such as the dev salt master (for dev environments)
+  # and developer users.
+  assume_role_policy = <<EOF
+{
+  "Version": "2012-10-17",
+  "Statement": [
+    {
+      "Effect": "Allow",
+      "Principal": {
+        "AWS": "arn:${ var.aws_partition }:iam::${ var.aws_account_id }:role/instance/xdr-phantom-instance-role"
+      },
+      "Action": "sts:AssumeRole"
+    }
+  ]
+}
+EOF
+
+  tags = merge(var.standard_tags, var.tags)
+}
+
+resource "aws_iam_role_policy_attachment" "phantom_s3_policy_attach" {
+  role = aws_iam_role.phantom_s3_role.name
+  policy_arn = aws_iam_policy.phantom_s3_policy.arn
+}
+
+resource "aws_iam_policy" "phantom_s3_policy" {
+  name  = "phantom_s3_policy"
+  path  = "/service/"
+  description = "Policy which allows phantom to read/write to the S3 bucket"
+  policy = data.aws_iam_policy_document.phantom_s3_policy_doc.json
+}
+
+data "aws_iam_policy_document" "phantom_s3_policy_doc" {
+  statement {
+    sid = "GeneralBucketAccess"
+    effect = "Allow"
+    actions = [
+      "s3:ListAllMyBuckets",
+      "s3:HeadBucket",
+    ]
+    resources = [ "*" ]
+  }
+
+  statement {
+    sid = "S3BucketAccess"
+    effect = "Allow"
+    actions = [
+      "s3:GetLifecycleConfiguration",
+      "s3:DeleteObjectVersion",
+      "s3:ListBucketVersions",
+      "s3:GetBucketLogging",
+      "s3:RestoreObject",
+      "s3:ListBuckets",
+      "s3:ListObjects",
+      "s3:ListObjectsV2",
+      "s3:GetBucketVersioning",
+      "s3:PutObject",
+      "s3:GetObject",
+      "s3:PutLifecycleConfiguration",
+      "s3:GetBucketCORS",
+      "s3:DeleteObject",
+      "s3:GetBucketLocation",
+      "s3:GetObjectVersion",
+    ]
+    resources = [
+      aws_s3_bucket.bucket.arn,
+      "${aws_s3_bucket.bucket.arn}/*",
+    ]
+  }
+
+  statement {
+    sid = "S3ReadOnlyBucketAccess"
+    effect = "Allow"
+    actions = [
+      "s3:ListBucketVersions",
+      "s3:ListBuckets",
+      "s3:GetBucketVersioning",
+      "s3:GetObject",
+      "s3:GetBucketCORS",
+      "s3:GetBucketLocation",
+      "s3:GetObjectVersion",
+    ]
+    resources = [
+      aws_s3_bucket.bucket.arn,
+      "${aws_s3_bucket.bucket.arn}/*",
+    ]
+  }
+
+  statement {
+    sid = "KMSKeyAccess"
+    effect = "Allow"
+    actions = [
+      "kms:Decrypt",
+      "kms:GenerateDataKeyWithoutPlaintext",
+      "kms:Verify",
+      "kms:GenerateDataKeyPairWithoutPlaintext",
+      "kms:GenerateDataKeyPair",
+      "kms:ReEncryptFrom",
+      "kms:Encrypt",
+      "kms:GenerateDataKey",
+      "kms:ReEncryptTo",
+      "kms:Sign",
+    ]
+    resources = [ aws_kms_key.bucketkey.arn ]
+  }  
+}

+ 97 - 0
base/phantom_s3_bucket/kms.tf

@@ -0,0 +1,97 @@
+resource "aws_kms_key" "bucketkey" {
+  description             = "S3 KMS for ${local.bucket_name}."
+  deletion_window_in_days = 30
+  enable_key_rotation     = true
+  policy                  = data.aws_iam_policy_document.kms_key_policy.json
+  tags                    = merge(var.standard_tags, var.tags)
+}
+
+resource "aws_kms_alias" "bucketkey" {
+  name          = "alias/PhantomArchive"
+  target_key_id = aws_kms_key.bucketkey.key_id
+}
+
+data "aws_iam_policy_document" "kms_key_policy" {
+  depends_on = [ aws_iam_role.phantom_s3_role ]
+  policy_id = local.bucket_name
+  statement {
+    sid    = "Enable IAM User Permissions"
+    effect = "Allow"
+    principals {
+      type = "AWS"
+      identifiers = local.principals
+    }
+    actions   = ["kms:*"]
+    resources = ["*"]
+  }
+
+  statement {
+    sid    = "Allow access for Engineers"
+    effect = "Allow"
+    principals {
+      type = "AWS"
+      identifiers = [
+        "arn:${var.aws_partition}:iam::${var.aws_account_id}:user/MDRAdmin",
+        "arn:${var.aws_partition}:iam::${var.aws_account_id}:role/user/mdr_terraformer",
+      ]
+    }
+
+    actions = [
+      "kms:Create*",
+      "kms:Describe*",
+      "kms:Enable*",
+      "kms:List*",
+      "kms:Put*",
+      "kms:Update*",
+      "kms:Revoke*",
+      "kms:Disable*",
+      "kms:Get*",
+      "kms:Delete*",
+      "kms:TagResource",
+      "kms:UntagResource",
+      "kms:ScheduleKeyDeletion",
+      "kms:CancelKeyDeletion"
+    ]
+    resources = ["*"]
+  }
+
+  statement {
+    sid    = "Allow use of the key to encrypt and decrypt"
+    effect = "Allow"
+    principals {
+      type        = "AWS"
+      identifiers = local.principals
+    }
+    actions = [
+      "kms:Encrypt",
+      "kms:Decrypt",
+      "kms:ReEncrypt*",
+      "kms:GenerateDataKey*",
+      "kms:DescribeKey"
+    ]
+    resources = ["*"]
+  }
+
+  statement {
+    sid    = "Allow attachment of persistent resources"
+    effect = "Allow"
+    principals {
+      type = "AWS"
+      identifiers = [
+        "arn:${var.aws_partition}:iam::${var.aws_account_id}:user/MDRAdmin",
+        "arn:${var.aws_partition}:iam::${var.aws_account_id}:role/user/mdr_terraformer",
+      ]
+    }
+    actions = [
+      "kms:CreateGrant",
+      "kms:ListGrants",
+      "kms:RevokeGrant"
+    ]
+    resources = ["*"]
+    condition {
+      test     = "Bool"
+      variable = "kms:GrantIsForAWSResource"
+      values   = ["true"]
+    }
+  }
+}

+ 91 - 0
base/phantom_s3_bucket/main.tf

@@ -0,0 +1,91 @@
+locals {
+  bucket_name  = "xdr-${var.splunk_prefix}-${var.environment}-phantom-archives"
+  principals = [
+    "arn:${var.aws_partition}:iam::${var.aws_account_id}:user/MDRAdmin",
+    "arn:${var.aws_partition}:iam::${var.aws_account_id}:role/user/mdr_terraformer",
+    aws_iam_role.phantom_s3_role.arn
+  ]
+}
+
+resource "aws_s3_bucket" "bucket" {
+  bucket = local.bucket_name
+  acl    = "private"
+
+  versioning {
+    enabled = true
+  }
+
+  tags = merge(var.standard_tags, var.tags)
+
+  lifecycle_rule {
+    id      = "INTELLIGENT_TIERING"
+    enabled = true
+
+    abort_incomplete_multipart_upload_days = 2
+
+    transition {
+      days          = 30
+      storage_class = "INTELLIGENT_TIERING"
+    }
+
+    transition {
+      days          = 365
+      storage_class = "DEEP_ARCHIVE"
+    }
+
+    expiration {
+      days = 7*365
+    }
+  }
+
+  server_side_encryption_configuration {
+    rule {
+      apply_server_side_encryption_by_default {
+        kms_master_key_id = aws_kms_key.bucketkey.arn
+        sse_algorithm     = "aws:kms"
+      }
+    }
+  }
+}
+
+resource "aws_s3_bucket_public_access_block" "public_access_block" {
+  bucket                  = aws_s3_bucket.bucket.id
+  block_public_acls       = true
+  block_public_policy     = true
+  ignore_public_acls      = true
+  restrict_public_buckets = true
+
+  # Not technically dependent, but prevents a "Conflicting conditional operation" conflict.
+  # See https://github.com/hashicorp/terraform-provider-aws/issues/7628
+  depends_on = [aws_s3_bucket_policy.policy]
+}
+
+resource "aws_s3_bucket_policy" "policy" {
+  depends_on = [ aws_iam_role.phantom_s3_role ]
+
+  bucket = aws_s3_bucket.bucket.id
+
+  policy = <<POLICY
+{
+  "Version": "2012-10-17",
+  "Id": "AllowThisAccount",
+  "Statement": [
+    {
+      "Sid": "AccountAllow",
+      "Effect": "Allow",
+      "Principal": {
+        "AWS": ${ jsonencode(local.principals) }
+      },
+      "Action": [
+        "s3:GetObject",
+        "s3:ListBucket"
+      ],
+      "Resource": [
+        "${aws_s3_bucket.bucket.arn}",
+        "${aws_s3_bucket.bucket.arn}/*"
+      ]
+    }
+  ]
+}
+POLICY
+}

+ 11 - 0
base/phantom_s3_bucket/outputs.tf

@@ -0,0 +1,11 @@
+output "BucketName" {
+  value = aws_s3_bucket.bucket.id
+}
+
+output "s3_role_arn" {
+  value = aws_iam_role.phantom_s3_role.arn
+}
+
+output "s3_bucket_name" {
+  value = aws_s3_bucket.bucket.id
+}

+ 18 - 0
base/phantom_s3_bucket/vars.tf

@@ -0,0 +1,18 @@
+variable "splunk_prefix" {
+  type = string
+}
+
+variable "tags" {
+  description = "Tags for the bucket and kms key."
+  type = map
+}
+
+# ----------------------------------
+# Below this line are variables inherited from higher levels, so they
+# do not need to be explicitly passed to this module.
+variable "standard_tags" { type = map }
+variable "aws_account_id" { type = string }
+variable "account_list" { type = list }
+variable "aws_region" { type = string }
+variable "aws_partition" { type = string }
+variable "environment" { type = string }