Эх сурвалжийг харах

Merge pull request #304 from mdr-engineering/feature/ftd_MSOCI-1913_AWS_VPN_Test

Making an archived copy of the AWS Client VPN Module
Frederick Damstra 3 жил өмнө
parent
commit
f648143ce2

+ 22 - 0
base/_archive/aws_client_vpn/README.md

@@ -0,0 +1,22 @@
+# AWS Client VPN
+Users must download the client vpn: https://aws.amazon.com/vpn/client-vpn-download/
+
+They must be provided with the configuration file which can be downloaded from vpc->client VPN endpoints in the aws console (same for every user, and a self-service portal is possible but not set up in this).
+
+
+# Findings:
+
+## Pros:
+* SAML works great, meaning we could use either RHSSO or Okta and easily move from one to thte other or two a different IdP
+* Pricing seems reasonable. Base price of $0.10/hour for the VPN + $0.05/hour per connection.
+* The ability to write a lambda function to handle connections gives us some great flexibility to handle connection-related requirements (e.g. only 2 concurrent connections)
+ 
+## Cons:
+* It requires an OpenVPN client that support “auth-federate”, which does not include viscosity. (But aws has a free client themselves for OS X, Windows, and Ubuntu, and the community openvpn client should work)
+* It does not appear to play nice with zScalar. We may be able to work with their team to allow it to work, but uncertain.
+* It does not have a lot of flexibility. Configuration is bare minimum: networks, split tunneling, and DNS servers. I suspect we have FedRAMP requirements we’d be unable to meet. With some ingenuity, some of it could be addressed via the connection handler and/or lambda functions.
+ 
+## Summary/Conclusion
+
+That last bullet, I think, is a big strike against this as a VPN solution to solve the split tunneling issue. For example, there’s no idle timeout setting (which is an absolutely absurd setting to have on a non-split-tunneled vpn, btw), and the current AWS response is basically, “Write a lambda function to disconnect users after a time period.” I admire the flexibility (“just write some python”) but also hate the complexity of it (“just maintain some python somebody else wrote.”).
+

+ 5 - 0
base/_archive/aws_client_vpn/auth.tf

@@ -0,0 +1,5 @@
+resource "aws_ec2_client_vpn_authorization_rule" "vpn_auth_rule" {
+  client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.vpn.id
+  target_network_cidr = "0.0.0.0/0"
+  authorize_all_groups = true
+}

+ 35 - 0
base/_archive/aws_client_vpn/certificate.tf

@@ -0,0 +1,35 @@
+#Certificate 
+resource "aws_acm_certificate" "cert" {
+  domain_name       = "${ var.dns_name }.${var.dns_info["public"]["zone"]}"
+  validation_method = "DNS"
+
+  lifecycle {
+    create_before_destroy = true
+  }
+
+  tags = merge(var.standard_tags, var.tags)
+}
+
+resource "aws_acm_certificate_validation" "cert" {
+  certificate_arn         = aws_acm_certificate.cert.arn
+  validation_record_fqdns = [for record in aws_route53_record.cert_validation: record.fqdn]
+}
+
+resource "aws_route53_record" "cert_validation" {
+  provider = aws.mdr-common-services-commercial
+
+  for_each = {
+    for dvo in aws_acm_certificate.cert.domain_validation_options : dvo.domain_name => {
+      name   = dvo.resource_record_name
+      record = dvo.resource_record_value
+      type   = dvo.resource_record_type
+    }
+  }
+
+  allow_overwrite = true
+  name            = each.value.name
+  records         = [each.value.record]
+  ttl             = 60
+  type            = each.value.type
+  zone_id         = var.dns_info["public"]["zone_id"]
+}

+ 14 - 0
base/_archive/aws_client_vpn/cloudwatch.tf

@@ -0,0 +1,14 @@
+resource "aws_cloudwatch_log_group" "vpn" {
+  name = "/aws/vpn"
+  retention_in_days = 30
+
+  # TODO: Encrypt
+  # kms_key_id = <arn>
+  
+  tags = merge(var.standard_tags, var.tags)
+}
+
+resource "aws_cloudwatch_log_stream" "vpn" {
+  name           = "ClientVPN"
+  log_group_name = aws_cloudwatch_log_group.vpn.name
+}

+ 17 - 0
base/_archive/aws_client_vpn/files/saml-metadata-okta-test.xml

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?><md:EntityDescriptor entityID="http://www.okta.com/exkadm0qec8baM8uM297" xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"><md:IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"><md:KeyDescriptor use="signing"><ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:X509Data><ds:X509Certificate>MIIDqjCCApKgAwIBAgIGAWrbB00GMA0GCSqGSIb3DQEBCwUAMIGVMQswCQYDVQQGEwJVUzETMBEG
+A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEU
+MBIGA1UECwwLU1NPUHJvdmlkZXIxFjAUBgNVBAMMDW1kci1tdWx0aXBhc3MxHDAaBgkqhkiG9w0B
+CQEWDWluZm9Ab2t0YS5jb20wHhcNMTkwNTIxMTUzMzA5WhcNMjkwNTIxMTUzNDA5WjCBlTELMAkG
+A1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTAL
+BgNVBAoMBE9rdGExFDASBgNVBAsMC1NTT1Byb3ZpZGVyMRYwFAYDVQQDDA1tZHItbXVsdGlwYXNz
+MRwwGgYJKoZIhvcNAQkBFg1pbmZvQG9rdGEuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAjVWbGnlG3G858/K0b8jVw5OFAef+eFWNmjD6eAfGMgOzQ3ZhJmZ5TAFxaUaH15Q7Vi10
+p/zKHo8rZAurh31r35ED9JT+45J/IsDtOUK55quSEeh4d0Ih7NTBXgP5yEsSa7YVqBL4mI450JRr
+8BTTfatUP0/TRxSx92QxlNhLi0jYmGtgzQ/3TeTEWIzZKntTkX7Arn42Dt7JkCdI+ElEfcNQYV3l
+//Olv0TEVFasbmIb8iNgVOi+ssq5UyqAjoWYJOc2VvkerUE9FDs7DkC3S1/sXR72vpTfXpz1fW+x
+/aHJjgwXgB2SW9fZk8CQjqEI5s6QCMBsHSOhU+xDkbzAnwIDAQABMA0GCSqGSIb3DQEBCwUAA4IB
+AQCKqio8wrvhbkGRptCD6sEnRmC7/NBE133tIv7Z3R/Cve8DgO3GcKKrCUh+gZJLFV3eWw95FTWW
+MY7KrYEd353mKP8hL7mEc+qSmWuwfFw+6JePHsNDiFKCY2PfzbWgsG9nX7T6H7n8cn2hzVn4gBmb
+8TAXei+x0id9h24oSvtISZhMg+ED72c0BbO4wPZOQeisXPO4vugdRdbyB5wvIU2ILHb7WJnDNSai
+XSHqKUBigvQua2KSjh+GW7fMlvRbDkYxq3okj6sZlyCLN79IM4NZgKfCC4t8FoUA9ofIDUV9u70G
++Utb6eeVogPzFlv4LuMRAEKbnV9G3yyDbxYsEcpY</ds:X509Certificate></ds:X509Data></ds:KeyInfo></md:KeyDescriptor><md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat><md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://mdr-multipass.okta.com/app/aws_clientvpn/exkadm0qec8baM8uM297/sso/saml"/><md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://mdr-multipass.okta.com/app/aws_clientvpn/exkadm0qec8baM8uM297/sso/saml"/></md:IDPSSODescriptor></md:EntityDescriptor>

+ 5 - 0
base/_archive/aws_client_vpn/saml.tf

@@ -0,0 +1,5 @@
+resource "aws_iam_saml_provider" "okta" {
+  name                   = "okta_aws_vpn"
+  saml_metadata_document = file("files/saml-metadata-okta-${var.environment}.xml")
+  tags = merge(var.standard_tags, var.tags)
+}

+ 51 - 0
base/_archive/aws_client_vpn/security-groups.tf

@@ -0,0 +1,51 @@
+resource "aws_security_group" "vpn_access" {
+  name_prefix = "${ var.dns_name }_vpn_access"
+  description = "Security Group for the AWS VPN"
+  vpc_id = var.vpc_id
+  tags = merge(var.standard_tags, var.tags)
+}
+
+resource "aws_security_group_rule" "vpn-in-443-tcp" {
+  type              = "ingress"
+  from_port         = 443
+  to_port           = 443
+  protocol          = "tcp"
+  cidr_blocks       = [ "0.0.0.0/0" ]
+  security_group_id = aws_security_group.vpn_access.id
+}
+
+resource "aws_security_group_rule" "vpn-in-443-udp" {
+  type              = "ingress"
+  from_port         = 443
+  to_port           = 443
+  protocol          = "udp"
+  cidr_blocks       = [ "0.0.0.0/0" ]
+  security_group_id = aws_security_group.vpn_access.id
+}
+
+resource "aws_security_group_rule" "vpn-in-1194-tcp" {
+  type              = "ingress"
+  from_port         = 1194
+  to_port           = 1194
+  protocol          = "tcp"
+  cidr_blocks       = [ "0.0.0.0/0" ]
+  security_group_id = aws_security_group.vpn_access.id
+}
+
+resource "aws_security_group_rule" "vpn-in-1194-udp" {
+  type              = "ingress"
+  from_port         = 1194
+  to_port           = 1194
+  protocol          = "udp"
+  cidr_blocks       = [ "0.0.0.0/0" ]
+  security_group_id = aws_security_group.vpn_access.id
+}
+
+resource "aws_security_group_rule" "vpn-out" {
+  type              = "egress"
+  from_port         = -1
+  to_port           = -1
+  protocol          = -1
+  cidr_blocks       = [ "0.0.0.0/0" ]
+  security_group_id = aws_security_group.vpn_access.id
+}

+ 47 - 0
base/_archive/aws_client_vpn/terragrunt.hcl.example

@@ -0,0 +1,47 @@
+# Backup of the terragrunt.hcl
+# Originally found in ~/xdr-terraform-live/test/aws-us-gov/mdr-test-c2/087-aws-client-vpn
+
+locals {
+  # If you want to use any of the variables in _this_ file, you have to load them here.
+  # However, they will all be available as inputs to the module loaded in terraform.source
+  # below.
+  environment_vars = read_terragrunt_config(find_in_parent_folders("env.hcl"))
+  partition_vars = read_terragrunt_config(find_in_parent_folders("partition.hcl"))
+  region_vars = read_terragrunt_config(find_in_parent_folders("region.hcl"))
+  account_vars = read_terragrunt_config(find_in_parent_folders("account.hcl"))
+  global_vars = read_terragrunt_config(find_in_parent_folders("globals.hcl"))
+}
+
+# Terragrunt will copy the Terraform configurations specified by the source parameter, along with any files in the
+# working directory, into a temporary folder, and execute your Terraform commands in that folder.
+terraform {
+  # Double slash is intentional and required to show root of modules
+  source = "git@github.xdr.accenturefederalcyber.com:mdr-engineering/xdr-terraform-modules.git//base/aws_client_vpn?ref=v3.1.11"
+}
+
+dependency "vpc-access" {
+  config_path = "../010-vpc-access"
+}
+
+# Include all settings from the root terragrunt.hcl file
+include {
+  path = find_in_parent_folders()
+}
+
+# These are the variables we have to pass in to use the module specified in the terragrunt source above
+inputs = {
+  # All of the inputs from the inherited hcl files are available automatically
+  # (via the `inputs` section of the root `terragrunt.hcl`). However, modules
+  # will be more flexible if you specify particular input values.
+  tags = {
+    Purpose = "VPN - Employee Access"
+    Terraform = "aws/${basename(get_parent_terragrunt_dir())}/${path_relative_to_include()}/"
+  }
+  dns_name = "vpn" # TODO: Fix this when actual swap is taking place.
+  vpc_id = dependency.vpc-access.outputs.vpc_id
+  azs = dependency.vpc-access.outputs.azs
+  private_subnets = dependency.vpc-access.outputs.private_subnets
+  public_subnets = dependency.vpc-access.outputs.public_subnets
+}
+terraform_version_constraint = "= 1.0.7"
+terragrunt_version_constraint = "= 0.32.3"

+ 25 - 0
base/_archive/aws_client_vpn/vars.tf

@@ -0,0 +1,25 @@
+variable "dns_name" {
+  type = string
+  description = "Used for the certificate"
+}
+
+variable "tags" {
+  description = "Tags to add to the resource (in addition to global standard tags)"
+  type        = map
+  default     = { }
+}
+
+variable "azs" { type = list(string) }
+variable "private_subnets" { type = list(string) }
+variable "public_subnets" { type = list(string) }
+variable "vpc_id" { type = string }
+variable "cidr_map" { type = map }
+variable "dns_info" { type = map }
+variable "dns_servers" { type = list(string) }
+variable "standard_tags" { type = map }
+variable "environment" { type = string }
+variable "aws_region" { type = string }
+variable "aws_partition" { type = string }
+variable "aws_partition_alias" { type = string }
+variable "common_services_account" { type = string }
+variable "instance_termination_protection" { type = bool }

+ 56 - 0
base/_archive/aws_client_vpn/vpn.tf

@@ -0,0 +1,56 @@
+resource "aws_ec2_client_vpn_endpoint" "vpn" {
+  description = "VPN for Employee Access"
+  client_cidr_block = "172.16.0.0/22"
+  split_tunnel = true
+  server_certificate_arn = aws_acm_certificate.cert.arn
+  self_service_portal = "disabled" # requires a self_service_saml_provider in authentication_options
+
+  # TODO: Specify DNS Servers
+  dns_servers = var.dns_servers
+
+  # Certificate based authenticaiton requires the certificate be in the same account
+  #authentication_options {
+  #  type = "certificate-authentication"
+  #  root_certificate_chain_arn = "arn:aws-us-gov:acm-pca:us-gov-east-1:701290387780:certificate-authority/cb0ea325-a347-4297-9cb8-2134410c3889"
+  #}
+
+  authentication_options {
+    type = "federated-authentication"
+    saml_provider_arn = aws_iam_saml_provider.okta.arn
+  }
+
+  connection_log_options {
+    enabled = true
+    cloudwatch_log_group  = aws_cloudwatch_log_group.vpn.name
+    cloudwatch_log_stream = aws_cloudwatch_log_stream.vpn.name
+  }
+
+  # Possible required with zscalar?
+  transport_protocol = "udp"
+
+  tags = merge(var.standard_tags, var.tags)
+}
+
+resource "aws_ec2_client_vpn_network_association" "vpn_subnets" {
+  count = length(var.public_subnets)
+  #count = 1 # we don't need the redundancy for now
+
+  client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.vpn.id
+  subnet_id = var.public_subnets[count.index]
+  security_groups = [aws_security_group.vpn_access.id]
+
+  lifecycle {
+    // The issue why we are ignoring changes is that on every change
+    // terraform screws up most of the vpn assosciations
+    // see: https://github.com/hashicorp/terraform-provider-aws/issues/14717
+    ignore_changes = [subnet_id]
+  }
+}
+
+resource "aws_ec2_client_vpn_route" "default" {
+  count = length(var.public_subnets)
+  #count = 1 # we don't need the redundancy for now
+  client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.vpn.id
+  destination_cidr_block = "10.0.0.0/8"
+  target_vpc_subnet_id   = aws_ec2_client_vpn_network_association.vpn_subnets[count.index].subnet_id
+}