Ver código fonte

Expand the Teleport Module

Adds S3, dynamodb, kms, and iam to dynamodb.

Fixes dynaomdb endpoints in the security group for defualt-vpc.

To be (eventually) tagged 1.24.2
Fred Damstra [afs macbook] 4 anos atrás
pai
commit
8edca8e457

+ 15 - 5
base/teleport-single-instance/alb.tf

@@ -19,15 +19,17 @@ resource "aws_alb" "external" {
 
 # Create a new target group
 resource "aws_alb_target_group" "external" {
-  name                 = "${var.instance_name}-alb-external"
-  port                 = 443
+  # use name_prefix instead of name and create-before-destroy on security groups and alb target groups to make future changes easier,
+  # otherwise, you get stuck in `destroying` during routine changes.
+  name_prefix          = substr(var.instance_name, 0, 6)
+  port                 = 3080
   protocol             = "HTTPS"
   #deregistration_delay = "${local.lb_deregistration_delay}"
   vpc_id               = var.vpc_id
 
   health_check {
     protocol = "HTTPS"
-    port     = "443"
+    port     = "3080"
     path     = "/"
     matcher  = "200-400"
     timeout  = "4"
@@ -40,12 +42,15 @@ resource "aws_alb_target_group" "external" {
   }
 
   tags = merge(var.standard_tags, var.tags)
+  lifecycle {
+    create_before_destroy = true
+  }
 }
 
 resource "aws_lb_target_group_attachment" "external" {
   target_group_arn = aws_alb_target_group.external.arn
   target_id        = aws_instance.instance.id
-  port             = 443 # maybe 3080?
+  port             = 3080
 }
 
 # Create a new alb listener
@@ -99,9 +104,14 @@ module "public_dns_record_for_alb" {
 
 resource "aws_security_group" "alb_server_external" {
   vpc_id      = var.vpc_id
-  name        = "${var.instance_name}-alb-sg-external"
+  # use name_prefix instead of name and create-before-destroy on security groups and alb target groups to make future changes easier,
+  # otherwise, you get stuck in `destroying` during routine changes.
+  name_prefix = "${var.instance_name}-alb-sg-external"
   description = "Teleport LB SG"
   tags = merge(var.standard_tags, var.tags)
+  lifecycle {
+    create_before_destroy = true
+  }
 }
 
 #----------------------------------------------------------------------------

+ 133 - 0
base/teleport-single-instance/dynamo.tf

@@ -0,0 +1,133 @@
+/* 
+ ORIGINAL SOURCE: https://raw.githubusercontent.com/gravitational/teleport/master/examples/aws/terraform/starter-cluster/dynamo.tf
+ 
+DynamoDB is used to store cluster state, event
+metadata, and a simple locking mechanism for SSL 
+cert generation and renewal.
+*/
+
+// DynamoDB table for storing cluster state
+resource "aws_dynamodb_table" "teleport" {
+  name           = var.instance_name
+  read_capacity  = 10
+  write_capacity = 10
+  hash_key       = "HashKey"
+  range_key      = "FullPath"
+  server_side_encryption {
+    kms_key_arn = aws_kms_key.s3.arn
+    enabled = true
+  }
+
+  lifecycle {
+    ignore_changes = [
+      read_capacity,
+      write_capacity,
+    ]
+  }
+
+  attribute {
+    name = "HashKey"
+    type = "S"
+  }
+
+  attribute {
+    name = "FullPath"
+    type = "S"
+  }
+
+  stream_enabled   = "true"
+  stream_view_type = "NEW_IMAGE"
+
+  ttl {
+    attribute_name = "Expires"
+    enabled        = true
+  }
+
+  tags = merge(var.standard_tags, var.tags, { Name = var.instance_name })
+}
+
+// DynamoDB table for storing cluster events
+resource "aws_dynamodb_table" "teleport_events" {
+  name           = "${var.instance_name}-events"
+  read_capacity  = 10
+  write_capacity = 10
+  hash_key       = "SessionID"
+  range_key      = "EventIndex"
+
+  server_side_encryption {
+    kms_key_arn = aws_kms_key.s3.arn
+    enabled = true
+  }
+
+  global_secondary_index {
+    name            = "timesearch"
+    hash_key        = "EventNamespace"
+    range_key       = "CreatedAt"
+    write_capacity  = 10
+    read_capacity   = 10
+    projection_type = "ALL"
+  }
+
+  lifecycle {
+    ignore_changes = [
+      read_capacity,
+      write_capacity,
+    ]
+  }
+
+  attribute {
+    name = "SessionID"
+    type = "S"
+  }
+
+  attribute {
+    name = "EventIndex"
+    type = "N"
+  }
+
+  attribute {
+    name = "EventNamespace"
+    type = "S"
+  }
+
+  attribute {
+    name = "CreatedAt"
+    type = "N"
+  }
+
+  ttl {
+    attribute_name = "Expires"
+    enabled        = true
+  }
+
+  tags = merge(var.standard_tags, var.tags, { Name = var.instance_name })
+}
+
+// DynamoDB table for simple locking mechanism
+resource "aws_dynamodb_table" "locks" {
+  name           = "${var.instance_name}-locks"
+  read_capacity  = 5
+  write_capacity = 5
+  hash_key       = "Lock"
+
+  billing_mode = "PROVISIONED"
+
+  lifecycle {
+    ignore_changes = [
+      read_capacity,
+      write_capacity,
+    ]
+  }
+
+  attribute {
+    name = "Lock"
+    type = "S"
+  }
+
+  ttl {
+    attribute_name = "Expires"
+    enabled        = true
+  }
+
+  tags = merge(var.standard_tags, var.tags, { Name = var.instance_name })
+}

+ 225 - 0
base/teleport-single-instance/iam.tf

@@ -0,0 +1,225 @@
+resource "aws_iam_instance_profile" "teleport" {
+  name       = "${var.instance_name}-role"
+  role       = aws_iam_role.auth.name
+  #depends_on = [aws_iam_role_policy.auth_ssm]
+}
+
+// Teleport instance profile and roles
+resource "aws_iam_role" "auth" {
+  name = "${var.instance_name}-role"
+  path = "/instance/"
+
+  assume_role_policy = <<EOF
+{
+    "Version": "2012-10-17",
+    "Statement": [
+        {
+            "Effect": "Allow",
+            "Principal": {"Service": "ec2.amazonaws.com"},
+            "Action": "sts:AssumeRole"
+        }
+    ]
+}
+EOF
+
+}
+
+# FTD: No ssm in our deployment
+#resource "aws_iam_role_policy" "ssm" {
+#  name = "${var.instance_name}-teleport-ssm"
+#  role = aws_iam_role.auth.id
+#
+#  policy = <<EOF
+#{
+#    "Version": "2012-10-17",
+#    "Statement": [
+#        {
+#            "Effect": "Allow",
+#            "Action": [
+#                "ssm:DescribeParameters",
+#                "ssm:GetParameters",
+#                "ssm:GetParametersByPath",
+#                "ssm:GetParameter",
+#                "ssm:PutParameter",
+#                "ssm:DeleteParameter"
+#            ],
+#            "Resource": "arn:${var.aws_partition}:ssm:${var.aws_region}:${var.aws_account_id}:parameter/teleport/${var.instance_name}/*"
+#        },
+#        {
+#         "Effect":"Allow",
+#         "Action":[
+#            "kms:Decrypt"
+#         ],
+#         "Resource":[
+#            "arn:${var.aws_partition}:kms:${var.aws_region}:${var.aws_account_id}:key/${data.aws_kms_alias.ssm.target_key_id}"
+#         ]
+#      }
+#    ]
+#}
+#EOF
+#
+#}
+
+// Auth server uses DynamoDB as a backend, and this is to allow read/write from the dynamo tables
+data "aws_iam_policy_document" "policy_auth_dynamo" {
+  statement {
+    sid       = "AllActionsOnTeleportDB"
+    effect    = "Allow"
+    resources = ["arn:${var.aws_partition}:dynamodb:${var.aws_region}:${var.aws_account_id}:table/${aws_dynamodb_table.teleport.name}"]
+    actions   = ["dynamodb:*"]
+  }
+
+  statement {
+    sid       = "AllActionsOnTeleportEventsDB"
+    effect    = "Allow"
+    resources = ["arn:${var.aws_partition}:dynamodb:${var.aws_region}:${var.aws_account_id}:table/${aws_dynamodb_table.teleport_events.name}"]
+    actions   = ["dynamodb:*"]
+  }
+
+  statement {
+    sid       = "AllActionsOnTeleportEventsIndexDB"
+    effect    = "Allow"
+    resources = ["arn:${var.aws_partition}:dynamodb:${var.aws_region}:${var.aws_account_id}:table/${aws_dynamodb_table.teleport_events.name}/index/*"]
+    actions   = ["dynamodb:*"]
+  }
+
+  statement {
+    sid       = "AllActionsOnTeleportStreamsDB"
+    effect    = "Allow"
+    resources = ["arn:${var.aws_partition}:dynamodb:${var.aws_region}:${var.aws_account_id}:table/${aws_dynamodb_table.teleport.name}/stream/*"]
+    actions   = ["dynamodb:*"]
+  }
+}
+
+resource "aws_iam_policy" "auth_dynamo" {
+  name        = "${var.instance_name}-auth-dynamo"
+  policy      = data.aws_iam_policy_document.policy_auth_dynamo.json
+}
+
+resource "aws_iam_role_policy_attachment" "attach_auth_dynamo" {
+  role       = aws_iam_role.auth.name
+  policy_arn = aws_iam_policy.auth_dynamo.arn
+}
+
+// Allow auth servers to update locks
+data "aws_iam_policy_document" "policy_auth_locks" {
+  statement {
+    sid       = "AllActionsOnLocks"
+    effect    = "Allow"
+    resources = ["arn:${var.aws_partition}:dynamodb:${var.aws_region}:${var.aws_account_id}:table/${aws_dynamodb_table.locks.name}"]
+    actions   = ["dynamodb:*"]
+  }
+}
+
+resource "aws_iam_policy" "auth_locks" {
+  name = "${var.instance_name}-auth-locks"
+  policy      = data.aws_iam_policy_document.policy_auth_locks.json
+}
+
+resource "aws_iam_role_policy_attachment" "attach_auth_locks" {
+  role       = aws_iam_role.auth.name
+  policy_arn = aws_iam_policy.auth_locks.arn
+}
+
+// S3 is used for letsencrypt, auth servers request certificates from letsencrypt
+// and publish to S3 encrypted bucket. SSM is not used, because certificates and private keys
+// are too big for SSM.
+data "aws_iam_policy_document" "policy_auth_s3" {
+  statement {
+    sid       = ""
+    effect    = "Allow"
+    resources = ["arn:${var.aws_partition}:s3:::${aws_s3_bucket.storage.bucket}"]
+
+    actions = [
+      "s3:ListBucket",
+      "s3:ListBucketVersions",
+    ]
+  }
+
+  statement {
+    sid       = ""
+    effect    = "Allow"
+    resources = ["arn:${var.aws_partition}:s3:::${aws_s3_bucket.storage.bucket}/*"]
+
+    actions = [
+      "s3:PutObject",
+      "s3:GetObject",
+      "s3:GetObjectVersion",
+    ]
+  }
+}
+
+resource "aws_iam_policy" "auth_s3" {
+  name = "${var.instance_name}-auth-s3"
+  policy      = data.aws_iam_policy_document.policy_auth_s3.json
+}
+
+resource "aws_iam_role_policy_attachment" "attach_auth_s3" {
+  role       = aws_iam_role.auth.name
+  policy_arn = aws_iam_policy.auth_s3.arn
+}
+
+// Allow use of the key
+data "aws_iam_policy_document" "policy_kms" {
+  statement {
+    sid       = "AllowKMSUse"
+    effect    = "Allow"
+    resources = [ aws_kms_key.s3.arn ]
+
+    actions = [
+      "kms:Encrypt",
+      "kms:Decrypt",
+      "kms:ReEncrypt*",
+      "kms:GenerateDataKey*",
+      "kms:DescribeKey"
+    ]
+  }
+}
+
+resource "aws_iam_policy" "auth_kms" {
+  name = "${var.instance_name}-kms"
+  policy      = data.aws_iam_policy_document.policy_kms.json
+}
+
+resource "aws_iam_role_policy_attachment" "attach_kms" {
+  role       = aws_iam_role.auth.name
+  policy_arn = aws_iam_policy.auth_kms.arn
+}
+
+// FTD: This is for letsencrypt, which we don't (presently) use.
+// Auth server uses route53 to get certs for domain, this allows
+// read/write operations from the zone.
+#resource "aws_iam_role_policy" "auth_route53" {
+#  name = "${var.instance_name}-auth-route53"
+#  role = aws_iam_role.auth.id
+#
+#  policy = <<EOF
+#{
+#    "Version": "2012-10-17",
+#    "Id": "certbot-dns-route53 policy",
+#    "Statement": [
+#        {
+#            "Effect": "Allow",
+#            "Action": [
+#                "route53:ListHostedZones",
+#                "route53:GetChange"
+#            ],
+#            "Resource": [
+#                "*"
+#            ]
+#        },
+#        {
+#            "Effect" : "Allow",
+#            "Action" : [
+#                "route53:ChangeResourceRecordSets"
+#            ],
+#            "Resource" : [
+#                "arn:${var.aws_partition}:route53:::hostedzone/${data.aws_route53_zone.proxy.zone_id}"
+#            ]
+#        }
+#    ]
+#}
+#EOF
+#
+#}
+

+ 120 - 0
base/teleport-single-instance/kms.tf

@@ -0,0 +1,120 @@
+// KMS key for S3
+resource "aws_kms_key" "s3" {
+  description             = "KMS key for Teleport"
+  enable_key_rotation     = true
+  policy                  = data.aws_iam_policy_document.kms_key_encryption_policy.json
+}
+
+resource "aws_kms_alias" "s3" {
+  name          = "alias/teleport-${var.instance_name}"
+  target_key_id = aws_kms_key.s3.key_id
+}
+
+data "aws_iam_policy_document" "kms_key_encryption_policy" {
+  #policy_id = "key-consolepolicy-3"
+  statement {
+    sid = "Enable IAM User Permissions"
+    effect = "Allow"
+    principals {
+      type = "AWS"
+      identifiers = [
+        "arn:${var.aws_partition}:iam::${var.aws_account_id}:role/user/mdr_terraformer",
+        "arn:${var.aws_partition}:iam::${var.aws_account_id}:user/MDRAdmin"
+        ]
+    }
+    actions   = [ "kms:*" ]
+    resources = [ "*" ]
+  }
+
+  statement {
+    sid = "Allow access for Key Administrators"
+    effect = "Allow"
+    principals {
+      type = "AWS"
+      identifiers = [
+        "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"
+    effect = "Allow"
+    principals {
+      type = "AWS"
+      identifiers = [
+        aws_iam_role.auth.arn
+      ]
+    }
+    actions = [
+      "kms:Encrypt",
+      "kms:Decrypt",
+      "kms:ReEncrypt*",
+      "kms:GenerateDataKey*",
+      "kms:DescribeKey"
+    ]
+    resources = [ "*" ]
+  }
+
+  statement  {
+    sid = "Allow access through Amazon S3 for all principals in the account that are authorized to use Amazon S3"
+    effect = "Allow"
+    principals {
+      type = "AWS"
+      identifiers = [ "*" ]
+    }
+    actions = [
+        "kms:Encrypt",
+        "kms:Decrypt",
+        "kms:ReEncrypt*",
+        "kms:GenerateDataKey*",
+        "kms:DescribeKey"
+    ]
+    resources = [ "*" ]
+
+    condition {
+      test = "StringEquals"
+      variable = "kms.ViaService"
+      values = [ "s3.${var.aws_region}.amazonaws.com", "dynamodb.${var.aws_region}.amazonaws.com" ]
+    }
+
+    condition {
+      test = "StringEquals"
+      variable = "kms.CallerAccount"
+      values = [ var.aws_account_id ]
+    }
+  }
+
+  statement {
+    sid =  "Allow DynamoDB to get information about the CMK"
+    effect = "Allow"
+    principals {
+      type =  "Service"
+      identifiers = ["dynamodb.amazonaws.com"]
+    }
+    actions = [
+        "kms:Describe*",
+        "kms:Get*",
+        "kms:List*"
+    ]
+    resources = [ "*" ]
+  }
+}

+ 1 - 1
base/teleport-single-instance/main.tf

@@ -34,7 +34,7 @@ resource "aws_instance" "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.teleport.name
 
   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.

+ 29 - 0
base/teleport-single-instance/s3.tf

@@ -0,0 +1,29 @@
+/* 
+Configuration of S3 bucket for certs and replay
+storage. Uses server side encryption to secure
+session replays and SSL certificates.
+*/
+// S3 bucket for cluster storage
+resource "aws_s3_bucket" "storage" {
+  bucket        = "${var.instance_name}-${var.environment}"
+  acl           = "private"
+  force_destroy = var.instance_termination_protection ? false : true # reverse of termination protection, destroy if no termination protection
+
+  server_side_encryption_configuration {
+    rule {
+      apply_server_side_encryption_by_default {
+        kms_master_key_id = aws_kms_key.s3.arn
+        sse_algorithm = "aws:kms"
+      }
+    }
+  }
+}
+
+resource "aws_s3_bucket_public_access_block" "awsconfig_bucket_block_public_access" {
+  block_public_acls       = true
+  block_public_policy     = true
+  bucket                  = aws_s3_bucket.storage.id
+  ignore_public_acls      = true
+  restrict_public_buckets = true
+}
+

+ 16 - 1
base/teleport-single-instance/security-groups.tf

@@ -6,10 +6,15 @@ data "aws_security_group" "typical-host" {
 }
 
 resource "aws_security_group" "instance" {
-  name = "instance-${var.instance_name}"
+  # use name_prefix instead of name and create-before-destroy on security groups and alb target groups to make future changes easier,
+  # otherwise, you get stuck in `destroying` during routine changes.
+  name_prefix = "instance-${var.instance_name}"
   description = "Instances of type ${var.instance_name}"
   vpc_id = var.vpc_id
   tags = merge(var.standard_tags, var.tags)
+  lifecycle {
+    create_before_destroy = true
+  }
 }
 
 resource "aws_security_group_rule" "instance-http-in" {
@@ -51,3 +56,13 @@ resource "aws_security_group_rule" "instance-teleport-in-3026" {
 #  cidr_blocks = [ "0.0.0.0/0" ]
 #  security_group_id = aws_security_group.instance.id
 #}
+
+resource "aws_security_group_rule" "instance-teleport-out-ssh" {
+  description = "Outbound SSH"
+  type = "egress"
+  from_port = "22"
+  to_port = "22"
+  protocol = "tcp"
+  cidr_blocks = [ "0.0.0.0/0" ]
+  security_group_id = aws_security_group.instance.id
+}

+ 18 - 0
submodules/security_group/typical_host/main.tf

@@ -9,6 +9,13 @@ data "aws_prefix_list" "private_s3" {
   }
 }
 
+data "aws_prefix_list" "private_dynamodb" {
+  filter {
+    name = "prefix-list-name"
+    values = [ "com.amazonaws.*.dynamodb" ]
+  }
+}
+
 locals {
   vpc_name = lookup(data.aws_vpc.this.tags, "Name", data.aws_vpc.this.cidr_block)
 }
@@ -196,6 +203,17 @@ resource "aws_security_group_rule" "outbound_to_ec2_s3_endpoint" {
   count = length([ data.aws_prefix_list.private_s3.id ]) > 0 ? 1 : 0 # todo: handle case of no s3 prefix list
 }
 
+resource "aws_security_group_rule" "outbound_to_ec2_dynamodb_endpoint" {
+  security_group_id = aws_security_group.security_group.id
+  type = "egress"
+  description = "Outbound to dynamodb endpoint"
+  from_port = 443
+  to_port = 443
+  protocol = "tcp"
+  prefix_list_ids = [ data.aws_prefix_list.private_dynamodb.id ]
+  count = length([ data.aws_prefix_list.private_dynamodb.id ]) > 0 ? 1 : 0 # todo: handle case of no s3 prefix list
+}
+
 resource "aws_security_group_rule" "outbound_to_sensu" {
   security_group_id = aws_security_group.security_group.id
   type = "egress"