<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Nite</title>
    <link>https://www.nite07.com/zh-cn/</link>
    <description>这里是Nite的小窝！</description>
    <generator>Hugo 0.164.0 &amp; FixIt v1.0.0-alpha</generator>
    <language>zh-CN</language>
    <managingEditor>nite@nite07.com (Nite)</managingEditor>
    <webMaster>nite@nite07.com (Nite)</webMaster>
    <lastBuildDate>Tue, 04 Aug 2026 03:10:52 +1000</lastBuildDate>
    <atom:link href="https://www.nite07.com/zh-cn/index.xml" rel="self" type="application/rss+xml" /><item>
      <title>Go GMP 模型</title>
      <link>https://www.nite07.com/zh-cn/posts/go-gmp/</link>
      <pubDate>Tue, 04 Aug 2026 03:10:52 +1000</pubDate><author>nite@nite07.com (Nite)</author>
      <guid>https://www.nite07.com/zh-cn/posts/go-gmp/</guid>
      <category domain="https://www.nite07.com/zh-cn/categories/note/">笔记</category>
      <description>&lt;p&gt;GMP 是 Go 调度器的三个核心组件：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;G（Goroutine，协程）：&lt;code&gt;go func()&lt;/code&gt; 创建的任务。&lt;/li&gt;&#xA;&lt;li&gt;M（Machine，机器）：操作系统线程。&lt;/li&gt;&#xA;&lt;li&gt;P（Processor，处理器）：调度上下文，管理 G 队列。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;M 就是内核级线程，由 runtime 通过 &lt;code&gt;newm&lt;/code&gt;/&lt;code&gt;newosproc&lt;/code&gt;（创建 M、创建线程的函数）调用系统接口创建，再交给 OS 调度到 CPU 上执行。M 是执行 Go 代码的最小单位，没有 P 的 M 只能休眠或处理系统调用。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;g-的状态与生命周期&#34;&gt;&lt;span&gt;G 的状态与生命周期&lt;/span&gt;&#xA;  &lt;a href=&#34;#g-%e7%9a%84%e7%8a%b6%e6%80%81%e4%b8%8e%e7%94%9f%e5%91%bd%e5%91%a8%e6%9c%9f&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;G 有明确的运行状态（&lt;code&gt;runtime2.go&lt;/code&gt; 里的枚举）：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Gidle（空闲）：刚分配，还没初始化。&lt;/li&gt;&#xA;&lt;li&gt;Grunnable（可运行）：在运行队列里排队，等 M 取。&lt;/li&gt;&#xA;&lt;li&gt;Grunning（运行中）：正在执行用户代码。&lt;/li&gt;&#xA;&lt;li&gt;Gsyscall（系统调用中）：执行 syscall，暂时脱离调度。&lt;/li&gt;&#xA;&lt;li&gt;Gwaiting（等待中）：被 park（挂起），等 channel、锁、IO。&lt;/li&gt;&#xA;&lt;li&gt;Gdead（已退出）：执行完，进 free 列表等待复用。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;pre&gt;&lt;code&gt;flowchart LR&#xA;    A[&amp;#34;Gidle 空闲&amp;#34;] --&amp;gt; B[&amp;#34;Grunnable 可运行&amp;#34;]&#xA;    B --&amp;gt; C[&amp;#34;Grunning 运行中&amp;#34;]&#xA;    C --&amp;gt; D[&amp;#34;Gsyscall 系统调用中&amp;#34;]&#xA;    C --&amp;gt; E[&amp;#34;Gwaiting 等待中&amp;#34;]&#xA;    D --&amp;gt; B&#xA;    E --&amp;gt; B&#xA;    C --&amp;gt; F[&amp;#34;Gdead 已退出&amp;#34;]&#xA;    F --&amp;gt; B&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;G 执行完后不直接销毁，而是进入空闲 G 缓存，等下次创建新 G 时复用。缓存分两级：每个 P 有一个本地缓存（&lt;code&gt;pp.gFree&lt;/code&gt;），全局有一个共享缓存池（&lt;code&gt;sched.gFree&lt;/code&gt;，加锁）。归还时先进 P 的本地缓存；创建新 G 时先从本地缓存取，本地缓存空了再去全局池&lt;strong&gt;一次搬 32 个&lt;/strong&gt;补进来。一次搬一批是为了少碰全局锁：接下来 32 次创建都只从本地取。&lt;/p&gt;&#xA;&lt;p&gt;复用的前提是栈能省则省。G 的栈会动态增长，执行完可能已经远大于起始大小。回收时栈大小不等于标准起始栈的，直接把栈释放；复用取出来发现栈不匹配，也释放重新分配。栈大小正好匹配的 G 才能连栈一起复用。&lt;/p&gt;&#xA;&lt;p&gt;goroutine 栈起始只有 2KB（&lt;code&gt;stackMin&lt;/code&gt;），远小于线程默认栈（MB 级），这是 G 轻量、能开几百万个的根本原因。栈不够用时动态增长，上限 1GB（64 位）。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;队列结构&#34;&gt;&lt;span&gt;队列结构&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e9%98%9f%e5%88%97%e7%bb%93%e6%9e%84&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;每个 P 有一个本地 G 队列，全局还有一个共享 G 队列。全局队列有锁保护（&lt;code&gt;sched.lock&lt;/code&gt;，调度器锁），本地队列无锁，这是调度性能的关键。&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;flowchart TB&#xA;    GQ[&amp;#34;全局 G 队列&amp;lt;br/&amp;gt;加锁保护&amp;#34;]&#xA;    P1[&amp;#34;P1&amp;#34;]&#xA;    P2[&amp;#34;P2&amp;#34;]&#xA;    P3[&amp;#34;P3&amp;#34;]&#xA;    Q1[&amp;#34;本地队列&amp;lt;br/&amp;gt;下个执行位 &amp;#43; 运行队列[256]&amp;#34;]&#xA;    Q2[&amp;#34;本地队列&amp;#34;]&#xA;    Q3[&amp;#34;本地队列&amp;#34;]&#xA;    M1[&amp;#34;M1 内核线程&amp;#34;]&#xA;    M2[&amp;#34;M2 内核线程&amp;#34;]&#xA;    M3[&amp;#34;M3 内核线程（空闲）&amp;#34;]&#xA;    CPU[&amp;#34;CPU 核心&amp;lt;br/&amp;gt;OS 调度&amp;#34;]&#xA;    GQ --&amp;gt;|取 G| Q1&#xA;    GQ --&amp;gt;|取 G| Q2&#xA;    GQ --&amp;gt;|取 G| Q3&#xA;    Q1 --- P1&#xA;    Q2 --- P2&#xA;    Q3 --- P3&#xA;    P1 --- M1&#xA;    P2 --- M2&#xA;    P3 -. 空闲 .-&amp;gt; M3&#xA;    M1 --&amp;gt; CPU&#xA;    M2 --&amp;gt; CPU&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;新 G 创建时优先放进当前 P 的本地队列，最优先的位置是 &lt;code&gt;runnext&lt;/code&gt;（下个优先执行位，每个 P 一个单槽，可能为空）。&lt;code&gt;runnext&lt;/code&gt; 空就直接占住；被占着就把旧的挤出去，放进 &lt;code&gt;runq&lt;/code&gt;（本地运行队列）环形数组。&lt;code&gt;runq&lt;/code&gt; 是固定长度 256 的数组，加 &lt;code&gt;runnext&lt;/code&gt; 共容纳 257 个 G。&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;runnext&lt;/code&gt; 里放的是这几类 G：新建的 G（&lt;code&gt;go func()&lt;/code&gt;）、被唤醒且需要尽快执行的 G（&lt;code&gt;ready&lt;/code&gt; 传 next=true，比如 finalizer G）、被抢占让出的 G——让它 STW 结束后立刻续跑。&lt;/p&gt;&#xA;&lt;p&gt;这是局部性原则：新 G 是当前 G 刚创建的，和它在同一 P 上运行，数据大概率还在 CPU 缓存里，立刻执行命中率最高。&lt;/p&gt;&#xA;&lt;p&gt;本地队列放不下（&lt;code&gt;runnext&lt;/code&gt; 被占且 &lt;code&gt;runq&lt;/code&gt; 满 256）时，&lt;code&gt;runqputslow&lt;/code&gt;（把一半 G 挪到全局队列的函数）把&lt;strong&gt;队首的一半&lt;/strong&gt;（最早入队的 128 个 G）连同被挤出的旧 G（原本蹲在 &lt;code&gt;runnext&lt;/code&gt; 里、还没轮到执行的 G）一起移到全局队列。移走旧的不移走新的：新 G 要马上执行，留在 runnext；队首的 G 等待最久，进全局队列后能被其他空闲 P 取走，顺带做了负载均衡。所以「P 的 G 队列长度最大值」是 256 + 1，不是无限。&lt;/p&gt;&#xA;&lt;h4 class=&#34;heading-element&#34; id=&#34;为什么一次搬一半不把旧-g-单独放全局&#34;&gt;&lt;span&gt;为什么一次搬一半，不把旧 G 单独放全局？&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e4%b8%ba%e4%bb%80%e4%b9%88%e4%b8%80%e6%ac%a1%e6%90%ac%e4%b8%80%e5%8d%8a%e4%b8%8d%e6%8a%8a%e6%97%a7-g-%e5%8d%95%e7%8b%ac%e6%94%be%e5%85%a8%e5%b1%80&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h4&gt;&lt;p&gt;疑问：新 G 占了 runnext，把被挤出的旧 G 丢进全局队列不就行了？为什么还要连队首一半（128 个 G）一起搬？&lt;/p&gt;&#xA;&lt;p&gt;答案分两层：&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;旧 G 必须有个去处，runq 必须腾位置。&lt;/strong&gt; 被挤出的旧 G 进不了 runnext（已被新 G 占住），只能进 runq，而 runq 已经满 256。所以从 runq 腾出空间是硬需求，队首的 G 必然要动。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;一次搬一批是为了少碰全局锁。&lt;/strong&gt; 全局队列有锁（&lt;code&gt;sched.lock&lt;/code&gt;）。如果每次只腾 1 个位置：创建 G → runq 又满 → 又腾 1 个 → 又拿一次全局锁。goroutine 创建是热路径，每次创建都触发全局锁不可接受。一次搬 128 个，锁只拿一次，换来 runq 半空（128 个空位），接下来很多次入队都走无锁的本地路径，直到队列再次填满。搬一半也是留缓冲：runq 从 256 减到 128，新 G 不会立刻又顶满。&lt;/p&gt;&#xA;&lt;p&gt;疑问：空闲 P 反正会 work stealing 来偷满队列，不搬不也行？&lt;/p&gt;&#xA;&lt;p&gt;偷取本身有成本：多个空闲 P 同时盯上同一个满队列，会互相抢，抢输的只能重试，白白消耗 CPU。把一半搬进全局后，空闲 P 直接走 &lt;code&gt;globrunqgetbatch&lt;/code&gt; 一次锁拿一批，不用抢。但 work stealing 只是兜底手段——队列满说明生产速度大于消费速度，靠别人来偷解决不了本 P 入队被堵的问题，腾位置是必须的。&lt;/p&gt;&#xA;&lt;p&gt;所以搬一半的核心原因是「没地方放 + 不能每次创建都加锁」，负载均衡只是顺带收益。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;p-和-m-的数量&#34;&gt;&lt;span&gt;P 和 M 的数量&lt;/span&gt;&#xA;  &lt;a href=&#34;#p-%e5%92%8c-m-%e7%9a%84%e6%95%b0%e9%87%8f&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;P 的数量等于 GOMAXPROCS（最大处理器数），默认是 CPU 逻辑核心数。可以用 &lt;code&gt;GOMAXPROCS&lt;/code&gt; 环境变量或 &lt;code&gt;runtime.GOMAXPROCS(n)&lt;/code&gt; 修改。&lt;/p&gt;&#xA;&lt;p&gt;M 的数量动态变化：有工作需要处理但空闲 M 不够时新建；空闲 M 进入休眠列表（&lt;code&gt;sched.midle&lt;/code&gt;，空闲 M 链表）等待复用，长期空闲会被销毁。上限默认 10000，可以用 &lt;code&gt;runtime/debug.SetMaxThreads&lt;/code&gt;（设置最大线程数）修改。&lt;/p&gt;&#xA;&lt;p&gt;每个 M 同一时刻绑定 0 个或 1 个 P：持有 P 的 M 才能执行 Go 代码。M 通过绑定的 P 获取 G，顺序是 &lt;code&gt;runnext&lt;/code&gt; → 本地队列 → 全局队列 → 偷其他 P。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;复用线程&#34;&gt;&lt;span&gt;复用线程&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e5%a4%8d%e7%94%a8%e7%ba%bf%e7%a8%8b&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;h4 class=&#34;heading-element&#34; id=&#34;work-stealing工作窃取&#34;&gt;&lt;span&gt;work stealing（工作窃取）&lt;/span&gt;&#xA;  &lt;a href=&#34;#work-stealing%e5%b7%a5%e4%bd%9c%e7%aa%83%e5%8f%96&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h4&gt;&lt;p&gt;P 的本地队列空了，取 G 的顺序是：先全局队列（一次取一批，&lt;code&gt;globrunqgetbatch&lt;/code&gt;，数量 &lt;code&gt;min(128, 全局长度, 全局长度/GOMAXPROCS+1)&lt;/code&gt; 个，全局长度是全局队列当前排队的 G 数；除以 GOMAXPROCS 是为了平均分给每个 P，128 是本地队列放不下的上限），再网络轮询（netpoll，把数据就绪的 G 捡回来），还空就偷其他 P。&lt;/p&gt;&#xA;&lt;p&gt;偷的时候从目标 P 的 &lt;code&gt;runq&lt;/code&gt; 取&lt;strong&gt;队首的一半&lt;/strong&gt;（&lt;code&gt;runqgrab&lt;/code&gt; 里 &lt;code&gt;n = n - n/2&lt;/code&gt;，从 head 开始数 n 个），也就是最早入队的那一半。偷不到、全局也空，P 进入空闲列表等待。P 的数量固定，不会销毁；会休眠或销毁的是 M。&lt;/p&gt;&#xA;&lt;p&gt;取 G 还有一层公平性保护：每 61 次调度（&lt;code&gt;schedtick%61 == 0&lt;/code&gt;）强制检查一次全局队列，有 G 就先取。防止两个 G 在本地队列里互相 spawn 出新 G，把全局队列里的 G 活活饿死。&lt;/p&gt;&#xA;&lt;h4 class=&#34;heading-element&#34; id=&#34;hand-off交接&#34;&gt;&lt;span&gt;hand off（交接）&lt;/span&gt;&#xA;  &lt;a href=&#34;#hand-off%e4%ba%a4%e6%8e%a5&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h4&gt;&lt;p&gt;G 阻塞分两种情况，处理方式不同：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;普通阻塞（channel 通道、锁、sleep 睡眠）：G 被 park（挂起，暂停执行、等条件满足再唤醒），M 不换，继续从 P 的队列取下一个 G 执行。这时不需要新 M。&lt;/li&gt;&#xA;&lt;li&gt;系统调用阻塞：M 带着 G 一起进入系统调用，P 被释放（&lt;code&gt;handoffp&lt;/code&gt;，交接函数），从休眠 M 里唤醒一个接管这个 P，没有就新建。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;系统调用返回时，M 先尝试要回 P：有闲 P 就直接继续执行 G；没有闲 P，G 被放进全局队列（&lt;code&gt;globrunqput&lt;/code&gt;），M 进入休眠（&lt;code&gt;stopm&lt;/code&gt;），等下次被唤醒。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;负载均衡&#34;&gt;&lt;span&gt;负载均衡&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e8%b4%9f%e8%bd%bd%e5%9d%87%e8%a1%a1&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;每次新建 G（&lt;code&gt;newproc&lt;/code&gt;，创建新 G 的函数）都会调用 &lt;code&gt;wakep&lt;/code&gt;（唤醒 M 的函数）：如果当前没有自旋 M，且有空闲 P，就唤醒一个休眠 M 或新建 M，绑上这个 P，进入自旋模式去抢 G。&lt;/p&gt;&#xA;&lt;p&gt;自旋 M 的取 G 顺序：本地队列（刚拿到的闲 P，队列本来就是空的）→ 全局队列 → 偷其他 P。&lt;/p&gt;&#xA;&lt;p&gt;自旋 M 不会无限烧 CPU：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;自旋 M 的数量有限制，最多是忙 P 数的一半（&lt;code&gt;2 × 自旋M数 &amp;lt; GOMAXPROCS - 空闲P数&lt;/code&gt; 才允许新增）。&lt;/li&gt;&#xA;&lt;li&gt;全局和所有 P 都找不到工作时，自旋 M 释放 P 回空闲列表，自己休眠（&lt;code&gt;stopm&lt;/code&gt; → &lt;code&gt;mPark&lt;/code&gt;，线程挂起），等下次 &lt;code&gt;wakep&lt;/code&gt; 唤醒。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;sysmon-与抢占&#34;&gt;&lt;span&gt;sysmon 与抢占&lt;/span&gt;&#xA;  &lt;a href=&#34;#sysmon-%e4%b8%8e%e6%8a%a2%e5%8d%a0&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;sysmon（系统监控线程）是 runtime 启动时创建的独立线程（&lt;code&gt;newm&lt;/code&gt; 创建，跑 &lt;code&gt;sysmon&lt;/code&gt; 函数），负责几件事：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;抢占&lt;/strong&gt;：最多每 10ms 检查一次，发现某个 G 在同一 P 上连续运行超过 10ms（&lt;code&gt;forcePreemptNS&lt;/code&gt;，抢占时间阈值），就通过 SIGURG 信号（一个用于抢占的 Unix 信号）异步抢占（Go 1.14+）。G 让出 CPU 回到队列继续排队，之后还会被调度到。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;syscall 接管&lt;/strong&gt;：P 卡在系统调用里超过约 20us，sysmon 直接把 P 抢回来交给别的 M（配合 hand off）。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;网络轮询兜底&lt;/strong&gt;：超过 10ms 没人轮询网络事件，sysmon 自己轮一次 epoll，把就绪的 G 唤醒。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;定时 GC&lt;/strong&gt;：&lt;code&gt;forcegcperiod&lt;/code&gt;（2 分钟）没触发过 GC 就强制触发一次。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;网络-io-与-netpoll&#34;&gt;&lt;span&gt;网络 IO 与 netpoll&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e7%bd%91%e7%bb%9c-io-%e4%b8%8e-netpoll&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;网络 IO（socket 读写）在 Go 里默认走非阻塞模式，底层是 epoll（Linux）/ kqueue（macOS）这类 IO 多路复用。G 发起的读写没就绪时，&lt;code&gt;netpollblock&lt;/code&gt; 里 &lt;code&gt;gopark&lt;/code&gt; 挂起（waitReasonIOWait），&lt;strong&gt;不占 M&lt;/strong&gt;。就绪事件由 netpoll 收集，把对应 G 唤醒塞回队列。&lt;/p&gt;&#xA;&lt;p&gt;所以大量 goroutine 同时做网络 IO 时，M 不会被 IO 阻塞。这是 GMP 支撑高并发网络服务的核心：连接数再多，活跃线程也就 GOMAXPROCS 上下。&lt;/p&gt;&#xA;&lt;p&gt;注意区分：文件 IO、syscall 阻塞走另一条路（hand off，占 M），网络 IO 走 netpoll，不占 M。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;调度流程&#34;&gt;&lt;span&gt;调度流程&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e8%b0%83%e5%ba%a6%e6%b5%81%e7%a8%8b&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;ol&gt;&#xA;&lt;li&gt;&lt;code&gt;go func(){...}()&lt;/code&gt;：创建 G，优先放当前 P 的 &lt;code&gt;runnext&lt;/code&gt;，其次本地队列；本地队列满 256 时，队首一半 G 连同挤出的旧 G 一起移到全局队列。&lt;/li&gt;&#xA;&lt;li&gt;M 从绑定的 P 取 G：&lt;code&gt;runnext&lt;/code&gt; → 本地队列 → 全局队列 → 偷其他 P。&lt;/li&gt;&#xA;&lt;li&gt;G 阻塞：普通阻塞 M 继续取下一个 G；系统调用阻塞则交接 P 给其他 M。&lt;/li&gt;&#xA;&lt;li&gt;系统调用返回：G 重新入队，M 抢 P 或休眠。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;pre&gt;&lt;code&gt;flowchart LR&#xA;    A[&amp;#34;go func()&amp;#34;] --&amp;gt; B[&amp;#34;创建 G&amp;#34;]&#xA;    B --&amp;gt; C{&amp;#34;P 本地队列满？&amp;#34;}&#xA;    C -- 否 --&amp;gt; D[&amp;#34;放下个执行位 / 本地队列&amp;#34;]&#xA;    C -- 是 --&amp;gt; E[&amp;#34;队首一半 G &amp;#43; 挤出的旧 G 移到全局队列&amp;#34;]&#xA;    D --&amp;gt; F[&amp;#34;M 从 P 取 G 执行&amp;#34;]&#xA;    E --&amp;gt; F&#xA;    F --&amp;gt; G{&amp;#34;G 阻塞？&amp;#34;}&#xA;    G -- 普通阻塞 --&amp;gt; H[&amp;#34;挂起 G，M 继续执行下一个 G&amp;#34;]&#xA;    G -- 系统调用 --&amp;gt; I[&amp;#34;交接 P 给其他 M&amp;#34;]&#xA;    H --&amp;gt; F&#xA;    I --&amp;gt; F&lt;/code&gt;&lt;/pre&gt;&lt;h3 class=&#34;heading-element&#34; id=&#34;p-的状态&#34;&gt;&lt;span&gt;P 的状态&lt;/span&gt;&#xA;  &lt;a href=&#34;#p-%e7%9a%84%e7%8a%b6%e6%80%81&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;P 有四种状态（&lt;code&gt;runtime2.go&lt;/code&gt;）：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Pidle（空闲）：在空闲 P 列表里，队列为空，等着被 M 领走。&lt;/li&gt;&#xA;&lt;li&gt;Prunning（运行中）：被某个 M 持有，正在执行 G。&lt;/li&gt;&#xA;&lt;li&gt;Pgcstop（GC 停止）：GC 的 STW（stop the world，暂停所有线程）阶段，所有 P 停在这里。&lt;/li&gt;&#xA;&lt;li&gt;Pdead（已销毁）：GOMAXPROCS 调小时多余的 P 被销毁，调大时还能复用。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;GC 开始时 STW：所有 M 跑到安全点，P 转 Pgcstop，调度暂停。标记阶段并发进行，P 恢复跑用户代码。GC 的完整原理见 &lt;a href=&#34;https://www.nite07.com/posts/go-gc/&#34;&gt;Go GC 机制&lt;/a&gt;。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;m0-和-g0&#34;&gt;&lt;span&gt;M0 和 G0&lt;/span&gt;&#xA;  &lt;a href=&#34;#m0-%e5%92%8c-g0&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;M0 是程序启动后的第一个 M（主线程），负责初始化（安装信号处理、创建 sysmon 线程等），且永不退出——&lt;code&gt;mexit&lt;/code&gt;（线程退出流程）里对 m0 是 wedge（钉住，卡住不动），直到进程结束。&lt;/p&gt;&#xA;&lt;p&gt;G0 是每个 M 的专属调度 goroutine。每次创建 M（&lt;code&gt;allocm&lt;/code&gt;，创建 M 的函数）都会同时创建它的 g0，存在 M 的 &lt;code&gt;g0&lt;/code&gt; 字段里。g0 不进任何队列、不占 P，只负责执行调度代码（schedule 调度、栈增长、信号处理等）。它独立于 P，只和 M 关联：M 阻塞时 g0 跟着 M 走，没有「g0 放哪里」的问题。&lt;/p&gt;&#xA;&lt;p&gt;也不存在「m0 和 g0 解绑」：g0 永远属于 m0，m0 通过 g0 进入调度循环。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;hello-world-执行流程&#34;&gt;&lt;span&gt;Hello world 执行流程&lt;/span&gt;&#xA;  &lt;a href=&#34;#hello-world-%e6%89%a7%e8%a1%8c%e6%b5%81%e7%a8%8b&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;ol&gt;&#xA;&lt;li&gt;程序启动，创建 m0 和它的 g0。&lt;/li&gt;&#xA;&lt;li&gt;g0 执行 &lt;code&gt;schedinit&lt;/code&gt;（调度器初始化）：创建 GOMAXPROCS 个 P、初始化全局队列。&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;newproc&lt;/code&gt; 创建 main goroutine，放入某个 P 的本地队列。&lt;/li&gt;&#xA;&lt;li&gt;m0 的 g0 进入调度循环，取出 main goroutine，m0 转而执行它。&lt;/li&gt;&#xA;&lt;li&gt;main 执行完毕，程序退出。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;runtimetrace&#34;&gt;&lt;span&gt;runtime/trace&lt;/span&gt;&#xA;  &lt;a href=&#34;#runtimetrace&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;pre&gt;&lt;code&gt;import &amp;#34;runtime/trace&amp;#34;&#xA;&#xA;f, _ := os.Create(&amp;#34;trace.out&amp;#34;)&#xA;trace.Start(f)&#xA;defer trace.Stop()&#xA;&#xA;// 业务代码&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;生成后用 &lt;code&gt;go tool trace trace.out&lt;/code&gt; 打开 Web UI（网页界面），可以看 G/P/M 的调度、阻塞、系统调用、GC 等事件。&lt;/p&gt;&#xA;</description>
    </item><item>
      <title>自部署 Gitea 被爬虫打爆流量的缓解措施</title>
      <link>https://www.nite07.com/zh-cn/posts/gitea-scraper-mitigation/</link>
      <pubDate>Sun, 02 Aug 2026 18:19:54 +1000</pubDate><author>nite@nite07.com (Nite)</author>
      <guid>https://www.nite07.com/zh-cn/posts/gitea-scraper-mitigation/</guid>
      <category domain="https://www.nite07.com/zh-cn/categories/tutorials/">教程</category>
      <description>&lt;h3 class=&#34;heading-element&#34; id=&#34;现象&#34;&gt;&lt;span&gt;现象&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e7%8e%b0%e8%b1%a1&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;自部署的 Gitea 某天流量开始异常。服务器商控制面板里，流量额度以肉眼可见的速度消耗，月流量包很快见底。访问日志按响应大小聚合后，git 域名占了绝大多数流量，其中 &lt;code&gt;/owner/repo/compare/&lt;/code&gt; 这类版本对比页面是大头，单页最大响应能到 10MB 级别。&lt;/p&gt;&#xA;&lt;p&gt;爬虫特征明显：Amazonbot、Lightpanda，以及一批伪造旧版 Chrome/Firefox UA 的轮换 IP，反复抓取 blame、compare 页面。大量独立 IP 各自只请求一两次，典型的分布式爬取。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;原因&#34;&gt;&lt;span&gt;原因&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e5%8e%9f%e5%9b%a0&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;ol&gt;&#xA;&lt;li&gt;爬虫 UA 复杂，既有知名爬虫（Amazonbot、Lightpanda），也有伪造浏览器 UA 的轮换 IP。&lt;/li&gt;&#xA;&lt;li&gt;重型页面单页体积大，compare 页面最大超过 10MB，且完全不压缩，爬虫每抓一页都是大流量。&lt;/li&gt;&#xA;&lt;li&gt;单 IP 限流对轮换 IP 池无效，绝大多数 IP 只请求一两次就换下一个。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;方法&#34;&gt;&lt;span&gt;方法&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e6%96%b9%e6%b3%95&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;h4 class=&#34;heading-element&#34; id=&#34;1-caddy-配置&#34;&gt;&lt;span&gt;1. Caddy 配置&lt;/span&gt;&#xA;  &lt;a href=&#34;#1-caddy-%e9%85%8d%e7%bd%ae&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h4&gt;&lt;p&gt;UA 黑名单（&lt;code&gt;@block_bots&lt;/code&gt; 命中直接 403）和重型页面限流（&lt;code&gt;@heavy&lt;/code&gt; 单 IP 2 次/10s）都写在同一个站点块里，按顺序执行。UA 名单覆盖常见爬虫和扫描器，可以按需增删：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;git.example.com {&#xA;&#x9;# 日志配置：按域名输出访问日志&#xA;&#x9;import log git.example.com&#xA;&#xA;&#x9;# 1. UA 黑名单：命中的爬虫直接 403&#xA;&#x9;@block_bots header_regexp User-Agent &amp;#34;(?i)(SemrushBot|Lightpanda|GPTBot|ClaudeBot|Bytespider|AhrefsBot|MJ12bot|DataForSeoBot|Amazonbot|DotBot|CCBot|cohere-ai|Diffbot|Google-Extended|Meta-ExternalAgent|Meta-ExternalFetcher|Applebot-Extended|Timpibot|ImagesiftBot|Crawl4AI|Scrapy|ChatGLM-Spider|DeepSeekBot|cohere-training-data-crawler|AI2Bot|TikTokSpider|omgili|BLEXBot|Exabot|360Spider|80legs|MegaIndex|Barkrowler|DataCha0s|Nikto|Nmap|Nessus|OpenVAS|Sqlmap|WPScan|Nuclei|Masscan|Shodan|Acunetix|Dirbuster|Whatweb|Havij|Fimap|Jbrofuzz|Zgrab|Censys)&amp;#34;&#xA;&#x9;handle @block_bots {&#xA;&#x9;&#x9;respond 403&#xA;&#x9;}&#xA;&#xA;&#x9;# 2. 重型页面（compare/blame 等）单 IP 限流&#xA;&#x9;@heavy path_regexp heavy_path ^/[^/]&amp;#43;/[^/]&amp;#43;/(?:compare|blame|commit|commits|archive|pulls|issues)(?:/|$)&#xA;&#x9;handle @heavy {&#xA;&#x9;&#x9;rate_limit {&#xA;&#x9;&#x9;&#x9;zone dynamic_zone {&#xA;&#x9;&#x9;&#x9;&#x9;key {client_ip}&#xA;&#x9;&#x9;&#x9;&#x9;events 2&#xA;&#x9;&#x9;&#x9;&#x9;window 10s&#xA;&#x9;&#x9;&#x9;}&#xA;&#x9;&#x9;&#x9;log_key&#xA;&#x9;&#x9;}&#xA;&#x9;&#x9;reverse_proxy 100.64.0.10:3000&#xA;&#x9;}&#xA;&#xA;&#x9;# 3. robots.txt：礼貌爬虫先读规则&#xA;&#x9;handle /robots.txt {&#xA;&#x9;&#x9;respond `User-agent: *&#xA;Disallow: /blame/&#xA;Disallow: /compare/&#xA;Disallow: /commit/&#xA;Disallow: /commits/&#xA;Disallow: /archive/&#xA;Disallow: /pulls/&#xA;Disallow: /issues/`&#xA;&#x9;}&#xA;&#xA;&#x9;# 4. 其余请求反代到 Gitea&#xA;&#x9;reverse_proxy 100.64.0.10:3000&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;@heavy&lt;/code&gt; 的路径规则同时覆盖 compare、blame、commit 等几类重型页面，单个 IP 在 10 秒内超过 2 次就返回 429。&lt;code&gt;log_key&lt;/code&gt; 开启后，被限流的请求会在访问日志里记下对应的 IP，方便调阈值。&lt;/p&gt;&#xA;&lt;h4 class=&#34;heading-element&#34; id=&#34;2-gitea-开启源站-gzip&#34;&gt;&lt;span&gt;2. Gitea 开启源站 gzip&lt;/span&gt;&#xA;  &lt;a href=&#34;#2-gitea-%e5%bc%80%e5%90%af%e6%ba%90%e7%ab%99-gzip&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h4&gt;&lt;p&gt;改 &lt;code&gt;app.ini&lt;/code&gt; 的 &lt;code&gt;[server]&lt;/code&gt; 段，加一行后重启 Gitea：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;[server]&#xA;ENABLE_GZIP = true&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;ENABLE_GZIP&lt;/code&gt; 只压缩运行时生成的内容，静态资源不受影响。放在源站而不是反代层，是因为链路是「源站 → 反代 → 访客」两段，双向计费下源站压缩两段都省；只在反代压只省一段。Caddy 的 &lt;code&gt;reverse_proxy&lt;/code&gt; 会替缺省客户端补 &lt;code&gt;Accept-Encoding: gzip&lt;/code&gt; 并原样透传上游的压缩响应，不需要在 Caddy 侧再配 encode。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;参考链接&#34;&gt;&lt;span&gt;参考链接&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e5%8f%82%e8%80%83%e9%93%be%e6%8e%a5&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/mholt/caddy-ratelimit&#34; target=&#34;_blank&#34; rel=&#34;external nofollow noopener noreferrer&#34;&gt;caddy-ratelimit&lt;i class=&#34;fa-solid fa-external-link-alt fa-xs ms-1 text-secondary&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://docs.gitea.com/administration/config-cheat-sheet&#34; target=&#34;_blank&#34; rel=&#34;external nofollow noopener noreferrer&#34;&gt;Gitea 配置手册（ENABLE_GZIP）&lt;i class=&#34;fa-solid fa-external-link-alt fa-xs ms-1 text-secondary&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://caddyserver.com/docs/caddyfile/directives/handle&#34; target=&#34;_blank&#34; rel=&#34;external nofollow noopener noreferrer&#34;&gt;Caddy handle 指令&lt;i class=&#34;fa-solid fa-external-link-alt fa-xs ms-1 text-secondary&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
    </item><item>
      <title>Gitea custom template 内联脚本被 CSP nonce 拦截</title>
      <link>https://www.nite07.com/zh-cn/posts/gitea-csp-nonce-inline-script/</link>
      <pubDate>Sun, 02 Aug 2026 02:03:49 +1000</pubDate><author>nite@nite07.com (Nite)</author>
      <guid>https://www.nite07.com/zh-cn/posts/gitea-csp-nonce-inline-script/</guid>
      <category domain="https://www.nite07.com/zh-cn/categories/tutorials/">教程</category>
      <description>&lt;h3 class=&#34;heading-element&#34; id=&#34;现象&#34;&gt;&lt;span&gt;现象&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e7%8e%b0%e8%b1%a1&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;把 Matomo 统计脚本放进 &lt;code&gt;$GITEA_CUSTOM/templates/custom/header.tmpl&lt;/code&gt;（渲染在 &lt;code&gt;&amp;lt;/head&amp;gt;&lt;/code&gt; 之前）后，页面里始终没有统计数据。浏览器控制台报：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;Executing inline script violates the following Content Security Policy directive &amp;#39;script-src * &amp;#39;nonce-&amp;lt;random&amp;gt;&amp;#39;&amp;#39;. Either the &amp;#39;unsafe-inline&amp;#39; keyword, a hash (&amp;#39;sha256-...&amp;#39;), or a nonce (&amp;#39;nonce-...&amp;#39;) is required to enable inline execution. The action has been blocked.&lt;/code&gt;&lt;/pre&gt;&lt;h3 class=&#34;heading-element&#34; id=&#34;原因&#34;&gt;&lt;span&gt;原因&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e5%8e%9f%e5%9b%a0&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;Gitea 1.27.0 把 CSP 的内联脚本策略改成了 per-request nonce（&lt;a href=&#34;https://github.com/go-gitea/gitea/pull/37232&#34; target=&#34;_blank&#34; rel=&#34;external nofollow noopener noreferrer&#34;&gt;PR #37232&lt;i class=&#34;fa-solid fa-external-link-alt fa-xs ms-1 text-secondary&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;&lt;/a&gt;）：每个响应生成一个随机 nonce，&lt;code&gt;script-src&lt;/code&gt; 变成 &lt;code&gt;* &#39;nonce-xxx&#39;&lt;/code&gt;，外部脚本照常放行，但内联 &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; 只有带上这个 nonce 才会执行。custom template 里的内联脚本没有 nonce 属性，直接被浏览器拦掉。1.27.0 的&lt;a href=&#34;https://blog.gitea.com/release-of-1.27.0/&#34; target=&#34;_blank&#34; rel=&#34;external nofollow noopener noreferrer&#34;&gt;发布说明&lt;i class=&#34;fa-solid fa-external-link-alt fa-xs ms-1 text-secondary&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;&lt;/a&gt;也提示了这点：custom templates 里注入的内联 &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; 需要更新后才能继续执行。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;解决&#34;&gt;&lt;span&gt;解决&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e8%a7%a3%e5%86%b3&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;给 script 标签加上 nonce 属性，值用模板变量 &lt;code&gt;{{ctx.CspScriptNonce}}&lt;/code&gt;：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;&amp;lt;script nonce=&amp;#34;{{ctx.CspScriptNonce}}&amp;#34;&amp;gt;&#xA;  // 你的内联脚本，例如 Matomo 统计代码&#xA;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;保存后重启 Gitea（容器部署直接重启容器），刷新页面，控制台不再报 CSP 拦截错误，统计面板里能看到访客。&lt;/p&gt;&#xA;</description>
    </item><item>
      <title>Goldberg Steam Emulator 补丁流程</title>
      <link>https://www.nite07.com/zh-cn/posts/gse-patching-guide/</link>
      <pubDate>Sun, 26 Jul 2026 12:30:00 +1000</pubDate><author>nite@nite07.com (Nite)</author>
      <guid>https://www.nite07.com/zh-cn/posts/gse-patching-guide/</guid>
      <category domain="https://www.nite07.com/zh-cn/categories/tutorials/">教程</category>
      <description>&lt;p&gt;Goldberg Steam Emulator（GSE）通过替换游戏的 &lt;code&gt;steam_api(64).dll&lt;/code&gt;，让游戏不依赖 Steam 客户端也能运行。游戏调 Steam API 时实际走的是模拟器，模拟器返回伪造响应，游戏以为 Steam 在跑。&lt;/p&gt;&#xA;&lt;p&gt;原版项目已经不怎么更新了，社区现在主要用 GBE Fork，一个活跃维护的分支。另外还需要 Steamless 来去除 exe 自带的 DRM。&lt;/p&gt;&#xA;&lt;p&gt;相关项目：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/Detanup01/gbe_fork&#34; target=&#34;_blank&#34; rel=&#34;external nofollow noopener noreferrer&#34;&gt;GBE Fork&lt;i class=&#34;fa-solid fa-external-link-alt fa-xs ms-1 text-secondary&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;&lt;/a&gt;，当前社区主流的模拟器&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/atom0s/Steamless&#34; target=&#34;_blank&#34; rel=&#34;external nofollow noopener noreferrer&#34;&gt;Steamless&lt;i class=&#34;fa-solid fa-external-link-alt fa-xs ms-1 text-secondary&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;&lt;/a&gt;，去掉 exe 上 SteamDRM 的工具&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;1-用-steamless-处理-exe&#34;&gt;&lt;span&gt;1. 用 Steamless 处理 exe&lt;/span&gt;&#xA;  &lt;a href=&#34;#1-%e7%94%a8-steamless-%e5%a4%84%e7%90%86-exe&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;Steamless 去掉的是 exe 上的 SteamDRM Wrapper（也叫 Steam Stub）。无论 exe 有没有被加密，都可以直接跑一次 Steamless——没 DRM 的文件会被安全跳过，不会损坏。&lt;/p&gt;&#xA;&lt;p&gt;下载 Steamless 之后，打开 &lt;code&gt;Steamless.exe&lt;/code&gt;，选游戏主 exe，点解包。处理完会在同目录生成一个 &lt;code&gt;.unbind&lt;/code&gt; 文件。原 exe 备份一下，把 &lt;code&gt;.unbind&lt;/code&gt; 重命名成原 exe 的名字替换就行。&lt;/p&gt;&#xA;&lt;p&gt;大 exe（超过 100MB）可能要花几分钟。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;2-找-steam_api64dll&#34;&gt;&lt;span&gt;2. 找 steam_api(64).dll&lt;/span&gt;&#xA;  &lt;a href=&#34;#2-%e6%89%be-steam_api64dll&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;在游戏文件夹里搜 &lt;code&gt;steam_api*.dll&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;p&gt;结果有两种情况。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;只有一个 DLL：&lt;/strong&gt; 通用/Unity 引擎游戏，替换这个 DLL，&lt;code&gt;steam_settings&lt;/code&gt; 文件夹放它旁边。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;有两个 DLL（Unreal Engine 游戏）：&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;&amp;lt;GameDir&amp;gt;\Engine\Binaries\ThirdParty\Steamworks\Steamv157\Win64\steam_api64.dll&#xA;&amp;lt;GameDir&amp;gt;\&amp;lt;GameName&amp;gt;\Binaries\Win64\steam_api64.dll&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;UE 引擎启动时从 Engine 目录加载 &lt;code&gt;steam_api64.dll&lt;/code&gt;，不是从游戏主目录。替换 Engine 目录的那个，&lt;code&gt;steam_settings&lt;/code&gt; 也放 Engine 目录。游戏主目录的 DLL 保持原版，不要动。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;3-备份原版-dll&#34;&gt;&lt;span&gt;3. 备份原版 DLL&lt;/span&gt;&#xA;  &lt;a href=&#34;#3-%e5%a4%87%e4%bb%bd%e5%8e%9f%e7%89%88-dll&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;找到要替换的 DLL 之后，复制一份，重命名成 &lt;code&gt;steam_api64.dll.old&lt;/code&gt;。后续生成接口文件要用到这个备份。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;4-替换成模拟器-dll&#34;&gt;&lt;span&gt;4. 替换成模拟器 DLL&lt;/span&gt;&#xA;  &lt;a href=&#34;#4-%e6%9b%bf%e6%8d%a2%e6%88%90%e6%a8%a1%e6%8b%9f%e5%99%a8-dll&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;从 GBE Fork 发布包里，把对应架构的 DLL 复制过去覆盖原版：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;64 位游戏：&lt;code&gt;regular\x64\steam_api64.dll&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;32 位游戏：&lt;code&gt;regular\x86\steam_api.dll&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;模拟器 DLL 大约 7–11MB，原版大约 200–400KB，差别很明显。&lt;/p&gt;&#xA;&lt;p&gt;如果是 UE 引擎游戏，游戏主目录的 DLL 保持原版不动。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;5-创建-steam_settings-目录&#34;&gt;&lt;span&gt;5. 创建 steam_settings 目录&lt;/span&gt;&#xA;  &lt;a href=&#34;#5-%e5%88%9b%e5%bb%ba-steam_settings-%e7%9b%ae%e5%bd%95&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;在模拟器 DLL 同级创建一个 &lt;code&gt;steam_settings&lt;/code&gt; 文件夹。UE 引擎放 Engine 目录，通用引擎放游戏 exe 同级。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;6-放-steam_appidtxt&#34;&gt;&lt;span&gt;6. 放 steam_appid.txt&lt;/span&gt;&#xA;  &lt;a href=&#34;#6-%e6%94%be-steam_appidtxt&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;在 Steam 商店页面的 URL 里找 AppID：&lt;code&gt;store.steampowered.com/app/&amp;lt;数字&amp;gt;/&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;p&gt;新建 &lt;code&gt;steam_settings\steam_appid.txt&lt;/code&gt;，只写纯数字 AppID。比如 &lt;code&gt;3683770&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;7-生成-steam_interfacestxt推荐&#34;&gt;&lt;span&gt;7. 生成 steam_interfaces.txt（推荐）&lt;/span&gt;&#xA;  &lt;a href=&#34;#7-%e7%94%9f%e6%88%90-steam_interfacestxt%e6%8e%a8%e8%8d%90&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;接口版本不匹配会导致崩溃或功能缺失。用 GBE Fork 里自带的 &lt;code&gt;generate_interfaces_x64.exe&lt;/code&gt;（或 x86 版），对备份的原版 DLL 跑一次。&lt;/p&gt;&#xA;&lt;p&gt;把备份的 &lt;code&gt;steam_api64.dll.old&lt;/code&gt; 拖进工具窗口就行。工具会在当前目录生成 &lt;code&gt;steam_interfaces.txt&lt;/code&gt;，把它移到 &lt;code&gt;steam_settings\&lt;/code&gt; 里。&lt;/p&gt;&#xA;&lt;p&gt;必须对原版 DLL 跑，不能用模拟器 DLL。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;8-可选配置-ini-文件&#34;&gt;&lt;span&gt;8. 可选：配置 INI 文件&lt;/span&gt;&#xA;  &lt;a href=&#34;#8-%e5%8f%af%e9%80%89%e9%85%8d%e7%bd%ae-ini-%e6%96%87%e4%bb%b6&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;&lt;code&gt;steam_settings.EXAMPLE\&lt;/code&gt; 里有模板，按需复制到 &lt;code&gt;steam_settings\&lt;/code&gt;，去掉 &lt;code&gt;.EXAMPLE&lt;/code&gt; 中缀。&lt;/p&gt;&#xA;&lt;p&gt;关键配置：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;configs.user.ini&lt;/code&gt;：&lt;code&gt;local_save_path=./path/relative/to/dll&lt;/code&gt;，让存档存到游戏目录而非全局 &lt;code&gt;%appdata%&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;configs.user.ini&lt;/code&gt;：&lt;code&gt;account_name=Player&lt;/code&gt;，改游戏里显示的名字&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;configs.user.ini&lt;/code&gt;：&lt;code&gt;language=english&lt;/code&gt;，语言&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;configs.main.ini&lt;/code&gt;：&lt;code&gt;offline=0&lt;/code&gt;，离线模式&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;configs.app.ini&lt;/code&gt;：&lt;code&gt;unlock_all=1&lt;/code&gt;，解锁所有 DLC&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;不配 INI 也行，模拟器有合理的默认值。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;常见问题&#34;&gt;&lt;span&gt;常见问题&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e5%b8%b8%e8%a7%81%e9%97%ae%e9%a2%98&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;游戏闪退或无法启动&lt;/strong&gt;，按这个顺序排查：exe 有没有跑过 Steamless，DLL 有没有换对位置（UE 引擎看仔细），&lt;code&gt;steam_settings&lt;/code&gt; 是否和 DLL 同级，&lt;code&gt;steam_interfaces.txt&lt;/code&gt; 是否从原版 DLL 生成。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;默认存档&lt;/strong&gt;在 &lt;code&gt;%appdata%\GSE Saves\&lt;/code&gt;。想改成游戏目录本地存档，在 &lt;code&gt;configs.user.ini&lt;/code&gt; 里设 &lt;code&gt;local_save_path&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;UE 引擎游戏&lt;/strong&gt;只替换 Engine 目录的 DLL，游戏主目录的 DLL 不要动。&lt;code&gt;steam_settings&lt;/code&gt; 也放 Engine 目录。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;恢复原版&lt;/strong&gt;：把 &lt;code&gt;steam_api64.dll.old&lt;/code&gt; 改回 &lt;code&gt;steam_api64.dll&lt;/code&gt;，删掉 &lt;code&gt;steam_settings&lt;/code&gt; 文件夹和 &lt;code&gt;steam_appid.txt&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;steam_interfaces.txt&lt;/strong&gt; 生成在运行工具的当前目录，不是工具本身的位置。建议先切到模拟器 DLL 所在目录再跑，生成的文件直接移到 &lt;code&gt;steam_settings\&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;自动化工具&#34;&gt;&lt;span&gt;自动化工具&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e8%87%aa%e5%8a%a8%e5%8c%96%e5%b7%a5%e5%85%b7&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;不想手动操作的话，有两个社区工具：&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;ARMGDDN Autocracker&lt;/strong&gt;，基于 GBE Fork，装好后在游戏文件夹上右键就能自动完成补丁。仓库： &lt;a href=&#34;https://github.com/KaladinDMP/ARMGDDN-Autocracker&#34; target=&#34;_blank&#34; rel=&#34;external nofollow noopener noreferrer&#34;&gt;https://github.com/KaladinDMP/ARMGDDN-Autocracker&lt;i class=&#34;fa-solid fa-external-link-alt fa-xs ms-1 text-secondary&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;&lt;/a&gt;。作者 GitHub 账号曾被封过，最新版可能要从 Telegram 找。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;SteamAutoCracker&lt;/strong&gt;，内置调用 Steamless 和 Goldberg，一键处理 DRM 去除和 DLL 替换。仓库： &lt;a href=&#34;https://github.com/BigBoiCJ/SteamAutoCracker&#34; target=&#34;_blank&#34; rel=&#34;external nofollow noopener noreferrer&#34;&gt;https://github.com/BigBoiCJ/SteamAutoCracker&lt;i class=&#34;fa-solid fa-external-link-alt fa-xs ms-1 text-secondary&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;&lt;/a&gt;。&lt;/p&gt;&#xA;</description>
    </item><item>
      <title>用 vnStat 和 picosnitch 持续统计 Linux 流量</title>
      <link>https://www.nite07.com/zh-cn/posts/linux-traffic-monitoring-vnstat-picosnitch/</link>
      <pubDate>Thu, 23 Jul 2026 11:00:00 +1000</pubDate><author>nite@nite07.com (Nite)</author>
      <guid>https://www.nite07.com/zh-cn/posts/linux-traffic-monitoring-vnstat-picosnitch/</guid>
      <category domain="https://www.nite07.com/zh-cn/categories/tutorials/">教程</category>
      <description>&lt;p&gt;VPS 的流量异常时，通常要回答两个问题：哪个网卡在什么时候跑了多少流量，以及到底是哪个服务跑掉的。&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://humdi.net/vnstat/&#34; target=&#34;_blank&#34; rel=&#34;external nofollow noopener noreferrer&#34;&gt; vnStat&lt;i class=&#34;fa-solid fa-external-link-alt fa-xs ms-1 text-secondary&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;&lt;/a&gt; 记录网卡接口的收发流量，适合看总出口和按小时、天、月的趋势。&lt;a href=&#34;https://github.com/elesiuta/picosnitch&#34; target=&#34;_blank&#34; rel=&#34;external nofollow noopener noreferrer&#34;&gt;picosnitch&lt;i class=&#34;fa-solid fa-external-link-alt fa-xs ms-1 text-secondary&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;&lt;/a&gt; 记录每个可执行文件发起的连接，能继续查到进程、域名、端口和收发字节数。两个工具都在后台持续写数据，但统计维度不同，放在一起正好补全。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;vnstat按网卡统计&#34;&gt;&lt;span&gt;vnStat：按网卡统计&lt;/span&gt;&#xA;  &lt;a href=&#34;#vnstat%e6%8c%89%e7%bd%91%e5%8d%a1%e7%bb%9f%e8%ae%a1&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;vnStat 不是抓包工具。它读取内核提供的网卡计数器，&lt;code&gt;vnstatd&lt;/code&gt; 定时将数据写入数据库，资源占用很低。它会保存 5 分钟、小时、天、月和年的统计记录。&lt;/p&gt;&#xA;&lt;p&gt;Arch Linux 安装并启动服务：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;sudo pacman -S vnstat&#xA;sudo systemctl enable --now vnstat&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;服务首次启动时会为当前可用网卡创建数据库记录。先看已记录的接口：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;vnstat --dbiflist&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;之后新增的接口可以手动加入，例如 Tailscale 创建了 &lt;code&gt;tailscale0&lt;/code&gt;：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;sudo vnstat --add -i tailscale0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;常用的总览查询：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;# 默认摘要：今天、当前月、累计&#xA;vnstat -i wlan0&#xA;&#xA;# 短格式摘要&#xA;vnstat -i wlan0 -s&#xA;&#xA;# 一行可解析输出，适合脚本&#xA;vnstat --oneline -i wlan0&#xA;&#xA;# 最近 7 天、12 小时、最近月份&#xA;vnstat -i wlan0 -d 7&#xA;vnstat -i wlan0 -h 12&#xA;vnstat -i wlan0 -m&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;要查异常发生的时间段，&lt;code&gt;--begin&lt;/code&gt; 和 &lt;code&gt;--end&lt;/code&gt; 可以配合每日、每小时或 5 分钟列表使用。指定 &lt;code&gt;--end&lt;/code&gt; 后，列表末尾会多出一行 &lt;code&gt;sum&lt;/code&gt;，就是整个区间的上下行合计。&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;# 每日统计和区间合计&#xA;vnstat -i wlan0 -d --begin 2026-07-20 --end 2026-07-31&#xA;&#xA;# 一天内按小时查，时间精确到分钟&#xA;vnstat -i wlan0 -h --begin &amp;#34;2026-07-28 08:00&amp;#34; --end &amp;#34;2026-07-28 20:00&amp;#34;&#xA;&#xA;# 高峰时段按 5 分钟查&#xA;vnstat -i wlan0 -5 --begin &amp;#34;2026-07-28 12:00&amp;#34; --end &amp;#34;2026-07-28 14:00&amp;#34;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;--begin&lt;/code&gt; 支持 &lt;code&gt;YYYY-MM-DD HH:MM&lt;/code&gt;、&lt;code&gt;YYYY-MM-DD&lt;/code&gt; 和 &lt;code&gt;today&lt;/code&gt;；&lt;code&gt;--end&lt;/code&gt; 支持前两种日期格式。它们只能和列表输出、JSON 或 XML 一起使用。&lt;/p&gt;&#xA;&lt;p&gt;脚本需要原始字节数时用 JSON：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;vnstat -i wlan0 -d --begin 2026-07-28 --end 2026-07-30 --json \&#xA;  | jq &amp;#39;.interfaces[0].traffic.days&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;实时查看速率和生成图表：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;# Ctrl&amp;#43;C 后会显示本次运行期间的统计&#xA;vnstat --live -i wlan0&#xA;&#xA;# 最近 24 小时的终端条形图&#xA;vnstat -i wlan0 -hg&#xA;&#xA;# 每日统计渲染为 PNG&#xA;vnstati -i wlan0 -d -o ~/wlan0.png&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;默认只保留 48 小时的 5 分钟粒度数据。要更久地保留高峰时段的细粒度记录，修改 &lt;code&gt;/etc/vnstat.conf&lt;/code&gt; 中的 &lt;code&gt;5MinuteHours&lt;/code&gt;，然后重启服务：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;5MinuteHours 336&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;sudo systemctl restart vnstat&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;vnStat 从 &lt;code&gt;vnstatd&lt;/code&gt; 开始运行后才有数据，无法补回此前经过网卡的流量。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;picosnitch按进程统计&#34;&gt;&lt;span&gt;picosnitch：按进程统计&lt;/span&gt;&#xA;  &lt;a href=&#34;#picosnitch%e6%8c%89%e8%bf%9b%e7%a8%8b%e7%bb%9f%e8%ae%a1&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;vnStat 能发现哪段时间的总流量增长，但无法说明流量属于谁。picosnitch 是使用 BPF 和 fanotify 的用户态 daemon，按可执行文件记录网络连接，同时写入 SQLite。它会记录可执行文件路径和哈希、父进程、域名、端口、用户，以及收发字节数；容器内的应用也能识别。&lt;/p&gt;&#xA;&lt;p&gt;picosnitch 上游推荐用全局 pipx 安装。Arch Linux 可以这样部署：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;sudo pacman -S python-pipx&#xA;sudo pipx install picosnitch --global&#xA;sudo picosnitch systemd&#xA;sudo systemctl enable --now picosnitch&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;需要 Python 3.12 或更高版本，以及能运行现代 libbpf CO-RE 程序的 Linux 内核。&lt;/p&gt;&#xA;&lt;p&gt;数据有三种查看方式：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;# 终端 TUI，浏览历史连接&#xA;picosnitch tui&#xA;&#xA;# Web UI，默认监听 http://localhost:5100&#xA;picosnitch webui&#xA;&#xA;# 实时事件流&#xA;sudo picosnitch top&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;配置在 &lt;code&gt;/etc/picosnitch/config.toml&lt;/code&gt;。本地 SQLite 数据库默认位于 &lt;code&gt;/var/lib/picosnitch/picosnitch.db&lt;/code&gt;，默认保留 30 天。需要延长历史记录时修改 &lt;code&gt;retention_days&lt;/code&gt;，改完重启服务：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;[database]&#xA;retention_days = 90&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;sudo systemctl restart picosnitch&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Web UI 默认只监听本机。需要让局域网或反代访问时，可以在启动前设置监听地址和端口；公开暴露前应自行做好访问控制：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;PICOSNITCH_HOST=0.0.0.0 PICOSNITCH_PORT=5100 picosnitch webui&lt;/code&gt;&lt;/pre&gt;&lt;h3 class=&#34;heading-element&#34; id=&#34;一起排查流量异常&#34;&gt;&lt;span&gt;一起排查流量异常&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e4%b8%80%e8%b5%b7%e6%8e%92%e6%9f%a5%e6%b5%81%e9%87%8f%e5%bc%82%e5%b8%b8&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;先用 vnStat 查实际经过网卡的流量，确定异常从哪天或哪个小时开始。时间范围收窄后，打开 picosnitch 的 TUI 或 Web UI，按对应时间查看可执行文件、域名和端口。&lt;/p&gt;&#xA;&lt;p&gt;例如，vnStat 显示 &lt;code&gt;wlan0&lt;/code&gt; 在中午两小时内发送流量异常增长，接着在 picosnitch 中查看同一时间范围。如果流量集中在 &lt;code&gt;caddy&lt;/code&gt;，再看它连接到哪些客户端；如果集中在备份程序或下载器，就从对应服务的日志和配置继续排查。网卡统计负责发现问题，进程统计负责定位来源。&lt;/p&gt;&#xA;</description>
    </item><item>
      <title>修复 WSL2 Arch Linux 里 Podman build 的 newuidmap 报错</title>
      <link>https://www.nite07.com/zh-cn/posts/wsl-rootless-podman-newuidmap/</link>
      <pubDate>Sat, 11 Jul 2026 20:23:42 +1000</pubDate><author>nite@nite07.com (Nite)</author>
      <guid>https://www.nite07.com/zh-cn/posts/wsl-rootless-podman-newuidmap/</guid>
      <category domain="https://www.nite07.com/zh-cn/categories/tutorials/">教程</category>
      <description>&lt;p&gt;在 Windows 上的 WSL2 Arch Linux 里跑 rootless Podman，构建镜像时可能直接卡在 user namespace 初始化阶段：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;podman build . -t game-crawler:latest&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;报错类似这样：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;WARN[0000] &amp;#34;/&amp;#34; is not a shared mount, this could cause issues or missing mounts with rootless containers&#xA;ERRO[0000] running `/usr/sbin/newuidmap 61029 0 1000 1 1 100000 65536`: newuidmap: Could not set caps&#xA;Error: cannot set up namespace using &amp;#34;/usr/sbin/newuidmap&amp;#34;: should have setuid or have filecaps setuid: exit status 1&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;&amp;quot;/&amp;quot; is not a shared mount&lt;/code&gt; 只是 warning。这里真正让 build 失败的是 &lt;code&gt;newuidmap: Could not set caps&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;触发条件&#34;&gt;&lt;span&gt;触发条件&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e8%a7%a6%e5%8f%91%e6%9d%a1%e4%bb%b6&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;这是 rootless Podman 的问题，不是 Dockerfile 本身的问题。&lt;/p&gt;&#xA;&lt;p&gt;Podman 官方的 &lt;a href=&#34;https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md&#34; target=&#34;_blank&#34; rel=&#34;external nofollow noopener noreferrer&#34;&gt;rootless tutorial&lt;i class=&#34;fa-solid fa-external-link-alt fa-xs ms-1 text-secondary&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;&lt;/a&gt; 里写到，rootless Podman 需要 &lt;code&gt;/etc/subuid&lt;/code&gt; 和 &lt;code&gt;/etc/subgid&lt;/code&gt; 里的 subordinate UID/GID 范围。&lt;code&gt;newuidmap(1)&lt;/code&gt; 的手册也说明，它负责设置 user namespace 的 UID 映射，并会检查调用者是否被 &lt;code&gt;/etc/subuid&lt;/code&gt; 授权。&lt;/p&gt;&#xA;&lt;p&gt;也就是说，rootless Podman 创建容器命名空间时，需要这两个辅助程序：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;/usr/bin/newuidmap&#xA;/usr/bin/newgidmap&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在 Arch Linux 里它们来自 &lt;code&gt;shadow&lt;/code&gt; 包。如果二进制没有 setuid，也没有对应 file capability，普通用户就没有权限完成 UID/GID 映射，Podman build 会在创建 namespace 时失败。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;检查当前状态&#34;&gt;&lt;span&gt;检查当前状态&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e6%a3%80%e6%9f%a5%e5%bd%93%e5%89%8d%e7%8a%b6%e6%80%81&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;先看文件权限和 capability：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;stat -c &amp;#39;%A %U:%G %a %n&amp;#39; /usr/bin/newuidmap /usr/bin/newgidmap&#xA;getcap /usr/bin/newuidmap /usr/bin/newgidmap&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;出问题时可能是这样：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;-rwxr-xr-x root:root 755 /usr/bin/newuidmap&#xA;-rwxr-xr-x root:root 755 /usr/bin/newgidmap&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;getcap&lt;/code&gt; 没有任何输出。&lt;/p&gt;&#xA;&lt;p&gt;再确认当前用户有 subordinate ID：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;grep &amp;#34;^$USER:&amp;#34; /etc/subuid /etc/subgid&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;正常应该有类似输出：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;/etc/subuid:nite:100000:65536&#xA;/etc/subgid:nite:100000:65536&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果这里没有当前用户的记录，需要先补 &lt;code&gt;/etc/subuid&lt;/code&gt; 和 &lt;code&gt;/etc/subgid&lt;/code&gt;。如果这里已经有记录，但 &lt;code&gt;newuidmap/newgidmap&lt;/code&gt; 没有 setuid 或 capability，那就继续修权限。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;用-file-capability-修复&#34;&gt;&lt;span&gt;用 file capability 修复&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e7%94%a8-file-capability-%e4%bf%ae%e5%a4%8d&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;优先给两个辅助程序加最小 capability：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;sudo setcap cap_setuid=ep /usr/bin/newuidmap&#xA;sudo setcap cap_setgid=ep /usr/bin/newgidmap&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;检查结果：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;getcap /usr/bin/newuidmap /usr/bin/newgidmap&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;应该看到：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;/usr/bin/newuidmap cap_setuid=ep&#xA;/usr/bin/newgidmap cap_setgid=ep&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Arch 里 &lt;code&gt;/usr/sbin&lt;/code&gt; 通常是指向 &lt;code&gt;bin&lt;/code&gt; 的链接，所以报错路径即使写的是 &lt;code&gt;/usr/sbin/newuidmap&lt;/code&gt;，修 &lt;code&gt;/usr/bin/newuidmap&lt;/code&gt; 也是同一个文件。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;验证-rootless-namespace&#34;&gt;&lt;span&gt;验证 rootless namespace&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e9%aa%8c%e8%af%81-rootless-namespace&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;不用完整 build。先用 &lt;code&gt;podman unshare&lt;/code&gt; 验证 UID/GID 映射能不能建起来：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;podman unshare sh -c &amp;#39;echo uid_map:; cat /proc/self/uid_map; echo gid_map:; cat /proc/self/gid_map&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;修好后会看到类似：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;uid_map:&#xA;         0       1000          1&#xA;         1     100000      65536&#xA;gid_map:&#xA;         0       1000          1&#xA;         1     100000      65536&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这里的 &lt;code&gt;1000&lt;/code&gt; 是当前 WSL 用户的 UID，&lt;code&gt;100000 65536&lt;/code&gt; 来自 &lt;code&gt;/etc/subuid&lt;/code&gt; 和 &lt;code&gt;/etc/subgid&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;p&gt;然后重新 build：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;podman build . -t game-crawler:latest&lt;/code&gt;&lt;/pre&gt;&lt;h3 class=&#34;heading-element&#34; id=&#34;如果-setcap-不可用&#34;&gt;&lt;span&gt;如果 setcap 不可用&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e5%a6%82%e6%9e%9c-setcap-%e4%b8%8d%e5%8f%af%e7%94%a8&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;先确认 &lt;code&gt;setcap/getcap&lt;/code&gt; 是否存在：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;command -v setcap getcap&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果没有，安装提供它们的包：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;sudo pacman -S libcap&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;还有一种传统做法是给 &lt;code&gt;newuidmap/newgidmap&lt;/code&gt; 加 setuid：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;sudo chmod u&amp;#43;s /usr/bin/newuidmap /usr/bin/newgidmap&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;检查：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;stat -c &amp;#39;%A %U:%G %n&amp;#39; /usr/bin/newuidmap /usr/bin/newgidmap&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;期望看到 &lt;code&gt;-rwsr-xr-x&lt;/code&gt;。如果 file capability 可用，优先用 &lt;code&gt;setcap&lt;/code&gt;，权限范围更窄。&lt;/p&gt;&#xA;</description>
    </item><item>
      <title>修复 Arch Linux WSL2 中无法调用 Windows 程序的问题</title>
      <link>https://www.nite07.com/zh-cn/posts/wsl-arch-interop/</link>
      <pubDate>Thu, 09 Jul 2026 16:33:30 +0800</pubDate><author>nite@nite07.com (Nite)</author>
      <guid>https://www.nite07.com/zh-cn/posts/wsl-arch-interop/</guid>
      <category domain="https://www.nite07.com/zh-cn/categories/tutorials/">教程</category>
      <description>&lt;h3 class=&#34;heading-element&#34; id=&#34;什么是-wsl-interop&#34;&gt;&lt;span&gt;什么是 WSL Interop&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e4%bb%80%e4%b9%88%e6%98%af-wsl-interop&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;WSL Interop 是 WSL2 的核心功能之一，允许你在 WSL 终端中直接调用 Windows 可执行文件。例如：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;powershell.exe -NoProfile -Command &amp;#34;Get-Process | Select-Object -First 5&amp;#34;&#xA;winget.exe search nushell&#xA;explorer.exe .&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Windows 文件系统也通过 &lt;code&gt;/mnt/c/&lt;/code&gt;、&lt;code&gt;/mnt/d/&lt;/code&gt; 等路径挂载，可以直接读写。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;问题&#34;&gt;&lt;span&gt;问题&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e9%97%ae%e9%a2%98&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;在 Arch Linux WSL2 中，有时会发现以上命令全部失效，报错为&amp;quot;exec format error&amp;quot;或&amp;quot;未找到命令&amp;quot;。这是因为 WSL Interop 的注册依赖 &lt;code&gt;systemd-binfmt.service&lt;/code&gt;，而该服务在 Arch Linux 上会因条件检查未满足而被跳过——Arch 不像 Ubuntu 那样预置 binfmt.d 配置文件。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;修复&#34;&gt;&lt;span&gt;修复&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e4%bf%ae%e5%a4%8d&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;在 WSL 终端中执行：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;sudo mkdir -p /etc/binfmt.d&#xA;sudo tee /etc/binfmt.d/wsl-interop.conf &amp;gt; /dev/null &amp;lt;&amp;lt; &amp;#39;EOF&amp;#39;&#xA;:WSLInterop:M::MZ::/init:P&#xA;EOF&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;然后重启服务：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;sudo systemctl restart systemd-binfmt&lt;/code&gt;&lt;/pre&gt;&lt;h3 class=&#34;heading-element&#34; id=&#34;验证&#34;&gt;&lt;span&gt;验证&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e9%aa%8c%e8%af%81&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;pre&gt;&lt;code&gt;# 检查服务状态，应为 active (exited) 而非 skipped&#xA;systemctl status systemd-binfmt&#xA;&#xA;# 检查 WSLInterop 已注册&#xA;cat /proc/sys/fs/binfmt_misc/WSLInterop&#xA;&#xA;# 测试调用 Windows 程序&#xA;powershell.exe -NoProfile -Command &amp;#34;Write-Output &amp;#39;interop OK&amp;#39;&amp;#34;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;该配置文件是持久化的，每次 WSL 启动时自动生效，无需重复操作。&lt;/p&gt;&#xA;</description>
    </item><item>
      <title>给 Podman 邮局配置 Fail2Ban：用 UFW 封禁 SASL 暴力破解</title>
      <link>https://www.nite07.com/zh-cn/posts/fail2ban-postfix-podman/</link>
      <pubDate>Thu, 09 Jul 2026 15:30:00 +0800</pubDate><author>nite@nite07.com (Nite)</author>
      <guid>https://www.nite07.com/zh-cn/posts/fail2ban-postfix-podman/</guid>
      <category domain="https://www.nite07.com/zh-cn/categories/tutorials/">教程</category>
      <description>&lt;p&gt;我的自建邮局（Postfix + Dovecot）跑在 netcup VPS 的 rootless Podman 容器里。查看 Postfix 日志摘要时，24 小时内有 2477 封邮件被拒，其中 2471 封来自同一 IP；同时出现了 152 次 SASL LOGIN 认证失败，以及扫描、中继滥用和 TLS 探测。&lt;/p&gt;&#xA;&lt;p&gt;Postfix 自己可以拒绝垃圾邮件，但 SASL 密码猜测是另一类问题。Fail2Ban 能从日志里识别重复失败的来源，再让防火墙封禁 IP。服务器已经用 UFW 管理入站规则，因此这里不让 Fail2Ban 直接写 iptables，而是使用它内置的 &lt;code&gt;ufw&lt;/code&gt; action。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;环境&#34;&gt;&lt;span&gt;环境&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e7%8e%af%e5%a2%83&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;项目&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;值&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;宿主机&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Arch Linux&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;容器运行时&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Podman rootless&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;邮局镜像&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;code&gt;ghcr.io/docker-mailserver/docker-mailserver:latest&lt;/code&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;邮局服务&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;code&gt;systemd-mailserver&lt;/code&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Quadlet 文件&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;code&gt;~/pod/docker-mailserver/mailserver.container&lt;/code&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;宿主机邮件日志&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;code&gt;~/pod/docker-mailserver/logs/mail.log&lt;/code&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;容器内邮件日志&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;code&gt;/var/log/mail/mail.log&lt;/code&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Fail2Ban&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;1.1.0&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;防火墙&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;UFW&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;1-安装并检查现有状态&#34;&gt;&lt;span&gt;1. 安装并检查现有状态&lt;/span&gt;&#xA;  &lt;a href=&#34;#1-%e5%ae%89%e8%a3%85%e5%b9%b6%e6%a3%80%e6%9f%a5%e7%8e%b0%e6%9c%89%e7%8a%b6%e6%80%81&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;安装并启动 Fail2Ban：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;sudo pacman -S fail2ban&#xA;sudo systemctl enable --now fail2ban&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;先确认 UFW 已启用、Fail2Ban 正在运行，以及当前启用的 jail：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;sudo ufw status verbose&#xA;sudo systemctl is-active fail2ban&#xA;sudo fail2ban-client status&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这里应看到 UFW 的 &lt;code&gt;Status: active&lt;/code&gt;。完成配置后，jail 列表会包含：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;Status&#xA;|- Number of jail:  3&#xA;`- Jail list:       postfix, postfix-sasl, sshd&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;backend&lt;/code&gt; 与 &lt;code&gt;banaction&lt;/code&gt; 是两个不同的设置：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;backend&lt;/code&gt; 决定 Fail2Ban 从哪里读取失败记录，例如 journald 或普通日志文件。&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;banaction&lt;/code&gt; 决定命中阈值后怎样封禁 IP。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;这个邮件容器将日志写到宿主机挂载文件，所以邮件 jail 要使用 &lt;code&gt;backend = polling&lt;/code&gt;；切换到 UFW 不会改变日志读取方式。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;2-让-postfix-jail-读取容器日志&#34;&gt;&lt;span&gt;2. 让 Postfix jail 读取容器日志&lt;/span&gt;&#xA;  &lt;a href=&#34;#2-%e8%ae%a9-postfix-jail-%e8%af%bb%e5%8f%96%e5%ae%b9%e5%99%a8%e6%97%a5%e5%bf%97&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;传统安装的 Postfix 日志通常在 &lt;code&gt;/var/log/mail.log&lt;/code&gt;。容器里的这个路径对宿主机 Fail2Ban 不可见，应该填写宿主机 Volume 挂载后的真实路径。&lt;/p&gt;&#xA;&lt;p&gt;Arch 的 Fail2Ban 默认会通过 &lt;code&gt;paths-arch.conf&lt;/code&gt; 为 Postfix 选择 systemd 日志后端。这里的日志是文件，因此在本地覆盖配置里明确设置 &lt;code&gt;polling&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;p&gt;创建或编辑 &lt;code&gt;/etc/fail2ban/jail.d/mailserver.local&lt;/code&gt;：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;[DEFAULT]&#xA;postfix_backend = polling&#xA;postfix_log = /home/nite/pod/docker-mailserver/logs/mail.log&#xA;&#xA;[postfix-sasl]&#xA;enabled   = true&#xA;mode      = auth&#xA;port      = smtp,465,submission,imap,imaps,pop3,pop3s&#xA;logpath   = /home/nite/pod/docker-mailserver/logs/mail.log&#xA;backend   = polling&#xA;maxretry  = 5&#xA;findtime  = 10m&#xA;bantime   = 1h&#xA;banaction = ufw&#xA;&#xA;[postfix]&#xA;enabled   = true&#xA;mode      = more&#xA;port      = smtp,465,submission&#xA;logpath   = /home/nite/pod/docker-mailserver/logs/mail.log&#xA;backend   = polling&#xA;maxretry  = 5&#xA;findtime  = 10m&#xA;bantime   = 1h&#xA;banaction = ufw&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;文件后缀要用 &lt;code&gt;.local&lt;/code&gt;。Fail2Ban 依次读取 &lt;code&gt;jail.conf&lt;/code&gt;、&lt;code&gt;jail.d/*.conf&lt;/code&gt;、&lt;code&gt;jail.local&lt;/code&gt;、&lt;code&gt;jail.d/*.local&lt;/code&gt;，这个本地文件会在最后覆盖默认值。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;3-将-sshd-的封禁-action-改为-ufw&#34;&gt;&lt;span&gt;3. 将 sshd 的封禁 action 改为 UFW&lt;/span&gt;&#xA;  &lt;a href=&#34;#3-%e5%b0%86-sshd-%e7%9a%84%e5%b0%81%e7%a6%81-action-%e6%94%b9%e4%b8%ba-ufw&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;不要编辑 &lt;code&gt;/etc/fail2ban/jail.conf&lt;/code&gt;，它由软件包提供，更新时可能被覆盖。&lt;/p&gt;&#xA;&lt;p&gt;如果希望默认 jail 都使用 UFW，编辑 &lt;code&gt;/etc/fail2ban/jail.local&lt;/code&gt;，在 &lt;code&gt;[DEFAULT]&lt;/code&gt; 中设置：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;[DEFAULT]&#xA;banaction = ufw&#xA;banaction_allports = ufw&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;若只想切换 SSH jail，也可以只写：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;[sshd]&#xA;banaction = ufw&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Postfix 和 postfix-sasl 已在 &lt;code&gt;mailserver.local&lt;/code&gt; 中各自指定 &lt;code&gt;banaction = ufw&lt;/code&gt;，不会依赖这个全局设置。&lt;/p&gt;&#xA;&lt;p&gt;Fail2Ban 自带的 UFW action 会调用 &lt;code&gt;ufw&lt;/code&gt; 添加和删除规则。这样不需要再通过 &lt;code&gt;iptables -L&lt;/code&gt; 查 Fail2Ban 的封禁链，直接查看 UFW 即可。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;4-先检查配置再重启-jail&#34;&gt;&lt;span&gt;4. 先检查配置，再重启 jail&lt;/span&gt;&#xA;  &lt;a href=&#34;#4-%e5%85%88%e6%a3%80%e6%9f%a5%e9%85%8d%e7%bd%ae%e5%86%8d%e9%87%8d%e5%90%af-jail&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;编辑完成后先解析配置：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;sudo fail2ban-client -d &amp;gt;/dev/null&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这条命令只检查解析结果，不修改防火墙。&lt;/p&gt;&#xA;&lt;p&gt;切换 action 后应完整重启服务：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;sudo systemctl restart fail2ban&lt;/code&gt;&lt;/pre&gt;&lt;h3 class=&#34;heading-element&#34; id=&#34;5-验证日志action-和-ufw-规则&#34;&gt;&lt;span&gt;5. 验证日志、action 和 UFW 规则&lt;/span&gt;&#xA;  &lt;a href=&#34;#5-%e9%aa%8c%e8%af%81%e6%97%a5%e5%bf%97action-%e5%92%8c-ufw-%e8%a7%84%e5%88%99&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;确认邮件 jail 正在读取宿主机日志：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;sudo fail2ban-client status postfix-sasl&#xA;sudo fail2ban-client status postfix&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出中的 &lt;code&gt;File list&lt;/code&gt; 应为：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;/home/nite/pod/docker-mailserver/logs/mail.log&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;不实际封禁的情况下，可以测试 SASL 过滤器是否匹配已有日志：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;sudo fail2ban-regex /home/nite/pod/docker-mailserver/logs/mail.log &amp;#34;postfix[mode=auth]&amp;#34;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;服务重启后，检查运行时 action，而不是只看配置文件：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;sudo systemctl is-active fail2ban&#xA;sudo fail2ban-client get sshd actions&#xA;sudo fail2ban-client get postfix actions&#xA;sudo fail2ban-client get postfix-sasl actions&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;本次实际输出：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;active&#xA;The jail sshd has the following actions:&#xA;ufw&#xA;The jail postfix has the following actions:&#xA;ufw&#xA;The jail postfix-sasl has the following actions:&#xA;ufw&lt;/code&gt;&lt;/pre&gt;&lt;h3 class=&#34;heading-element&#34; id=&#34;6-做一次端到端测试&#34;&gt;&lt;span&gt;6. 做一次端到端测试&lt;/span&gt;&#xA;  &lt;a href=&#34;#6-%e5%81%9a%e4%b8%80%e6%ac%a1%e7%ab%af%e5%88%b0%e7%ab%af%e6%b5%8b%e8%af%95&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;pre&gt;&lt;code&gt;sudo fail2ban-client set sshd banip 192.0.2.1&#xA;sudo ufw status numbered | grep -F 192.0.2.1&#xA;sudo fail2ban-client set sshd unbanip 192.0.2.1&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;第二条命令应显示带有 &lt;code&gt;by Fail2Ban&lt;/code&gt; 注释的 UFW 规则；第三条命令会立即删除测试封禁。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;参考&#34;&gt;&lt;span&gt;参考&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e5%8f%82%e8%80%83&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/fail2ban/fail2ban/blob/1.1.0/config/action.d/ufw.conf&#34; target=&#34;_blank&#34; rel=&#34;external nofollow noopener noreferrer&#34;&gt;Fail2Ban 1.1.0 UFW action&lt;i class=&#34;fa-solid fa-external-link-alt fa-xs ms-1 text-secondary&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/fail2ban/fail2ban/blob/1.1.0/config/jail.conf&#34; target=&#34;_blank&#34; rel=&#34;external nofollow noopener noreferrer&#34;&gt;Fail2Ban 1.1.0 默认 jail 配置&lt;i class=&#34;fa-solid fa-external-link-alt fa-xs ms-1 text-secondary&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
    </item><item>
      <title>隐藏 WSL2 开机自启的控制台窗口</title>
      <link>https://www.nite07.com/zh-cn/posts/wsl-autostart-hidden/</link>
      <pubDate>Thu, 09 Jul 2026 02:00:00 +0800</pubDate><author>nite@nite07.com (Nite)</author>
      <guid>https://www.nite07.com/zh-cn/posts/wsl-autostart-hidden/</guid>
      <category domain="https://www.nite07.com/zh-cn/categories/tutorials/">教程</category>
      <description>&lt;p&gt;WSL2 在所有终端窗口关闭后会自动停止实例。虽然可以通过 &lt;code&gt;.wslconfig&lt;/code&gt; 设置 &lt;code&gt;vmIdleTimeout=-1&lt;/code&gt; 和 &lt;code&gt;instanceIdleTimeout=-1&lt;/code&gt; 来缓解，但部分版本仍然会出现实例被终止的情况。一个可靠的方案是在登录时启动一个常驻进程来保持实例运行，但直接通过任务计划程序运行 &lt;code&gt;wsl.exe&lt;/code&gt; 会弹出一个黑色控制台窗口。本文介绍用 VBS 脚本包装来隐藏这个窗口。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;创建-vbs-启动脚本&#34;&gt;&lt;span&gt;创建 VBS 启动脚本&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e5%88%9b%e5%bb%ba-vbs-%e5%90%af%e5%8a%a8%e8%84%9a%e6%9c%ac&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;在 Windows 上创建一个 VBS 脚本文件，例如 &lt;code&gt;C:\Users\你的用户名\start-wsl.vbs&lt;/code&gt;，内容如下：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;Set WshShell = CreateObject(&amp;#34;WScript.Shell&amp;#34;)&#xA;WshShell.Run &amp;#34;wsl.exe -d archlinux -u root /bin/bash -c &amp;#34;&amp;#34;sleep infinity&amp;#34;&amp;#34;&amp;#34;, 0, False&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;将 &lt;code&gt;archlinux&lt;/code&gt; 替换为你的 WSL 发行版名称（可在 PowerShell 中执行 &lt;code&gt;wsl -l&lt;/code&gt; 查看）。&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;WshShell.Run&lt;/code&gt; 的参数说明：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;第二个参数 &lt;code&gt;0&lt;/code&gt; — 隐藏窗口，不显示任何界面&lt;/li&gt;&#xA;&lt;li&gt;第三个参数 &lt;code&gt;False&lt;/code&gt; — 不等待子进程结束，脚本立即退出&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;创建计划任务&#34;&gt;&lt;span&gt;创建计划任务&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e5%88%9b%e5%bb%ba%e8%ae%a1%e5%88%92%e4%bb%bb%e5%8a%a1&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;在 PowerShell 中执行以下命令，创建一个在登录时自动运行的计划任务：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;$action = New-ScheduledTaskAction -Execute &amp;#34;wscript.exe&amp;#34; -Argument &amp;#39;&amp;#34;C:\Users\你的用户名\start-wsl.vbs&amp;#34;&amp;#39;&#xA;$trigger = New-ScheduledTaskTrigger -AtLogOn&#xA;$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -ExecutionTimeLimit (New-TimeSpan -Seconds 0)&#xA;Register-ScheduledTask -TaskName &amp;#34;WSL2 AutoStart&amp;#34; -Action $action -Trigger $trigger -Settings $settings&lt;/code&gt;&lt;/pre&gt;&lt;h3 class=&#34;heading-element&#34; id=&#34;验证&#34;&gt;&lt;span&gt;验证&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e9%aa%8c%e8%af%81&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;注销并重新登录（或重启），在 PowerShell 中执行 &lt;code&gt;wsl -l --running&lt;/code&gt; 确认 WSL 实例已启动。整个过程中不会出现任何控制台窗口。&lt;/p&gt;&#xA;</description>
    </item><item>
      <title>mihomo TUN 模式断网：ip_forward、UFW 与 auto-redirect</title>
      <link>https://www.nite07.com/zh-cn/posts/mihomo-tun-no-network/</link>
      <pubDate>Tue, 30 Jun 2026 03:15:00 +1000</pubDate><author>nite@nite07.com (Nite)</author>
      <guid>https://www.nite07.com/zh-cn/posts/mihomo-tun-no-network/</guid>
      <category domain="https://www.nite07.com/zh-cn/categories/tutorials/">教程</category>
      <description>&lt;p&gt;mihomo 用 TUN 模式（&lt;code&gt;stack: system&lt;/code&gt;，&lt;code&gt;auto-route: true&lt;/code&gt;）时，启动后全局断网，SSH 也掉线。问题不在 mihomo 配置本身，而是系统网络转发设置没配齐。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;原因&#34;&gt;&lt;span&gt;原因&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e5%8e%9f%e5%9b%a0&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;&lt;code&gt;stack: system&lt;/code&gt; 让 mihomo 依赖内核网络栈转发 TUN 流量。&lt;code&gt;auto-route: true&lt;/code&gt; 会接管默认路由，把所有流量导入 TUN 网卡。但内核转发需要满足三个条件：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;code&gt;net.ipv4.ip_forward&lt;/code&gt; 必须为 &lt;code&gt;1&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;防火墙 forward 链不能 DROP&lt;/li&gt;&#xA;&lt;li&gt;内核不能因 rp_filter 丢弃 TUN 网卡返回的 TCP 包&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Arch Linux 默认 &lt;code&gt;ip_forward=0&lt;/code&gt;，UFW 默认 &lt;code&gt;DEFAULT_FORWARD_POLICY=&amp;quot;DROP&amp;quot;&lt;/code&gt;，这两个挡住转发。就算前两步都改了，在内核 6.x 上 ping 能通但 curl 超时——TCP 握手包从 TUN 网卡回来时被 &lt;code&gt;rp_filter=1&lt;/code&gt; 丢掉。表现就是 mihomo 日志能看到 &lt;code&gt;[TCP] ... using DIRECT&lt;/code&gt;，但连接一直 timeout。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;修复&#34;&gt;&lt;span&gt;修复&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e4%bf%ae%e5%a4%8d&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;h4 class=&#34;heading-element&#34; id=&#34;1-开启-ip_forward&#34;&gt;&lt;span&gt;1. 开启 ip_forward&lt;/span&gt;&#xA;  &lt;a href=&#34;#1-%e5%bc%80%e5%90%af-ip_forward&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;echo &amp;#39;net.ipv4.ip_forward = 1&amp;#39; | sudo tee /etc/sysctl.d/99-mihomo.conf&#xA;sudo sysctl -p /etc/sysctl.d/99-mihomo.conf&lt;/code&gt;&lt;/pre&gt;&lt;h4 class=&#34;heading-element&#34; id=&#34;2-ufw-forward-改为-accept&#34;&gt;&lt;span&gt;2. UFW forward 改为 ACCEPT&lt;/span&gt;&#xA;  &lt;a href=&#34;#2-ufw-forward-%e6%94%b9%e4%b8%ba-accept&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;sudo sed -i &amp;#39;s/DEFAULT_FORWARD_POLICY=&amp;#34;DROP&amp;#34;/DEFAULT_FORWARD_POLICY=&amp;#34;ACCEPT&amp;#34;/&amp;#39; /etc/default/ufw&#xA;sudo ufw reload&lt;/code&gt;&lt;/pre&gt;&lt;h4 class=&#34;heading-element&#34; id=&#34;3-开启-auto-redirect&#34;&gt;&lt;span&gt;3. 开启 auto-redirect&lt;/span&gt;&#xA;  &lt;a href=&#34;#3-%e5%bc%80%e5%90%af-auto-redirect&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h4&gt;&lt;p&gt;在 config.yaml 的 &lt;code&gt;tun&lt;/code&gt; 部分加 &lt;code&gt;auto-redirect: true&lt;/code&gt;：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;tun:&#xA;  enable: true&#xA;  stack: system&#xA;  auto-route: true&#xA;  auto-redirect: true&#xA;  auto-detect-interface: true&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;auto-redirect&lt;/code&gt; 会自动配置 nftables 规则重定向 TCP 连接，绕过 rp_filter 对 TUN 网卡返回包的过滤。改完重启 mihomo。&lt;/p&gt;&#xA;&lt;h3 class=&#34;heading-element&#34; id=&#34;另一个选择&#34;&gt;&lt;span&gt;另一个选择&lt;/span&gt;&#xA;  &lt;a href=&#34;#%e5%8f%a6%e4%b8%80%e4%b8%aa%e9%80%89%e6%8b%a9&#34; class=&#34;heading-mark&#34;&gt;&lt;svg class=&#34;octicon octicon-link&#34; viewBox=&#34;0 0 16 16&#34; version=&#34;1.1&#34; width=&#34;16&#34; height=&#34;16&#34; aria-hidden=&#34;true&#34;&gt;&lt;path d=&#34;m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z&#34;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&#xA;&lt;/h3&gt;&lt;p&gt;不想改系统转发配置的话，可以换 TUN 协议栈：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;mixed&lt;/code&gt;：TCP 用 system 栈，UDP 用 gvisor 栈，兼容性比 system 好，性能比纯 gvisor 高&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;gvisor&lt;/code&gt;：完全在用户态处理 TCP/IP，不依赖内核转发，兼容性最强但性能略低&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;把 config.yaml 里 &lt;code&gt;tun.stack&lt;/code&gt; 从 &lt;code&gt;system&lt;/code&gt; 改成 &lt;code&gt;mixed&lt;/code&gt; 或 &lt;code&gt;gvisor&lt;/code&gt; 即可，不需要改系统配置。&lt;/p&gt;&#xA;</description>
    </item>
  </channel>
</rss>
