Monday, September 30, 2019

Terraform Google VPN VPC PFSense

I spent a few days learning Terraform to automate the creation of a VPN to extend my local networks and create a VPC hybrid connection to my lab network. In other words, I extended my lab network to Google Compute using a VPN. I used Terraform to build my GCP infrastructure.  I use PFSense on the public edge of my local lab network.

This is not a "how to use Terraform" or a "how to use Google Compute" or "how VPNs work" or "how to use Pfsense".

This article does provide an example using Terraform configuration, PFsense configuration and my insight to making it work.

Before I started, I manually created a VPN between PFsense and GCP. I know what pieces were needed to make it work. Building on that work, I took those pieces and created  Terraform configurations to do it for me as well as make it repeatable.

You need a few things to do this.

  1. PFsense with an direct Internet connection. You probably could do it with port forwarding on your firewall, but that is not how I did it.
  2.  PFsense as the static route to the subnet being created in GCP.
    1. I use a PFsense virtual machine which has a NIC interface on the internal network and one on the Internet network. I have a static route on my internal router to point to the GCP network via the internal IP address of the PFsense vRouter.
  3. Google Compute account
  4. Google Compute Project
  5. Owner or appropriate permissions to create resources. I do not use a service account but I could.
  6. Internet connection to Google
  7. Credit card for Google to bill you for resources (after you initial credit is used up) or a billing account with Google
  8. Terraform (I use windows)
  9. GCloud client. Not required but really helpful and part of setting the windows account and password
I created a local folder path of c:\terraform\vpn and work exclusively with the VPN in that folder. I assume you have Terraform installed an in the system path.

Before doing this, you may want to jump down to the PFsense setup and generate a preshared key to be used in the VPN Tunnel configuration. Youwil need one from somewhere. I've had problems using too long of a key so I just use PFsense to generate. Step 6.

Terraform configuration with comments

//vpn config
// I use variables for project, zone, region in a variables.tf file in the same folder.
//VPN Tunnel Config
// I suggest keeping the resource name and name = the same. Much easier to follow.
// I have also variableised these but not in the example
 resource "google_compute_vpn_tunnel" "vpn-1-tunnel-1" {
name = "vpn-1-tunnel-1"
// IP address of the PFSense gateway
peer_ip = "8.8.8.8"
// same exact string in PFsense
shared_secret = "sharedsupersecretstring"
description = "vpn"
ike_version = "2"
// vpn-gatewate-1 is created below somewhere. It is used a few times like this
target_vpn_gateway = "${google_compute_vpn_gateway.vpn-gateway-1.self_link}"
// I am allowing all traffic between the GCP VPC and my local network. you can restrict as you like.
// the CIDR in quotes inside the brackets can have multiple entries. All in quotes separated by a comma
// e.g. ["1.2.3.4/24", "10.10.0.0/16"]
local_traffic_selector= ["0.0.0.0/0"]
remote_traffic_selector= ["0.0.0.0/0"]
//These are created later and are the defaults for a VPN to establish an IPSec connection
depends_on = [
"google_compute_forwarding_rule.fr_esp",
"google_compute_forwarding_rule.fr_udp500",
"google_compute_forwarding_rule.fr_udp4500",
]
}
// Creating the VPN Gateway/ Later when we create rules, it gets assigned an IP address
// The IP is sticky in that I have created and destroyed and created the VPN again and get the same IP assigned.
resource "google_compute_vpn_gateway" "vpn-gateway-1" {
name = "vpn-gateway-1"
description = "vpn gateway"
network = "${google_compute_network.vpn-network.self_link}"
}
// Creating the VPC Network
resource "google_compute_network" "vpn-network" {
name = "vpn-network"
auto_create_subnetworks = false
}
// Creating the VPC network. This is a class C of my lab Class B network.
// It is used by the compute instances virtual machines
resource "google_compute_subnetwork" "vpn-subnet" {
  name          = "vpn-subnet"
  ip_cidr_range = "172.16.99.0/24"
  region        = var.region
  private_ip_google_access = true
  network       = "${google_compute_network.vpn-network.self_link}"
}
//Retrieving the IP used by the VPN gateway for forwarding rules
resource "google_compute_address" "vpn-ip" {
name = "vpn-ip"
}
// Setting the route for my internal lab network
resource "google_compute_route" "vpn-1-tunnel-1-route-1" {
name = "vpn-1-tunnel-1-route-1"
network = "${google_compute_network.vpn-network.self_link}"
// next_hop_vpn_tunnel = "vpn-1-tunnel-1"
next_hop_vpn_tunnel = "${google_compute_vpn_tunnel.vpn-1-tunnel-1.self_link}"
dest_range = "172.16.0.0/16"
}
// Default forwarding tule to allow IPsec
resource "google_compute_forwarding_rule" "fr_esp" {
  name        = "fr-esp"
  ip_protocol = "ESP"
  ip_address  = "${google_compute_address.vpn-ip.address}"
  target      = "${google_compute_vpn_gateway.vpn-gateway-1.self_link}"
}
// Default forwarding tule to allow IPsec
resource "google_compute_forwarding_rule" "fr_udp500" {
  name        = "fr-udp500"
  ip_protocol = "UDP"
  port_range  = "500"
  ip_address  = "${google_compute_address.vpn-ip.address}"
  target      = "${google_compute_vpn_gateway.vpn-gateway-1.self_link}"
}
// Default forwarding tule to allow IPsec
resource "google_compute_forwarding_rule" "fr_udp4500" {
  name        = "fr-udp4500"
  ip_protocol = "UDP"
  port_range  = "4500"
  ip_address  = "${google_compute_address.vpn-ip.address}"
  target      = "${google_compute_vpn_gateway.vpn-gateway-1.self_link}"
}
// Firewall rule to allow all traffic outbound from the vpn subnet
resource "google_compute_firewall" "vpn-allow-all-egress" {
  name    = "vpn-allow-all-egress"
// This is required though you can have multiple protocols and ports. for me, it is allow all
  allow {
protocol = "all"
  }
  network = "${google_compute_network.vpn-network.name}"
  // the CIDR in quites inside the brackets can have multiple entries. All in quites separate by a comma
  // e.g. ["1.2.3.4/24", "10.10.0.0/16"]
  destination_ranges = ["172.16.0.0/16"]
    direction = "EGRESS"
}
// Firewall rule to allow all traffic inbound into the vpn subnet
resource "google_compute_firewall" "vpn-allow-all-ingress" {
  name    = "vpn-allow-all-ingress"
  network = "${google_compute_network.vpn-network.name}"
// This is required though you can have multiple protocols and ports. for me, it is allow all
  allow {
protocol = "all"
  }
  // the CIDR in quites inside the brackets can have multiple entries. All in quites separate by a comma
  // e.g. ["1.2.3.4/24", "10.10.0.0/16"]
  source_ranges = ["172.16.0.0/16"]
  direction = "INGRESS"
}



Here are my variables



variable "credential_file" {
  type    = "string"
  default = "credfiledownloadedfromgoogle.json"
}
variable "project_id" {
  type    = "string"
  default = "mygoogleproject"
}
variable "region" {
  type    = "string"
  default = "us-central1"
}
variable "zone" {
  type    = "string"
  default = "us-central1-a"
}
provider "google" {
 credentials = "${file(var.credential_file)}"
 project     = "${var.project_id}"
 region      = "${var.region}"
}
variable "instance_name" {
  type    = "string"
  default = "windows2016vm"



Now for my Compute Instance Terraform configuration


resource "google_compute_instance" "default" {
 count = 1
 name         = "${var.instance_name}-${count.index}"
 machine_type = var.machine_type
 zone         = var.zone
 boot_disk {
   initialize_params {
     image = var.image
   }
 }
// VPN Network created earlier
 network_interface {
   network = "vpn-network"
   subnetwork = "vpn-subnet"
 }
}

I save these to main.tf and variables .tf respectively.  The Instance is a separate folder with it's own main.tf and variables.tf.

In the vlan folder, run terraform plan
If it looks OK, run terraform plan -out vpn.plan
Next, terraform apply vpn.plan

PFSense


On PFsense, I create the following configuration. 

This assumes you have PFsense already setup and working and are just adding an IPSec tunnel.
Local lab network 172.16.0.0/16
Extending subnet 172.16.99.0/24 into GCP as a Hybrid Cloud.
  1. Get the IP address of the VPN Gateway. I use gcloud compute addresses list which returns a list of IP addresses then look for the vpn-tunnel and the gateway IP. I will use 30.30.30.30 for my gateway IP.
  2. Navigate to the PFsense VPN then IPsec page
  3. Click + Add P1
    1. Key Exchange Version: IKE V2
    2. Internet Protocol: IPV4
    3. Interface: Internet Interface
    4. Remote Gateway: IP of Google VPN Gateway e.g. 30.30.30.30
    5. Description: give it a nice description
    6. Pre-Shared Key: paste the same preshared key used in your Terraform configuration for the VPN Tunnel. e.g. "sharedsupersecretstring" OR generate one and use it in the Terraform configuration
    7. My Identifier: My IP Address
    8. Peer Identifier: Peer IP Address
    9. Encryption Algorithm: AES128-GCM - 128 Bits - 14 (2048 bit)
    10. Lifetime 36000
    11. Enable DPD
    12. Delay 10
    13. Max failures 5
    14. Save
  4. Click Show Phase 2 Entries 
  5. Click + Add P2 (This is to allow the Tunnel Network)
    1. Mode: Tunnel IPv4
    2. Local Network: Type: Network - Network : 169.254.0.2/30
    3. Nat: None
    4. Remote Network: Type: Network - Network: 169.254.0.1/30
    5. Protocol: ESP
    6. Encryption Algorithms: AES125-GCM - 128 Bits 
    7. Hash Algorithms: SHA256
    8. PFS Key Group: 14 (2048 bits)
    9. Lifetime 10800
    10. Save
  6. Click + Add P2 (This is the subnet rule in GCP which is extended from your lab)
    1. Mode: Tunnel IPv4
    2. Local Network: Select Local Network Interface
    3. Nat: None
    4. Remote Network: Type: Network - Address: VPN Subnet from Terraform e.g. 172.16.99.0/24
    5. Protocol: ESP
    6. Encryption Algorithms: AES125-GCM - 128 Bits
    7. Hash Algorithms: SHA256
    8. PFS Key Group: 14 (2048 bits)
    9. Lifetime 10800
    10. Save
  7. Click + Add P2  (This is the local lab network rule )
    1. Mode: Tunnel IPv4
    2. Local Network: Type: Network - Address: lab subnet. e.g 172.16.0.0/16
    3. Nat: None
    4. Remote Network: Type :Network - Address: VPN Subnet from Terraform e.g. 172.16.99.0/24
    5. Protocol: ESP
    6. Encryption Algorithms: AES125-GCM - 128 Bits
    7. Hash Algorithms: SHA256
    8. PFS Key Group: 14 (2048 bits)
    9. Lifetime 10800
    10. Save
  8. Repeat for any other local networks you want to adverstise to the google subnet.
Create a Virtual IP
  1. Click Firewall the Virtual IPs
  2. Click + Add
    1. Interface: Internet Interface
    2. Address(es): 169.254.0.2 / 30
    3. Description: give is a nice description like GCP VPN TUN IP
Allow IPSec traffic through the firewall. Note, I pass all traffic
  1. Click Firewall then Rules
  2. Click IPSec
  3. Click Add
    1. Action: Pass
    2. Address Family: IPV4
    3. Protocol: Any
    4. Click Save
That's all. The IPSec tunnel should come up now.

Setting the GCP Windows Instance User and Password Credentials

I use the following command gcloud compute reset-windows-password --user userx windows2016vm-0
You will be rewarded with the account being created, userx in this case, and the randomly created password. 

You can get the IP Address of the instance using this command gcloud compute instances describe windows2016vm-0

If you are on a network you allowed across the VPN, you should be able to RDP to the windows machine name. 


No comments: