In the previous post, we went through the initial setup steps for the AWS to Meraki VPN:

# Meraki info step
# ———————————–
# 1. Get MX public IP
# AWS VPN creation steps
# ———————————–
# 1. Create Customer Gateway
# 2. Create Virtual Private Gateway
# 3. Attach VGW to VPC
# 4. Create VPN Connection using CGW + VPG
# 5. View Downloaded config with public IP and password
# 6. Add LAN routes to VPN table
# 7. Propogate VPC subnet into VPG
# Meraki VPN Creation step
# ————————————
# 1. read existing non Meraki VPN config
# 2. update config with AWS VPN peer detail

Now we will finish setting up both the AWS and then Meraki VPNs, and make sure they connect and provide private traffic flow from the Meraki LAN to the VPC EC2 instance.

# # 1. create a customer gateway (requires Meraki MX Public IP) #     response = client.create_customer_gateway(         BgpAsn = 65000,         PublicIp = meraki_public_ip,         Type= ‘ipsec.1’,         DeviceName = ‘MerakiMX’,         DryRun=False     )

    print (“\nCustomer Gateway result:”)     print(json.dumps(response[“CustomerGateway”], sort_keys=True, indent=4))     print (“Customer GatewayID:”, response[“CustomerGateway”][“CustomerGatewayId”])     myCGW = response[“CustomerGateway”][“CustomerGatewayId”]

This call uses the meraki IP we read from the previous post to create a CGW. BGP ASN is required – but not going to be used since this will be based on simple static routing.

# # 2. Create Virtual Private Gateway #     response = client.create_vpn_gateway(         Type=’ipsec.1′,     )

    print (“\nVirtual Private Gateway result:”)     print(json.dumps(response[“VpnGateway”], sort_keys=True, indent=4))     print (“VGW ID:”, response[“VpnGateway”][“VpnGatewayId”])     myVGW = response[“VpnGateway”][“VpnGatewayId”]

This call creates the VGW, which initially is standalone.

# # 3. Attach VGW to VPC #     response = client.attach_vpn_gateway(         VpcId = myVpc,         VpnGatewayId = myVGW     )          print (“\nAttachement result:”)     print(json.dumps(response[“VpcAttachment”], sort_keys=True, indent=4))     print (“Status:”, response[“VpcAttachment”][“State”])     status = response[“VpcAttachment”][“State”]     print (“Waiting for [attached] status (rechecking every 5s)”)     while status != “attached”:         time.sleep(5)         response = client.describe_vpn_gateways (            VpnGatewayIds=[                myVGW,            ],         ) #        print(json.dumps(response[“VpnGateways”], sort_keys=True, indent=4))         for line in response[“VpnGateways”]:             for line2 in line[“VpcAttachments”]:                 status = line2[“State”]                 print (“Status:”,status)

Now we attach the VGW to our VPC, and then put a simple 5s times loop in to wait for the status of the attachement to complete (normally takes 15-20s)

# # 4. Create VPN Connection using CGW + VPG # 5. Display VPN config data (public IP/passphrase) #     response = client.create_vpn_connection(         CustomerGatewayId = myCGW,         Type = ‘ipsec.1’,         VpnGatewayId = myVGW,         Options = {             ‘StaticRoutesOnly’: True         }     )

    myVPN = response[“VpnConnection”][“VpnConnectionId”]     print (“VPN ID:”, myVPN)     print (“\nVPN Configuration Data (showing VPN0)\n———————————-“)

    doc = xmltodict.parse(response[“VpnConnection”][“CustomerGatewayConfiguration”]) #    print(json.dumps(doc, sort_keys=True, indent=4))     for tunnels in doc[“vpn_connection”][“ipsec_tunnel”]:         print (“VPN0 Passphrase:”, tunnels[“ike”][“pre_shared_key”])         myPassPhrase = tunnels[“ike”][“pre_shared_key”]         print (“VPN0 Public IP:”, tunnels[“vpn_gateway”][“tunnel_outside_address”][“ip_address”])         aws_public_ip = tunnels[“vpn_gateway”][“tunnel_outside_address”][“ip_address”]         break

This ties the CGW and VGW together, and creates the VPN connection. We then receive back a response which includes the VPN Gateway config, which is in XML format. Convert this to a dictionary variable with xmltodict to make it easier to read (and reference).

# # 6. Add remote LAN routes to VPN table #     client.create_vpn_connection_route(            DestinationCidrBlock = my_meraki_lan_subnet,            VpnConnectionId = myVPN         )

# # 7. Propogate VPN routes to route table #     client.enable_vgw_route_propagation(         RouteTableId = myRouteID,         GatewayId = myVGW     )

Then we add in our Meraki LAN subnet to the VPN connection created in #5, and connect our new VGW to the route table of the VPC so we can route traffic between the VPC and the VPN.

# # Wait for VPN to be ready to use #     status = response[“VpnConnection”][“State”]     print (“\nStatus:”, status)     print (“Waiting for [available] status (rechecking every 30s)”)     while status != “available”:         time.sleep(30)         response = client.describe_vpn_connections(             VpnConnectionIds=[                 myVPN,             ],         )         for line in response[“VpnConnections”]:             status = line[“State”]             print (“Status:”,status)

To finish, we now wait for the VPN to be ready, so we can complete the Meraki VPN setup, with another simple 30s loop status check routine.

Now lets create the VPN tunnel on the Meraki !

# Return the third party VPN peers for an organization #     try:         non_meraki_peers = meraki_client.organizations.get_organization_third_party_vpn_peers(params[“organization_id”])         #print (non_meraki_peers)     except APIException as e:         print(e) 

    print (“\n”) # # Add the additional peer to the existing third party VPN peers for an organization #     try:         collect = {}         collect[‘organization_id’] = params[“organization_id”]         update_organization_third_party_vpn_peers = UpdateOrganizationThirdPartyVPNPeersModel()         update_organization_third_party_vpn_peers.peers = []

        peer_num=0         # iterate through the existing peers and add to update variable         for line in non_meraki_peers:                update_organization_third_party_vpn_peers.peers.append(PeerModel())             update_organization_third_party_vpn_peers.peers[peer_num].name = line[‘name’]             update_organization_third_party_vpn_peers.peers[peer_num].public_ip = line[‘publicIp’]             if ‘remoteId’ in line:                 update_organization_third_party_vpn_peers.peers[peer_num].remote_id = line[‘remoteId’]             update_organization_third_party_vpn_peers.peers[peer_num].private_subnets = line[‘privateSubnets’]             update_organization_third_party_vpn_peers.peers[peer_num].ipsec_policies_preset = line[‘ipsecPoliciesPreset’]             update_organization_third_party_vpn_peers.peers[peer_num].secret = line[‘secret’]             update_organization_third_party_vpn_peers.peers[peer_num].network_tags = line[‘networkTags’]             peer_num +=1

        # add new aws peer to variable             update_organization_third_party_vpn_peers.peers.append(PeerModel())         update_organization_third_party_vpn_peers.peers[peer_num].name = ‘AWS Gateway’         update_organization_third_party_vpn_peers.peers[peer_num].public_ip = aws_public_ip         # note remote_id missing from meraki_sdk file – need to update
\python3\Lib\site-packages\meraki_sdk\models\         update_organization_third_party_vpn_peers.peers[peer_num].remote_id = ‘’         update_organization_third_party_vpn_peers.peers[peer_num].private_subnets = my_aws_lan_subnet         update_organization_third_party_vpn_peers.peers[peer_num].ipsec_policies_preset = ‘aws’

        update_organization_third_party_vpn_peers.peers[peer_num].secret = myPassPhrase         update_organization_third_party_vpn_peers.peers[peer_num].network_tags = [‘all’]

        collect[‘update_organization_third_party_vpn_peers’] = update_organization_third_party_vpn_peers

        result = meraki_client.organizations.update_organization_third_party_vpn_peers(collect)         print(“MX Non-Meraki VPN peers:\n”, json.dumps(result, sort_keys=True, indent=4))

This final routine reads in the existing non-Meraki VPN config (under Security->Site to Site VPN), and then adds in the new AWS VPN to the config, and writes it back to the dashboard. As per the GUI guide from the first page, Meraki provides an AWS VPN config, so that you don’t need to deep dive on ipsec parameters and work out what should be configured to connect to AWS ipsec VPN  correctly (thus the ipsec policy = aws).

And we are complete ! AWS VPN is up, Meraki VPN is up, and we should be able to ping/ssh/rdp from the Meraki LAN to the VPN EC2 instance via the private IP.

In the final post, we will have a look at the status output and Ill provide a copy of the completed application to run.

AWS to Meraki VPN Tunnel with Python Part2
Tagged on:                 

One thought on “AWS to Meraki VPN Tunnel with Python Part2

Leave a Reply

Your email address will not be published. Required fields are marked *