前言
这功能前前后后弄了得有两月吧,虽不是特别完美,还好最终还是搞定了,哈哈哈
本方案实现基于 iptables 管控,关于 iptables 的详细使用介绍可参考朱双印大佬的博文,那是相当的详细本文将不再介绍
本文实现过程中遇到的问题集锦
问题1、iptables 指令需要在 root 权限才能执行,adb shell 中执行提示
iptables v1.8.7 (legacy): can't initialize iptables table `filter': Permission denied (you must be root) Perhaps iptables or your kernel needs to be upgraded.
为了解决这个问题,我们需要在 init.rc 中配置一个服务,通过 prop 值来控制启动这个服务,达到提权的效果
问题2、iptables 指令生成的网络规则重启会失效,为了解决这个问题,我们需要重启后再次执行一遍上一次的指令,
通过 persist 开头 prop 可解决该问题。
问题3、persist 开头 prop 和新增加执行 sh 文件编译和运行对应 selinux 规则处理起来贼麻烦,
本文并没解决而是简单粗暴直接关闭 selinux 了。
问题4、iptables 中不识别域名,在设备联网的情况下增加域名会自动转换为 ip 在规则表中,
若设备没网络情况下,增加域名失败,所以最好增加 ip。获取域名对应 ip,可通过 ping 方式查看
问题5、设置 iptables 规则后,设备 wifi 或者 eth 可能会提示已连接但无法访问互联网
解决办法找到系统当前检测域名对应 ip 将其增加进白名单中
实现原理如下
修改清单
frameworks/base/core/api/current.txt
frameworks/base/core/java/android/app/SystemServiceRegistry.java
frameworks/base/core/java/android/content/Context.java
frameworks/base/core/java/android/app/INetworkFireWallListManager.aidl
frameworks/base/core/java/android/app/NetworkFireWallListManager.java
frameworks/base/services/core/java/com/android/server/NetworkFireWallListManagerService.java
frameworks/base/services/java/com/android/server/SystemServer.java
prebuilts/sdk/31/public/api/android.txt
prebuilts/sdk/32/public/api/android.txt
system/core/rootdir/init.rc
system/sepolicy/prebuilts/api/31.0/private/file_contexts
system/sepolicy/prebuilts/api/32.0/private/file_contexts
system/sepolicy/private/file_contexts
system/sepolicy/prebuilts/api/31.0/private/netfirewall.te
system/sepolicy/prebuilts/api/32.0/private/netfirewall.te
system/sepolicy/private/netfirewall.te
1、增加 INetworkFireWallListManager aidl 相关类
frameworks/base/core/java/android/app/INetworkFireWallListManager.aidl
package android.app;
interface INetworkFireWallListManager{
void addBlackListRule(in List<String> ipList);
void addWhiteListRule(in List<String> ipList);
void clearRule(in String sheetName);
}
frameworks/base/core/java/android/app/NetworkFireWallListManager.java
package android.app;
import android.annotation.SystemService;
import android.compat.annotation.UnsupportedAppUsage;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.util.Singleton;
import android.util.Log;
import java.util.List;
import android.content.Context;
import android.app.INetworkFireWallListManager;
@SystemService(Context.NETWORKFIREWALLLISTMANAGER_SERVICE)
public class NetworkFireWallListManager {
private static String TAG = "NetworkFireWallListManager";
private INetworkFireWallListManager mService;
private static NetworkFireWallListManager sInstance;
/**
*@hide
*/
public NetworkFireWallListManager(INetworkFireWallListManager service){
mService = service;
}
/**
*@hide
*/
@NonNull
@UnsupportedAppUsage
public static NetworkFireWallListManager getInstance() {
synchronized (NetworkFireWallListManager.class) {
if (sInstance == null) {
try {
IBinder b = ServiceManager.getServiceOrThrow(Context.NETWORKFIREWALLLISTMANAGER_SERVICE);
sInstance = new NetworkFireWallListManager(INetworkFireWallListManager.Stub
.asInterface(ServiceManager.getServiceOrThrow(Context.NETWORKFIREWALLLISTMANAGER_SERVICE)));
} catch (ServiceNotFoundException e) {
throw new IllegalStateException(e);
}
}
Log.d(TAG,"NetworkFireWallListManager getInstance");
return sInstance;
}
}
public void addBlackListRule(@Nullable List<String> ipList){
try{
mService.addBlackListRule(ipList);
} catch (RemoteException e){
throw e.rethrowFromSystemServer();
}
}
public void addWhiteListRule(@Nullable List<String> ipList){
try{
mService.addWhiteListRule(ipList);
} catch (RemoteException e){
throw e.rethrowFromSystemServer();
}
}
public void clearRule(@Nullable String sheetName){
try{
mService.clearRule(sheetName);
} catch (RemoteException e){
throw e.rethrowFromSystemServer();
}
}
}
frameworks/base/services/core/java/com/android/server/NetworkFireWallListManagerService.java
package com.android.server;
import android.app.INetworkFireWallListManager;
import android.os.RemoteException;
import android.util.Log;
import android.os.FileUtils;
import android.os.SystemProperties;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
public class NetworkFireWallListManagerService extends INetworkFireWallListManager.Stub {
static final String TAG = "NetworkFireWallListManagerService";
@Override
public void addBlackListRule(List<String> ipList) throws RemoteException {
Log.d(TAG,"addBlackListRule ");
}
@Override
public void addWhiteListRule(List<String> ipList) throws RemoteException{
Log.d(TAG,"addWhiteListRule ");
}
@Override
public void clearRule(String sheetName) throws RemoteException{
Log.d(TAG,"clearRule "+sheetName);
}
}
2、新增系统服务
frameworks/base/core/java/android/app/SystemServiceRegistry.java
@@ -233,6 +233,8 @@ import com.android.internal.net.INetworkWatchlistManager;
import com.android.internal.os.IDropBoxManagerService;
import com.android.internal.policy.PhoneLayoutInflater;
import com.android.internal.util.Preconditions;
+//cczheng add
+import android.app.NetworkFireWallListManager;//end
import java.util.Map;
import java.util.Objects;
@@ -320,7 +322,14 @@ public final class SystemServiceRegistry {
IAlarmManager service = IAlarmManager.Stub.asInterface(b);
return new AlarmManager(service, ctx);
}});
-
+ //cczheng add
+ registerService(Context.NETWORKFIREWALLLISTMANAGER_SERVICE, NetworkFireWallListManager.class,
+ new CachedServiceFetcher<NetworkFireWallListManager>() {
+ @Override
+ public NetworkFireWallListManager createService(ContextImpl ctx) {
+ return NetworkFireWallListManager.getInstance();
+ }});
+ //end
registerService(Context.AUDIO_SERVICE, AudioManager.class,
new CachedServiceFetcher<AudioManager>() {
@Override
frameworks/base/core/java/android/content/Context.java
@@ -3715,6 +3715,7 @@ public abstract class Context {
//@hide: SPEECH_RECOGNITION_SERVICE,
UWB_SERVICE,
MEDIA_METRICS_SERVICE,
+ NETWORKFIREWALLLISTMANAGER_SERVICE,//cczheng add
})
@Retention(RetentionPolicy.SOURCE)
public @interface ServiceName {}
@@ -5650,6 +5651,17 @@ public abstract class Context {
*/
public static final String MEDIA_METRICS_SERVICE = "media_metrics";
+ //cczheng add
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.app.NetworkFireWallListManager} for manage netfirewall on the device.
+ *
+ * @see #getSystemService(String)
+ * @see android.app.NetworkFireWallListManager
+ */
+ public static final String NETWORKFIREWALLLISTMANAGER_SERVICE = "networkfirewalllistmanager";
+ //end
+
frameworks/base/services/java/com/android/server/SystemServer.java
@@ -1460,6 +1460,13 @@ public final class SystemServer implements Dumpable {
ServiceManager.addService("dynamic_system", dynamicSystem);
t.traceEnd();
+ //cczheng add
+ t.traceBegin("StartNetworkFireWallListManagerService");
+ ServiceManager.addService(Context.NETWORKFIREWALLLISTMANAGER_SERVICE,
+ new NetworkFireWallListManagerService());
+ t.traceEnd();
+ //end
+
if (!isWatch) {
t.traceBegin("StartConsumerIrService");
consumerIr = new ConsumerIrService(context);
3、解决新增系统服务编译报错问题
out/srcjars/android/app/INetworkFireWallListManager.java:200: error: Missing nullability on parameter sheetName in method clearRule [MissingNullability]
out/srcjars/android/app/INetworkFireWallListManager.java:10: error: Methods calling system APIs should rethrow RemoteException as RuntimeException (but do not list it in the throws clause) [RethrowRemoteException]
out/srcjars/android/app/INetworkFireWallListManager.java:10: error: Missing nullability on parameter ipList in method addBlackListRule [MissingNullability]
out/srcjars/android/app/INetworkFireWallListManager.java:13: error: Methods calling system APIs should rethrow RemoteException as RuntimeException (but do not list it in the throws clause) [RethrowRemoteException]
out/srcjars/android/app/INetworkFireWallListManager.java:13: error: Missing nullability on parameter ipList in method addWhiteListRule [MissingNullability]
12 more error(s) omitted. Search the log for 'error:' to find all of them.
Your API changes are triggering API Lint warnings or errors. To make these errors go away, fix the code according to the error and/or warning messages above.
If it is not possible to do so, there are workarounds:
- You can suppress the errors with @SuppressLint("")
- You can add a baseline file of existing lint failures to the build rule of api-stubs-docs-non-updatable.
exit status 255 [ 30% 1641/5426] //frameworks/base:test-api-stubs-docs-non-updatable metalava merged [common]
frameworks/base/core/java/android/os/PackageTagsList.java:111: info: Method can be invoked as a "in" operator from Kotlin: contains (this is usually desirable; just make sure it makes sense for this type of object) [KotlinOperator]
10:04:37 ninja failed with: exit status 1
根据报错提示将代码中增加 Nullable 等关键字,没有新增直接 copy 我提供的就好
新增 api make 后报错,执行下面指令更新 current.txt
cp out/soong/.intermediates/frameworks/base/api-stubs-docs-non-updatable/android_common/metalava/api-stubs-docs-non-updatable_api.txt frameworks/base/core/api/current.txt
frameworks/base/core/api/current.txt
@@ -5378,6 +5378,30 @@ package android.app {
field public static final int GAME_MODE_UNSUPPORTED = 0; // 0x0
}
+ public interface INetworkFireWallListManager extends android.os.IInterface {
+ method public void addBlackListRule(java.util.List<java.lang.String>) throws android.os.RemoteException;
+ method public void addWhiteListRule(java.util.List<java.lang.String>) throws android.os.RemoteException;
+ method public void clearRule(String) throws android.os.RemoteException;
+ field public static final String DESCRIPTOR = "android.app.INetworkFireWallListManager";
+ }
+
+ public static class INetworkFireWallListManager.Default implements android.app.INetworkFireWallListManager {
+ ctor public INetworkFireWallListManager.Default();
+ method public void addBlackListRule(java.util.List<java.lang.String>) throws android.os.RemoteException;
+ method public void addWhiteListRule(java.util.List<java.lang.String>) throws android.os.RemoteException;
+ method public android.os.IBinder asBinder();
+ method public void clearRule(String) throws android.os.RemoteException;
+ }
+
+ public abstract static class INetworkFireWallListManager.Stub extends android.os.Binder implements android.app.INetworkFireWallListManager {
+ ctor public INetworkFireWallListManager.Stub();
+ method public android.os.IBinder asBinder();
+ method public static android.app.INetworkFireWallListManager asInterface(android.os.IBinder);
+ method public static android.app.INetworkFireWallListManager getDefaultImpl();
+ method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException;
+ method public static boolean setDefaultImpl(android.app.INetworkFireWallListManager);
+ }
+
public class Instrumentation {
ctor public Instrumentation();
method public android.os.TestLooperManager acquireLooperManager(android.os.Looper);
@@ -5623,6 +5647,12 @@ package android.app {
field public static final String META_DATA_LIB_NAME = "android.app.lib_name";
}
+ public class NetworkFireWallListManager {
+ method public void addBlackListRule(@Nullable java.util.List<java.lang.String>);
+ method public void addWhiteListRule(@Nullable java.util.List<java.lang.String>);
+ method public void clearRule(@Nullable String);
+ }
+
public class Notification implements android.os.Parcelable {
ctor public Notification();
ctor @Deprecated public Notification(int, CharSequence, long);
@@ -10672,6 +10702,7 @@ package android.content {
field public static final int MODE_PRIVATE = 0; // 0x0
field @Deprecated public static final int MODE_WORLD_READABLE = 1; // 0x1
field @Deprecated public static final int MODE_WORLD_WRITEABLE = 2; // 0x2
+ field public static final String NETWORKFIREWALLLISTMANAGER_SERVICE = "networkfirewalllistmanager";
field public static final String NETWORK_STATS_SERVICE = "netstats";
field public static final String NFC_SERVICE = "nfc";
field public static final String NOTIFICATION_SERVICE = "notification";
将上面 current.txt 和 prebuilts 目录下文件对比差异新增 NetworkFireWall 部分
prebuilts/sdk/31/public/api/android.txt prebuilts/sdk/32/public/api/android.txt
@@ -5373,6 +5373,30 @@ package android.app {
field public static final int GAME_MODE_UNSUPPORTED = 0; // 0x0
}
+ public interface INetworkFireWallListManager extends android.os.IInterface {
+ method public void addBlackListRule(java.util.List<java.lang.String>) throws android.os.RemoteException;
+ method public void addWhiteListRule(java.util.List<java.lang.String>) throws android.os.RemoteException;
+ method public void clearRule(String) throws android.os.RemoteException;
+ field public static final String DESCRIPTOR = "android.app.INetworkFireWallListManager";
+ }
+
+ public static class INetworkFireWallListManager.Default implements android.app.INetworkFireWallListManager {
+ ctor public INetworkFireWallListManager.Default();
+ method public void addBlackListRule(java.util.List<java.lang.String>) throws android.os.RemoteException;
+ method public void addWhiteListRule(java.util.List<java.lang.String>) throws android.os.RemoteException;
+ method public android.os.IBinder asBinder();
+ method public void clearRule(String) throws android.os.RemoteException;
+ }
+
+ public abstract static class INetworkFireWallListManager.Stub extends android.os.Binder implements android.app.INetworkFireWallListManager {
+ ctor public INetworkFireWallListManager.Stub();
+ method public android.os.IBinder asBinder();
+ method public static android.app.INetworkFireWallListManager asInterface(android.os.IBinder);
+ method public static android.app.INetworkFireWallListManager getDefaultImpl();
+ method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException;
+ method public static boolean setDefaultImpl(android.app.INetworkFireWallListManager);
+ }
+
public class Instrumentation {
ctor public Instrumentation();
method public android.os.TestLooperManager acquireLooperManager(android.os.Looper);
@@ -5618,6 +5642,12 @@ package android.app {
field public static final String META_DATA_LIB_NAME = "android.app.lib_name";
}
+ public class NetworkFireWallListManager {
+ method public void addBlackListRule(@Nullable List<String>);
+ method public void addWhiteListRule(@Nullable List<String>);
+ method public void clearRule(@Nullable String);
+ }
+
public class Notification implements android.os.Parcelable {
ctor public Notification();
ctor @Deprecated public Notification(int, CharSequence, long);
@@ -11142,6 +11172,7 @@ package android.content {
field public static final int MODE_PRIVATE = 0; // 0x0
field @Deprecated public static final int MODE_WORLD_READABLE = 1; // 0x1
field @Deprecated public static final int MODE_WORLD_WRITEABLE = 2; // 0x2
+ field public static final String NETWORKFIREWALLLISTMANAGER_SERVICE = "networkfirewalllistmanager";
field public static final String NETWORK_STATS_SERVICE = "netstats";
field public static final String NFC_SERVICE = "nfc";
field public static final String NOTIFICATION_SERVICE = "notification";
4、新增 prop 改变监听执行 netfirewall.sh 并解决编译报错
system/core/rootdir/init.rc
@@ -834,6 +834,9 @@ on post-fs-data
mkdir /data/app 0771 system system encryption=Require
mkdir /data/property 0700 root root encryption=Require
+ #cczheng create directory for netfirewall files.
+ mkdir /data/netfirewall 0771 system system encryption=None
+
# create directory for updated font files.
mkdir /data/fonts/ 0771 root root encryption=Require
mkdir /data/fonts/files 0771 system system
@@ -1203,6 +1206,17 @@ on property:sys.boot_completed=1
exec - system system -- /bin/rm -rf /data/per_boot
mkdir /data/per_boot 0700 system system encryption=Require key=per_boot_ref
+#cczheng add
+on property:persist.sys.runfire=1
+ start netfirewall
+
+service netfirewall /system/bin/sh /data/netfirewall/netfirewall.sh
+ user root
+ group root
+ disabled
+ oneshot
+ seclabel u:object_r:netfirewall_exec:s0
+
# system server cannot write to /proc/sys files,
system/sepolicy/private/file_contexts system/sepolicy/prebuilts/api/31.0/private/file_contexts system/sepolicy/prebuilts/api/32.0/private/file_contexts
@@ -646,6 +646,8 @@
/data/vendor_ce(/.*)? u:object_r:vendor_data_file:s0
/data/vendor_de(/.*)? u:object_r:vendor_data_file:s0
+#/data/netfirewall/netfirewall.sh u:object_r:netfirewall_exec:s0
+/system/bin/netfirewall.sh u:object_r:netfirewall_exec:s0
# storaged proto files
/data/misc_de/[0-9]+/storaged(/.*)? u:object_r:storaged_data_file:s0
/data/misc_ce/[0-9]+/storaged(/.*)? u:object_r:storaged_data_file:s0
system/sepolicy/private/netfirewall.te system/sepolicy/prebuilts/api/32.0/private/netfirewall.te system/sepolicy/prebuilts/api/32.0/private/netfirewall.te
type netfirewall, coredomain;
type netfirewall_exec, system_file_type, exec_type, file_type;
#Allow cppreopts to execute itself using #!/system/bin/sh
#allow netfirewall shell_exec:file { r_file_perms execute };
init_daemon_domain(netfirewall);
至此 make 可成功编译,且服务正常启动,可通过编写测试脚本 netfirewall.sh
#!/system/bin/sh
echo "start add rule"
iptables -I OUTPUT -d 180.101.49.11 -j ACCEPT
iptables -I OUTPUT -d 180.101.49.12 -j ACCEPT
iptables -P OUTPUT DROP
echo "add rule end"
将 netfirewall.sh 预制到 system/bin 目录下,
同时修改 init.rc 中 prop 对应文件为 system/bin/netfirewall.sh 测试,
最终测试效果为浏览器只能访问百度,其它网页均不能访问
5、最终构建黑名单和白名单规则
黑名单和白名单是互斥功能,均通过 iptables OUTPUT 表规则实现
黑名单,OUTPUT 默认为 ACCEPT,在其中增加禁止访问 ip 对应为 DROP
白名单,将 OUTPUT 修改为 DROP,在其中增加允许访问 ip 对应为 ACCEPT
iptables -nL OUTPUT 查询当前 OUTPUT 对应规则
改造后的 NetworkFireWallListManagerService.java
frameworks/base/services/core/java/com/android/server/NetworkFireWallListManagerService.java
package com.android.server;
import android.app.INetworkFireWallListManager;
import android.os.RemoteException;
import android.util.Log;
import android.os.FileUtils;
import android.os.SystemProperties;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
public class NetworkFireWallListManagerService extends INetworkFireWallListManager.Stub {
static final String TAG = "NetworkFireWallListManagerService";
static final String PROP_CONTROL_FIRETYPE = "persist.sys.firetype";
@Override
public void addBlackListRule(List<String> ipList) throws RemoteException {
Log.d(TAG,"addBlackListRule ");
SystemProperties.set(PROP_CONTROL_FIRETYPE, "addBlack");
String cmd = makeIptableString(ipList);
writeShFile(cmd);
}
@Override
public void addWhiteListRule(List<String> ipList) throws RemoteException{
Log.d(TAG,"addWhiteListRule ");
SystemProperties.set(PROP_CONTROL_FIRETYPE, "addWhite");
String cmd = makeIptableString(ipList);
writeShFile(cmd);
}
@Override
public void clearRule(String sheetName) throws RemoteException{
Log.d(TAG,"clearRule "+sheetName);
SystemProperties.set(PROP_CONTROL_FIRETYPE, "clear");
String cmd = makeIptableString(null);
writeShFile(cmd);
}
private String makeIptableString(List<String> ipList){
StringBuilder sb = new StringBuilder();
sb.append("echo start make rule\n");
//clear
sb.append("handleType=$(getprop persist.sys.firetype)\n");
sb.append("if [ $handleType = \"clear\" ]; then\n");
sb.append("echo handleType is clear\n");
sb.append("iptables -F INPUT\n");
sb.append("iptables -F OUTPUT\n");
sb.append("fi\n");
//addWhite
sb.append("if [ $handleType = \"addWhite\" ]; then\n");
if (ipList != null && ipList.size() > 0) {
sb.append("echo handleType is addWhite\n");
for (int i = 0; i < ipList.size(); i++) {
String ipname = ipList.get(i);
sb.append("iptables -I OUTPUT -d ");
sb.append(ipname);
sb.append(" -j ACCEPT\n");
}
sb.append("iptables -P OUTPUT DROP\n");
}else {
sb.append("all White rule pass\n");
}
sb.append("fi\n");
//addBlack
sb.append("if [ $handleType = \"addBlack\" ]; then\n");
if (ipList != null && ipList.size() > 0) {
sb.append("echo handleType is addBlack \n");
for (int i = 0; i < ipList.size(); i++) {
String ipname = ipList.get(i);
sb.append("iptables -I OUTPUT -d ");
sb.append(ipname);
sb.append(" -j DROP\n");
}
}else {
sb.append("all Black rule pass\n");
}
sb.append("fi\n");
sb.append("echo make rule end\n");
return sb.toString();
}
private void writeShFile(String cmd){
try {
final File file = new File("/data/netfirewall/", "netfirewall.sh");
if (file.exists()) {
file.delete();
}
file.createNewFile();
final String abspath = file.getAbsolutePath();
FileUtils.setPermissions(abspath, 00700, -1, -1);
Runtime.getRuntime().exec("chmod 777 " + abspath).waitFor();
final OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file));
if (new File("/system/bin/sh").exists()) {
out.write("#!/system/bin/sh\n");
}
out.write(cmd);
if (!cmd.endsWith("\n"))
out.write("\n");
out.write("exit 0\n");
out.flush();
out.close();
SystemProperties.set("persist.sys.runfire", "1");
} catch (Exception e) {
e.printStackTrace();
}
}
}
客户端调用代码
INetworkFireWallListManager fireWallListManager
= INetworkFireWallListManager.Stub.asInterface(getService("networkfirewalllistmanager"));
List<String> list = new ArrayList<String>();
list.add("101.37.115.180");//www.cnblog.com
list.add("116.62.84.58");
list.add("180.101.49.11");///www.baidu.com
list.add("180.101.49.12");
try {
fireWallListManager.addWhiteListRule(list);
} catch (Exception e) {
e.printStackTrace();
}
参考文章
Android 12 (S) 新加系统服务 iptables详解(3):iptables规则管理 Android 利用Iptables实现网络黑白名单(防火墙)