阅读视图

发现新文章,点击刷新页面。
🔲 ☆

告别 NPM Token:迁移到 Trusted Publishing 全记录

发生了什么?

2025 年 12 月 9 日,npm 正式永久撤销了所有 Classic Token。与此同时,npm 引入了基于会话的认证(2 小时有效期)和新的 CLI Token 管理方式。这意味着:

  • 所有 Classic Token 立即失效,无法恢复
  • npm login 现在只发放 2 小时的短期会话令牌
  • CI/CD 场景推荐使用 Granular Access TokenOIDC Trusted Publishing

我的 GitHub 项目 CI 工作流自然也挂了 —— 因为 Secrets 里存的还是那个已被吊销的 Classic Token。

Trusted Publishing 是什么?

简单说,Trusted Publishing 利用 OIDC(OpenID Connect) 协议,让 CI/CD 平台(如 GitHub Actions)直接与 npm 交换短期凭证来发布包,完全不需要存储任何 npm token

优势很明显:

  • 没有长期 Token,不存在泄露风险
  • 自动附带 Provenance(来源证明),包的构建来源可追溯
  • 配置一次,后续发版无需维护密钥

目前支持 GitHub Actions(GitHub-hosted runners)和 GitLab CI/CD(shared runners),不支持自托管 runner。

版本要求:npm CLI ≥ 11.5.1,Node.js ≥ 22.14.0。

迁移步骤

1. 在 npmjs.com 配置 Trusted Publisher

进入目标 Package 的 Setting 页面,找到 Trusted Publisher 部分,填写:

  • Owner:GitHub 用户名或组织名
  • Repository:仓库名
  • Workflow filename:工作流文件名(含 .yml 扩展名,如 publish.yml
  • Environment(可选):GitHub 部署环境名

注意:文件名、仓库名区分大小写,必须与 workflow 文件完全一致。

2. 配置 GitHub Actions Workflow

核心改动就两点:声明 OIDC 权限、直接用 npm publish

以下是我最终可用的 workflow(使用 pnpm):

name: publish

on:
  release:
    types: [published, released]
  workflow_dispatch:

jobs:
  publish:
    runs-on: ubuntu-latest

    permissions:
      id-token: write  # OIDC 必需
      contents: read

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install pnpm
        uses: pnpm/action-setup@v4
        with:
          version: 10

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: 24
          registry-url: 'https://registry.npmjs.org'
          cache: 'pnpm'

      - name: Install dependencies
        run: pnpm install --no-frozen-lockfile

      - name: Build
        run: pnpm run build

      - name: Publish
        if: github.repository == 'wayjam/picgo-plugin-s3'
        run: pnpm publish --no-git-checks --ignore-scripts --provenance --access public

不再需要 NPM_TOKEN、不再需要 NODE_AUTH_TOKEN,也不再需要 JS-DevTools/npm-publish 这类第三方 Action。直接原生 npm publish / pnpm publish 就行。

注意事项与故障排除

package.json 必须包含 repository 字段

npm 的 Provenance 机制会校验发布环境与包元数据是否匹配。如果 package.json 中没有 repository 字段(或值为空),会返回 422 错误:

npm error 422 Unprocessable Entity - PUT https://registry.npmjs.org/<package-name>
- Error verifying sigstore provenance bundle: Failed to validate repository information:
  package.json: "repository.url" is "", expected to match
  "https://github.com/<owner>/<repo>" from provenance

确保 package.json 中有正确且与 npm 配置一致的 repository 字段:

"repository": {
  "type": "git",
  "url": "git+https://github.com/<owner>/<repo>.git"
}

pnpm 添加 --no-git-checks(可选)

pnpm publish 默认会执行 git 检查,拒绝从非主分支或脏工作区发布。而 GitHub Actions 的 actions/checkout 默认是 Detached HEAD 状态,会直接触发该检查失败。如果是非 main/master 发布则需加上这个参数:

pnpm publish --no-git-checks --ignore-scripts --provenance --access public

Workflow 必须显式声明权限

没有在 job 级别声明 permissions,OIDC token 交换会直接失败。以下两项缺一不可:

permissions:
  id-token: write  # 用于 OIDC token 交换
  contents: read   # 用于 checkout 代码

报错 “Access token expired or revoked”

看到这个错误不一定是 token 问题。更常见的原因是:npm 网页端填写的 workflow 文件名与实际文件名不一致(包括大小写、.yml/.yaml 扩展名),导致 OIDC 验证时仓库/workflow 信息比对失败。逐项核对 npm 设置中的 owner、repo、workflow filename 即可。

清理

配置成功并验证发布正常后:

  1. 从 GitHub 仓库的 Settings → Secrets 中删除旧的 NPM_TOKEN
  2. 如果之前用的是 JS-DevTools/npm-publish,可以从 workflow 中移除该依赖
  3. 在 npm 包设置中,可以启用 “Require two-factor authentication and disallow tokens” 进一步加固安全

参考

🔲 ☆

Sing-box:IPv6 优先出站与 Netflix 强制 IPv6 分流

很多 VPS 的 IPv4 出口因为被滥用、共享 NAT 或者 IP 段"脏",导致访问某些服务时体验不佳。相比之下,IPv6 出口往往更干净——毕竟 IPv6 地址段大、用的人少、很多服务商对 IPv6 的风控也没那么严格。

本文记录如何在 sing-box 上实现:

  • 全局 IPv6 优先:所有出站流量优先走 IPv6,IPv6 不可用时自动回退 IPv4
  • Netflix 强制 IPv6:针对 Netflix 这类对 IP 质量敏感的服务,强制只走 IPv6(不回退)

前置检查

确认宿主机 IPv6 可用

在配置 sing-box 之前,先确保系统网络层的 IPv6 是通的:

# 查看 IPv6 地址(需要看到 scope global 的地址)
ip -6 addr show

# 查看 IPv6 默认路由
ip -6 route show

# 测试 IPv6 连通性
ping6 -c 4 2001:4860:4860::8888

# 测试 IPv6 HTTPS
curl -6 -I https://www.google.com

如果上述命令不通,先解决网络层问题(检查 VPS 控制台是否开启 IPv6、路由配置、防火墙规则等),否则后面配置再多也没用。

检测流媒体解锁状态

使用一键脚本检测当前 IP 的流媒体解锁情况:

bash <(curl -L -s check.unlock.media)

这个脚本会分别测试 IPv4 和 IPv6 出口对各大流媒体平台的解锁状态。如果你发现 IPv6 的解锁结果明显优于 IPv4,那就更有理由把流量往 IPv6 上引。

核心概念:domain_strategy

sing-box 的出站(outbound)支持 domain_strategy 参数,用于控制域名解析策略:

策略 行为
prefer_ipv4 优先 A 记录,失败回退 AAAA
prefer_ipv6 优先 AAAA 记录,失败回退 A
ipv4_only 只用 A 记录,无 A 则失败
ipv6_only 只用 AAAA 记录,无 AAAA 则失败
  • 默认出站设置 prefer_ipv6:大部分流量优先走 IPv6,保留 IPv4 作为兜底
  • Netflix 专用出站设置 ipv6_only:强制走 IPv6,不回退

配置:全局 IPv6 优先

最简单的做法是在默认的 direct 出站上加一个 domain_strategy

{
  "outbounds": [
    {
      "type": "direct",
      "tag": "direct",
      "domain_strategy": "prefer_ipv6"
    }
  ],
  "route": {
    "auto_detect_interface": true,
    "final": "direct"
  }
}

这样所有走 direct 出站的流量都会优先尝试 IPv6。如果目标域名没有 AAAA 记录或 IPv6 不通,会自动回退到 IPv4,不影响正常使用。

进阶:Netflix 强制 IPv6

如果想对 Netflix 更激进一点——强制只走 IPv6,不要回退——可以单独定义一个出站,并用规则集把 Netflix 流量指过去。

新增 IPv6 专用出站

{
  "outbounds": [
    {
      "type": "direct",
      "tag": "IPv6-only",
      "domain_strategy": "ipv6_only"
    },
    {
      "type": "direct",
      "tag": "direct",
      "domain_strategy": "prefer_ipv6"
    }
  ]
}

配置 Netflix 规则集

{
  "route": {
    "rules": [
      {
        "rule_set": [
          "geoip-netflix",
          "geosite-netflix"
        ],
        "outbound": "IPv6-only"
      }
    ],
    "rule_set": [
      {
        "tag": "geoip-netflix",
        "type": "remote",
        "format": "binary",
        "url": "https://github.com/MetaCubeX/meta-rules-dat/raw/refs/heads/sing/geo/geoip/netflix.srs",
        "download_detour": "direct",
        "update_interval": "1d"
      },
      {
        "tag": "geosite-netflix",
        "type": "remote",
        "format": "binary",
        "url": "https://github.com/MetaCubeX/meta-rules-dat/raw/refs/heads/sing/geo/geosite/netflix.srs",
        "download_detour": "direct",
        "update_interval": "1d"
      }
    ],
    "auto_detect_interface": true,
    "final": "direct"
  }
}

说明

  • 双规则集geoip-netflix 匹配 Netflix 服务器 IP 段,geosite-netflix 匹配 Netflix 相关域名,双保险。
  • 规则集来源:使用 MetaCubeX 维护的规则集,.srs 是 sing-box 专用的二进制格式,体积小、加载快。
  • download_detour:规则集下载走 direct,避免下载本身被绕来绕去。
  • update_interval:每天自动更新一次规则集。

重启 sing-box 并检查状态:

systemctl restart sing-box
systemctl status sing-box

注意事项

Outbound Tag 不能重复

sing-box 启动时会校验所有 outbound 的 tag 必须唯一。如果你用的是一键脚本安装(如 233boy),可能存在"主配置 + 子配置合并"的机制,需要注意:

  • 不要在多个配置文件里定义同名的 tag
  • 典型报错:duplicate outbound/endpoint tag: direct
  • 解决:统一在主配置里定义,子配置里不要重复

ipv6_only 的风险

ipv6_only 意味着"没有 AAAA 记录就直接失败",不会回退 IPv4。如果 Netflix 某些边缘节点或 CDN 在你的网络环境下拿不到 AAAA,访问就会中断。

如果遇到问题,可以把 Netflix 的出站策略从 ipv6_only 改回 prefer_ipv6

客户端配置(Clash/Mihomo)

如果你的客户端使用 Clash 或 Mihomo,需要确保域名是在远端(服务器侧)解析,而不是在本地解析。否则 domain_strategy 就失效了——因为流量到达 sing-box 时已经是 IP 地址,服务器无法再根据域名决定走 IPv4 还是 IPv6。

客户端模式 域名解析位置 客户端是否需要配置
Fake-IP 远端 无需额外配置
Real-IP 本地 需开启 sniffer + override-destination

Fake-IP 模式(推荐)

如果客户端使用 Fake-IP 模式,域名天然就是在远端解析的:

  • 客户端 DNS 返回一个假的 IP(如 198.18.x.x
  • 实际域名随连接一起发送到服务器
  • 服务器侧的 sing-box 拿到的是域名,domain_strategy 正常生效

Fake-IP 模式下无需额外配置,开箱即用。

dns:
  enable: true
  enhanced-mode: fake-ip
  fake-ip-range: 198.18.0.1/16
  # ...

Real-IP 模式(需开启嗅探)

如果客户端使用 Real-IP(redir-host)模式,DNS 查询会在本地完成,流量到达服务器时已经是 IP 地址。这种情况下需要在 Mihomo 客户端开启流量嗅探(sniffer),从流量中还原出域名并覆盖目标地址。

在 Mihomo 配置中添加:

sniffer:
  enable: true
  override-destination: true
  sniff:
    HTTP:
      ports: [80, 8080-8880]
    TLS:
      ports: [443, 8443]
    QUIC:
      ports: [443, 8443]

关键参数

  • enable: true:开启流量嗅探
  • override-destination: true:用嗅探到的域名覆盖原始目标地址,这样服务端的 domain_strategy 才能基于域名工作

注意override-destination 会改变连接的目标地址。如果你的规则依赖 IP 匹配(如 GEOIP),需要确保规则顺序正确,或者同时配置 GEOSITE 规则。

🔲 ☆

[All In One] HomeLab 虚拟化 iKuai + Mihomo - IPv6 支持

本文是 All In One HomeLab 虚拟化 iKuai + Mihomo 的 IPv6 扩展配置,介绍如何在现有的透明网关(Debian/LXC 作为 Clash/Mihomo 透明网关)架构中启用 IPv6 支持。

本文默认目标:只让透明网关本机具备 IPv6 出口能力,内网终端不强制获取公网 IPv6;如果希望终端也能拿到公网 IPv6,通常还需要额外处理 NDP前缀下发(DHCPv6-PD) 等问题,正文相关步骤会顺带提示。

iKuai IPv6 配置

WAN1 配置 IPv6

确保 iKuai 的主外网口(WAN1)能够获取 IPv6 地址:

  1. 如果是 PPPoE 拨号:在 iKuai 的 WAN1 设置中,勾选 “获取 IPv6” 选项。
  2. 如果是 DHCP:确保光猫支持 IPv6 并已开启 IPv6 功能,iKuai 会自动获取 IPv6 前缀和地址。

LAN 口配置 IPv6(推荐无状态 / 混合)

最容易踩坑的点:如果 iKuai 只开“有状态 DHCPv6”,而透明网关用的是 iface wan0 inet6 auto(SLAAC/无状态),它默认不会去跑 DHCPv6 客户端,最终表现就是“怎么都拿不到 IPv6”。

Debian 是透明网关(必须开 net.ipv6.conf.all.forwarding=1),更推荐让 iKuai 在 LAN 侧提供 RA + SLAAC(无状态),或者 混合模式(SLAAC + DHCPv6)

  1. 进入 网络设置 > LAN1 > IPv6 设置
    • 外网接口:动态接口;绑定 wan1
    • 内网接口:
      • 模式:选择 无状态混合(无状态 + 有状态)
      • RA 发送:开启(默认网关/前缀等信息主要依赖 RA)
      • M(Managed)标识:建议 关闭(M=1 会暗示“用 DHCPv6 拿地址”;Android 不支持有状态 DHCPv6
      • O(Other)标识:建议 开启(用于下发 DNS 等其他信息,具体取决于 iKuai 的实现)

如果希望“终端设备也能拿公网 IPv6”,至少要保证 LAN 侧能 SLAAC(无状态)或混合模式(否则 Android 直接拿不到 IPv6 地址);另外在“透明网关/旁路网关”这种拓扑里,终端要真正拥有公网 IPv6,往往还会遇到 NDP前缀下发(PD) 的问题:

  • NDP:常见做法是在 Debian 上做 NDP 代理(例如 ndppd),让上游认为下游终端“就在同一链路”。
  • PD:更正统的做法是上游支持 DHCPv6-PD,让 Debian 申请一个子前缀(例如 /64)再分配给下游 LAN。

如果“只想让透明网关这一台设备拿 IPv6”,两种做法:

  • 做法 A(更省心):仍然用 无状态/混合,但在 iKuai 上用 IPv6 防火墙策略限制终端的 IPv6 出口(让它们即便拿到 IPv6,也访问不了公网)。
  • 做法 B(坚持有状态 DHCPv6):继续用 DHCPv6 白名单 只给网关发地址,但请注意 Debian 侧要改成 DHCPv6 客户端,见下文“有状态方案”。(参考:特定设备分配 IPv6

ikuai-ipv6

透明网关 IPv6 配置

方案 1:无状态 或者 混合(推荐)

编辑 /etc/network/interfaces,为 wan0(连接到 iKuai LAN)启用 SLAAC,并在“网关/转发模式”下强制接受 RA(透明网关必开 forwarding=1,Linux 默认会停止接收 RA;不收 RA 往往拿不到 IPv6 默认路由):

auto lo
iface lo inet loopback

auto lan0
iface lan0 inet static
        address 192.168.200.1/24
        gateway 192.168.200.2

auto wan0
iface wan0 inet static
        address 192.168.100.2/24

# 无状态(SLAAC)获取 IPv6 地址
iface wan0 inet6 auto
        # 关键:作为网关开启 forwarding 后,Linux 默认不再接受 RA,需要设为 2
        post-up sysctl -w net.ipv6.conf.wan0.accept_ra=2

应用配置:

systemctl restart networking

方案 2:iKuai 纯有状态 DHCPv6(不推荐,但可用)

如果在 iKuai 上做了 DHCPv6 白名单,只给透明网关发 IPv6,那么 Debian 侧就别用 inet6 auto 了,改成 DHCPv6:

auto wan0
iface wan0 inet static
        address 192.168.100.2/24

# 有状态 DHCPv6 获取 IPv6 地址(需要 DHCPv6 客户端)
iface wan0 inet6 dhcp
        post-up sysctl -w net.ipv6.conf.wan0.accept_ra=2

说明:即使用 DHCPv6 拿到了地址,默认路由通常仍依赖 RA,所以 accept_ra=2 依然很关键。

PVE 特殊配置

如果是 PVE LXC 容器记得调整 LXC 配置,否则重启后 network 配置会被重置

pve-lxc-ipv6

开启内核转发(关键)

既然是透明网关,Debian 必须开启 IPv6 转发功能。编辑 /etc/sysctl.conf

# 作为路由/网关:开启 IPv6 转发
net.ipv6.conf.all.forwarding=1
net.ipv6.conf.default.forwarding=1

# 仅在要做 NDP 代理时才需要
# net.ipv6.conf.all.proxy_ndp=1

应用配置:

sysctl -p

验证 IPv6 地址获取

检查透明网关是否成功获取 IPv6 地址和默认路由:

# 查看 wan0 的 IPv6 地址
ip -6 addr show wan0

# 查看 IPv6 路由表(重点看 default)
ip -6 route show

# 测试 IPv6 连通性
curl -6 ifconfig.me

Mihomo IPv6 支持

配置 IPv6 DNS

编辑 /etc/mihomo/config.yaml,添加 IPv6 DNS 服务器:

dns:
  enable: true
  listen: 0.0.0.0:1053
  ipv6: true  # 启用 IPv6 DNS 查询
...

参考资料

🔲 ☆

[All In One] HomeLab 虚拟化 iKuai + Mihomo

本文思路基本遵循 iKuai与Openwrt的有机结合:传统旁路由方案的完美替代,主要差异点如下:

  • 虚拟化平台: unRaid 替换为 Proxmox VE (PVE)
  • 透明网关: OpenWRT 替换为 Debian + Mihomo
  • DNS 服务: paopaodns 替换为 SmartDNS + AdGuard Home 组合

网络拓扑

为方便描述,下文统一将处理出站流量的虚拟机称为透明网关 (gateway)

架构核心思想

本方案采用“主路由 + 透明网关”的模式。iKuai 作为主路由,以其出色的稳定性、多线负载和流控能力负责整体网络的 DHCP、NAT 和流量调度。透明网关则是一个专职处理特定流量(如国际流量)的“处理器”。iKuai 通过策略路由(PBR)将特定流量“扔”给透明网关处理,处理完毕后再由透明网关“扔”回给 iKuai,由 iKuai 统一出口。这种架构实现了功能解耦,稳定性和灵活性兼得。

  • iKuai (主路由):

    • WAN1: 用于 PPPoE 拨号或从光猫 DHCP 连接到外网。
      • 物理网卡:eth3 (硬件直通)
    • LAN: 地址为 192.168.100.1,桥接所有局域网内的网卡,构成统一的二层网络。
      • eth1, eth2: 硬件直通,连接 AP 或其他有线设备。
      • vmbr0: PVE 虚拟网桥,专用于 PVE 的 Web 管理界面访问,桥接到 LAN 使其可从内网访问。
      • vmbr2: PVE 虚拟网桥,作为通用网桥,接入其他虚拟机或容器。
    • WAN2: 逻辑接口,作为 iKuai 与透明网关之间的数据通道。
      • IP 地址: 192.168.200.2
      • 网关指向: 192.168.200.1 (透明网关的 LAN 地址)
      • 网卡:vmbr1 (PVE 虚拟网桥)
  • gateway (透明网关):

    • LAN: 接收来自 iKuai (WAN2) 的流量。
      • IP 地址: 192.168.200.1
      • 网卡:vmbr1 (连接 iKuai WAN2)
    • WAN: 将处理后的流量发回 iKuai 的 LAN。
      • IP 地址: 192.168.100.2
      • 网关指向: 192.168.100.1 (iKuai 的 LAN 地址)
      • 网卡:vmbr2 (连接 iKuai LAN)

网络拓扑图

PVE 网络设置

pve-net

iKuai 安装与初始化

虚拟机配置

在 PVE 中为 iKuai 创建虚拟机时,以下是一些关键配置建议:

  • 机型: q35。提供更现代的硬件特性,是使用 PCIe Passthrough (硬件直通) 的推荐选项。
  • BIOS: 默认 (SeaBIOS)。对 iKuai 兼容性良好。
  • SCSI 控制器: VirtIO SCSI single。使用半虚拟化驱动 VirtIO 可最大化磁盘 I/O 性能。

安装步骤

  1. 从 iKuai 官网下载 32 位 ISO 镜像并上传到 PVE。

  2. 新建虚拟机,可以暂时不添加网络设备,稍后统一配置。

  3. 硬件直通 (PCI Passthrough): 进入 iKuai 虚拟机的“硬件”菜单,将物理网卡(除 PVE 管理口外)直通给虚拟机。

    什么是硬件直通 (PCI Passthrough)?

    它允许虚拟机绕过 Hypervisor (PVE) 直接控制物理硬件设备。对于网卡而言,这意味着虚拟机可以获得接近物理机的网络性能和延迟,并能直接利用网卡的硬件特性,是追求极致性能的软路由方案首选。

  4. 添加虚拟网卡: 添加 vmbr0, vmbr1, vmbr2 这三个 PVE 虚拟网桥作为虚拟网卡。

  5. 启动虚拟机,iKuai 系统将自动安装。完成后关闭虚拟机,在“选项”中调整引导顺序至硬盘,并移除 ISO 镜像。

  6. 重启虚拟机,通过 PVE 控制台初始化 iKuai。根据 PVE 硬件信息中显示的 MAC 地址,将网卡与 iKuai 的接口(如 eth0, eth1)一一对应绑定,并设置 LAN 口 IP。

系统内设置

使用 192.168.100.1 访问 iKuai Web 管理界面,完成配置。

  • WAN1: 配置为 PPPoE 拨号或 DHCP,作为主外网出口。
  • WAN2: 配置为静态 IP,绑定 vmbr1 对应的虚拟网卡。
    • IP 地址:192.168.200.2
    • 网关:192.168.200.1
    • 关键配置: 将此线路设置为 默认线路。此举的目的是让所有流量在默认情况下都经过透明网关,再通过后续的分流规则将国内流量“豁免”。
    • 开启 掉线切换,并设置线路检测,确保透明网关异常时网络可用性。
  • LAN1:
    • 在“高级设置”中,启用 链路桥接,将所有内网接口(物理和虚拟)桥接在一起,创建一个统一的广播域,确保所有设备都在同一局域网内。

wan2

配置分流

  1. 进入 流控分流 > 多线负载,添加自定义运营商。将 china_ip_list 的 IP 段复制进来。由于 iKuai UI 存在输入长度限制,需要将 IP 列表分批添加。
  2. 添加负载均衡规则,将目标地址为国内 IP 的流量,强制走 WAN1 出口,实现国内流量直连。

分流

添加端口分流规则防止回环

什么是路由回环 (Routing Loop)?

在本架构中,一个数据包的路径可能是:客户端 -> iKuai -> 透明网关 -> iKuai -> 互联网。当数据包从透明网关返回 iKuai 时,如果 iKuai 依然按照默认路由(即 WAN2)处理,就会把这个包再次发给透明网关,形成 iKuai -> 网关 -> iKuai 的死循环,导致网络中断。

为防止回环,需添加一条规则:将源地址为透明网关 IP (192.168.100.2) 的所有流量,强制从 WAN1 出口发出。

防止回环

透明网关

推荐使用 LXC 容器而非完整虚拟机,因为它更轻量,资源开销小,网络性能接近宿主机。我选择 PVE 官方 CT 模板的 debian 12 作为 mihomo 和 dns 服务的运行时。

新建 LXC 容器

LXC 配置文件 (/etc/pve/lxc/111.conf) 示例:

arch: amd64
cores: 2
features: nesting=1
hostname: gateway
memory: 2048
net0: name=wan0,bridge=vmbr2,gw=192.168.100.1,hwaddr=BC:24:11:B8:D5:64,ip=192.168.100.2/24,type=veth
net1: name=lan0,bridge=vmbr1,hwaddr=BC:24:11:AF:9E:03,ip=192.168.200.1/24,type=veth
ostype: debian
rootfs: local:111/vm-111-disk-0.raw,size=8G
swap: 2048
unprivileged: 1
nameserver: 223.5.5.5

网络配置

编辑 LXC 容器内的 /etc/network/interfaces,为 lan0(接收 iKuai 流量)和 wan0(发回 iKuai)配置静态 IP。

auto lo
iface lo inet loopback

auto lan0
iface lan0 inet static
        address 192.168.200.1/24
        gateway 192.168.200.2

auto wan0
iface wan0 inet static
        address 192.168.100.2/24

Mihomo

安装与配置

  1. 从 Github Releases 下载二进制文件并放置到 /usr/local/bin/
  2. 创建配置目录 /etc/mihomo
  3. 编写核心配置文件 /etc/mihomo/config.yaml。与本方案相关的关键配置如下:
# ... (其他配置)
tproxy-port: 7894 # TProxy 监听端口,需与 nftables 脚本一致
routing-mark: 6666 # Mihomo 自身发出流量的标记,用于在 nftables 中豁免,防止代理自身

dns:
  enable: true
  listen: 0.0.0.0:1053 # DNS 服务监听端口
# ... (其他配置)

服务化 (Systemd)

创建 /etc/systemd/system/mihomo.service 文件。

Systemd Capabilities (权能) 说明

CapabilityBoundingSetAmbientCapabilities 是 Linux 的安全特性,用于授予进程精确的权限,而非给予完整的 root 权限。

  • CAP_NET_ADMIN: 允许程序修改网络接口、路由表、防火墙规则等。TProxy 设置需要此权限。
  • CAP_NET_RAW: 允许程序使用 RAW 和 PACKET 套接字。
  • CAP_NET_BIND_SERVICE: 允许程序绑定到 1024 以下的特权端口。

这种配置方式比直接使用 User=root 更安全。

[Unit]
Description=Mihomo Daemon, a Clash API compatible core.
After=network.target

[Service]
Type=simple
LimitNPROC=500
LimitNOFILE=1000000
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE
Restart=always
ExecStartPre=/usr/bin/sleep 1s
ExecStart=/usr/local/bin/mihomo -d /etc/mihomo
ExecReload=/bin/kill -HUP $MAINPID

[Install]
WantedBy=multi-user.target

TProxy 透明代理

什么是 TProxy?

TProxy (Transparent Proxy) 是 Linux 内核的一项功能,它允许一个应用程序(如 Mihomo)在不修改数据包目的 IP 地址和端口的情况下,接收并处理本应发往其他地址的数据包。相比传统的 REDIRECT (DNAT) 模式,TProxy 能让服务端程序看到真实的客户端源 IP 地址,且对 UDP 的支持更完善,性能也更优。

步骤一:开启 IP 转发

tee /etc/sysctl.conf <<-'EOF'
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
EOF

步骤二:配置防火墙与策略路由

启动脚本: /etc/mihomo/ip-route-poststart.sh

#!/bin/sh
# 1. 在策略路由表 100 中,添加一条默认路由,所有流量从 lan0 发出
ip route add local default dev lan0 table 100
# 2. 添加一条规则:为防火墙标记(fwmark)为 1 的数据包,应用策略路由表 100
ip rule add fwmark 1 lookup 100
# 3. 加载 nftables 防火墙规则集
nft -f /etc/mihomo/nftrules.nft

停止脚本: /etc/mihomo/ip-route-stop.sh

#!/bin/sh
# 按相反顺序清理规则
nft delete table ip clash
ip route del local default dev lan0 table 100
ip rule del fwmark 1 lookup 100

nftables 规则集: /etc/mihomo/nftrules.nft

#!/usr/sbin/nft -f

## 只处理指定网卡的流量,要和ip规则中的接口操持一致
define interface = lan0

## clash的透明代理端口
define tproxy_port = 7894

## clash打的标记(routing-mark)
define clash_mark = 6666

## 常规流量标记,ip rule中加的标记,要和ip规则中保持一致,对应 "ip rule add fwmark 1 lookup 100" 中的 "1"
define default_mark = 1

## 本机运行了服务并且需要在公网上访问的tcp端口(本机开放在公网上的端口),仅本地局域网访问的服务端口可不用在此变量中,以半角逗号分隔
define local_tcp_port = {
    22,      # ssh,按需设置
    8000-9999  # http
}

## 要绕过的局域网内tcp流量经由本机访问的目标端口,也就是允许局域网内其他主机主动设置DNS服务器为其他服务器,而非旁路由
define lan_2_dport_tcp = {
    53, 5353, 5354     # dns查询
}

## 要绕过的局域网内udp流量经由本机访问的目标端口,也就是允许局域网内其他主机主动设置DNS服务器为其他服务器,而非旁路由;另外也允许局域网内其他主机访问远程的NTP
服务器
define lan_2_dport_udp = {
    53, 5353, 5354,    # dns查询
    123    # ntp端口
}

## 保留ip地址
define private_address = {
    127.0.0.0/8,
    100.64.0.0/10,
    169.254.0.0/16,
    224.0.0.0/4,
    240.0.0.0/4,
    10.0.0.0/8,
    172.16.0.0/12,
    192.168.0.0/16
}

table ip clash {
    ## 保留ipv4集合
    set private_address_set {
        type ipv4_addr
        flags interval
        elements = $private_address
    }

    ## prerouting链
    chain prerouting {
        type filter hook prerouting priority filter; policy accept;
        ip protocol { tcp, udp } socket transparent 1 meta mark set $default_mark accept # 绕过已经建立的连接
        meta mark $default_mark goto clash_tproxy                                        # 已经打上default_mark标记的属于本机流量转过来的,直接进入透明代理
        fib daddr type { local, broadcast, anycast, multicast } accept                   # 绕过本地、单播、组播、多播地址
        tcp dport $lan_2_dport_tcp accept                                                # 绕过经由本机到目标端口的tcp流量
        udp dport $lan_2_dport_udp accept                                                # 绕过经由本地到目标端口的udp流量
        ip daddr @private_address_set accept                                             # 绕过目标地址为保留ip的地址
        goto clash_tproxy                                                                # 其他流量透明代理到clash
    }

    ## 透明代理
    chain clash_tproxy {
        ip protocol { tcp, udp } tproxy to :$tproxy_port meta mark set $default_mark
    }

    ## output链
    chain output {
        type route hook output priority filter; policy accept;
        oifname != $interface accept                                   # 绕过本机内部通信的流量(接口lo)
        meta mark $clash_mark accept                                   # 绕过本机clash发出的流量
        fib daddr type { local, broadcast, anycast, multicast } accept # 绕过本地、单播、组播、多播地址
        udp dport { 53, 5353, 123 } accept                             # 绕过本机dns查询、NTP流量
        tcp sport $local_tcp_port accept                               # 绕过本地运行了服务的tcp端口,如果并不需要从公网访问这些端口,可以注释掉本行
        ip daddr @private_address_set accept                           # 绕过目标地址为保留ip的地址
        ip protocol { tcp, udp } meta mark set $default_mark           # 其他流量重路由到prerouting
    }
}

步骤三:集成到 systemd 服务

/etc/systemd/system/mihomo.service 加入声明周期脚本,令 systemd 维护 mihomo 的时候自动执行:

ExecStartPost=/etc/mihomo/ip-route-poststart.sh
ExecStop=/etc/mihomo/ip-route-stop.sh

DNS

DNS 查询链

本方案构建了一个“责任链模式”的 DNS 解析流程:客户端 -> AdGuard Home -> SmartDNS -> 最终上游 DNS。每个环节只做自己的专职工作:AdGuard Home 负责过滤广告和黑名单,SmartDNS 负责对国内外域名进行智能、快速的解析。这种分层设计使得结构清晰,易于排错和维护。

  1. 安装 SmartDNS: apt install smartdns。配置好国内外两组上游 DNS 服务器。为获得最佳性能,禁用测速 (speed-check-mode none) 并开启缓存持久化。
  2. 安装 AdGuard Home:
    • 在 AdGuard Home 的上游 DNS 设置中,仅填入 SmartDNS 的监听地址(如 127.0.0.1:5353)。
    • 关闭 AdGuard Home 的查询缓存,避免与 SmartDNS 重复缓存。
    • 去除 客户端 DNS 查询限制
  3. 整合: 在 iKuai 的 DHCP 服务 -> LAN 口设置 中,将主 DNS 服务器地址修改为透明网关的地址:192.168.200.1。这样所有内网设备获取到的 DNS 服务器就是 AdGuard Home。

dns

自动更新配置

为了实现低维护运行,各类配置应尽可能自动化更新。

  • iKuai 大陆 IP 分流: 手动更新即可,IP 列表变动不频繁。
  • Mihomo 订阅配置: 推荐部署私有 sub-store 用于聚合。在透明网关中设置 cron 定时任务,定期 wget 新的配置文件并 systemctl restart mihomo
  • SmartDNS 国内域名列表: 定时任务从 Olixn/china_list_for_smartdns 等项目下载更新列表并重启 SmartDNS。
  • AdGuard Home 过滤规则: 直接在 AdGuard Home 的 Web UI 中订阅优秀的过滤列表即可自动更新。
  • AdGuard Home 过滤规则自定义规则同步: TODO。

参考资料

🔲 ⭐

Fluency Support Full Rss

最近 Follow 这个内容浏览器十分热门, 我把自己博客添加进去后发现 Hugo 的 RSS 默认并没有全文输出.

所以给当前在用的 Fluency 主题增加了一个可选配置,可以开启全文 RSS 输出.

又水一篇.

This message is used to verify that this feed (feedId:55671961817022580) belongs to me (userId:67409085208279040). Join me in enjoying the next generation information browser https://follow.is.

🔲 ⭐

PVE 安装 Android x86 & 安装 Magisk

下载镜像

首先选择镜像,我一开始尝试的是成 CM 的 7.0 镜像但由于是全虚拟的系统,这个版本的系统开启 ADB 后,虽然可以开启网络端口监听,但无法首次就用网络连接(无法 auth)。PrimeOS 也是遇到同样的问题甚至无法直接开启网络 ADB。后来换用 Android x86 9.0 版本,自带 root 权限,默认开启 ADB 且支持首次网络连接。

镜像下载:https://mirrors.tuna.tsinghua.edu.cn/osdn/android-x86/

折腾完发现有打好 Magisk 的镜像,https://androidx86magisk.osdn.io/,如果早点看到本文完结。

提取 boot.img

  1. 从 ISO 镜像中复制出 kernelramdisk.img 这两个文件
mkdir -p /mnt/temp
mount -t iso9660 -o loop android.iso /mnt/temp
cp /mnt/temp/kernel .
cp /mnt/temp/ramdisk.img .
umount /mnt/temp
  1. 安装工具软件 mkbootimgabootimg(在 Linux 开发系统,如以下命令以 Debian 系为例)
apt install android-tools-mkbootimg abootimg
  1. kernelramdisk.img 打包成 boot.img
mkbootimg --kernel kernel --ramdisk ramdisk.img -o boot.img

mkbootimg 是 Android 系统中一个用于创建内核镜像文件的工具,常见用法和参数说明如下:

mkbootimg \
--kernel <kernel-image> \
[--ramdisk <ramdisk-image>] \
[--second <second-image>] \
[--dtb <dtb-image>] \
[--cmdline <kernel-command-line>] \
[--base <address>] \
[--pagesize <size-in-bytes>] \
[--ramdisk_offset <address>] \
[--tags_offset <address>] \
[--ivt_offset <address>] \
[--os_version <version>] \
[--os_patch_level <yyyy-mm-dd>] \
[--header_version <version>] \
-o <image-output-file>

参数说明:

  • --kernel: 指定内核镜像文件路径。
  • --ramdisk: 可选参数,指定 RAM disk 镜像文件路径。如果在 cmdline 中指定将使用另外一个 ramdisk-image。
  • --second: 可选参数,指定第二阶段启动器镜像文件路径。
  • --dtb: 可选参数,指定设备树二进制文件路径。
  • --cmdline: 内核命令行,指定内核启动参数。
  • --base: 可选参数,指定内存基址,为一个 16 进制数。
  • --pagesize: 页面大小,指定内存页面大小,默认为 2048。
  • --ramdisk_offset: RAM disk 镜像文件在内存中的 offset。
  • --tags_offset: Tag 信息在内存中的 offset。
  • --ivt_offset: IVT 信息在内存中的 offset。
  • --os_version: 操作系统版本号。
  • --os_patch_level: 操作系统补丁等级,格式为 yyyy-mm-dd
  • --header_version: Boot image header 版本号,默认值为 1。
  • -o: 输出镜像的文件路径。

除了上述参数外,还有其他较少用到的参数,如 --hash 等。根据实际情况需要设置不同的参数。

开启 ADB

  1. 系统 - 关于 中多次点击版本号,直到开启开发者模式
  2. 开发者选项 中开启ADB
  3. 关于 - 状态 中查看当前 IP
  4. 通过 ADB 连接 adb connect 192.168.100.1

boot.img 补丁

  1. 在 PVE 中创建虚拟机(如下图参数,不添加额外 EFI 盘),用上面步骤下载的 ISO 安装系统(如果有现有的 Android 系统,安装 Magisk 即可用来打补丁)

  1. 引导菜单进入 ISO 启动菜单,选择 Advence Options,然后选择自动安装(此次安装系统只是为了打补丁,并不用于实际运行)。进入系统后,安装 Magisk APK。

  2. 用 ABD 将 boot.img 传输到 Android 系统中

adb push ./boot.img /storage/self/primary/Download/
  1. 在 Magisk 中点击安装,选择并修补一个文件(选择 boot.img

  1. Magisk 补丁后将会在相同目录生成一个 img 文件 magisk_patched-xxx.img,使用 ADB pull 将文件下载到本地。

打包新系统镜像

abootimg -x patched_boot.img

先解压 patched_boot,然后将 zImage 重命名为 kernelinitrd.img 重命名为 ramdisk.img

# 新建临时处理文件夹
mkdir -p /tmp/original_iso
mkdir -p /tmp/new_iso
# 挂载原始镜像 android.iso
mount -t iso9660 -o loop ./android.iso /tmp/original_iso
# 复制 ISO 中所有文件到新的镜像临时目录
cd  /tmp/original_iso && tar cf - . | (cd /tmp/new_iso; tar xfp -)
# 放入新的 patched 文件
cp ./kernel /tmp/new_iso
cp ./ramdisk.img /tmp/new_iso
# 重新打包为 EFI 可引导 ISO
xorriso -as mkisofs -c isolinux/boot.cat -b isolinux/isolinux.bin -no-emul-boot -boot-load-size 4 -boot-info-table -eltorito-alt-boot -e boot/grub/efi.img -no-emul-boot -isohybrid-gpt-basdat -o ./androidx86-patched.iso  /tmp/new_iso

处理完毕后解除挂载 umount /tmp/original_iso

Windows 系统使用 UltraISO 打开原始 Android x86 镜像,将补丁后的 kernelramdisk.img 拖进去,导出新镜像。

安装

用上述提到的 PVE 参数新建虚拟机(或者复用上述现有虚拟机),ISO 镜像选择导出的新镜像。启动虚拟机,选择普通安装模式。

进入 gdisk 分区界面

  • 选择 New 创建 EFI 分区(起始位置默认,大小 250Mb,分区类型 ef00
  • 再次选择 New 创建系统分区(起始位置默认,大小默认全部,分区类型默认 8300
  • 选择 Write 写入

将系统分区格式化为 ext4,第一个分区(EFI)将会默认格式化为 vfat

/system 设置为可读写(下图选择 yes

启动 & 使用

安装系统后,启动 Android,并安装 Magisk 可以看到已经是安装好的状态。

问题

由于 x86 官方镜像里已带有 su,打开 Magisk 检测到多个 su 则会提示:不属于Magisk的su文件异常状态怎么办

开发机内# adb root
开发机内# adb shell
android 内# su root
android 内# rm /system/xin/su

另外此种方式安装的 Magisk 每次启动都会如下图提示,但功能不受影响。

安装 Lsposed

首先安装 riru,从 Github 下载 riru-xxx-release.zip,使用 adb push 上传到 Andorid 中,然后在 Magisk 模块中选择 从本地安装

然后下载 LSPosed-xxx-riru-release.zip,使用 adb push 上传到 Andorid 中,然后在 Magisk 模块中选择 从本地安装。重启系统即可。

相关项目和参考

🔲 ⭐

[All In One] HomeLab 2023 搭建总结

上个月由于搬家+宽带升级,所以就想将自己原来的传家宝 N1 旁路由方案升级一下,既然硬件升级了,那就要承载更多的应用。

“All-in-One”(一体机)是指一台设备包含了多个功能模块的电子产品。这些设备通常把所有的功能集成到一个单一的、紧凑的设备中,包括计算机、显示器、摄像头、扬声器、输入设备等等。All-in-One产品可用于各种场合,特别是在Office、教育、医疗和娱乐等领域,由于其一体化的设计和简单易用的操作,深受消费者的欢迎。 Homelab(个人实验室)是一个IT专业人士用来测试、研究、学习、模拟企业环境的地方,通常由个人在家中或小型办公室中自行搭建,其中包括了不同类型的计算机、网络设备、虚拟化软件、存储设备等。Homelab和NAS的概念有些相似,都是为了满足个人或小型组织的需求而搭建的私有化的计算机和存储环境。

搭建一个 HomeLab 应该需要考虑以下要点:

  • 运行的稳定性
  • 功能的可扩展性
  • 网络的安全性
  • 数据的容错性和安全性
  • 运行成本

但每个人都应该根据自己的使用场景,对这几个方面进行权衡。对于我来说,我并不会完全把全部数据存储在本地,关键数据都会做云端备份,所以对于本地的 Homelab 来说,运行成本,合理利用资源是大于数据的容错性和安全性,在这前提之下,运行要保证基本的稳定性,在 all in boom 的场景有一定的 fallback 路径,并且最大化的利用软硬件资源。比如我的存储并没有组 Raid 或者 NAS 存储池令其容错性更高,我的无线也直接使用无线网卡,而没有使用更高速稳定的硬AP,所以我的实践仅供参考。

这几篇文章记录了我初步开始 HomeLab 的过程与结果,HomeLab 没有最终形态,一切都是在使用中演化成最适合自己的模式。

硬件清单

设备 用途 价格 购买途径
R71S N6000 小主机 主服务器 519 拼多多 Intel N6000
金百达 16GB DDR4内存 内存 173 京东
致态 Ti5000 1T 固态 存储 358 京东
西部数据 Element 12T 存储 1295 亚马逊 2020年
MTK MT7922 miniPCIE接口网卡 无线 WiFi 98 淘宝
NGFF 转 miniPCIE 板 转接板 7 淘宝
1.25mm 软路由风扇 散热 14 拼多多
信越 7921 硅脂 4克 散热 12 拼多多
固态散热马甲 散热 12 拼多多

功耗

可能功耗在大部分玩家的场景不是需要关心的,毕竟使用 E5 / i7 等等的大有人在。但我目前的租房电费较贵,功耗则成为我需要考虑的一个要点。

  • 光猫 + AP路由 + N1 + 西部数据Element(单独供电):19w
  • 光猫 + AP路由 + N1:9~10w
  • 光猫 + AP路由: 7w
  • 主机(新配置)PE待机 :9w
  • 光猫 + 主机(新配置)+西部数据Element(单独供电):22w

可以看出来总体的功耗增加了 3w,完全可以接受。N6000 的设计 TDP 为 6W,但待机功耗的统计数据确实不好看,果然网络上其他使用 11 代 CPU(如N5105)的反馈基本属实。另外 N1 不愧历经那么多年依旧能打,毕竟实际功耗可以达到 1~3w。

总体结构图

外部访问

  • tailscale:提供VPN访问
  • cloudflare tunnel + nginx proxy manager:内部服务穿透到外部

系列文章

🔲 ☆

[All In One] PVE LXC 安装 OpenWrt 软路由以及网络规划

作为 All in One 里面最重要的一环,软路由的安装和配置是最为重要的操作。这里我选择纯软路由,且无硬 AP 的拓扑。如果有多拨需求,可以选择 iKuai 主路由 + OpenWrt 旁路由的方案,我觉得我的场景已经真的 All In Boom 了,就不再改光猫桥接,在真的 boom 的时候还方便一点直接接光猫救急,所以这里我只选用 OpenWrt 作为主路由。路由的部署方式可以使用 VM 或者 LXC,在上一篇也提到了 Intel 11 代 CPU 的潜在虚拟化问题后,我这里部署方式选择为 PVE 下 LXC 容器。

OpenWrt 是一个开源的路由器固件,它基于 Linux 操作系统,并可运行在各种不同品牌的路由器上。OpenWrt 提供了丰富的功能和高度的可定制性,用户可以轻松地对其进行配置和定制,以满足不同的网络需求。OpenWrt 还支持安装各种软件包,例如 VPN、DNS、Web 服务器等,从而进一步扩展其功能。

LXC(Linux Containers)是 Linux 内核中的一个软件容器化工具,它提供了一种轻量级的虚拟化技术。与传统虚拟机相比,LXC 的优点在于更快的启动时间、更少的资源占用以及更高的性能。用户可以使用 LXC 在 Linux 操作系统中创建多个独立的容器,每个容器都提供一个隔离的运行环境,与主机和其他容器完全隔离开来。在容器内,用户可以运行各种不同的应用程序和服务,例如 Web 服务器、数据库、文件共享等。由于 LXC 能够更加高效地利用系统资源,因此它已成为开发人员、测试人员、系统管理员和云计算提供商的首选工具之一。

固件选择

OpenWrt 固件选择上我尝试过官网原版、Lean 预编译版、自编译 Lean 版、其他第三方编译版,包括自编译增加无线驱动,也在不同程度上出现:无法识别无线网卡、无线无法启动等等问题。最终选择以下固件最省心。

https://www.right.com.cn/forum/thread-928319-1-1.html

获取 CT 模板

SquashFS 是一种只读文件系统,不允许对其进行写操作,它可以将多个文件和目录合并到一个文件中,从而提高存储空间的利用率,同时也可以提高对这些文件的访问效率。SquashFS 文件系统具有高效的压缩和解压缩速度,支持很多不同的压缩算法,能够在存储和传输文件时减少网络流量和存储空间的占用,广泛应用于嵌入式系统、上传和下载镜像等场景。

Rootfs 是 Linux 系统启动时候使用的根文件系统,是指在文件系统概念中的根目录。它包含了 Linux 系统启动所必需的一些必要文件和目录,例如/etc、/proc、/bin、/sbin 和/usr 等。rootfs 文件系统通常会以类似于 ext4 或 SquashFS 文件系统的形式储存于嵌入式系统或者云计算平台中,为 Linux 系统提供启动和运行的必要支持。由于 rootfs 文件系统在启动过程中是唯一可用的文件系统,在系统运行时,它也会被其他文件系统所覆盖。

无论是 OpenWrt 官网下载,还是第三方打包的固件,必须是*-rootfs.img*-rootfs.img.gz 或者 *-rootfs.tar.gz 的文件 。*-rootfs.img*-rootfs.img.gz 需要解包后打包 。

首先需要获取 CT 模板,如果是 rootfs 格式打包的上传至 PVE CT 模板目录即可,否则需要先解包重新打包使用。

以下以 *.img.gz 固件包为例:

cd workspace
mkdir openwrt
# 文件名
img_name="openwrt-x86-64-generic-squashfs-combined.img.gz"
# gzip 解压出 img
gzip -d "$img_name"
# 去除变量 gz 后缀
img_name=$(basename "$img_name" .gz)
# 挂载偏移
root_partition=$((`fdisk -l "$img_name" | grep .img2 | awk '{print $2}'` * 512))
# 挂载到 openwrt 目录
mount -o loop,offset=$root_partition "$img_name" ./openwrt
# 打包
cd openwrt && tar zcf /var/lib/vz/template/cache/"$img_name".tar.gz * && cd ..
# 卸载
umount ./openwrt

创建 LXC 容器

pct create 100 \
	local:vztmpl/openwrt.tar.gz \
	--rootfs local:2 \
	--ostype unmanaged \
	--hostname OpenWrt \
	--arch amd64 \
	--cores 2 \
	--memory 1024 \
	--swap 0

此处以 vmid=100 为例,可自定义,创建完先进行配置再启动。

配置 LXC 容器

修改容器配置

root@pve:~ # cat /etc/pve/lxc/100.conf
arch: amd64
cores: 2
hostname: OpenWrt
memory: 1024
ostype: unmanaged
rootfs: local:101/vm-101-disk-0.raw,size=2G
swap: 0
# 设置DNS,根据实际情况填写
nameserver: 192.168.1.1
# 引用 PVE自带 openwrt 配置文件,包含一些基本设置
lxc.include: /usr/share/lxc/config/openwrt.common.conf
# 将主机的网卡 enp6s0/wlp2s0 分配给容器使用,根据自己的实际情况更改
xc.net.0.type: phys
lxc.net.0.link: enp6s0
lxc.net.0.flags: up
lxc.net.0.name: wan
lxc.net.1.type: phys
lxc.net.1.link: wlp2s0
lxc.net.1.flags: up
lxc.net.1.name: wlan
lxc.net.2.type: veth
lxc.net.2.link: vmbr0
lxc.net.2.flags: up
lxc.net.2.name: eth1
lxc.net.3.type: veth
lxc.net.3.link: vmbr1
lxc.net.3.flags: up
lxc.net.3.name: eth2

挂载 PPP/TUN

方法 1: 使用 hook 脚本(需特权容器):

# 创建 hook 脚本
mkdir -p /var/lib/vz/snippets
cp /usr/share/pve-docs/examples/guest-example-hookscript.pl /var/lib/vz/snippets/lxc-hookscript.pl
# vim /var/lib/vz/snippets/lxc-hookscript.pl
...
    # Second phase 'post-start' will be executed after the guest
    # successfully started.

    system("lxc-device add -n $vmid /dev/ppp");
    system("lxc-device add -n $vmid /dev/net/tun");

    print "$vmid started successfully.\n";
...

在容器配置中添加 hook:

hookscript: local:snippets/lxc-hookscript.pl

方法 2: 直接在容器配置中加入挂载配置

挂载 ppp/tun 设备,以及允许容器使用 major=108,minor=0 以及 major=10,minor=200 的设备。

lxc.cgroup2.devices.allow: c 108:0 rwm
lxc.mount.entry: /dev/ppp dev/ppp none bind,create=file
lxc.cgroup2.devices.allow: c 10:200 rwm
lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file

启动 LXC 容器

正常启动信息如下

root@pve:~ # pct start 100
GUEST HOOK: 100 pre-start
100 is starting, doing preparations.
GUEST HOOK: 100 post-start
100 started successfully.

如果无法启动可使用 debug 模式(日志保存在 debug.log 文件)

lxc-start -n 100 --logfile debug.log --logpriority TRACE

如果是挂载了无线网卡可能因为没有安装 iw 而启动失败:

apt install iw

OpenWrt 配置

Attach 到容器内进行操作 lxc-attach 100

设置密码

passwd
# Changing password for root
# New password:
# Retype password:
# passwd: password for root changed by root

配置网络

vim /etc/config/network
...
config interface 'lan'
        option type 'bridge'
        list ifname 'eth1'
        option proto 'static'
        option ip6assign '60'
        option netmask '255.255.255.0'
        option ipaddr '192.168.1.1' # 关键配置 IP 地址
        option gateway '192.168.1.1' # 本机
...

重启网络 /etc/init.d/network restart,完成之后就能使用 SSH 或者 网页打开 OpenWrt 了。

开启无线

  1. ifconfig -a 查看无线网卡是否能被识别
  2. wifi config
  3. LUCI 配置无线
    1. 国家用美国,2.4G 开信道 11,5G 开信道 149(解决 无线未开启或者未关联

OpenWrt 自身无法上网

如果是二级路由,在 DHCP/DNS 中设置 DNS 转发至上层路由或者临时修改 /etc/resolve.conf

openclash 无法启动

refer to https://github.com/vernesong/OpenClash/issues/1915

openclash 启动失败,日志中出现

Unable to set capabilities [--caps=cap_sys_resource,cap_dac_override,cap_net_raw,cap_net_bind_service,cap_net_admin+eip]

是因为引用的 PVE 默认的 openwrt 配置中 /usr/share/lxc/config/openwrt.common.conf 中 drop 掉了权限 lxc.cap.drop = sys_resource

可以在容器配置中加入此行

root@pve:~ # cat /etc/pve/lxc/100.conf
# ...
lxc.cap.drop:

Adguard Home 与 OpenClash

科学上网方案我还是沿用一直使用的 Adguard + OpenClash。

  • OpenClash 接管流量做分流和科学上网
  • OpenClash 使用 meta 内核(基本支持 Premium 的功能)
  • DNS 查询路径:client->adguard->clash(fake-ip),跟以前使用过的方案路径有不一样了,好处是 Adguard Home 中可以看到客户端 IP,也不会因为广告域名走了 clash 的代理模式而不走 Adguard 解析。

另外说一句, OpenClash 的 GUI 配置实在是太难用了(无论是布局、菜单、交互),还好有直接编辑 YAML 的方式配置。

网络配置

以下是我的网络拓扑示意,目前我的所有设备基本上都是以无线方式连接,还有两个网口是闲置状态。

  • 除了无线网卡和与光猫通信的网口是直通之外,其余网口都是以网桥方式接入。
  • enp3s0、enp4s0、enp5s0 这几个网口连接进 OpenWrt 的方式可以有很多种,不一定是我这里使用的这种,比如只建立一个网桥 vmbr0 关联三个网卡,或者建立 bond 绑定三个网口,再基于 bond 建立网桥,都可以正常使用。
  • 下图的 vmbr4 也可以省略,直接使用有物理网口的网桥也可以正常使用。

OpenWrt 容器的网络挂载(页面没有显示直通设备,可以在 /etc/pve/lxc/vmid.conf 中配置)

远程组网

  1. 安装 tailsacle:opkg install tailscale
  2. 登录:tailscale up
  3. 开启子网路由:tailscale up --advertise-routes=192.168.100.0/24 --accept-dns=false
  4. Tailscale 页面编辑对应的设备,禁用 Key 过期
  5. Tailscale 页面编辑对应的设备,开启子网

这时就可以从其他网络的 tailscale 连通设备连接 OpenWrt 的子网。

参考

总结

总体来说,OpenWrt 使用 LXC 方式部署成软路由已经非常成熟,大部分固件都可以运行。只是如果要使用无线网卡的话,必须要解决好驱动问题,就很稳定了。

速度测试

用 iperf3 进行测试(TCP 双向传输) iperf3 -c 192.168.100.6 -d -t 60

发送 接收
无线(WIFI6) 688 Mbits/sec 688 Mbits/sec 近距离
有线(千兆) 936 Mbits/sec 935 Mbits/sec MacBook + 千兆 HUB
内网 20.3 Gbits/sec 20.2 Gbits/sec PVE 两个 Docker 间

有线基本跑满硬件带宽,无线有一些损耗,但我的宽带也就 600M,所以基本达到上限。

无线网卡选择?

本来打算购买 Intel AX210 无线网卡,但购买之前,看恩山论坛的讨论,由于驱动限制 AX210 似乎还不能以 AP 模式在 OpenWrt 下使用,所以购买了 MTK 7922 网卡。

实测 MTK 7922 网卡可以以 AP 模式发出 5G Wi-Fi 信号,但不能同时发出 2.4G 信号。

设备找不到?

在测试固件的过程中,由于部分固件的驱动兼容不好,容易造成无线网卡假死等情况。由于网卡是直通 LXC 容器,有可能会导致 PVE 系统丢失了设备,设备能识别,但是设备加载失败。

所以如果想要隔离性更好还是选择 VM 安装 OpenWrt。

[    0.359071] DMAR: IOMMU feature mts inconsistent
[    3.378372] mt7921e 0000:02:00.0: enabling device (0000 -> 0002)
[    3.378663] mt7921e 0000:02:00.0: Direct firmware load for mediatek/WIFI_RAM_CODE_MT7922_1.bin failed with error -2
[    3.398324] mt7921e 0000:02:00.0: ASIC revision: 79220010
[    3.483152] mt7921e 0000:02:00.0: Direct firmware load for mediatek/WIFI_MT7922_patch_mcu_1_1_hdr.bin failed with error -2
[    3.566637] mt7921e 0000:02:00.0: Direct firmware load for mediatek/WIFI_MT7922_patch_mcu_1_1_hdr.bin failed with error -2
[    3.650631] mt7921e 0000:02:00.0: Direct firmware load for mediatek/WIFI_MT7922_patch_mcu_1_1_hdr.bin failed with error -2
[    3.734476] mt7921e 0000:02:00.0: Direct firmware load for mediatek/WIFI_MT7922_patch_mcu_1_1_hdr.bin failed with error -2
[    3.818421] mt7921e 0000:02:00.0: Direct firmware load for mediatek/WIFI_MT7922_patch_mcu_1_1_hdr.bin failed with error -2
[    3.906385] mt7921e 0000:02:00.0: Direct firmware load for mediatek/WIFI_MT7922_patch_mcu_1_1_hdr.bin failed with error -2
[    3.994418] mt7921e 0000:02:00.0: Direct firmware load for mediatek/WIFI_MT7922_patch_mcu_1_1_hdr.bin failed with error -2
[    4.082389] mt7921e 0000:02:00.0: Direct firmware load for mediatek/WIFI_MT7922_patch_mcu_1_1_hdr.bin failed with error -2
[    4.166425] mt7921e 0000:02:00.0: Direct firmware load for mediatek/WIFI_MT7922_patch_mcu_1_1_hdr.bin failed with error -2
[    4.250406] mt7921e 0000:02:00.0: Direct firmware load for mediatek/WIFI_MT7922_patch_mcu_1_1_hdr.bin failed with error -2
[    4.336774] mt7921e 0000:02:00.0: hardware init failed
🔲 ☆

AdguardHome 和 Clash 透明代理配合使用

image-20210417112325830

配置

我的方案如上图是 Adguard 作为 Clash 的上游 DNS。然后将路由的 DHCP 配置的 DNS 服务器改为 Clash 的 DNS 地址即可。

首先启动 Adguard:

docker run -d \
--name adguard \
--restart unless-stopped \
--log-opt max-size=1m \
-v /root/adguard/work:/opt/adguardhome/work \
-v /root/adguard/conf:/opt/adguardhome/conf \
-p 5656:53/tcp \
-p 5656:53/udp \
-p 3000:3000 \
-p 3001:80 \
adguard/adguardhome

然后 Clash 配置入口 DNS 为 5353 端口,然后设置 nameserver 上游 DNS 为 Adguard 地址。

dns:
  enable: true
  listen: "0.0.0.0:5353"
  ipv6: false
  nameserver:
    - "127.0.0.1:5656"

然后使用 iptables 将 53 端口流量转发到实际的 5353 端口:

iptables -t nat -I PREROUTING -p udp --dport 53 -j REDIRECT --to 5353

部分发行版 53 端口可能已被 systemd-resolved 占用,先改掉:

vi /etc/systemd/resolved.conf

DNSStubListener=no  #取消注释,把yes改为no

问题

但是发现 Clash 的 DNS 查询全部超时失败了,直接使用 AdguardHome 的 DNS 查询也超时。排查日志发现:

  1. Clash 代理流量需要连接代理服务器,我的代理服务器配置的是域名,然后 DNS 查询这个代理服务器的域名失败了
  2. AdguardHome 连接 DOT/DOH DNS 查询时连接失败

想了很久没有想出原因,后来尝试进入 AdguardHome 容器使用 nc 连接 IP 调试:

nc 1.2.3.4 80

# 然后在 clash 日志则出现连接日志
time="2020-11-01T05:59:39Z" level=info msg="[TCP] 172.17.0.3:38804 --> 1.2.3.4 match GeoIP(CN) using Domestic[DIRECT]"

由于我这台旁路由配置了透明代理(详细见Docker Clash 和 透明代理),使用iptables 规则接管了全部网络流量,导致 AdguardHome 容器也走了代理流量,但 Clash 代理服务器的域名解析又依赖 AdguardHome DNS才能初始化代理。解决方案是:

iptables -t nat -I PREROUTING -p udp --dport 53 -d 192.168.0.0/16 ! -i docker0 -j REDIRECT --to 5353 # 只有局域网流量以及非 docker0 网卡的流量才重定向 53 端口

# 流量转发排除 docker0 网卡,即旁路由的所有 docker 容器都不再经过 clash
iptables -t nat -A PREROUTING -p tcp ! -i docker0 -j CLASH
iptables -t mangle -A PREROUTING -p udp ! -i docker0 -j CLASH
iptables -t nat -I CLASH -i docker0 -j RETURN
iptables -t mangle -I CLASH -i docker0 -j RETURN

使用效果

当然只用 Clash 也可以做到去污染、去广告等效果,但配置比 Adguard 麻烦且应用配置需重启服务。以下是 Adguard 使用效果。

image-20210417110255625

🔲 ⭐

使用 Kind 部署 Prow 手记

Prow 是 Kubernetes 使用的云原生 CI/CD 系统(https://github.com/kubernetes/test-infra/tree/master/prow),用于管理 Kubernetes 的 Issue 和 PullRequest 以支持开源协同。Prow 通过 GitHub 事件触发 job,通过处理事件状态实现自动化的 ChatOps。

实验环境:

Linux VM-16-11-debian 4.19.0-6-amd64 #1 SMP Debian 4.19.67-2+deb10u2 (2019-11-11) x86_64 GNU/Linux Docker Version 20.10.1

Install kubectl

# 下载 kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
# 下载 kubectl.sha256
curl -LO "https://dl.k8s.io/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl.sha256"
# 校验
echo "$(<kubectl.sha256) kubectl" | sha256sum --check
# sudo 安装
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl

或者用户安装

mkdir -p ~/.local/bin/kubectl
mv ./kubectl ~/.local/bin/kubectl
# and then add ~/.local/bin/kubectl to $PATH

参考文档 https://kubernetes.io/docs/tasks/tools/install-kubectl/

Install Kind

curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.10.0/kind-linux-amd64
chmod +x ./kind
mv ./kind /usr/local/bin/kind

参考文档 https://kind.sigs.k8s.io/docs/user/quick-start/#advanced

创建集群

kind create cluster

这将使用一个预编译好的节点镜像快速启动一个 Kubernetes 集群,默认集群名称为 kind ,可以使用 —name 参数指定自定义名称。

参考文档 https://kind.sigs.k8s.io/docs/user/quick-start/#installation

自定义创建集群参数

创建集群时候,可通过 —config 参数指定配置文件,示例配置见 kind-example-config

kind create cluster --config example.yaml

可通过指定节点角色的数量配比,实现不同需求。如下面示例配置则会创建一个双 master + 三worker 节点的高可用集群 

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: control-plane
- role: worker
- role: worker
- role: worker

部署 Prow

创建 prow namespace

kubectl create ns prow

GitHub 创建机器人用令牌

调试的时候可以直接用自己的 GitHub 帐号创建Token 使用,在正式使用的时候,为区分操作人,建议使用单独的机器人Github 帐号。

  • Token 需要有 repo:statuspublic_repo 权限
  • 项目仓库需将机器人帐号加入合作者中
  • 私有仓库则必须要有 repo 全部权限
  • 如果是 GitHub 组织接入,则额外需要勾选 admin:org_hook

创建 GitHub Token 对应的 secret

echo "my-github-token" > ./github-token
kubectl create secret -n prow generic github-token --from-file=./github-token

创建一个用于 Github WebHook 认证的 hmac 令牌,并创建 kubernetes secret

openssl rand -hex 20 > ./secret
kubectl create secret -n prow generic hmac-token --from-file=hmac=./secret

创建集群 Role Binding

kubectl create clusterrolebinding cluster-admin-binding-"${USER}" \
  --clusterrole=cluster-admin --user="${USER}"

应用 yaml 配置部署

下载 https://github.com/kubernetes/test-infra/blob/master/config/prow/cluster/starter-s3.yaml 配置,修改

  • 替换 Github token:<<insert-token-here>> 
  • 替换生成的 hmac token: << insert-hmac-token-here >>
  • 替换域名: << your-domain.com >>
  • 如果有使用证书管理器,则需更新 cert-manager.io/cluster-issuer:
  • 替换目标组织/项目: << your_github_org >>(如 wayjam/test-repoan-org/test-repo
tide:
      queries:
      - labels:
        - lgtm
        - approved
        missingLabels:
        - needs-rebase
        - do-not-merge/hold
        - do-not-merge/work-in-progress
        - do-not-merge/invalid-owners-file
        orgs:
        - << your_github_org >>
				repos:
				- wayjam/prow-test

应用了配置之后,发现部分 pods 启动失败(CrashLoopBackOff)

{"component":"prow-controller-manager","error":"tide query (index 0) is invalid: 'orgs' and 'repos' cannot both be empty","file":"prow/cmd/prow-controller-manager/main.go:123","func":"main.main","level":"fatal","msg":"Error starting config agent.","severity":"fatal","time":"2021-02-24T14:20:54Z"}

发现是有部份目标没有完全替换,重新修改配置,应用则全部 pods 可以正常启动。

配置外网访问

所有 pod 运行成功之后,上面的 starter.yaml 只包含了 ingress,但是没有包含 ingress controller,这里选择官方的 nginx ingress 作为流量入口,由于是测试,则不额外添加 LB。

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/kind/deploy.yaml

但是应用后发现 nginx pod 一直 pending,排查日志发现是由于我初始化的 kind 集群只有一个master 节点,不满足上面 yaml 配置的调度规则

Warning  FailedScheduling  76s (x5 over 100s)  default-scheduler  0/1 nodes are available: 1 node(s) didn't match Pod's node affinity

先把 Deployment 中的 tolerations 去掉,然后给 master 节点打上所需标签:

kubectl label node kind-control-plane ingress-ready=true

最好就是在集群初始化的时候按照 https://kind.sigs.k8s.io/docs/user/ingress/ 文档指引预先配置好集群,集群创建之后则不能再更改配置

除了节点标签之外,集群的端口还需对外暴露以提供服务,初始化集群的时候需按上面文档使用 extraPortMappings 参数指定转发的端口。

如果集群已经创建并已经部署好了服务,也像我一样不想重新创建集群,也可通过下面方法做快速端口转发。当然自行通过 Nginx 容器 link 到 kind 网络的 kind-control-plane 容器即可( nodeport 监听所在)。

root@VM-debian:~/$ kubectl -n ingress-nginx describe service ingress-nginx-controller
Name:                     ingress-nginx-controller
Namespace:                ingress-nginx
Labels:                   app.kubernetes.io/component=controller
                          app.kubernetes.io/instance=ingress-nginx
                          app.kubernetes.io/name=ingress-nginx
Annotations:              <none>
Selector:                 app.kubernetes.io/component=controller,app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx
Type:                     NodePort
IP Families:              <none>
IP:                       10.96.250.65
IPs:                      10.96.250.65
Port:                     http  80/TCP
TargetPort:               http/TCP
NodePort:                 http  32291/TCP
Endpoints:                10.244.0.107:80
Port:                     https  443/TCP
TargetPort:               https/TCP
NodePort:                 https  32191/TCP
Endpoints:                10.244.0.107:443
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

# 将 ingress-nginx 服务的 NodePort 32291 端口映射到本地 80 端口
$ docker run -itd --name ingress --network kind -p 80:80 --link kind-control-plane zhangsean/nginx-port-proxy kind-control-plane:32291
# 将 ingress-nginx 服务的 NodePort 32191 端口映射到本地 443 端口
$ docker run -itd --name ingress-https --network kind -p 443:80 --link kind-control-plane zhangsean/nginx-port-proxy kind-control-plane:32191

配置 WebHook

首先测试公网请求是否能通:

http POST https://prow.lockthedoor.ltd/hook --verify no

到 GitHub 项目设置添加 WebHook

  1. Payload URL: 填入公网地址,如 https://prow.lockthedoor.ltd/hook
  2. Content Type: application/json
  3. Secrets: 刚才随机生成的 HMAC
  4. Which events would you like to trigger this webhook: Send me everything
  5. SSL verification: 如果没有使用可信证书,则选择 Disable

配置webhook

新建一个 issue(如 https://github.com/wayjam/prow-test/issues/1),在评论框输入 /meow 机器人收到时间则会发送一个猫咪评论。

image-20210301010751579

🔲 ☆

自动给模板生成 Inline-Svg

在写 Hugo/Hexo 等主题的时候,如果需要使用 inline svg,则经常要手工从官方文档 copy 或者下载 collection 再自行拆分。于是有了这个项目,自动从模板中提取需要使用的 icon,生成 svg 到独立的模板文件内,模板编写者不再需要考虑 HTML 与图标来源,只需要写好模板,然后执行这个 cli 工具即可。

比如,在模板内编写 {{ partial "utils/icons" "logo-archive" }},工具会为你生成:

{{ $icon := . }}

{{ if eq $icon "archive" }}
<svg xmlns='http://www.w3.org/2000/svg' class='ionicon' viewBox='0 0 512 512'><path d='M64 164v244a56 56 0 0056 56h272a56 56 0 0056-56V164a4 4 0 00-4-4H68a4 4 0 00-4 4zm267 151.63l-63.69 63.68a16 16 0 01-22.62 0L181 315.63c-6.09-6.09-6.65-16-.85-22.38a16 16 0 0123.16-.56L240 329.37V224.45c0-8.61 6.62-16 15.23-16.43A16 16 0 01272 224v105.37l36.69-36.68a16 16 0 0123.16.56c5.8 6.37 5.24 16.29-.85 22.38z'/><rect x='32' y='48' width='448' height='80' rx='32' ry='32'/></svg>
{{ end }}

Github: https://github.com/wayjam/template-icon-extractor/,欢迎提交 PR。

🔲 ☆

「家庭娱乐」N1刷 EmuELEC 做游戏盒子

emuelec

前情

https://wayjam.me/posts/n1%E5%88%B7%E5%85%A5armbian/

下载

我采用B站大佬人中日月的整合包:https://www.bilibili.com/video/BV1DJ411H7Nb(以下所说网盘内容都在这个视频简介内)。由于之前已经装上 Armbian 了,那当然要利用好(使用 YAAW for Chrome 导出到 Aira2 下载百度网盘)。

以下操作均在 N1 with Armbian 中操作

下载整合包

解压

所有分卷下载完成后,解压出 IMG

apt-get install unrar
unrar e any-one-of-the-rar-file  # 任意一分卷,所有分卷在同一目录

如果系统语言没有设置好,解压完成可能会有错误:

WARNING: Cannot change attributes of EmuELEC3.3.1_2020?64G????S905.S912?_20200120.img
Cannot create ??.txt
No such file or directory
Total errors: 1

此时我们只要将解压出来的文件重命名为:emuelec.img 就可以了。

刷入

这里我选择的是 64G 的三星 TF卡,装入读卡器插入 N1 的 USB 口。

写入

由于镜像比较大,写入时间比较长,所以使用 nohup 让其在后台运行。

nohup dd if=/media/disk1/emuelec/EmuELEC3.3.1_2020.img of=/dev/sdb status=progress > dd.log &

由于是 USB2.0 接口,速度比较慢。如果你是下载镜像到自己的电脑然后再刷入,速度会快很多。

769131008 bytes (769 MB, 734 MiB) copied, 213 s, 3.6 MB/

修改 DTB

首先先根据 fdisk -l 查询对应U盘或TF卡的设备名称,然后使用 mount 挂载第一个 FAT32 分区。

然后根据自己的机型在 device_trees文件夹选择合适的 dtb 放到根目录。N1 的话直接将网盘内的 N1P1补丁包 - EmuELEC专用.zip 下载并解压,复制到根目录并覆盖即可。

升级

  1. 把压缩包(如 EmuELEC-3.4_S905.S912春节整合系统更新包.rar)里的两个文件解压后,替换TF卡FAT分区的同名文件 .1.1 如果跟我一样是在 Armbian 下操作,可以将 TF 卡插入 USB 并将其 FAT 分区挂载,然后将更新包复制到挂载点即可
  2. TF卡插入盒子开机后自动升级
  3. 系统自动进入启动画面并升级,升级完成会自动重启一遍

增量包

增量包的使用方法跟增加新游戏是一样的,具体可参考网盘内的 EmuELEC使用指南.pdf

emuelec游戏列表

配置

使用方法参照人中日月大佬的b站视频。

开机动画

可在设置关闭。

手柄

手柄或者无线接收器不支持热插拔,需要在开机之前插入好,否则无法识别。

设置手柄

🔲 ☆

「家庭娱乐」N1刷 Armbian 做家庭服务器

前言

由于想找点有趣的事情做,而且一直想玩玩家庭服务器,所以捡了一台 N1 做家庭服务器。由于卖家已经刷过 CoreELEC 了,所以不用再做降低等操作。

N1 作为去年矿渣的优秀代表,其硬件素质是非常过硬。N1 所用的 SOC 方案是晶晨 S905D,机器运行内存 2G,内置闪存 8G,相对于普通的 S905M,增加了 4K 60Hz 输出、VP9 硬解、HDR 以及千兆网卡。只是 USB 口都是 2.0,有点拉跨。并且在大神们的挖掘下玩法非常多样,既有传统 Armbian、也有专项应用系统:下载专用Linux 小钢炮、影音 CoreELEC、游戏 EmuELEC。玩家可以根据自己的喜好刷入对应的系统。

以下以 MacOS 环境为例

刷入 Armbian

根据恩山大神的经验,选择比较稳定的版本 Armbian_5.77_Aml-s905_Ubuntu_bionic_default_5.0.2_20190401.img ,毕竟做一个稳定的小服务器,就不再追新了。

使用 diskutil list 查看插入的 U盘 设备名称,然后将镜像写入

sudo dd if=./Armbian_5.77_Aml-s905_Ubuntu_bionic_default_5.0.2_20190401.img of=/dev/disk2 bs=1m

镜像写入后,打开 U盘,将 meson-gxl-s905d-phicomm-n1-xiangsm.dtb (从恩山论坛下载)文件放到 dtb/meson-gxl-s905d-phicomm-n1-xiangsm.dtb 下面,修改 uEnv.ini 内容:

dtb_name=/dtb/meson-gxl-s905d-phicomm-n1-xiangsm.dtb

查看 dd 的进度(macos):

ps aux | grep dd | grep -v "grep"
sudo kill -SIGINFO 40637

启动

N1 断电后,将 U盘 插入靠近 HDMI 接口的 USB 口,通电启动。然后使用 SSH 登录(在路由器找到 aml 的ip):

ssh root@the-ip-of-aml

刷入 EMMC

执行 nand–sata-install 将系统刷入 emmc。

其实应该先在 U盘 启动的时候,先配置好,然后再写入,以后如果需要重新安装,直接再次将U盘系统刷入 emmc 即可,或者先将 U盘打成 dmg 保存,需要的时候再写入 U盘 使用。但我这里不打算这样做,直接写入 emmc 再配置。

启动后发现某些操作会出现 Readonly 问题,Google 了一下可能是 emmc 出现坏快错误,再次重启使用U盘启动,修复 emmc 即可:

e2fsck /dev/mmcblk1p2

如果你是先配置好再写入,那么可以清除缓存加快一点点的写入速度:

  • history -c
  • apt-get clean

配置

挂载硬盘

根据 blkid 查看硬盘 UUID,修改 /etc/fstab 文件实现自动加载。如 EXT4 硬盘:

UUID=8b71cd3d-a8a6-4dce-8ba9-afc1da12e1ed /media/disk1 ext4 defaults 0 0

软件源配置

# /etc/apt/source.list
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ bionic main restricted universe multiverse
#deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ bionic main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ bionic-security main restricted universe multiverse
#deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ bionic-security main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ bionic-updates main restricted universe multiverse
#deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ bionic-updates main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ bionic-backports main restricted universe multiverse
#deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ bionic-backports main restricted universe multiverse

然后执行 apt-get update

Docker

执行 curl -fsSL https://get.docker.com/ | sh 安装,配置国内镜像 registry:

{
	"registry-mirrors": [
		"https://registry.docker-cn.com"
	]
}

个人觉得没有必要使用 Portainer,如果使用不是太熟练的话确实可以使用 UI 界面操作。

Samba

执行以下命令安装:

apt-get update
apt-get install samba

配置 Samba(一般挂载的硬盘都在 /media,所以我选择整个 /media共享 ):

vi /etc/samba/smb.conf
[n1]
   path = /media
   available = yes
   browseable = yes
   public = yes
   writable = yes

给用户设置 Samba 密码:smbpasswd -a root

Aria2

使用 p3terx 制作的 Aria2Pro 镜像:https://p3terx.com/archives/docker-aria2-pro.html

docker run -d \
--name aria2-pro \
--restart unless-stopped \
--log-opt max-size=1m \
--network host \
-e PUID=0 -e PGID=0 -e RPC_PORT=6800 -e LISTEN_PORT=6888 \
-v ~/aria-config:/config \
-v /media:/media \
p3terx/aria2-pro

Nginx

安装 Nginx,apt-get install nginx

Ariang

下载最新 Release:https://github.com/mayswind/AriaNg/releases,然后解压到 /var/www/html(Nginx 默认 serve 文件夹)。

ZeroTier

创建帐号:https://my.zerotier.com/

安装:curl -s https://install.zerotier.com | sudo bash

使用:zerotier-cli info 查看是否安装成功

加入:zerotier-cli join xxxx-network-id

设备加入之后一定要记得在 Network 页面勾选 Auth。

其他客户端

其他端的客户端可在官网下载:https://www.zerotier.com/download/

iOS 客户端在国区没有,可使用美区帐号下载

Locale 配置

设置完成之后中文不会显示乱码

# vi /etc/environment
LC_ALL="en_US.UTF-8"
LANG="en_US.UTF-8"

时区

ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo "Asia/Shanghai" > /etc/timezone

使用

  1. 使用 Aria2 下载

下载

首先需要使用油猴脚本(如网盘助手),生成下载直链;然后导入到 Aria2(我使用 YAAW 插件配置地址到 N1 的Aria2,右键网盘助手生成的链接即可导入)。

导出百度网盘下载到Aria2

  1. 通过 Samba 播放 Aria2 下载的剧

通过 Samba 播放

  1. 通过 ZeroTier 在其他网络访问 N1 上的资源

zerotier-usage

❌