Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ terraform-hpvs/**/.env
terraform-hpvs/log-encryption/compose/key.pub
terraform-vpnserver/*.ovpn
build
.vscode
8 changes: 2 additions & 6 deletions terraform-vpnserver/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# Deploys a VPN Server with Terraform

This is a sample Terraform configuration for generating a client-to-site VPN server on IBM Cloud. This creates a single subnetwork VPC
and deploys a client-to-site VPN server into it. This configuration produces certificates for use by the VPN server and client and stores them in an IBM Cloud Secrets Manager instance (free plan). It does not establish separate, by userid, login credentials, so anyone with the resulting certificate file will be able to
establish a VPN to the environment. Thanks to now archived [Helpers for Secrets Manager](https://github.com/we-work-in-the-cloud/terraform-ibm-secrets-manager) for samples on how to import certificates. Once the IBM Terraform provider has been enriched to import certificates directly, it should be used instead of this project.
and deploys a client-to-site VPN server into it. This configuration produces certificates for use by the VPN server and client and stores them in an IBM Cloud Secrets Manager instance (free plan). It does not establish separate, by userid, login credentials, so anyone with the resulting certificate file will be able to establish a VPN to the environment.

## Preparations

Expand All @@ -22,10 +21,7 @@ establish a VPN to the environment. Thanks to now archived [Helpers for Secrets

## Create

You must run the create in two phases as the URL of the Secrets Manager must be set once the resource is created and cannot be determined beforehand.

1. `terraform apply -target=module.phase1`
2. `terraform apply`
1. `terraform apply`

## Destroy

Expand Down
232 changes: 206 additions & 26 deletions terraform-vpnserver/main.tf
Original file line number Diff line number Diff line change
@@ -1,38 +1,218 @@
locals{
full_zone = "${var.region}-${var.zone}"
full_vpnclient_cert_common_name = "${var.region} ${var.vpnclient_cert_common_name}"
full_ca_cert_common_name = "${var.region} ${var.ca_cert_common_name}"
full_vpnserver_cert_common_name = "${var.region} ${var.vpnserver_cert_common_name}"

module "phase1" {
## Can't make this a variable without a lot of work for the security group
vpn_protocol = "udp"
}


## Part 1
##
## Create a Secrets Manager on IBM Cloud. Generate a CA certificate and have it issue
## 2 certificates, one for the VPN server and one for the VPN client. Finally import
## all the certificates into the Secrets Manager.

source = "./phase1"
# Create the Secrets Manager
resource "ibm_resource_instance" "secrets_mgr" {
name = var.secrets_manager_name
location = var.region
plan = "trial"
service = "secrets-manager"
}

region = var.region
ibmcloud_api_key = var.ibmcloud_api_key
cert_service_name = var.cert_service_name
vpc = var.vpc
# Create a group within the Secrets Manager
resource "ibm_sm_secret_group" "sm_secret_group"{
instance_id = ibm_resource_instance.secrets_mgr.guid
region = var.region
name = "${var.secrets_manager_group_name}"
description = "Secrets group for ${var.secrets_manager_name}"
}

module "phase2" {

source = "./phase2"
# Create private key for certificate authority
resource "tls_private_key" "ca_private_key" {
algorithm = "RSA"
rsa_bits = "2048"
}

cert_service_name = var.cert_service_name
region = var.region
ibmcloud_api_key = var.ibmcloud_api_key
ca_cert_common_name = var.ca_cert_common_name
vpnserver_cert_common_name = var.vpnserver_cert_common_name
vpnclient_cert_common_name = var.vpnclient_cert_common_name
zone = var.zone
subnetwork_name = var.subnetwork_name
total_ipv4_address_count = var.total_ipv4_address_count
security_group_name = var.security_group_name
vpn_port = var.vpn_port
vpnserver_name = var.vpnserver_name
vpnserver_client_ip_pool = var.vpnserver_client_ip_pool
vpc_address_prefixes_map = module.phase1.vpc_address_prefixes_map
vpc_guid = module.phase1.vpc_guid
secrets_manager_guid = module.phase1.secrets_manager_guid
# Create private key for vpn server
resource "tls_private_key" "vpnserver_private_key" {
algorithm = "RSA"
rsa_bits = "2048"
}

# Create private key for vpn client
resource "tls_private_key" "vpnclient_private_key" {
algorithm = "RSA"
rsa_bits = "2048"
}

# Create CA certificate
resource "tls_self_signed_cert" "ca_cert" {
private_key_pem = tls_private_key.ca_private_key.private_key_pem
allowed_uses = [
"cert_signing"
]
validity_period_hours = 8766*5 # 5 years
subject {
common_name = local.full_ca_cert_common_name
}
is_ca_certificate = true
}

# Create certificate request for VPN Server
resource "tls_cert_request" "vpnserver_csr" {
private_key_pem = tls_private_key.vpnserver_private_key.private_key_pem

subject{
common_name = local.full_vpnserver_cert_common_name
}

}

# Create certificate request for VPN Client
resource "tls_cert_request" "vpnclient_csr" {
private_key_pem = tls_private_key.vpnclient_private_key.private_key_pem

subject{
common_name = local.full_vpnclient_cert_common_name
}

}

# Sign the certificate request for VPN Server
resource "tls_locally_signed_cert" "vpnserver_cert" {
cert_request_pem = tls_cert_request.vpnserver_csr.cert_request_pem
ca_private_key_pem = tls_self_signed_cert.ca_cert.private_key_pem
ca_cert_pem = tls_self_signed_cert.ca_cert.cert_pem

validity_period_hours = 8766*1 # 1 year

allowed_uses = [
"key_encipherment",
"digital_signature",
"server_auth",
]
}

# Sign the certificate request for VPN client
resource "tls_locally_signed_cert" "vpnclient_cert" {
cert_request_pem = tls_cert_request.vpnclient_csr.cert_request_pem
ca_private_key_pem = tls_self_signed_cert.ca_cert.private_key_pem
ca_cert_pem = tls_self_signed_cert.ca_cert.cert_pem

validity_period_hours = 8766*1 # 1 year

allowed_uses = [
"key_encipherment",
"digital_signature",
"client_auth",
]
}

# Import the CA certificate
resource "ibm_sm_imported_certificate" "vpn_ca_cert" {
instance_id = ibm_resource_instance.secrets_mgr.guid
region = var.region
name = "${var.region}-imported-ca-cert"
description = "${local.full_ca_cert_common_name}"
secret_group_id = ibm_sm_secret_group.sm_secret_group.secret_group_id
certificate = tls_self_signed_cert.ca_cert.cert_pem
private_key = tls_self_signed_cert.ca_cert.private_key_pem
intermediate = null
}

# Import the VPN Server certificate
resource "ibm_sm_imported_certificate" "vpnserver_cert" {
instance_id = ibm_resource_instance.secrets_mgr.guid
region = var.region
secret_group_id = ibm_sm_secret_group.sm_secret_group.secret_group_id
name = "${var.region}-imported-vpnserver-cert"
description = "${local.full_vpnserver_cert_common_name}"
certificate = tls_locally_signed_cert.vpnserver_cert.cert_pem
private_key = tls_private_key.vpnserver_private_key.private_key_pem
intermediate = tls_self_signed_cert.ca_cert.cert_pem
}

# Import the VPN Client certificate
resource "ibm_sm_imported_certificate" "vpnclient_cert" {
instance_id = ibm_resource_instance.secrets_mgr.guid
region = var.region
secret_group_id = ibm_sm_secret_group.sm_secret_group.secret_group_id
name = "${var.region}-imported-vpnclient-cert"
description = "${local.full_vpnclient_cert_common_name}"
certificate = tls_locally_signed_cert.vpnclient_cert.cert_pem
private_key = tls_private_key.vpnclient_private_key.private_key_pem
intermediate = tls_self_signed_cert.ca_cert.cert_pem
}

## Part 2
##
## Create a VPC and single subnet on IBM Cloud. Create a Security Group that
## manages the VPN port. Create a VPN Server, using the certificates
## created in Part 1 as the authentication mechanism. Finally create an OpenVPN
## client configuration file that could be used to connect to the VPN server.

# Create a VPC
resource "ibm_is_vpc" "vpc" {
name = var.vpc
}

# subnetwork
resource "ibm_is_subnet" "vpc_subnet" {
name = var.subnetwork_name
vpc = ibm_is_vpc.vpc.id
zone = local.full_zone
total_ipv4_address_count = var.total_ipv4_address_count
}

# security group
resource "ibm_is_security_group" "vpnserver_security_group" {
name = var.security_group_name
vpc = ibm_is_vpc.vpc.id
}

# Configure Security Group Rule to open the VPN port
resource "ibm_is_security_group_rule" "vpnserver_security_group_rule_vpn" {
group = ibm_is_security_group.vpnserver_security_group.id
direction = "inbound"
remote = "0.0.0.0/0"
udp {
port_min = var.vpn_port
port_max = var.vpn_port
}
}

# Create a VPN server. Use the certificates created in Part 1 for authentication. Note that we
# only are using certificates for client authentication. Associate both the default security
# group and the created security group to the VPN server.
resource "ibm_is_vpn_server" "vpn_server" {
certificate_crn = ibm_sm_imported_certificate.vpnserver_cert.crn
client_authentication {
method = "certificate"
client_ca_crn = ibm_sm_imported_certificate.vpnclient_cert.crn
}
client_ip_pool = var.vpnserver_client_ip_pool
enable_split_tunneling = true
name = var.vpnserver_name
port = var.vpn_port
protocol = local.vpn_protocol
subnets = [ibm_is_subnet.vpc_subnet.id]
security_groups = [ibm_is_security_group.vpnserver_security_group.id, ibm_is_vpc.vpc.default_security_group]
}

# Add the VPC subnet into the VPN Server routing tables since we setup a split tunnel VPN
resource "ibm_is_vpn_server_route" "server_routes" {
vpn_server = ibm_is_vpn_server.vpn_server.id
destination = ibm_is_subnet.vpc_subnet.ipv4_cidr_block
action = "translate"
}

# Create an OpenVPN Connect configuration file that can be used to connect to this VPN
resource "local_file" "ovpn" {
filename = "${var.region}.ovpn"
content = "client\ndev tun\nproto ${module.phase2.vpn_protocol}\nport ${var.vpn_port}\nremote ${module.phase2.vpn_hostname}\nresolv-retry infinite\nremote-cert-tls server\nnobind\n\nauth SHA256\ncipher AES-256-GCM\nverb 3\nreneg-sec 0\n<ca>\n${module.phase2.vpn_ca_cert_content}</ca>\n<cert>\n${module.phase2.vpn_client_cert_content}</cert>\n<key>\n${module.phase2.vpn_client_key_content}</key>"
filename = "${var.vpc}-${var.region}.ovpn"
content = "client\ndev tun\nproto ${local.vpn_protocol}\nport ${var.vpn_port}\nremote ${ibm_is_vpn_server.vpn_server.hostname}\nresolv-retry infinite\nremote-cert-tls server\nnobind\n\nauth SHA256\ncipher AES-256-GCM\nverb 3\nreneg-sec 0\n<ca>\n${tls_self_signed_cert.ca_cert.cert_pem}</ca>\n<cert>\n${tls_locally_signed_cert.vpnclient_cert.cert_pem}</cert>\n<key>\n${tls_private_key.vpnclient_private_key.private_key_pem}</key>"
}
29 changes: 0 additions & 29 deletions terraform-vpnserver/phase1/main.tf

This file was deleted.

11 changes: 0 additions & 11 deletions terraform-vpnserver/phase1/variables.tf

This file was deleted.

8 changes: 0 additions & 8 deletions terraform-vpnserver/phase2/locals.tf

This file was deleted.

Loading