自学内容网 自学内容网

华为USG系列防火墙 RESTCONF NAT配置 Python实现

目录

前言

文档下载 

开启RESTCONF接口

Python 实现SNAT增删改查

查看nat映射列表

查看私网地址池

查看源地址池(公网)

查看nat映射规则

创建nat映射规则

创建私网地址池

创建源地址池

创建nat映射规则

修改NAT映射规则

删除NAT映射规则

补充


前言

一般的NAT规则要在Web界面或console控制台配置,都是手动的操作,想实现自动化或接入业务,可通过华为提供的RESTCONF接口实现。本文章适合有NAT基础的人群观看,主要讲解SNAT的自动化接口配置,补充部分也有一些NAT技术的资料参考。

文档下载 

先找到对应型号的RESTCONF文档,下面是官方链接。

华为 USG12000, USG9500, USG6000F, USG6000F-S, USG6000E, USG6000E-S, USG6000系列防火墙 | 配置手册、产品文档、PDF - 华为

开启RESTCONF接口

登录Web界面进行配置

Python 实现SNAT增删改查

创建一条NAT规则,需要配置源转换地址池(公网段),创建私网地址池(内网段),创建nat映射规则。代码上没有太多可讲的,注意参数的配置即可。特别删改操作,建议做鉴权功能,防止滥用,下面直接贴代码。

查看nat映射列表

代码只贴一份,替换下url即可,注意请求方式为GET

import ssl  
import requests  
from requests.adapters import HTTPAdapter, PoolManager  
from requests.auth import HTTPBasicAuth  
from urllib3.exceptions import InsecureRequestWarning  
from urllib3 import disable_warnings  

HOST = "xxx" #填自己的ip
USER = "xxx" 
PASSWD = "xxx"
vsys = "public"
name = "xxxx"

class MyAdapter(HTTPAdapter):  
    def init_poolmanager(self, connections, maxsize, block=False, **pool_kwargs):  
        self.poolmanager = PoolManager(num_pools=connections,  
                                       maxsize=maxsize,  
                                       block=block,  
                                       ssl_version=ssl.PROTOCOL_TLSv1_2)


url = f'https://{HOST}/restconf/data/huawei-address-set:address-set/addr-object={vsys},{name}'  #查看私网地址
url = f'https://{HOST}/restconf/data/huawei-nat-address-group:nat-address-group/nat-address-group={name},{vsys}'  #查看源地址池
url = f'https://{HOST}/restconf/data/huawei-nat-policy:nat-policy/vsys={vsys}/rule={rule}'  #查看nat映射规则


header = {  
    'Host': HOST,  
    'Accept': '*/*',
}  

disable_warnings(InsecureRequestWarning) 

basic = HTTPBasicAuth(USER, PASSWD)  
s = requests.Session()  
s.mount('https://', MyAdapter())  

r = s.get(url=url, headers=header, auth=basic, verify=False)  

print(r.request.headers)  
print(r.status_code)  
print(r.text)  
s.close()

三个查看点:

  1. 查看私网地址池
  2. 查看源地址
  3. 查看nat映射规则

查看私网地址池

一个地址组的格式形如下,<vsys>表示地址组名,<name>地址名,<elements>元素集,<elem-id>元素id,<address-ipv4>元素地址

<address-set>

<addr-object>

<vsys>public</vsys>

<name>xxx</name>

<elements>

<elem-id>0</elem-id>

<address-ipv4>10.10.10.1/24</address-ipv4>

</elements>

<elements><elem-id>1</elem-id>

<address-ipv4>10.10.10.2/24</address-ipv4>

</elements>

</addr-object>

</address-set>

查看源地址池(公网)

url

  • 查看全部 GET https://{HOST}/restconf/data/huawei-nat-address-group:nat-address-group/nat-address-group
  • 查看某个 GET https://{HOST}/restconf/data/huawei-nat-address-group:nat-address-group/nat-address-group={name},{vsys}

一个地址组的格式形如下,

<vsys>表示地址组名,<name>地址名,

<mode>表示地址类型(如全锥型、对称型、端口受限锥形、地址受限锥形),

<address-zone>地址区域(默认全局),

<no-reverse>配置不带no-reverse参数的nat server后,当公网用户访问服务器时,设备能将服务器的公网地址转换成私网地址;同时,当服务器主动访问公网时,设备也能将服务器的私网地址转换成公网地址配置参数no-reverse后,设备只将公网地址转换成私网地址,不能将私网地址转换成公网地址。当内部服务器主动访问外部网络时需要执行outbound的nat策略

<elements>元素集,<id>元素id,

<start-ip>起始地址,<end-ip>结束地址(注意可以是多个或一个,如开始210.50.46.123,结束210.50.46.123,则只有一个地址,否则可以连续,如210.50.46.123,210.50.46.250,地址从x.123-x.250

<nat-address-group>
<name>nat_ippool</name>
<vsys>public</vsys>
<mode>full-cone</mode>
<mode-pattern>
<address-zone>global</address-zone>
<no-reverse>false</no-reverse>
</mode-pattern>
<section>
<id>0</id>
<start-ip>210.50.46.123</start-ip>
<end-ip>210.50.46.125</end-ip>
</section>
</nat-address-group>

查看nat映射规则

url

  • 查看所有 GET https://{HOST}/restconf/data/huawei-nat-policy:nat-policy
  • 查看单个 GET  https://{HOST}/restconf/data/huawei-nat-policy:nat-policy/vsys={vsys}/rule={rule}

<vsys><name> 表示地址组名,<name>地址名

<rule><name>规则名

<source-zone> 源地址区域 trust(信任区,一般为内网地址或端口) untrust(不守信区,一般为外网地址或端口)

<address-set> 对应私网地址池的<name>名

<enable> 是否打开 

<nat-address-group> 对应源地址池的<name>

<vsys>

<name>public</name>

<rule>

<name>xxx</name>

<source-zone>trust</source-zone>

<destination-zone>untrust</destination-zone>

<source-ip>

<address-set>10.10.10.1/24</address-set>

</source-ip>

<enable>true</enable>

<action>nat-address-group</action>

<nat-address-group>210.50.46.123</nat-address-group>

<nat-type>nat</nat-type>

</rule>

</vsys>

创建nat映射规则

注意:HTTP方法可以是POST、PUT或者PATCH,依次对应NETCONF协议中的create动作、replace动作和merge动作。如果是PUT方法,服务端如果已存在对应配置则会被直接覆盖为此次下发的内容。

建议不要使用PUT方法,可能会替换为目前已有规则,如果是POST,则会报重名的错误,避免了替换规则。

注:以下案例中得body内没有<name>,地址池或规则名称都嵌到URL请求链接中了

创建私网地址池

POST https://{HOST}/restconf/data/huawei-address-set:address-set/addr-object={vsys},{name}

url请求中申明了地址池name,所以body中就无需添加了,基本格式如下,希望添加得地址放到<elem>中即可(默认id从0起)

<addr-object>
<elements>
<elem-id>0</elem-id>
<address-ipv4>10.10.10.1/24</address-ipv4>
</elements>
<elements>
<elem-id>1</elem-id>
<address-ipv4>10.10.10.2/24</address-ipv4>
</elements>
<elements>
</addr-object>

import ssl  
import requests  
from requests.adapters import HTTPAdapter, PoolManager  
from requests.auth import HTTPBasicAuth  
from urllib3.exceptions import InsecureRequestWarning  
from urllib3 import disable_warnings  

HOST = "xxx"
USER = "xxx" 
PASSWD = "xxx"
vsys = "xxx"
name = "xxx"


class MyAdapter(HTTPAdapter):  
    def init_poolmanager(self, connections, maxsize, block=False, **pool_kwargs):  
        self.poolmanager = PoolManager(num_pools=connections,  
                                       maxsize=maxsize,  
                                       block=block,  
                                       ssl_version=ssl.PROTOCOL_TLSv1_2)


url = f'https://{HOST}/restconf/data/huawei-address-set:address-set/addr-object={vsys },{name }'
header = {  
    'Host': HOST,  
    'Accept': '*/*',
}  

body = """
<addr-object>
<elements>
<elem-id>0</elem-id>
<address-ipv4>10.10.10.1/24</address-ipv4>
</elements>
<elements>
<elem-id>1</elem-id>
<address-ipv4>10.10.10.2/24</address-ipv4>
</elements>
<elements>
</addr-object>
"""

disable_warnings(InsecureRequestWarning) 

basic = HTTPBasicAuth(USER, PASSWD)  
s = requests.Session()  
s.mount('https://', MyAdapter())  

r = s.post(url=url, headers=header, data=body, auth=basic, verify=False)  

print(r.request.headers)  
print(r.status_code)  
print(r.text)  
s.close()

调用成功返回201

创建源地址池

POST https://{HOST}/restconf/data/huawei-nat-address-group:nat-address-group/nat-address-group={name},{vsys}}

一下默认开放了全部端口,地址范围是 210.50.46.123 -210.50.46.125 ,nat类型是全锥(三元组)

<nat-address-group>
<mode>full-cone</mode>
<mode-pattern>
<address-zone>global</address-zone>
<no-reverse>false</no-reverse>
</mode-pattern>
<section>
<id>0</id>
<start-ip>210.50.46.123</start-ip>
<end-ip>210.50.46.125</end-ip>
</section>
</nat-address-group>

import ssl  
import requests  
from requests.adapters import HTTPAdapter, PoolManager  
from requests.auth import HTTPBasicAuth  
from urllib3.exceptions import InsecureRequestWarning  
from urllib3 import disable_warnings  

HOST = "xxx"
USER = "xxx" 
PASSWD = "xxx"
vsys = "xxx"
name = "xxx"

class MyAdapter(HTTPAdapter):  
    def init_poolmanager(self, connections, maxsize, block=False, **pool_kwargs):  
        self.poolmanager = PoolManager(num_pools=connections,  
                                       maxsize=maxsize,  
                                       block=block,  
                                       ssl_version=ssl.PROTOCOL_TLSv1_2)


url = f'https://{HOST}/restconf/data/huawei-nat-address-group:nat-address-group/nat-address-group={name},{vsys}'
header = {  
    'Host': HOST,  
    'Accept': '*/*',
}  

body = """
<nat-address-group>
<mode>full-cone</mode>
<mode-pattern>
<address-zone>global</address-zone>
<no-reverse>false</no-reverse>
</mode-pattern>
<section>
<id>0</id>
<start-ip>210.50.46.123</start-ip>
<end-ip>210.50.46.125</end-ip>
</section>
</nat-address-group>
"""

disable_warnings(InsecureRequestWarning) 

basic = HTTPBasicAuth(USER, PASSWD)  
s = requests.Session()  
s.mount('https://', MyAdapter())  

r = s.post(url=url, headers=header, data=body, auth=basic, verify=False)  

print(r.request.headers)  
print(r.status_code)  
print(r.text)  
s.close()

创建nat映射规则

POST https://{HOST}/restconf/data/huawei-nat-policy:nat-policy/vsys={vsys}/rule={name}

<address-set></address-set> 写私有地址池得<name>

<nat-address-group></nat-address-group>  写源地址池(公网)得<name>

<rule>
<source-zone>trust</source-zone>
<destination-zone>untrust</destination-zone>
<source-ip>
<address-set>private_add</address-set>
</source-ip>
<enable>true</enable>
<action>nat-address-group</action>
<nat-address-group>public_add</nat-address-group>
<nat-type>nat</nat-type>
</rule>

import ssl  
import requests  
from requests.adapters import HTTPAdapter, PoolManager  
from requests.auth import HTTPBasicAuth  
from urllib3.exceptions import InsecureRequestWarning  
from urllib3 import disable_warnings  

HOST = "xxx"
USER = "xxx" 
PASSWD = "xxx"
vsys = "xxx"
name = "xxx"

class MyAdapter(HTTPAdapter):  
    def init_poolmanager(self, connections, maxsize, block=False, **pool_kwargs):  
        self.poolmanager = PoolManager(num_pools=connections,  
                                       maxsize=maxsize,  
                                       block=block,  
                                       ssl_version=ssl.PROTOCOL_TLSv1_2)


url = f'https://{HOST}/restconf/data/huawei-nat-policy:nat-policy/vsys={vsys}/rule={name}'
header = {  
    'Host': HOST,  
    'Accept': '*/*',
}  

body = """
<rule>
<source-zone>trust</source-zone>
<destination-zone>untrust</destination-zone>
<source-ip>
<address-set>fortest</address-set>
</source-ip>
<enable>true</enable>
<action>nat-address-group</action>
<nat-address-group>justfortest</nat-address-group>
<nat-type>nat</nat-type>
</rule>
"""

disable_warnings(InsecureRequestWarning) 

basic = HTTPBasicAuth(USER, PASSWD)  
s = requests.Session()  
s.mount('https://', MyAdapter())  

r = s.post(url=url, headers=header, data=body, auth=basic, verify=False)  

print(r.request.headers)  
print(r.status_code)  
print(r.text)  
s.close()

修改NAT映射规则

修改(如在原地址池得基础上加一个地址段),大致思路就是获取老的结构体,加入新得ip段并构造一个新的结构体,再使用PUT请求替换。华为原文档提供得方法是使用PUT请求覆盖,但覆盖前先得获取原本结构得信息,不建议用原文档得方式。

修改一条NAT规则,主要就是对私网地址池做修改,或对源地址池(公网)做修改,比如增加或删除一个地址段,参考代码如下(增加一个ip段)

代码流程思路:

  1. 获取对应名<name>得xml结构体
  2. 通过结构体获取ip列表
  3. 将新的ip加入到ip列表
  4. 重构结构体并上报
def build_new_request_body(ip_addresses,newip):
    """
    模板
    <addr-object>
    <elements>
    <elem-id>0</elem-id>
    <address-ipv4>10.10.10.1/32</address-ipv4>
    </elements>
    </addr-object>
    """
    #构造老的地址
    fragments = ['<addr-object>']
    num=0
    for count, ip in enumerate(ip_addresses):
        elements = f"<elements><elem-id>{count}</elem-id><address-ipv4>{ip}</address-ipv4></elements>"
        fragments.append(elements)
        num+=1
    
    #加入新的instanceIP
    new_instanceIP = f"<elements><elem-id>{num}</elem-id><address-ipv4>{newip}</address-ipv4></elements>"
    fragments.append(new_instanceIP)

    #拼接
    fragments.append("</addr-object>")
    addr_object = ''.join(fragments)

    return addr_object


#构建新的请求体,获取现有地址列表
def get_request_body(existing_xml):
    try:        
        # 解析XML
        root = etree.fromstring(existing_xml)
        
        # 定义命名空间
        ns = {
            'ns0': 'urn:ietf:params:xml:ns:netconf:base:1.0',
            'ns1': 'urn:huawei:params:xml:ns:yang:huawei-address-set'
        }
        
        # 查找所有<address-ipv4>元素
        ipv4_elements = root.findall('.//ns1:address-ipv4', ns) + root.findall('.//address-ipv4')
        
        # 提取IP地址
        ip_addresses = [elem.text for elem in ipv4_elements]
        
        logger.info("提取的IP地址:", ip_addresses)
        return ip_addresses
    
    except etree.XMLSyntaxError as e:
        logger.error(f"XML解析错误: {e}")
    except Exception as e:
        logger.error(f"发生错误: {e}")
    
    return None

def get_existing_addr_object(addName):
    vsys = "public"
    addr_object_url = f"{base_url}/data/huawei-address-set:address-set/addr-object={vsys},{addName}"
    response = requests.get(addr_object_url, headers=headers, auth=HTTPBasicAuth(username, password), verify=False)
    logger.info(response)
    if response.status_code == 200:
        return response.text
    else:
        raise Exception(f"Failed to get address object: {response.status_code}, {response.text}")

def addInstanceIP(addName,newip):
    # 获取现有XML数据
    existing_xml = get_existing_addr_object(addName)

    ip_addresses = get_request_body(existing_xml)

    # 构造新的add结构
    addr_object = build_new_request_body(ip_addresses,newip)

    # 请求防火墙
    status_code = send_restconf_request(addName,addr_object)

    return status_code



addInstanceIP("fortest","10.10.10.1/24")

删除NAT映射规则

删除规则比较简单,调用DELETE进行删除即可,分为全部和单个

  • 删除所有 DELETE /restconf/data/huawei-nat-address-group:nat-address-group
  • 删除某个 DELETE /restconf/data/huawei-nat-address-group:nat-address-group/nat-address-group=test,public

修改和删除得逻辑相似,都不建议直接调用原生接口,因为大多数得时候并不希望删除整个规则,而是希望删除某个具体得地址段,可以参考修改NAT映射规则,加一个地址判断逻辑,在构造新的结构体时不加入新段即可。

def build_new_request_body(ip_addresses,deleteIP):
    """
    模板
    <addr-object>
    <elements>
    <elem-id>0</elem-id>
    <address-ipv4>10.10.10.1/32</address-ipv4>
    </elements>
    </addr-object>
    """
    #构造老的地址
    fragments = ['<addr-object>']
    num=0
    for count, ip in enumerate(ip_addresses):
        if not deleteIP:
            elements = f"<elements><elem-id>{count}</elem-id><address-ipv4>{ip}</address-ipv4></elements>"
            fragments.append(elements)
            num+=1
    
    #拼接
    fragments.append("</addr-object>")
    addr_object = ''.join(fragments)

    return addr_object

补充

网络技术:NAT 网络地址转换 - 乌漆WhiteMoon - 博客园

NAT(网络地址转换) - eiSouthBoy - 博客园

42张图详解 NAT : 换个马甲就能上网-腾讯云开发者社区-腾讯云

网络地址转换:DNAT和SNAT有啥区别?分别用于什么场景?-腾讯云开发者社区-腾讯云


原文地址:https://blog.csdn.net/ck784101777/article/details/144244622

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!