还是之前的 Android 机顶盒,在上面装了 Entware 和 Termux,跑了 Nginx Mumble Samba AdGuardHome qBittorrent 很多年了。这次来总结下遇到的坑。

禁用 SELinux

首先是禁用 SELinux ,不然的话会有很多奇奇怪怪的问题。

1
setenforce 0

关闭 Android 虚拟机 zygote

Android 程序太占内存和 CPU 了,不用的时候我们可以直接把他关掉。

android-stop.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/sh
setprop ctl.stop media
setprop ctl.stop zygote
setprop ctl.stop surfaceflinger
setprop ctl.stop drm
setprop ctl.stop debuggerd
setprop ctl.stop healthd
setprop ctl.stop cameraserver
setprop ctl.stop audioserver
setprop ctl.stop imageserver
setprop ctl.stop gatekeeperd
setprop ctl.stop mdnsd
setprop ctl.stop lmkd
setprop ctl.stop logd

普通用户无网络

然后是普通用户联网的问题,Entware 的程序默认是 root 在跑,如果换用普通用户跑会无法联网,这个是 Android 的问题,需要在 Entware 下新建对应 Android 有网络权限的组,并把用户加到这个组。

1
2
3
4
5
groupadd -g 3001 net_bt_admin
groupadd -g 3002 net_bt
groupadd -g 3003 inet
groupadd -g 3004 net_raw
usermod -a -G net_bt_admin,net_bt,inet,net_raw yourusername

如果提示没有 groupadd 命令,需要安装 shadow-groupadd

Android 带的 am pm svc 命令 在 Entware 中提示 Aborted。

在 /opt/etc/profile 最下添加,然后重启 shell。

1
2
3
4
5
6
export PATH=$PATH:/opt/etc/init.d:/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin
export ANDROID_DATA=/data
export ANDROID_ROOT=/system
export ANDROID_ASSETS=/system/app
export ANDROID_STORAGE=/storage
export EXTERNAL_STORAGE=/sdcard

reboot 命令无效

这是因为 Entware 的 reboot 是链接到 busybox 的,删掉这个软链接就好了。

1
rm /opt/bin/reboot

/lib

Android 没有 /lib 和 /usr/lib ,有些软件会提示 Can not load library ,可以软链接到 Entware 的 lib。

1
2
ln -s /opt/lib /lib
ln -s /opt/lib /usr/lib

/etc/mtab

一些软件会提示 Couldn't access /etc/mtab: No such file or directory ,也是软链接。

1
ln -s /proc/mounts /etc/mtab

/dev/stdin

Android 没有 /dev/stdin ,一些软件会报错。

1
2
3
ln -s /proc/self/fd/0 /dev/stdin
ln -s /proc/self/fd/1 /dev/stdout
ln -s /proc/self/fd/2 /dev/stderr

/tmp

Android 没有 /tmp ,一些软件会往里面写东西。把 tmpfs 挂载到 /opt/tmp ,再链接到 /tmp 。

1
2
3
/system/bin/chmod 1777 /opt/tmp
/system/bin/mount -t tmpfs tmpfs /opt/tmp
ln -s /opt/tmp /tmp

时区

1
2
opkg install zoneinfo-asia
ln -s /opt/share/zoneinfo/Asia/Singapore /etc/localtime

resolv.conf

好多程序还是会读 /etc/resolv.conf 里面的配置,但是 Android 没有这个文件。

1
2
echo 'nameserver 223.5.5.5' > /opt/etc/resolv.conf
ln -s /opt/etc/resolv.conf /etc/resolv.conf

Python

好多项目都会提示使用虚拟环境
python3 -m venv venv
而 Entware 下执行这条命令会提示
/opt/bin/python3: No module named venv
这时候可以安装 virtualenv

1
2
pip install --user virtualenv
python3 -m virtualenv venv

acme.sh

首先要软链接一下,因为大多脚本的 shebang 都写死了 /usr/bin/env 这个位置,所以直接软链接更省事。

1
2
mkdir -p /usr/bin
ln -s /opt/bin/env /usr/bin/env

然后安装依赖。

1
opkg install openssl-util coreutils-od xxd

不然会出现下面错误:
Invalid status, Verify error detail:Incorrect TXT record
od: invalid option -- 'A'

PS. 根域名不能是 CNAME。

Mumble The issuer certificate could not be found

Mumble 注册到公共列表会提示 Registration: SSL Handshake error: The issuer certificate could not be found
这是因为找不到证书,位置在 /etc/ssl/certs/ca-certificates.crt ,而 Android 连 /etc/ssl 这个目录都没有。可以建软链接到 Entware 的 /opt/etc/ssl ,需要 Entware 安装 ca-certificates

1
ln -s /opt/etc/ssl /etc/ssl

qBittorrent 下载文件权限

qBittorrent 在 Entware 中是用 root 用户运行的,下载下来的文件也只有 root 能读写,Android 的 app 无法访问。
可以在下载完成后自动执行脚本修改权限。

/opt/etc/qBittorrent_entware/qb_update_permissions.sh
1
2
3
4
#!/opt/bin/bash
# Update permissions
find "$1" -type f -exec chmod 666 -- {} +
find "$1" -type d -exec chmod 777 -- {} +

然后在 选项 --> 下载 --> torrent 完成时运行外部程序 中填 /opt/etc/qBittorrent_entware/qb_update_permissions.sh "%F"

外网访问

IPv4: 从运营商分配到的 IP 地址是内网 IP , NAT 类型是 Full Cone NAT ,在路由器上用 natter/natmap + 端口转发就可以实现外网访问本机。
IPv6: 在防火墙的通信规则里面放行指定设备 IPv6 后缀的端口。
然后配置好 DDNS。

Mumble

使用 natter/natmap 配合 SRV 记录可以从外网 IPv4(TCP) IPv6(TCP+UDP) 访问本机的 Mumble 服务。
SRV 记录可以设置优先级,把 IPv6 的记录设置高优先就可以实现有 IPv6 的设备优先用 IPv6 访问,没有则会用 IPv4 访问。

Nginx

因为家宽的 80 443 的端口不能用,所以只能跑在非标准端口上。
这样外网访问的话需要在域名后面加上 :端口号,而支持端口号的 HTTPS DNS 记录目前只有苹果的浏览器支持,Chromium 系的虽然会去查询但不会使用。Firefox 则很迷,有人说在 DoH 下支持,但我试了还是不可以。
如果实在不想带端口号访问的话可以套一层 Cloudflare,再用他的 Origin Rules。

BitTorrent

BT 软件会向 tracker 汇报本机监听的端口,其他用户从 tracker 获取到 ip 和端口,然后发起连接。但是用 natter/natmap 打洞出来的外网端口是随机的,和 BT 软件设置里面的端口不一样,会导致其他用户连接失败。
解决方法是把 BT 软件的监听端口改成和打洞出来的外网端口一样的,可以用这个脚本

Termux

Entware 源里面的软件还是有些少,有时候会用到 Termux。
在 Entware 直接跑是 root 用户,会把 Termux 的文件权限搞乱,用 ssh 自己连自己又很麻烦。所以要在 Entware 里面建立对应 Android 中 Termux 的用户和组,然后 su 切换过去。
创建脚本 termux.sh

termux.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/opt/bin/bash

export ANDROID_DATA=/data
export ANDROID_ROOT=/system
tuser=$(/system/bin/ls -ld /data/data/com.termux|awk '{printf $3}')
id $tuser 2>/dev/null 1>/dev/nul
if [ $? -eq 1 ]; then
tid=$(/system/bin/ls -nd /data/data/com.termux|awk '{printf $3}')
tcacheg=${tuser}_cache
tacachegid=$((tid+10000))
tallg=$(echo $tuser|sed s/u0/all/)
tallgid=$((tid+40000))
groupadd -g 3003 inet
groupadd -g 9997 everybody
groupadd -g $tid $tuser
groupadd -g $tacachegid $tcacheg
groupadd -g $tallgid $tallg
useradd -g $tid -G $tid,3003,9997,$tacachegid,$tallgid -d /data/data/com.termux/files/home -s /data/data/com.termux/files/usr/bin/bash -u $tid $tuser
fi

if [[ "$*" == '' ]];then
su - $tuser
else
unset LC_ALL
su $tuser -c "source ~/.bashrc; $*"
fi

然后在 /data/data/com.termux/files/home/.bashrc 最上添加

/data/data/com.termux/files/home/.bashrc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
if [[ "$LD_PRELOAD" != "/data/data/com.termux/files/usr/lib/libtermux-exec.so" ]]; then
export COLORTERM=truecolor
export HISTCONTROL=ignoreboth
export TERMUX_IS_DEBUGGABLE_BUILD=0
export TERMUX_MAIN_PACKAGE_FORMAT=debian
export LANG=en_US.UTF-8
export ANDROID_DATA=/data
export ANDROID_ROOT=/system
export EXTERNAL_STORAGE=/sdcard
export HOME=/data/data/com.termux/files/home
export PATH=/data/data/com.termux/files/usr/bin
export PREFIX=/data/data/com.termux/files/usr
export LD_PRELOAD=/data/data/com.termux/files/usr/lib/libtermux-exec.so
export TMPDIR=/data/data/com.termux/files/usr/tmp
export TERM=xterm-256color
export TZ=Asia/Shanghai
unset MAIL HZ LC_ALL
fi

这样直接运行 termux.sh 就是切换到 Termux 环境。运行 termux.sh xxx 就可以直接执行 Termux 里面的命令。

init.d 启动脚本

开机启动 Entware 服务的脚本。

/etc/init.d/10-entware.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#!/system/bin/sh

# selinux
setenforce 0

# system rw
sleep 3
/system/bin/mount -o remount,rw /
/system/bin/mount -o remount,rw /system
/system/bin/mount -o remount,suid /data

# Entware

mkdir /opt
mount -o bind /data/entware /opt

mkdir /bin
ln -s /system/bin/sh /bin/sh

mkdir -p /usr/bin
ln -s /opt/bin/env /usr/bin/env

mkdir -p /var/run

ln -s /opt/lib /lib
ln -s /opt/lib /usr/lib

ln -s /proc/mounts /etc/mtab
ln -s /proc/self/fd/0 /dev/stdin
ln -s /proc/self/fd/1 /dev/stdout
ln -s /proc/self/fd/2 /dev/stderr

# tmp
chmod 1777 /opt/tmp
mount -t tmpfs tmpfs /opt/tmp
ln -s /opt/tmp /tmp

sleep 3

unset LD_PRELOAD
unset LD_LIBRARY_PATH

/opt/sbin/dropbear -p 22 -P /opt/var/run/dropbear.pid

sleep 3

ntpd -dnqp 10.0.0.1
#ntpd -dnqp 182.92.12.11

am force-stop com.player.diyp2020

/opt/etc/init.d/rc.unslung start

am start -n com.player.diyp2020/com.player.activity.LoginActivity