andkorn.org

a fine line between curiosity and madness.

Juniper SRX to Cisco Policy-Based IPsec VPN

If you have ever configured an IPsec VPN between a Cisco and a Juniper SRX you know it can be difficult. Actually, if you ever configured an IPsec VPN in general you know it can be a royal pain in the keester.

In any case, when you configure a Policy-Based IPsec VPN between Juniper and Cisco ISR routers, with more than one network on each side, you will find you will need an extraordinary number of policies on the SRX in order to play nice with the Cisco. If the networks allow, use IP Address Aggregation. Doing this on both sides will greatly simplify your config. In some (most?) cases you will not be this lucky.

For example, let’s say you have these networks:

Juniper side:

  • 192.168.1.0/24
  • 192.168.24.0/24
  • 192.168.15.0/24
  • 10.1.1.0/16
  • 10.2.1.0/16

Cisco side:

  • 192.168.12.0/24
  • 192.168.32.0/24
  • 10.3.1.0/16
  • 10.4.1.0/16
  • 10.5.1.0/16

On the Cisco side it is easy – just make sure your access lists allow the right groups of networks to get to the right groups of networks on the other side. On the Juniper you have to create 1 gateway, 25 VPN objects, and 50 security policies! That’s insane! And that is why Juniper recommends you use Route-Based VPNs if you can and avoid PBVPNs at all costs if you have a complex network. The math behind this is:

You have 5 networks on each side. you have to create one VPN object per each pair of networks. That’s 5 x 5 = 25. In a PBVPN you have to create two policies per each VPN object: one for incoming traffic and one for outgoing. That’s 2 x 25 = 50 security policies. Keeping track of this many objects as you put them in is difficult and stupid, if not impossible. I found a solution: write a python script to do the dirty work.

Now because I am lazy, you will still need to bypass NAT for the networks you are trying to reach (under security nat source) and create the IKE phase 1 and IPsec Phase 2 proposals and pre-shared keys, but at least you don’t have to create the bazillion statements to get all your networks across.

All I can recommend is this: create a simple PBVPN in a lab between a Cisco ASA 5505 or a Cisco ISR and a Juniper SRX100. If you don’t have an ASA 5505 use a Sonicwall with SonicOS 5.6 or greater, or a Fortinet; they handle PBVPNs in almost the same way. But the best way is to use very similar devices to what you will use in the real scenario. If you don’t have a spare SRX, you can create a virtual Juniper Olive to test out your stuff. I have created Olives and it is well documented on the ‘net how to do it.

Please feel free to mock my poor python scripting. This was quick and dirty and also my first python script. You can also see this script on pastebin.

UPDATE Sept 24 2012: I have updated the code below and on the pastebin link above. There were bugs in the code that made it not work; the v1.1 is now tested to work with Juniper JunOS 11.2R4.3.

Python Script for Complex Juniper SRX Policy-Based VPNs (junos-pbvpn.py) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#!/usr/bin/env python
#############################################################################
## andkorn Sept 21 2012
## This script is free to use under the BSD 3-clause license.
## this script reads in a few options and creates a juniper config for a policy-based vpn that will work with Cisco's access-list-based vpn.
# see also why policy-based VPNs are a pain:
# http://forums.juniper.net/t5/SRX-Services-Gateway/srx-route-mode-ipsec-vpn-with-sonicwall-gen3-gen4-standard-and/td-p/33658
# http://kb.juniper.net/InfoCenter/index?page=content&id=KB15745&smlogin=true
## version 1.1
##
import sys, re

print("---Configuring VPN Blocks")
gateway = raw_input("Enter 'ike gateway' object name:")
ipsec_policy = raw_input("Enter 'ipsec-policy' object name:")
print("---Configuring network Blocks")
trustzone = raw_input("Enter trust zone name (usually 'trust'):")
untrustzone = raw_input("Enter untrust zone name (usually 'untrust'):")
localprefix = raw_input("Enter local name prefix for objects (anything that makes sense):")
remoteprefix = raw_input("Enter remote name prefix for objects (anything that makes sense):")
print("Enter local networks in 192.168.1.0/24 format, one per line. Enter Ctrl+Z to end:")
localnetworkstxt = sys.stdin.read()
localnetworks = localnetworkstxt.split("\n")
print("Enter remote networks in 192.168.1.0/24 format, one per line. Enter Ctrl+Z to end:")
remotenetworkstxt = sys.stdin.read()
remotenetworks = remotenetworkstxt.split("\n")


#Clean up the inputted networks; remove invalid IP addresses
localnetworkstmp = localnetworks
localnetworks = filter(lambda x: re.search(r'((2[0-5]|1[0-9]|[0-9])?[0-9]\.){3}((2[0-5]|1[0-9]|[0-9])?[0-9])\/[0-3]?[0-9]', x), localnetworkstmp)
remotenetworkstmp = remotenetworks
remotenetworks = filter(lambda x: re.search(r'((2[0-5]|1[0-9]|[0-9])?[0-9]\.){3}((2[0-5]|1[0-9]|[0-9])?[0-9])\/[0-3]?[0-9]', x), remotenetworkstmp)


fsock = open(raw_input("Enter file to save to:"), 'w')
origstdout = sys.stdout
sys.stdout = fsock

print("##########Below is your config. Load this with 'load merge terminal' in JunOS")
print("##junos-pbvpn.py by andkorn Sept 21 2012")

print("security {")
print("    ipsec {")

networkcount = 1
for localnetwork in localnetworks[:]:
        for remotenetwork in remotenetworks[:]:
                print("        vpn vpn"+ localprefix+ "-to-"+remoteprefix+ "-"+str(networkcount)+" {")
                print("            ike {")
                print("                gateway "+ gateway +";")
                print("                ipsec-policy "+ ipsec_policy+";")
                print("            }")
                print("            establish-tunnels immediately;")
                print("        }")
                networkcount += 1

print(" }")
print("    policies {")

networkcount = 1
print("        from-zone "+ trustzone+" to-zone "+ untrustzone+" {")
for localnetwork in localnetworks[:]:
        for remotenetwork in remotenetworks[:]:
                print("            policy vpn-out-"+ localprefix+ "-"+localnetwork.replace("/","-").replace(".","-")+"-to-"+remoteprefix+ "-"+remotenetwork.replace("/","-").replace(".","-")+" {")
                print("                match {")
                print("                    source-address "+ localprefix+ "-"+localnetwork.replace("/","-").replace(".","-")+";")
                print("                    destination-address "+remoteprefix+ "-"+remotenetwork.replace("/","-").replace(".","-")+";")
                print("                    application any;")
                print("                }")
                print("                then {")
                print("                    permit {")
                print("                        tunnel {")
                print("                            ipsec-vpn vpn"+ localprefix+ "-to-"+remoteprefix+ "-"+str(networkcount)+";")
                print("                            pair-policy vpn-in-"+ localprefix+ "-"+localnetwork.replace("/","-").replace(".","-")+"-to-"+remoteprefix+ "-"+remotenetwork.replace("/","-").replace(".","-")+";")
                print("                        }")
                print("                    }")
                print("                }")
                print("            }")
                networkcount += 1
print("        }")

networkcount = 1
print("        from-zone "+ untrustzone+" to-zone "+ trustzone+" {")
for localnetwork in localnetworks[:]:
        for remotenetwork in remotenetworks[:]:
                print("            policy vpn-in-"+ localprefix+ "-"+localnetwork.replace("/","-").replace(".","-")+"-to-"+remoteprefix+ "-"+remotenetwork.replace("/","-").replace(".","-")+" {")
                print("                match {")
                print("                    source-address "+ remoteprefix+ "-"+remotenetwork.replace("/","-").replace(".","-")+";")
                print("                    destination-address "+ localprefix+ "-"+localnetwork.replace("/","-").replace(".","-")+";")
                print("                    application any;")
                print("                }")
                print("                then {")
                print("                    permit {")
                print("                        tunnel {")
                print("                            ipsec-vpn vpn"+ localprefix+ "-to-"+remoteprefix+ "-"+str(networkcount)+";")
                print("                            pair-policy vpn-out-"+ localprefix+ "-"+localnetwork.replace("/","-").replace(".","-")+"-to-"+remoteprefix+ "-"+remotenetwork.replace("/","-").replace(".","-")+";")
                print("                        }")
                print("                    }")
                print("                }")
                print("            }")
                networkcount += 1
print("        }")
print("    }")

print("    zones {")
print("        security-zone "+ trustzone+" {")
print("            address-book {")
for localnetwork in localnetworks[:]:
        print("                address "+ localprefix+ "-"+localnetwork.replace("/","-").replace(".","-") +" "+localnetwork+";")
print("            }")
print("        }")
print("        security-zone "+ untrustzone+" {")
print("            address-book {")
for remotenetwork in remotenetworks[:]:
        print("                address "+ remoteprefix+ "-"+remotenetwork.replace("/","-").replace(".","-") +" "+remotenetwork+";")
print("            }")
print("            host-inbound-traffic {")
print("                system-services {")
print("                    ike;")
print("                }")
print("            }")
print("         }")
print("    }")
print("}")
print("####END")

sys.stdout = origstdout
fsock.close()

Comments