Rendering IPsec configuration with python

preface

IPsec has many configuration dependencies. If you don't pay attention to it, it is easy to make a configuration error. As long as the configuration error is wrong, it is easy to cause IPsec to fail; So I hope to use some means to simplify these complex configurations; So I use python language, which is not difficult to learn but very mature, to solve this kind of problem.

1, Basic environment

Networking environment

Configure the IP address of each interface

Main basic configuration of headquarters exit router

sysname HQ
ip route-static 0.0.0.0 0 100.0.0.100
acl advanced 3000
rule 5 permit ip
interface GigabitEthernet0/2
nat outbound 3000

Main basic configuration of branch exit router

sysname branch
ip route-static 0.0.0.0 0 200.0.0.2
acl advanced 3000
rule 5 permit ip
interface GigabitEthernet0/0
nat outbound 3000

2, Write the jinja2 configuration template of IPsec

{# Create a stream of interest #}
acl advanced {{ AclNumber }}
rule permit ip source {{ SouNet }} destination {{ DestNet }}
quit
{# establish IKE proposal #}
ike proposal 1
authentication-method pre-share
encryption-algorithm aes-cbc-128
quit
{# establish IKE Preshared key #}
ike keychain {{ KPT }}
pre-shared-key address {{ RemotePublicIp }} key simple {{ IkePassword }}
quit
{# establish IKE-profile,Specify the local and peer public network addresses, and call the pre shared key and IKE proposal#}
ike profile {{ KPT }}
keychain {{ KPT }}
local-identity address {{ LocalPublicIp }}
match remote identity address {{ RemotePublicIp }}
proposal 1
quit
{# establish IPsec Conversion set,Tunnel mode and are used by default esp Package, so don't worry #}
ipsec transform-set {{ KPT }}
esp authentication-algorithm sha1
esp encryption-algorithm aes-cbc-128
quit
{# establish IPsec Policy, call the above configuration #}
ipsec policy {{ KPT }} 1 isakmp
security acl {{ AclNumber }}
ike-profile {{ KPT }}
transform-set {{ KPT }}
remote-address {{ RemotePublicIp }}
quit
{# Issue on the public network interface IPsec strategy #}
int {{ IntfPort }} 
ipsec apply policy {{ KPT }}
quit
{# Adaptation IPsec Public network of NAT ACL #}
acl advanced {{ NatNum }}
rule 1 deny ip source {{ SouNet }} destination {{ DestNet }}
rule permit ip source any
quit
{# Modify on public network interface NAT Export ACL #}
int {{ IntfPort }} 
nat outbound {{ NatNum }}
quit

Why should IPsec traffic be rejected first on the NAT ACL rules of the public network adapted to IPsec

Solution: if the traffic data matches NAT, it will go through NAT by default. If it fails to make the traffic data go through IPsec, it needs to be rejected first to match the subsequent IPsec policies, and then match the rules in IPsec.

2, Write IPsec rendering script

import winreg #Get the desktop environment, and output the configuration file directly to the desktop for easy search
from pathlib import Path	#The dependency package for creating files is easier to use than os, so I use this
from jinja2 import FileSystemLoader,Environment #Packages required for rendering using jinja2

def GetDesktop(): #Get desktop path
    key = winreg.OpenKey(winreg.HKEY_CURRENT_USER,
                          r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders",)
    return Path(winreg.QueryValueEx(key, "Desktop")[0])

def IpsecVPnRender(outputpath): #IpsecVPN rendering
    loader = FileSystemLoader("templates")  #Load templates template folder
    environment = Environment(loader=loader) #Added to jinja2's environment
    SouDevice = environment.get_template("IpsecVpn.conf.tp")
    DestDevice = environment.get_template("IpsecVpn.conf.tp")
    AclNumber = int(input("Input match IPsec of ACL Serial number:"))
    SouNet = input("Enter the network number of the local private network segment/host+Unmask:") #These two methods are reversed when rendering
    DestNet = input("Enter the network number of the destination private network segment/host+Unmask:")
    print("The following three modules will be uniformly named: ike-proposal,profile,transform")
    KPT = input("Enter the unified name of the three modules:") #Policy name of Src
    IkePassword = input("input IKE Password for:")
    LocalPublicIp = input("Input local public network IP Address without mask:")
    RemotePublicIp = input("Input peer-to-peer public network IP Address without mask:")
    IntfportSrc = input("Input local application IPsec Port for policy(Public network exit):") #Public network interface
    NatNum = input("Input local application NAT of ACL Serial number:") #Only advanced ACL S are supported because IPsec traffic should be rejected
    SouDevice.stream(AclNumber = AclNumber,
                     SouNet = SouNet,
                     DestNet = DestNet,
                     KPT = KPT,
                     IkePassword = IkePassword,
                     LocalPublicIp = LocalPublicIp,
                     RemotePublicIp = RemotePublicIp,
                     IntfPort = IntfportSrc,
                     NatNum = NatNum).dump(outputpath.format(f"IPSEC {LocalPublicIp}.conf")) #Such as the parameters required by the jinja2 template
    print("The local end has been rendered successfully, and the end-to-end rendering will be performed IPsec Mirror rendering, render?")
    tag = input("input y/n:")
    if tag == "y":
        KPT1 = KPT+"1"  #As the policy name of Dest
        IntfportDest = input("Apply this to the input end IPsec Port for policy(Public network exit):") #Public network interface
        NatNum = input("Input to end application NAT of ACL Serial number:")
        DestDevice.stream(AclNumber = AclNumber,
                          SouNet = DestNet,
                          DestNet = SouNet,
                          KPT = KPT1,
                          IkePassword = IkePassword,
                          LocalPublicIp = RemotePublicIp,
                          RemotePublicIp = LocalPublicIp,
                          IntfPort = IntfportDest,
                          NatNum = NatNum).dump(outputpath.format(f"IPSEC {RemotePublicIp}.conf"))
        print("Local end+Both ends have been rendered")
        
def main():#Use the main function to arrange the execution process
    DesktopPath = GetDesktop() / "config"
    DesktopPath.mkdir(parents=True,exist_ok=True)
    IpsecVPnRender(str(DesktopPath)+"\\{}") 

main()  #Execute main function

4, Run the python script and enter the parameters according to the prompt (my environment uses Jupiter)


On the desktop, we can see that the IPsec configuration file has been generated; In order to distinguish, I use the file ending in conf. right click and use Notepad to open it, and use the public network address to distinguish. The public network address file name is the configuration of the end where the public network address is located.

Open the file and the configuration file has been rendered. Directly use the shortcut commonly used by our network people Ctrl + A and then Ctrl + C; Right click and paste in the configuration interface; Of course, it needs to be improved for different environments, so the configuration file is generated. In this way, some other configurations can be attached and pasted together; You can also make corresponding configuration changes for different environments.

test


The opposite IP address is 2.1; Communication is normal. IPsec setup succeeded.

Why don't I use yaml to write configuration modules

Because the amount of code is too small and there are few modules, the efficiency of using yaml to write configuration files is still relatively low, especially for those who are not familiar with yaml, they also need to learn yaml syntax first; the loss outweighs the gain; For this interactive environment, running scripts can be used; Very convenient.

Keywords: Python ipsec

Added by ShaileshD on Tue, 25 Jan 2022 18:07:55 +0200