Harmony os 网络防火墙实战:用 @ohos.net.netFirewall 给应用加一道"网闸"
这段时间一直在翻 HarmonyOS 的 Network Kit 文档,看到一个以前没怎么注意过的小模块:@ohos.net.netFirewall。看完文档的第一反应是------这玩意儿太适合做"桌面级防火墙设置面板"了 ,而且官方已经把增删改查都封装好了,我们只要写 UI 就行。developer.huawei.com
这篇就当是我自己的学习记录,也顺手给以后要接这个能力的同学一点参考:怎么用 ArkTS 操作系统级网络防火墙。
一、这个模块到底能干嘛?
先把官方提供的能力翻成"人话":
- 查 / 改防火墙总开关和默认策略
getNetFirewallPolicy(userId)setNetFirewallPolicy(userId, policy)- 能看到当前防火墙是不是开着、默认入站是放行还是拒绝、默认出站是放行还是拒绝。developer.huawei.com+1
- 对规则做增删改查
- 新增:
addNetFirewallRule(rule): Promise<number> - 修改:
updateNetFirewallRule(rule) - 删除:
removeNetFirewallRule(userId, ruleId) - 查询单条:
getNetFirewallRule(userId, ruleId) - 分页查询:
getNetFirewallRules(userId, requestParam)(带排序、分页)developer.huawei.com
- 新增:
- 支持三类规则
- IP 规则:按 IP / 端口 / 协议控制流量
- 域名规则:按域名 / 通配符
*.example.com控制 - DNS 规则:按 DNS 服务器维度控制
再加几个现实中的限制:
- 支持设备 :PC / 2in1 / TV(手机上你是看不到这个能力的)。developer.huawei.com
- 系统能力 :
SystemCapability.Communication.NetManager.NetFirewall - 权限 :
- 查询类:
ohos.permission.GET_NET_FIREWALL - 管理类(增删改):
ohos.permission.MANAGE_NET_FIREWALL
- 查询类:
从能力和权限就能看出来,这玩意儿偏系统管理场景,大概率是给系统应用、桌面管理工具这类用的,普通三方 App 就别太乐观了。
二、先把"总闸"搞清楚:NetFirewallPolicy
整个防火墙的开关和默认策略由 NetFirewallPolicy 控制,结构很简单:developer.huawei.com
isOpen: booleantrue:防火墙整体开启false:防火墙整体关闭
inAction: FirewallRuleActionoutAction: FirewallRuleAction- 枚举
FirewallRuleAction:RULE_ALLOW = 0:允许RULE_DENY = 1:拒绝
- 枚举
典型用途:
- 家用环境:
- 入站:默认拒绝(
inAction = RULE_DENY) - 出站:默认允许(
outAction = RULE_ALLOW)
- 入站:默认拒绝(
- 超狠的管控环境:
- 入站 / 出站都默认拒绝,然后只放行白名单应用 / IP 段
查询 + 设置防火墙策略的示例
import { netFirewall } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';
const USER_ID = 100; // 示例多用户 ID,根据系统实际为准
async function toggleFirewall() {
try {
const policy = await netFirewall.getNetFirewallPolicy(USER_ID);
console.info('当前策略:', JSON.stringify(policy));
// 如果没开,就顺手打开,顺带设个"入站禁、出站放"的策略
if (!policy.isOpen) {
const newPolicy: netFirewall.NetFirewallPolicy = {
isOpen: true,
inAction: netFirewall.FirewallRuleAction.RULE_DENY,
outAction: netFirewall.FirewallRuleAction.RULE_ALLOW
};
await netFirewall.setNetFirewallPolicy(USER_ID, newPolicy);
console.info('防火墙已开启并更新策略');
}
} catch (err) {
const be = err as BusinessError;
console.error(`操作防火墙策略失败:code=${be.code}, msg=${be.message}`);
}
}
三、真正好玩的在这里:NetFirewallRule 长什么样?
所有具体"怎么拦、拦谁"的逻辑都落在 NetFirewallRule 上。这个结构比较长,我按块拆一下。developer.huawei.com
1. 通用字段(所有类型规则都有)
userId: number
多用户场景下属于哪个用户,必须是系统存在的 userId,否则直接 29400000 把你弹回去。name: string(必填,≤128 字符)
规则名,建议写人能看懂的那种,比如:"Block WeChat TCP 443"。description?: string(≤256 字符)
扩展说明,给以后排查问题的自己留点线索。direction: NetFirewallRuleDirectionRULE_IN = 1:入站RULE_OUT = 2:出站
action: FirewallRuleActionRULE_ALLOW/RULE_DENY
type: NetFirewallRuleTypeRULE_IP = 1:按 IP / 端口 / 协议控制RULE_DOMAIN = 2:按域名控制RULE_DNS = 3:按 DNS 服务器控制
isEnabled: booleantrue:规则生效false:规则存在但不生效(相当于 UI 上的开关)
id?: number
规则 ID,由系统在 add 时生成,后面 update / delete 都要用它。appUid?: number
针对某个应用 / 服务的 UID 做限制,用这个字段就能玩出"只拦某个 App"的效果。
2. IP 规则专属字段:localIps / remoteIps / ports / protocol
当 type = RULE_IP 时,这几个字段才有意义:developer.huawei.com+1
localIps: NetFirewallIpParams[]remoteIps: NetFirewallIpParams[]protocol?: number6:TCP17:UDP
localPorts: NetFirewallPortParams[]remotePorts: NetFirewallPortParams[]
NetFirewallIpParams 结构:
type: number(必填)1:单个 IP 或子网(需要address + mask)2:IP 段(需要startIp + endIp)
family?: number1:IPv42:IPv6(文档里写了,实际当前主要还是 IPv4)
address?: stringmask?: numberstartIp?: stringendIp?: string
数量限制 (否则会报 29400002 / 29400003):developer.huawei.com+1
localIps/remoteIps:最多 10 个localPorts/remotePorts:最多 10 个
端口结构 NetFirewallPortParams:
startPort: numberendPort: number
如果只要一个端口,就让startPort = endPort。
3. 域名规则字段:domains
当 type = RULE_DOMAIN 时才用得上:developer.huawei.com+1
domains: NetFirewallDomainParams[]
NetFirewallDomainParams:
isWildcard: booleantrue:带通配符,比如*.example.cnfalse:完整域名,比如www.example.cn
domain: string
同样有数量限制,不然会报:
29400004/29400005:域名规则 / 域名数量超限
4. DNS 规则字段:dns
当 type = RULE_DNS 时有效:developer.huawei.com+1
dns: NetFirewallDnsParamsprimaryDns: stringstandbyDns?: string
如果你加了两个一模一样的 DNS 规则,会被 29400007 告诉你"重复了"。
四、实战 1:给某个应用拦掉一段出站 IP 段
假设场景:
我想让用户 UID 为
20001的某个应用,禁止它访问 20.10.1.0/24 的 TCP 443 端口(比如禁它访问某个外网服务)。
可以写一条出站 IP 规则:
import { netFirewall } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';
const USER_ID = 100;
async function addIpDenyRule() {
const rule: netFirewall.NetFirewallRule = {
userId: USER_ID,
name: 'BlockApp20001_20.10.1.0_443',
description: '拦截 appUid=20001 访问 20.10.1.0/24:443',
direction: netFirewall.NetFirewallRuleDirection.RULE_OUT,
action: netFirewall.FirewallRuleAction.RULE_DENY,
type: netFirewall.NetFirewallRuleType.RULE_IP,
isEnabled: true,
appUid: 20001,
localIps: [
// 本地可以不填,表示本机所有地址
],
remoteIps: [
{
family: 1, // IPv4
type: 1, // 单 IP/子网
address: '20.10.1.0',
mask: 24
}
],
protocol: 6, // TCP
localPorts: [
// 本地端口不限
],
remotePorts: [
{
startPort: 443,
endPort: 443
}
]
};
try {
const ruleId = await netFirewall.addNetFirewallRule(rule);
console.info('新增防火墙规则成功,ruleId = ', ruleId);
} catch (err) {
const be = err as BusinessError;
console.error(`新增防火墙规则失败:code=${be.code}, msg=${be.message}`);
}
}
如果你把 IP / 端口数组写太多,遇到 29400002 / 29400003,十有八九就是你一条规则塞太满了,拆几条就好。
五、实战 2:用域名规则一键拉黑一批网站
再来一个"家长模式"的例子:
我想禁止某个用户访问
www.example.cn以及所有*.example.cn。
直接搞域名规则:
async function addDomainDenyRule() {
const rule: netFirewall.NetFirewallRule = {
userId: 100,
name: 'Block_example_domain',
description: '拦截 example.cn 全家桶',
direction: netFirewall.NetFirewallRuleDirection.RULE_OUT,
action: netFirewall.FirewallRuleAction.RULE_DENY,
type: netFirewall.NetFirewallRuleType.RULE_DOMAIN,
isEnabled: true,
appUid: 0, // 不指定具体应用,默认对用户全局生效
domains: [
{
isWildcard: false,
domain: 'www.example.cn'
},
{
isWildcard: true,
domain: '*.example.cn'
}
]
};
try {
const ruleId = await netFirewall.addNetFirewallRule(rule);
console.info('新增域名规则成功,ruleId = ', ruleId);
} catch (err) {
const be = err as BusinessError;
console.error(`新增域名规则失败:code=${be.code}, msg=${be.message}`);
}
}
如果你发现加第 N 条域名规则死活不成功,看一下返回错误是不是 29400004 / 29400005,很可能是数量超限了。developer.huawei.com+1
六、实战 3:做一个简单规则管理面板(分页 + 开关)
如果你想做一个"防火墙管理"应用,最基础的就是把所有规则列表拉出来,然后让用户能:
- 看见每条规则的名字 / 类型 / 方向 / 状态
- 点进去编辑
- 点一下按钮启用 / 禁用
- 删除
1)分页查询规则列表
getNetFirewallRules(userId, requestParam) 就是干这个的:developer.huawei.com
async function listRules(page: number, pageSize: number) {
const req: netFirewall.RequestParam = {
page,
pageSize,
orderField: netFirewall.NetFirewallOrderField.ORDER_BY_RULE_NAME,
orderType: netFirewall.NetFirewallOrderType.ORDER_ASC
};
try {
const result = await netFirewall.getNetFirewallRules(100, req);
console.info('分页结果:', JSON.stringify(result));
const { data, totalPage } = result;
// data 就是一页的规则列表,直接丢给你的 ArkUI 列表组件即可
return { rules: data, totalPage };
} catch (err) {
const be = err as BusinessError;
console.error(`获取规则列表失败:code=${be.code}, msg=${be.message}`);
return { rules: [], totalPage: 0 };
}
}
FirewallRulePage 结构:
page/pageSize/totalPagedata: NetFirewallRule[]
UI 上就很常规:分页按钮 + 列表。
2)开关"启用 / 禁用"规则
思路是:
-
先用
getNetFirewallRule(userId, ruleId)把整条规则拉出来 -
改一下
isEnabled,再updateNetFirewallRule(rule)async function toggleRuleEnabled(userId: number, ruleId: number, enabled: boolean) {
try {
const rule = await netFirewall.getNetFirewallRule(userId, ruleId);
rule.isEnabled = enabled;
await netFirewall.updateNetFirewallRule(rule);
console.info(规则 ${ruleId} 已更新 isEnabled=${enabled});
} catch (err) {
const be = err as BusinessError;
console.error(切换规则状态失败:code=${be.code}, msg=${be.message});
}
}
如果更新的时候提示 29400006,说明这条规则已经被别人删掉了,你这边的 ruleId 只是"尸体"。
3)删除规则
async function removeRule(userId: number, ruleId: number) {
try {
await netFirewall.removeNetFirewallRule(userId, ruleId);
console.info(`删除规则 ${ruleId} 成功`);
} catch (err) {
const be = err as BusinessError;
console.error(`删除规则失败:code=${be.code}, msg=${be.message}`);
}
}
七、错误码小抄(踩坑专用)
这个模块的错误码挺有"个性"的,我自己整理了一份"翻译"版:developer.huawei.com+1
- 201 Permission denied
- 权限不给面子:
- 要么你没在配置里声明对应权限
- 要么你的应用根本不在"能用这个系统能力"的白名单里
- 权限不给面子:
- 401 Parameter error / 2100001 Invalid parameter value
- 典型情况:
userId不存在page/pageSize越界mask/startIp/endIp搞错
- 典型情况:
- 2100002 Operation failed. Cannot connect to service
- 后台服务没起来 / 出问题了,这种一般只能看系统日志。
- 29400000 The specified user does not exist
- 多用户 ID 写错了,直接对着系统用户列表改。
- 29400001 防火墙规则总数超限
- 说明你太能加了,考虑把一些规则合并一下。
- 29400002 / 29400003 / 29400004 / 29400005
- 某一条规则里 IP / 端口 / 域名数量超过上限了,把一个庞然大物拆成几条小规则。
- 29400006 The specified rule does not exist
- 更新 / 删除的时候,ruleId 已经失效。
- 29400007 The dns rule is duplication
- 同样的 DNS 规则已经存在了,没必要再加一次。
八、适合拿来干什么?
从现在这套能力来看,我自己能想到几个比较顺手的用法:
- 系统级"网络安全管家"
- 做一个类似桌面系统的防火墙配置中心:
- 总开关 + 默认策略
- 应用维度 + IP / 域名维度的规则管理
- 把系统底层能力包装成一个较友好 UI
- 做一个类似桌面系统的防火墙配置中心:
- 企业/校园一体机的"策略下发终端"
- 企业在后台配置好一堆规则
- 前端设备上的 App 用
@ohos.net.netFirewall去同步本地策略
- 家长控制 / 自律模式 Demo
- 用域名规则禁一些类型的网站
- 再配合时间段控制(例如别的模块控制),可以玩出"晚上 10 点之后全网断联"这类功能
最后
@ohos.net.netFirewall 给人的感觉就是:底层已经帮我们把"拦网"这个脏活累活干完了,我们只需要专心搞好规则建模 + 管理 UI 就行。
以后如果我真的写一个"PC 版网络管家"或者"家长控制模式"的 Demo,大概率就是:
- UI 层:ArkUI + 状态管理
- 逻辑层:
netFirewall.get* / set* / add* / update* / remove* - 结合一点日志 / 拦截记录展示(拦截记录那块接口和分页查规则很像,这里就先不展开了)