diff --git a/terraform/vpn-only/account-rg.tf b/terraform/vpn-only/account-rg.tf new file mode 100644 index 0000000..06e14b0 --- /dev/null +++ b/terraform/vpn-only/account-rg.tf @@ -0,0 +1,15 @@ + +############################################################################## +# Create a resource group or reuse an existing one +############################################################################## + +variable "rg_name" { + type = string + default = "" + description = "Name of Resource Group" +} + +data "ibm_resource_group" "group" { + name = var.rg_name + +} \ No newline at end of file diff --git a/terraform/vpn-only/networking-vpc.tf b/terraform/vpn-only/networking-vpc.tf new file mode 100644 index 0000000..5b18e08 --- /dev/null +++ b/terraform/vpn-only/networking-vpc.tf @@ -0,0 +1,12 @@ + +variable "vpc_name" { + type = string + default = "" + description = "Name of VPC" +} + +############################################################################## + +data "ibm_is_vpc" "vpc" { + name = var.vpc_name +} \ No newline at end of file diff --git a/terraform/vpn-only/networking-vpn-certificates.tf b/terraform/vpn-only/networking-vpn-certificates.tf new file mode 100644 index 0000000..5482171 --- /dev/null +++ b/terraform/vpn-only/networking-vpn-certificates.tf @@ -0,0 +1,95 @@ + +module "pki" { + source = "particuleio/pki/tls" + version = "2.0.0" + + ca = { + algorithm = "RSA" + ecdsa_curve = "secp384r1" + subject = { + common_name = "${local.basename} CA" + organization = "Org" + organizational_unit = "OU" + street_address = [ + "Street" + ] + locality = "Locality" + province = "Province" + country = "Country" + postal_code = "Postal Code" + serial_number = "Serial Number" + } + validity_period_hours = 87600 + early_renewal_hours = 78840 + allowed_uses = [ + "cert_signing", + "crl_signing", + "code_signing", + "server_auth", + "client_auth", + "digital_signature", + "key_encipherment", + ] + } + + certificates = { + server = { + algorithm = "RSA" + ecdsa_curve = "secp384r1" + subject = { + common_name = "${local.basename} Server" + organization = "Org" + organizational_unit = "OU" + street_address = [ + "Street" + ] + locality = "Locality" + province = "Province" + country = "Country" + postal_code = "Postal Code" + serial_number = "Serial Number" + } + validity_period_hours = 8740 + early_renewal_hours = 8040 + dns_names = [ + "vpn-server.vpn.ibm.com" + ] + uris = [] + allowed_uses = [ + "server_auth", + "client_auth", + "digital_signature", + ] + } + + client = { + algorithm = "RSA" + ecdsa_curve = "secp384r1" + subject = { + common_name = "${local.basename} Client" + organization = "Org" + organizational_unit = "OU" + street_address = [ + "Street" + ] + locality = "Locality" + province = "Province" + country = "Country" + postal_code = "Postal Code" + serial_number = "Serial Number" + } + validity_period_hours = 8740 + early_renewal_hours = 8040 + dns_names = [ + "vpn-client.vpn.ibm.com" + ] + uris = [] + allowed_uses = [ + "server_auth", + "client_auth", + "digital_signature", + ] + } + } +} + diff --git a/terraform/vpn-only/networking-vpn-client2server.tf b/terraform/vpn-only/networking-vpn-client2server.tf new file mode 100644 index 0000000..c1ca6de --- /dev/null +++ b/terraform/vpn-only/networking-vpn-client2server.tf @@ -0,0 +1,110 @@ + +variable "vpn_client_ip_pool" { + default = "192.168.0.0/16" +} + +resource "ibm_is_vpn_server" "vpn" { + certificate_crn = ibm_sm_imported_certificate.server_cert.crn + client_authentication { + method = "certificate" + client_ca_crn = ibm_sm_imported_certificate.client_cert.crn + } + client_ip_pool = var.vpn_client_ip_pool + client_idle_timeout = 2800 + enable_split_tunneling = true + name = "${local.basename}-vpn-server" + port = 443 + protocol = "udp" + subnets = [ + data.ibm_is_vpc.vpc.subnets[0].id, data.ibm_is_vpc.vpc.subnets[1].id + ] + security_groups = [ + ibm_is_security_group.vpn.id + ] + resource_group = data.ibm_resource_group.group.id +} + +resource "ibm_is_security_group" "vpn" { + resource_group = data.ibm_resource_group.group.id + name = "${local.basename}-vpn-group" + vpc = data.ibm_is_vpc.vpc.id +} + +resource "ibm_is_security_group_rule" "vpn_inbound" { + group = ibm_is_security_group.vpn.id + direction = "inbound" + udp { + port_min = 443 + port_max = 443 + } +} + +# allow clients to use SSH to connect to hosts in the cloud +resource "ibm_is_security_group_rule" "vpn_ssh_outbound" { + group = ibm_is_security_group.vpn.id + direction = "outbound" + tcp { + port_min = 22 + port_max = 22 + } +} + +# allow clients to use SSH to ping +resource "ibm_is_security_group_rule" "vpn_icmp_outbound" { + group = ibm_is_security_group.vpn.id + direction = "outbound" + icmp { + type = 8 + code = 0 + } +} + +# allow clients to reach cloud service endpoints +resource "ibm_is_security_group_rule" "vpn_cse_outbound" { + group = ibm_is_security_group.vpn.id + direction = "outbound" + remote = "166.8.0.0/14" +} + +resource "ibm_is_vpn_server_route" "route_cse_to_vpc" { + vpn_server = ibm_is_vpn_server.vpn.id + action = "deliver" + destination = "166.8.0.0/14" + name = "route-2-ibm-cloud-service-endpoints" +} + +# allow clients to reach private backend +resource "ibm_is_security_group_rule" "vpn_private_outbound" { + group = ibm_is_security_group.vpn.id + direction = "outbound" + remote = "161.26.0.0/16" +} + +resource "ibm_is_vpn_server_route" "route_private_to_vpc" { + vpn_server = ibm_is_vpn_server.vpn.id + action = "deliver" + destination = "161.26.0.0/16" + name = "route-private-2-ibm-iaas-endpoints" +} + +data "ibm_is_vpn_server_client_configuration" "config" { + vpn_server = ibm_is_vpn_server.vpn.id +} + +output "full-ovpn-config" { + value = < +${nonsensitive(module.pki.certificates["client"].cert.cert_pem)} + + + +${nonsensitive(module.pki.certificates["client"].private_key.private_key_pem)} + +EOT +} + +output "vpn" { + value = ibm_is_vpn_server.vpn +} diff --git a/terraform/vpn-only/networking-vpn-secrets.tf b/terraform/vpn-only/networking-vpn-secrets.tf new file mode 100644 index 0000000..f027b7f --- /dev/null +++ b/terraform/vpn-only/networking-vpn-secrets.tf @@ -0,0 +1,88 @@ + +variable "existing_secrets_manager_guid" { + description = "GUID of an existing Secrets Manager instance located in the same region" +} + +resource "ibm_sm_secret_group" "secret_group" { + instance_id = var.existing_secrets_manager_guid + name = "${local.basename}-vpn-group" + region = var.region + description = "Created by terraform as part of the client VPN example." +} + +resource "ibm_sm_imported_certificate" "server_cert" { + instance_id = var.existing_secrets_manager_guid + name = "${local.basename}-server-cert" + description = "Server certificate created by terraform as part of the client VPN example." + secret_group_id = ibm_sm_secret_group.secret_group.secret_group_id + certificate = module.pki.certificates["server"].cert.cert_pem + private_key = module.pki.certificates["server"].private_key.private_key_pem + intermediate = module.pki.ca.cert.cert_pem +} + +resource "ibm_sm_imported_certificate" "client_cert" { + instance_id = var.existing_secrets_manager_guid + name = "${local.basename}-client-cert" + description = "Client certificate created by terraform as part of the client VPN example." + secret_group_id = ibm_sm_secret_group.secret_group.secret_group_id + certificate = module.pki.certificates["client"].cert.cert_pem + intermediate = module.pki.ca.cert.cert_pem +} + +resource "ibm_iam_authorization_policy" "secret_group_to_vpn" { + subject_attributes { + name = "accountId" + value = local.account_id + } + + subject_attributes { + name = "serviceName" + value = "is" + } + + subject_attributes { + name = "resourceType" + value = "vpn-server" + } + + roles = ["SecretsReader"] + + resource_attributes { + name = "accountId" + value = local.account_id + } + + resource_attributes { + name = "serviceName" + value = "secrets-manager" + } + + resource_attributes { + name = "resourceType" + value = "secret-group" + } + + resource_attributes { + name = "resource" + value = ibm_sm_secret_group.secret_group.secret_group_id + } +} + +output "server_cert_crn" { + value = ibm_sm_imported_certificate.server_cert.crn +} + +output "client_cert_crn" { + value = ibm_sm_imported_certificate.client_cert.crn +} + +output "client_cert" { + value = module.pki.certificates["client"].cert.cert_pem + sensitive = true +} + +output "client_key" { + value = module.pki.certificates["client"].private_key.private_key_pem + sensitive = true +} + diff --git a/terraform/vpn-only/provider.tf b/terraform/vpn-only/provider.tf new file mode 100644 index 0000000..d79e4e5 --- /dev/null +++ b/terraform/vpn-only/provider.tf @@ -0,0 +1,29 @@ +############################################################################## +# IBM Cloud Provider +############################################################################## + +terraform { + required_version = ">=1.5" + required_providers { + ibm = { + source = "IBM-Cloud/ibm" + version = "1.58.1" + } + logdna = { + source = "logdna/logdna" + version = ">= 1.14.0" + } + http-full = { + source = "salrashid123/http-full" + } + } +} + +provider "http-full" {} + +provider "ibm" { + ibmcloud_api_key = var.ibmcloud_api_key + region = var.region +} + +############################################################################## \ No newline at end of file diff --git a/terraform/vpn-only/testing.auto.tfvars b/terraform/vpn-only/testing.auto.tfvars new file mode 100644 index 0000000..81420f0 --- /dev/null +++ b/terraform/vpn-only/testing.auto.tfvars @@ -0,0 +1,10 @@ +############################################################################## +## Global Variables +############################################################################## + +#region = "eu-de" # eu-de for Frankfurt MZR + +vpc_name = "vpc-eu-de-iks" +rg_name = "demo" +existing_secrets_manager_guid = "d50e00f4-64c4-461a-9ce8-42117e433f73" + diff --git a/terraform/vpn-only/variables.tf b/terraform/vpn-only/variables.tf new file mode 100644 index 0000000..e234d96 --- /dev/null +++ b/terraform/vpn-only/variables.tf @@ -0,0 +1,47 @@ +############################################################################## +# Account Variables +############################################################################## + +variable "ibmcloud_api_key" { + description = "APIkey that's associated with the account to provision resources to" + type = string + default = "" + sensitive = true +} + +variable "prefix" { + type = string + default = "" + description = "A prefix for all resources to be created. If none provided a random prefix will be created" +} + +resource "random_string" "random" { + count = var.prefix == "" ? 1 : 0 + + length = 6 + special = false +} + +locals { + basename = lower(var.prefix == "" ? "icn-${random_string.random.0.result}" : var.prefix) +} + +variable "region" { + description = "IBM Cloud region where all resources will be provisioned (e.g. eu-de)" + default = "eu-de" +} + +variable "tags" { + description = "List of Tags" + type = list(string) + default = ["tf", "icn"] +} + +# Account ID is required for CBR (Context Based Restrictions) and SCC scope +############################################################################## +data "ibm_iam_auth_token" "tokendata" {} +data "ibm_iam_account_settings" "account_settings" {} + +locals { + account_id = data.ibm_iam_account_settings.account_settings.account_id +} \ No newline at end of file diff --git a/terraform/vpn/variables.tf b/terraform/vpn/variables.tf index e234d96..9a1ae3c 100644 --- a/terraform/vpn/variables.tf +++ b/terraform/vpn/variables.tf @@ -23,7 +23,7 @@ resource "random_string" "random" { } locals { - basename = lower(var.prefix == "" ? "icn-${random_string.random.0.result}" : var.prefix) + basename = lower(var.prefix == "" ? "vpn-${random_string.random.0.result}" : var.prefix) } variable "region" {