Often on our Cloud surgery calls I’ll have requests on how to go about configuring Virtual Private Networks (IPSec VPNs) between the various public clouds and customer sites.
The purpose of this blog is to demonstrate the process of establishing such a secure tunnel using the VPNaaS feature available on Fujitsu’s Cloud Service K5.
Prerequisites
The biggest key to success here is preparation – most issues are encountered when the customer’s VPN configuration and K5’s VPN configurations are not programmed with the same prerequisites. In order to try and simplify this process for you, here at CNETS we’ve put together a simple VPNaaS template that can be downloaded by clicking on the image below and shared with the customer. Please ensure that this is document is understood, agreed, and completed before trying to build your service – sounds sensible I hope – you’d be surprised at how often people skip this step.
Another prerequisite on your cloud environment is that you have already created the virtual network, subnet, router and added a Global IP (public ip address) to the virtual router.
Once you’ve completed these prerequisites it’s time to start building your VPN.
OpenStack IPSec VPNaaS Tool
Again CNETS come to the rescue, unless you enjoy manually building all the necessary API calls, we’ve built an Angular based User Interface to simplify all the necessary API calls to build and configure your VPNaaS. The tool is located here – https://cnets-vpnaas.uk-1.cf-app.net/ and needs a valid Fujitsu Cloud Service K5 administrative contract account to login.
I’ve also created the following diagram to illustrate what will be configured in a soon to be published video … as soon as I can get my OBS recording software to start playing nicely again. I will join two subnets across two K5 regions – one subnet is located in our US region and the other subnet is in our UK region. The exact same process is used if configuring K5’s VPNaaS when connecting to VPNaaS offerings in other public/private clouds or customer VPN appliances.
Note: One fundamental concept that needs to be understood is that you are linking one layer 3 subnet to another layer 3 subnet only. What I mean by this is that in the following example I’ll add the 192.168.10.0/24 subnet in the UK region and the 192.168.0.0/24 subnet in the US region to the IPSec VPN (these subnet CIDRS should not be over-lapping). Once the VPN tunnel is established and the correct security groups applied Server A will be able to communicate with Server C and vice-versa. However, Server B and Server D will NOT be able to communicate. An additional proxy service would be needed on each of the VPN peer subnets to facilitate routing traffic from Server B and Server D through the VPN tunnel.
As it’s near Christmas and we’re full of festive spirit here in CNETS this week (at least I am), I’ve got one final treat – for those of you who like to follow along – below I’ve also included the two heat templates that I’ve used for the demo to build the infrastructure in the diagram above. Don’t forget to change the input parameters and DNS setting to match your target AZ if using them on Fujitsu’s Cloud Service K5. These templates should also work with other OpenStack clouds.
Heat Template Project A
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
heat_template_version: 2013-05-23 | |
# Author: Graham Land | |
# Date: 15/11/2017 | |
# Purpose: Demo IPSec VPNaaS – Base for Project A | |
# | |
# Twitter: @allthingsclowd | |
# Blog: https://allthingscloud.eu | |
# | |
# | |
description: Template for VPNaaS (IPSec) Demo Project A – Two Networks and a Router with two Servers A & B | |
# Input parameters | |
parameters: | |
k5_image: | |
type: string | |
label: Image name or ID | |
description: Image to be used for compute instance | |
default: "Ubuntu Server 16.04 LTS (English) 01" | |
flavor: | |
type: string | |
label: Flavor | |
description: Type of instance (flavor) to be used | |
default: "P-1" | |
ssh_key_pair: | |
type: string | |
label: Key name | |
description: Name of key-pair to be used for compute instance | |
default: "LEMP-KP-AZ1" | |
availability_zone: | |
type: string | |
label: Availability Zone | |
description: Region AZ to use | |
default: "uk-1a" | |
my_ip: | |
type: string | |
label: The IP Address (CIDR format) of My PC on the Internet | |
description: PC Internet IP Address (CIDR format) | |
default: "31.53.253.24/32" | |
# K5 Infrastructure resources to be built | |
resources: | |
# Create a private network | |
private_network_a: | |
type: OS::Neutron::Net | |
properties: | |
availability_zone: { get_param: availability_zone } | |
name: "Network-A" | |
# Create a new subnet on the private network | |
private_subnet_a: | |
type: OS::Neutron::Subnet | |
depends_on: private_network_a | |
properties: | |
availability_zone: { get_param: availability_zone } | |
name: "Subnetwork-A" | |
network_id: { get_resource: private_network_a } | |
cidr: "192.168.0.0/24" | |
gateway_ip: "192.168.0.254" | |
allocation_pools: | |
– start: "192.168.0.100" | |
end: "192.168.0.150" | |
dns_nameservers: ["62.60.39.9", "62.60.42.9"] | |
# Create a second private network | |
private_network_b: | |
type: OS::Neutron::Net | |
properties: | |
availability_zone: { get_param: availability_zone } | |
name: "Network-B" | |
# Create a new subnet on the second private network | |
private_subnet_b: | |
type: OS::Neutron::Subnet | |
depends_on: private_network_b | |
properties: | |
availability_zone: { get_param: availability_zone } | |
name: "Subnetwork-B" | |
network_id: { get_resource: private_network_b } | |
cidr: "10.0.0.0/24" | |
gateway_ip: "10.0.0.254" | |
allocation_pools: | |
– start: "10.0.0.100" | |
end: "10.0.0.150" | |
dns_nameservers: ["62.60.39.9", "62.60.42.9"] | |
# Create a virtual router | |
virtual_router: | |
type: OS::Neutron::Router | |
properties: | |
availability_zone: { get_param: availability_zone } | |
name: "Simple-Virtual-Router" | |
# Connect an interface on the private network's subnet to the router | |
virtual_router_interface_1: | |
type: OS::Neutron::RouterInterface | |
depends_on: virtual_router | |
properties: | |
router_id: { get_resource: virtual_router } | |
subnet_id: { get_resource: private_subnet_a } | |
# Connect an interface on the private network's subnet to the router | |
virtual_router_interface_2: | |
type: OS::Neutron::RouterInterface | |
depends_on: virtual_router | |
properties: | |
router_id: { get_resource: virtual_router } | |
subnet_id: { get_resource: private_subnet_b } | |
# Create security group | |
security_group: | |
type: OS::Neutron::SecurityGroup | |
properties: | |
description: VPN Security Group | |
name: VPN-SG | |
rules: | |
# allow inbound traffic from remote VPN subnet | |
– remote_ip_prefix: 192.168.10.0/24 | |
protocol: tcp | |
– remote_ip_prefix: 192.168.10.0/24 | |
protocol: icmp | |
# allow inbound traffic from my remote PC | |
– remote_ip_prefix: { get_param: my_ip } | |
protocol: icmp | |
– remote_ip_prefix: { get_param: my_ip } | |
protocol: tcp | |
port_range_min: 22 | |
port_range_max: 22 | |
################### Server A ################################################################ | |
# Create a system volume for use with the server | |
server-A-sys-vol: | |
type: OS::Cinder::Volume | |
properties: | |
availability_zone: { get_param: availability_zone } | |
name: "Server-A-boot-vol" | |
size: 3 | |
volume_type: "M1" | |
image : { get_param: k5_image } | |
server-A-port: | |
type: OS::Neutron::Port | |
depends_on: private_network_a | |
properties: | |
availability_zone: { get_param: availability_zone } | |
network_id: { get_resource: private_network_a } | |
name: "Server-A-Port" | |
fixed_ips: | |
– subnet_id: { get_resource: private_subnet_a } | |
security_groups: [{ get_resource: security_group }] | |
# Build a server using the system volume defined above | |
server-A: | |
type: OS::Nova::Server | |
depends_on: server-A-port | |
properties: | |
availability_zone: { get_param: availability_zone } | |
key_name: { get_param: ssh_key_pair } | |
image: { get_param: k5_image } | |
flavor: { get_param: flavor } | |
admin_user: ubuntu | |
metadata: { "fcx.autofailover": True } | |
block_device_mapping: [{"volume_size": "3", "volume_id": {get_resource: server-A-sys-vol}, "delete_on_termination": True, "device_name": "/dev/vda"}] | |
name: "Server-A" | |
networks: | |
– port: { get_resource: server-A-port } | |
########################################################################################################### | |
################### Server B ################################################################ | |
# Create a system volume for use with the server | |
server-B-sys-vol: | |
type: OS::Cinder::Volume | |
depends_on: private_network_b | |
properties: | |
availability_zone: { get_param: availability_zone } | |
name: "Server-B-boot-vol" | |
size: 3 | |
volume_type: "M1" | |
image : { get_param: k5_image } | |
# Build a server using the system volume defined above | |
server-B: | |
type: OS::Nova::Server | |
properties: | |
availability_zone: { get_param: availability_zone } | |
key_name: { get_param: ssh_key_pair } | |
image: { get_param: k5_image } | |
flavor: { get_param: flavor } | |
admin_user: ubuntu | |
metadata: { "fcx.autofailover": True } | |
block_device_mapping: [{"volume_size": "3", "volume_id": {get_resource: server-B-sys-vol}, "delete_on_termination": True, "device_name": "/dev/vda"}] | |
name: "Server-B" | |
networks: | |
– network: { get_resource: private_network_b } | |
########################################################################################################### |
Heat Template Project B
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
heat_template_version: 2013-05-23 | |
# Author: Graham Land | |
# Date: 15/11/2017 | |
# Purpose: Demo IPSec VPNaaS – Base for Project B – Two Networks and a Router with two Servers C & D | |
# | |
# Twitter: @allthingsclowd | |
# Blog: https://allthingscloud.eu | |
# | |
# | |
description: Template for VPNaaS (IPSec) Demo Project B – Two Networks and a Router with two Servers C & D | |
# Input parameters | |
parameters: | |
k5_image: | |
type: string | |
label: Image name or ID | |
description: Image to be used for compute instance | |
default: "Ubuntu Server 16.04 LTS (English) 01" | |
flavor: | |
type: string | |
label: Flavor | |
description: Type of instance (flavor) to be used | |
default: "P-1" | |
ssh_key_pair: | |
type: string | |
label: Key name | |
description: Name of key-pair to be used for compute instance | |
default: "LEMP-KP-AZ2" | |
availability_zone: | |
type: string | |
label: Availability Zone | |
description: Region AZ to use | |
default: "uk-1b" | |
my_ip: | |
type: string | |
label: The IP Address (CIDR format) of My PC on the Internet | |
description: PC Internet IP Address (CIDR format) | |
default: "31.53.253.24/32" | |
# K5 Infrastructure resources to be built | |
resources: | |
# Create a private network | |
private_network_a: | |
type: OS::Neutron::Net | |
properties: | |
availability_zone: { get_param: availability_zone } | |
name: "Network-C" | |
# Create a new subnet on the private network | |
private_subnet_a: | |
type: OS::Neutron::Subnet | |
depends_on: private_network_a | |
properties: | |
availability_zone: { get_param: availability_zone } | |
name: "Subnetwork-C" | |
network_id: { get_resource: private_network_a } | |
cidr: "192.168.10.0/24" | |
gateway_ip: "192.168.10.254" | |
allocation_pools: | |
– start: "192.168.10.100" | |
end: "192.168.10.150" | |
dns_nameservers: ["62.60.39.9", "62.60.42.9"] | |
# Create a second private network | |
private_network_b: | |
type: OS::Neutron::Net | |
properties: | |
availability_zone: { get_param: availability_zone } | |
name: "Network-D" | |
# Create a new subnet on the second private network | |
private_subnet_b: | |
type: OS::Neutron::Subnet | |
depends_on: private_network_b | |
properties: | |
availability_zone: { get_param: availability_zone } | |
name: "Subnetwork-D" | |
network_id: { get_resource: private_network_b } | |
cidr: "10.0.10.0/24" | |
gateway_ip: "10.0.10.254" | |
allocation_pools: | |
– start: "10.0.10.100" | |
end: "10.0.10.150" | |
dns_nameservers: ["62.60.39.9", "62.60.42.9"] | |
# Create a virtual router | |
virtual_router: | |
type: OS::Neutron::Router | |
properties: | |
availability_zone: { get_param: availability_zone } | |
name: "Simple-Virtual-Router" | |
# Connect an interface on the private network's subnet to the router | |
virtual_router_interface_1: | |
type: OS::Neutron::RouterInterface | |
depends_on: virtual_router | |
properties: | |
router_id: { get_resource: virtual_router } | |
subnet_id: { get_resource: private_subnet_a } | |
# Connect an interface on the private network's subnet to the router | |
virtual_router_interface_2: | |
type: OS::Neutron::RouterInterface | |
depends_on: virtual_router | |
properties: | |
router_id: { get_resource: virtual_router } | |
subnet_id: { get_resource: private_subnet_b } | |
# Create security group | |
security_group: | |
type: OS::Neutron::SecurityGroup | |
properties: | |
description: VPN Security Group | |
name: VPN-SG | |
rules: | |
# allow inbound traffic from remote VPN subnet | |
– remote_ip_prefix: 192.168.0.0/24 | |
protocol: tcp | |
– remote_ip_prefix: 192.168.0.0/24 | |
protocol: icmp | |
# allow inbound traffic from my remote PC | |
– remote_ip_prefix: { get_param: my_ip } | |
protocol: icmp | |
– remote_ip_prefix: { get_param: my_ip } | |
protocol: tcp | |
port_range_min: 22 | |
port_range_max: 22 | |
################### Server A ################################################################ | |
# Create a system volume for use with the server | |
server-A-sys-vol: | |
type: OS::Cinder::Volume | |
properties: | |
availability_zone: { get_param: availability_zone } | |
name: "Server-C-boot-vol" | |
size: 3 | |
volume_type: "M1" | |
image : { get_param: k5_image } | |
server-A-port: | |
type: OS::Neutron::Port | |
properties: | |
availability_zone: { get_param: availability_zone } | |
network_id: { get_resource: private_network_a } | |
name: "Server-C-Port" | |
fixed_ips: | |
– subnet_id: { get_resource: private_subnet_a } | |
security_groups: [{ get_resource: security_group }] | |
# Build a server using the system volume defined above | |
server-A: | |
type: OS::Nova::Server | |
depends_on: private_network_a | |
properties: | |
availability_zone: { get_param: availability_zone } | |
key_name: { get_param: ssh_key_pair } | |
image: { get_param: k5_image } | |
flavor: { get_param: flavor } | |
admin_user: ubuntu | |
metadata: { "fcx.autofailover": True } | |
block_device_mapping: [{"volume_size": "3", "volume_id": {get_resource: server-A-sys-vol}, "delete_on_termination": True, "device_name": "/dev/vda"}] | |
name: "Server-C" | |
networks: | |
– port: { get_resource: server-A-port } | |
########################################################################################################### | |
################### Server B ################################################################ | |
# Create a system volume for use with the server | |
server-B-sys-vol: | |
type: OS::Cinder::Volume | |
depends_on: private_network_b | |
properties: | |
availability_zone: { get_param: availability_zone } | |
name: "Server-D-boot-vol" | |
size: 3 | |
volume_type: "M1" | |
image : { get_param: k5_image } | |
# Build a server using the system volume defined above | |
server-B: | |
type: OS::Nova::Server | |
properties: | |
availability_zone: { get_param: availability_zone } | |
key_name: { get_param: ssh_key_pair } | |
image: { get_param: k5_image } | |
flavor: { get_param: flavor } | |
admin_user: ubuntu | |
metadata: { "fcx.autofailover": True } | |
block_device_mapping: [{"volume_size": "3", "volume_id": {get_resource: server-B-sys-vol}, "delete_on_termination": True, "device_name": "/dev/vda"}] | |
name: "Server-D" | |
networks: | |
– network: { get_resource: private_network_b } | |
########################################################################################################### |
Video demo to follow shortly.
Happy Tunneling!
Graham