小白教小白-当ipv6地址发生变更后,自动更新openwrt软路由的防火墙规则
起因:我是移动宽带,只能获取ipv6公网,软路由使用istoreos系统,是根据openwrt修改而来的。Homeassistant是通过docker方式安装在unraid系统中的。因为需要公网访问hass,又不希望通过关闭软路由的防火墙来实现访问hass,因为这样会公开unraid系统的全部端口。所以只在istoreos系统中的网络-防火墙-通信规则,开放了unraid的8123端口。但是因为软路由重启等行为,unraid获取的ipv6地址会放生变化,又不希望每次手动更改,所以使用了node-red来进行自动化操作。 备注:ipv6地址的获取,可以通过openwrt来设置,这里也不说了,我也是小白,自己也不懂,说也说不来,而且获取分为有状态、无状态什么的,看网上,有状态好像可以固定ipv6后面的地址段,这样可以通过防火墙规则,匹配固定的地址段来达到开放端口的目的,但我实在看不懂,也不会设置,就想着干脆点,你的ip地址变了,那我防火墙规则自动变。所以有了下文。 注意,按下面的操作,你需要 1、除了你的路由器,你的主机必须获取ipv6,且外网可以正常通过ipv6访问到你的主机。 2、如果通过docker方式安装的node-red,那么网路模式必须是host,否则docker安装的node-red无法获取宿主机的ipv6地址。 3、如果你要公开的端口是docker的应用,那么同上,必须是host,理由同上。 比如我是在unraid系统安装的hass,你要通过外网ipv6访问到hass,那么首先,在路由器关闭网络防火墙的前提下,必须外网能通过ipv6访问到unraid系统node-red的1880端口和hass的8123端口。否则一切免谈。 先放流程图: 流程解释: 就是每10分钟运行一次流程,让node-red来获取一下宿主机的公网ipv6地址,然后对比原先的ipv6地址,如果一致,则不操作,如果不一致,就通过ssh的方式,对openwrt使用sed命令和/etc/init.d/firewall restart这个防火墙重启命令来,达到修改防火墙通信规则的目的。 1、新建一个时间戳的节点,让他每10分钟运行一下; 2、新建一个exec节点,让他运行curl -s ipv6.icanhazip.com命令,获取宿主机的ipv6地址; 3、新建一个函数节点,因为通过上面exec节点获取的ipv6地址,最后有一个回车符号,需要把这个回车符号去除掉。 4、新建一个read file节点,读取原来储存在名为“本机ipv6地址.txt”文件中的ipv6地址。注意,这个节点中,文件名称和路径要视你的实际情况来写。(你可以先新建一个文件,然后手动把宿主机的ipv6地址写进去,比如2409:aaaa:bbbb:cccc:1111:2222:3333:4444)。 5、实际上你也可以不要这个文件,可以通过设置node-red的全局变量(可持久化存储变量)来达到这个目的,详见https://bbs.hassbian.com/thread-5842-1-1.html但是我还是偷懒了。反正也无所谓。但node-red普通的全局变量不合适,因为万一node-red重启了,变量值就没有了。 6、新建一个函数节点,对新获得的ipv6地址和原来储存在文件中ipv6地址进行对比,并根据对比结果是否相同来触发后续流程。在本例中,我设置了4个输出,其中如果对比结果2个ip地址一致,就有第一个输出,结果不一致,就有后面第2、3、4个输出。其中第一个输出我实际上后面没有流程了;第二个输出是将新的ipv6地址保存到“本机ipv6地址.txt”文件中,用于后续的再比较;第三个输出就是通过ssh节点来运行一个修改路由器防火墙文件的命令;第4个输出,后面接了一个延迟节点,延迟5秒后运行一个重启路由器防火墙的命令。这个函数节点,我再内容里添加了备注,具体可以看一下节点函数。 7、新建一个ssh节点,这个节点你需要自行安装。可以在node-red的节点管理中,搜索并安装“node-red-contrib-ssh-v3”这个节点。安装后,节点的设置,按照你软路由的ip地址、用户名、密码等填写。 注意:这个流程,sed命令和/etc/init.d/firewall restart这个防火墙重启命令,可能要看你的软路由的情况,需要你自行查询测试。 放上一个istoreos的防火墙配置图片 名称:随意填写 目标地址:你主机的ipv6地址,比如我的unraid系统主机的ipv6地址 目标端口:你要公开的端口,比如hass的端口。 其他的都不需要更改。 保存后,不要忘了在通信规则的主界面,点击“保存并应用”按键。 最后放上流程和json文件。
[
{
"id": "44fdaba6710cfcac",
"type": "comment",
"z": "cbe57fd6f44c0398",
"name": "替换文件中的内容可以使用以下命令",
"info": "sed -i 's/原内容/现内容/g' /etc/config/firewall\n",
"x": 920,
"y": 180,
"wires": []
},
{
"id": "a8168fe07ed0533a",
"type": "comment",
"z": "cbe57fd6f44c0398",
"name": "每10分钟查询宿主机的公网ip地址,如果与原来的ip地址不一致,则更改openwrt的防火墙规则",
"info": "注意,要查询到宿主机的ipv6,必须在node-red的docker安装时,使用host网路。",
"x": 420,
"y": 180,
"wires": []
},
{
"id": "d97fad1588a88f22",
"type": "exec",
"z": "cbe57fd6f44c0398",
"command": "curl -s ipv6.icanhazip.com",
"addpay": "",
"append": "",
"useSpawn": "false",
"timer": "",
"winHide": false,
"oldrc": false,
"name": "",
"x": 370,
"y": 260,
"wires": [
[
"14707bd83ffd63c5"
],
[],
[]
]
},
{
"id": "84f29c9208408ba1",
"type": "inject",
"z": "cbe57fd6f44c0398",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "600",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 160,
"y": 260,
"wires": [
[
"d97fad1588a88f22"
]
]
},
{
"id": "8c1e7ebc76b47860",
"type": "file in",
"z": "cbe57fd6f44c0398",
"name": "",
"filename": "/mnt/cache/appdata/logs/本机ipv6地址.txt",
"filenameType": "str",
"format": "utf8",
"chunk": false,
"sendError": false,
"encoding": "none",
"allProps": false,
"x": 860,
"y": 260,
"wires": [
[
"e5e1d84306606703"
]
]
},
{
"id": "e5e1d84306606703",
"type": "function",
"z": "cbe57fd6f44c0398",
"name": "判断ip地址是否一致",
"func": "var old_ip = msg.payload;\n//新建一个变量,变量名称是old_ip,他的值是前面传下来的msg.payload的值\nvar new_ip = msg.new_ip;\n//新建一个变量,变量名称是new_ip,他的值是前面传下来的msg.new_ip的值\nvar msg1 = {\n "payload": old_ip,\n}\n//这实际上就是重新赋予msg.payload新的值,他的值就是变量old_ip的值\nvar msg2 = {\n "payload": new_ip,\n}\n//这实际上就是重新赋予msg.payload新的值,他的值就是变量new_ip的值\nvar msg3 = {\n "payload": "sed -i 's/" + old_ip + "/" + new_ip + "/g' /etc/config/firewall",\n}\n//这实际上就是重新赋予msg.payload新的值,他的值就是sed -i 's/" + old_ip + "/" + new_ip + "/g' /etc/config/firewall。\n//如果你原来的ip是192.168.0.1,新的ip是192.1.1.1,那么这个msg.payloa的值就是sed -i 's/192.168.0.1/192.1.1.1/g' /etc/config/firewall\n//这实际上是一个sed命令,用来搜索并替换文件内容,他搜索了/etc/config/firewall文件,并将文件中192.168.0.1替换为192.1.1.1\n//这个命令的用法可以自行百度搜索一下用法。配后后面ssh的节点,就可用ssh的方式登录路由器并修改防火墙规则的文件。\n\nvar msg4 = {\n "payload": '/etc/init.d/firewall restart',\n}\n//这实际上就是重新赋予msg.payload新的值,他的值就是/etc/init.d/firewall restart\n//这是一个重启openwrt路由防火墙的命令,因为openwrt系统的不一样,可能命令也不一样,请自修搜索并测试\n\nif (new_ip == old_ip) {\n return [msg1, null, null, null];\n}\nelse {\n return [null, msg2,msg3,msg4];\n}\n//用if进行判断,如果新的ip地址和旧的ip地址一样,就在第一个输出中,输出msg1,而后面3个输出中不予输出任何内容\n//如果新的ip地址和旧的ip地址不一样,那么第一个输出中不予输出任何内容,后面3个输出中,分别输出不同内容。\n//这个就可以进行判断ip地址是否更新,并触发后续不同的流程。\n",
"outputs": 4,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 210,
"y": 400,
"wires": [
[
"13b9aa9acd9c766e"
],
[
"3732bd003b93dbd1"
],
[
"9d4685449276e70c"
],
[
"a9ecd801ae2d9de2"
]
]
},
{
"id": "13b9aa9acd9c766e",
"type": "debug",
"z": "cbe57fd6f44c0398",
"name": "输出ip信息",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 430,
"y": 360,
"wires": []
},
{
"id": "14707bd83ffd63c5",
"type": "function",
"z": "cbe57fd6f44c0398",
"name": "去除回车符",
"func": "msg.new_ip = msg.payload.replace(/\\n/g, '');\nreturn msg;\n",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 590,
"y": 260,
"wires": [
[
"8c1e7ebc76b47860"
]
]
},
{
"id": "a32c6a90cea1c25c",
"type": "ssh-v3",
"z": "cbe57fd6f44c0398",
"name": "istoreos",
"conf": "a0d90d0d10c5af01",
"debug": false,
"x": 260,
"y": 600,
"wires": [
[
"bfd64169598baa27"
]
]
},
{
"id": "bfd64169598baa27",
"type": "debug",
"z": "cbe57fd6f44c0398",
"name": "输出ip信息",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 550,
"y": 600,
"wires": []
},
{
"id": "3732bd003b93dbd1",
"type": "file",
"z": "cbe57fd6f44c0398",
"name": "",
"filename": "/mnt/cache/appdata/logs/本机ipv6地址.txt",
"filenameType": "str",
"appendNewline": false,
"createDir": true,
"overwriteFile": "true",
"encoding": "none",
"x": 520,
"y": 400,
"wires": [
[
"bd019ed074eefa95"
]
]
},
{
"id": "bd019ed074eefa95",
"type": "debug",
"z": "cbe57fd6f44c0398",
"name": "输出ip信息",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 790,
"y": 400,
"wires": []
},
{
"id": "410692bda45b51c6",
"type": "link in",
"z": "cbe57fd6f44c0398",
"name": "ssh istoreos",
"links": [
"9d4685449276e70c",
"fb5aa5e12f07e062",
"4fd7d10edec1121f"
],
"x": 125,
"y": 600,
"wires": [
[
"a32c6a90cea1c25c"
]
]
},
{
"id": "9d4685449276e70c",
"type": "link out",
"z": "cbe57fd6f44c0398",
"name": "call ssh istoreos",
"mode": "link",
"links": [
"410692bda45b51c6"
],
"x": 405,
"y": 440,
"wires": []
},
{
"id": "a9ecd801ae2d9de2",
"type": "delay",
"z": "cbe57fd6f44c0398",
"name": "",
"pauseType": "delay",
"timeout": "5",
"timeoutUnits": "seconds",
"rate": "1",
"nbRateUnits": "1",
"rateUnits": "second",
"randomFirst": "1",
"randomLast": "5",
"randomUnits": "seconds",
"drop": false,
"allowrate": false,
"outputs": 1,
"x": 420,
"y": 480,
"wires": [
[
"4fd7d10edec1121f"
]
]
},
{
"id": "4fd7d10edec1121f",
"type": "link out",
"z": "cbe57fd6f44c0398",
"name": "call ssh istoreos",
"mode": "link",
"links": [
"410692bda45b51c6"
],
"x": 525,
"y": 480,
"wires": []
},
{
"id": "a0d90d0d10c5af01",
"type": "ssh-conf",
"ssh": "",
"name": "isoreos",
"userlabel": "isoreos"
}
]
|