Skip to main content

Private Cloud Networking with VPC

NetActuate VPCs provide isolated private networks with a managed gateway for NAT, firewall, and bastion access. VMs deployed inside a VPC receive private IP addresses and access the internet through the gateway. This guide walks through creating a VPC, configuring firewall rules, setting up port forwarding, and deploying a VM.

What You Will Build

  • A VPC with a private subnet and managed gateway
  • Firewall rules allowing SSH and HTTP traffic
  • A floating IP with reverse DNS
  • DNAT port forwarding from the gateway to an internal VM
  • A VM running inside the VPC with a private IP only

Prerequisites

  • Terraform 1.0 or later
  • A NetActuate API key (generate one at Account -> API Keys in the portal)
  • The NetActuate Terraform provider configured:
terraform {
required_providers {
netactuate = {
source = "netactuate/netactuate"
version = ">= 2.0.0"
}
}
}

provider "netactuate" {
api_key = var.api_key
}

VPC Architecture

A NetActuate VPC consists of several components working together:

  • Private network -- an isolated subnet (e.g., 192.168.16.0/20) that is not routable on the public internet
  • Gateway -- a managed router with public IPv4 and IPv6 addresses that acts as the entry point to the VPC
  • Default SNAT -- outbound internet access for all VMs through the gateway's public IP
  • Firewall rules -- stateful packet filtering applied at the gateway
  • VMs -- servers deployed inside the VPC that receive only private IP addresses via vpc_reserved_network

Traffic flow: inbound traffic arrives at the gateway's public IP, passes through firewall rules, and is forwarded to internal VMs via DNAT. Outbound traffic from VMs is translated to the gateway's public IP via SNAT.

Step 1: Create the VPC

Define the VPC with a private network range, nameservers, and default SNAT enabled:

resource "netactuate_vpc" "main" {
label = "production-vpc"
location = "SJC"
network_ipv4 = "192.168.16.0/20"
nameservers = ["1.1.1.1", "8.8.8.8"]
enable_default_snat = true
}

Configuration breakdown:

  • label -- a human-readable name for the VPC
  • location -- the NetActuate data center code
  • network_ipv4 -- the private subnet for the VPC; VMs receive addresses from this range
  • nameservers -- DNS servers pushed to VMs via DHCP
  • enable_default_snat -- when true, all VMs can reach the internet through the gateway without additional configuration

Run the apply:

terraform apply

Terraform creates the VPC, provisions the gateway, and assigns public IPv4 and IPv6 addresses to it.

Step 2: Add Firewall Rules

The VPC gateway blocks all inbound traffic by default. Add firewall rules to allow the traffic you need:

resource "netactuate_vpc_gateway_firewall_rule" "ssh" {
vpc_id = netactuate_vpc.main.id
direction = "in"
protocol = "tcp"
port = "22"
source = "0.0.0.0/0"
action = "accept"
description = "Allow SSH from anywhere"
}

resource "netactuate_vpc_gateway_firewall_rule" "http" {
vpc_id = netactuate_vpc.main.id
direction = "in"
protocol = "tcp"
port = "80"
source = "0.0.0.0/0"
action = "accept"
description = "Allow HTTP from anywhere"
}

resource "netactuate_vpc_gateway_firewall_rule" "https" {
vpc_id = netactuate_vpc.main.id
direction = "in"
protocol = "tcp"
port = "443"
source = "0.0.0.0/0"
action = "accept"
description = "Allow HTTPS from anywhere"
}

Each rule specifies:

  • direction -- in for inbound traffic, out for outbound
  • protocol -- tcp, udp, or icmp
  • port -- the destination port number or range
  • source -- the source CIDR; restrict this to your IP for SSH in production
  • action -- accept or drop

Note: Firewall rules are stateful. An accept rule for inbound TCP port 22 automatically allows the return traffic.

Step 3: Allocate a Floating IP

Floating IPs are additional public addresses that you can assign to the VPC gateway. They are useful for services that need a dedicated IP or custom reverse DNS:

resource "netactuate_vpc_floating_ip" "web" {
vpc_id = netactuate_vpc.main.id
ptr = "web.example.com"
}

The floating IP is bound to the gateway. Use DNAT rules (next step) to forward traffic from the floating IP to specific internal VMs.

Step 4: Configure Port Forwarding

DNAT rules forward traffic arriving at the gateway's public IP (or a floating IP) to a specific internal VM:

resource "netactuate_vpc_gateway_dnat_rule" "web_http" {
vpc_id = netactuate_vpc.main.id
protocol = "tcp"
external_port = "80"
internal_ip = netactuate_server.web.vpc_reserved_network
internal_port = "80"
description = "Forward HTTP to web server"
}

resource "netactuate_vpc_gateway_dnat_rule" "web_https" {
vpc_id = netactuate_vpc.main.id
protocol = "tcp"
external_port = "443"
internal_ip = netactuate_server.web.vpc_reserved_network
internal_port = "443"
description = "Forward HTTPS to web server"
}

resource "netactuate_vpc_gateway_dnat_rule" "web_ssh" {
vpc_id = netactuate_vpc.main.id
protocol = "tcp"
external_port = "2222"
internal_ip = netactuate_server.web.vpc_reserved_network
internal_port = "22"
description = "Forward SSH on port 2222 to web server"
}

In the last rule, external port 2222 maps to internal port 22. This lets you SSH to the VM through the gateway on a non-standard port while the VM itself listens on the standard SSH port.

Step 5: Deploy a VM

Deploy a VM inside the VPC. The VM receives a private IP from the VPC subnet instead of a public IP:

resource "netactuate_server" "web" {
hostname = "web-01"
plan = "VR2x2x25"
location = "SJC"
image = "Ubuntu 24.04"
vpc_id = netactuate_vpc.main.id
ssh_key = var.ssh_public_key
}

output "web_private_ip" {
value = netactuate_server.web.vpc_reserved_network
description = "Private IP assigned to the web VM"
}

output "gateway_public_ip" {
value = netactuate_vpc.main.gateway_ipv4
description = "Public IPv4 address of the VPC gateway"
}

The vpc_reserved_network output contains the private IP address assigned to the VM from the VPC subnet. This is the address you use in DNAT rules and for internal communication between VMs.

Accessing VMs

VMs inside a VPC do not have public IP addresses. To connect via SSH, use the gateway as a bastion:

# Direct SSH through port forwarding (using the DNAT rule from Step 4)
ssh -p 2222 root@<gateway_public_ip>

Alternatively, use SSH proxying through the gateway:

# SSH proxy jump through the gateway
ssh -J root@<gateway_public_ip> root@<vm_private_ip>

For the proxy jump to work, the gateway must allow SSH access and the gateway must have connectivity to the VM's private IP (which it does by default).

Note: Replace <gateway_public_ip> with the output of terraform output gateway_public_ip and <vm_private_ip> with the output of terraform output web_private_ip.

SNAT Rules

The default SNAT rule translates all outbound traffic from VMs to the gateway's primary public IP. If you need VMs to use a specific source IP for outbound traffic (for example, a floating IP), create a custom SNAT rule:

resource "netactuate_vpc_gateway_snat_rule" "web_outbound" {
vpc_id = netactuate_vpc.main.id
source = netactuate_server.web.vpc_reserved_network
snat_ip = netactuate_vpc_floating_ip.web.address
description = "Web server outbound via floating IP"
}

This ensures that outbound traffic from the web VM appears to originate from the floating IP instead of the gateway's default address.

Outputs Reference

OutputDescription
netactuate_vpc.main.idVPC identifier for use in other resources
netactuate_vpc.main.gateway_ipv4Gateway public IPv4 address
netactuate_vpc.main.gateway_ipv6Gateway public IPv6 address
netactuate_server.web.vpc_reserved_networkPrivate IP assigned to the VM
netactuate_vpc_floating_ip.web.addressFloating IP address

Teardown

To remove all VPC resources:

terraform destroy

This deletes the VMs, firewall rules, DNAT/SNAT rules, floating IPs, and the VPC itself. Terraform handles the dependency ordering automatically.

Reference


Need Help?

Contact support@netactuate.com or open a support ticket from the portal.