目录
前言
文档下载
开启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()
三个查看点:
- 查看私网地址池
- 查看源地址
- 查看nat映射规则
查看私网地址池
- 查看全部:GET https://{HOST}/restconf/data/huawei-address-set:address-set
- 查看具体:https://{HOST}/restconf/data/huawei-address-set:address-set/addr-object={vsys},{name}GET 查看具体:https://{HOST}/restconf/data/huawei-address-set:address-set/addr-object={vsys},{name}
一个地址组的格式形如下,<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段)
代码流程思路:
- 获取对应名<name>得xml结构体
- 通过结构体获取ip列表
- 将新的ip加入到ip列表
- 重构结构体并上报
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有啥区别?分别用于什么场景?-腾讯云开发者社区-腾讯云