<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Tailscale on heyaohua's Blog</title><link>https://blog.heyaohua.com/tags/tailscale/</link><description>Recent content in Tailscale on heyaohua's Blog</description><image><title>heyaohua's Blog</title><url>https://blog.heyaohua.com/og-image.png</url><link>https://blog.heyaohua.com/og-image.png</link></image><generator>Hugo</generator><language>zh-CN</language><lastBuildDate>Fri, 30 Oct 2026 10:24:44 +0800</lastBuildDate><atom:link href="https://blog.heyaohua.com/tags/tailscale/index.xml" rel="self" type="application/rss+xml"/><item><title>Tailscale：让散落各处的设备，像在同一个局域网</title><link>https://blog.heyaohua.com/posts/2026/10/tailscale-private-network-practical-guide/</link><pubDate>Fri, 30 Oct 2026 10:24:44 +0800</pubDate><guid>https://blog.heyaohua.com/posts/2026/10/tailscale-private-network-practical-guide/</guid><description>结合我本机的 Tailscale 配置，介绍 Mac 与 Linux 如何组成私有网络，以及远程开发、手机访问本地网页、NAS、数据库和 ESP32 的玩法。讲清 MagicDNS、子网路由、出口节点与 Serve/Funnel 的区别。</description><content:encoded><![CDATA[<p>电脑在家，人在外面，想打开一个开发页面、连一下数据库，或者看一眼自己的小设备，问题经常先卡在网络上：没有公网 IP，地址会变，路由器不方便配端口转发，还要考虑暴露哪些服务。</p>
<p>Tailscale 解决的是这段连接。设备加入同一个私有网络，并得到相应访问许可后，就可以通过稳定的地址或名称互访，使用体验很像“都在一个局域网里”。</p>
<p>这篇先用我本机的实际配置讲清它是什么，再聊远程开发、NAS、手机和 ESP32 能怎么接进来。<strong>配置核对是实际记录，后面的玩法是示例与构思，没有把“看得到设备”当成“已经测通所有服务”。</strong></p>
<h2 id="万物都能局域网可以怎样理解">“万物都能局域网”，可以怎样理解</h2>
<p>Tailscale 在已有网络之上组织一张私有 IP 网络，叫作 <strong>tailnet</strong>。两台设备可以分别在家中 Wi-Fi、手机热点或云端，不必真的接在同一台路由器上。</p>
<p>它基于 WireGuard 加密传输，帮助设备协商连接并尝试直连；直连困难时，可以通过 DERP 或已配置的 Peer Relay 中继。节点之间的数据仍受加密保护，但走中继和走直连，延迟与吞吐可能明显不同。不能把“能连上”理解成“保证满速”。<a href="https://tailscale.com/docs/reference/connection-types">连接方式说明</a>、<a href="https://tailscale.com/docs/concepts/tailscale-encryption">加密说明</a>分别解释了这两件事。</p>
<p>这里的“局域网”是访问体验的比喻。Tailscale 主要工作在 <strong>L3 网络层</strong>，不会把各地设备自动变成同一个以太网广播域。一些依赖广播、多播或 mDNS 的自动发现功能，需要另外处理；游戏、打印机或投屏设备，也不能一概承诺安装后就能自动发现。<a href="https://tailscale.com/docs/concepts/tailscale-osi">官方 OSI 说明</a>对此有明确区分。</p>
<p>实际使用时，先记住三个动作：<strong>找到目标设备的地址或名称，确认目标服务在运行，再检查访问权限。</strong> Tailscale 提供网络连接，SSH、文件共享、网页和远程桌面仍由对应的软件提供。</p>
<h2 id="我的本机配置先是一张简单的三节点网络">我的本机配置：先是一张简单的三节点网络</h2>
<p>2026 年 10 月 23 日，我对本机客户端做了一次只读核对：</p>
<table>
	<thead>
			<tr>
					<th>项目</th>
					<th>这次实际看到的状态</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>本机</td>
					<td>macOS，Tailscale App，版本 1.104.1</td>
			</tr>
			<tr>
					<td>运行状态</td>
					<td><code>Running</code>，虚拟网络接口已启用</td>
			</tr>
			<tr>
					<td>本机可见的其他节点</td>
					<td>一台 Linux、一台 Mac；核对时均显示在线</td>
			</tr>
			<tr>
					<td>MagicDNS</td>
					<td>tailnet 已启用，本机接受 Tailscale DNS 配置</td>
			</tr>
			<tr>
					<td>接受子网路由</td>
					<td>本机设置已开启</td>
			</tr>
			<tr>
					<td>发布子网路由</td>
					<td>本机没有发布任何网段</td>
			</tr>
			<tr>
					<td>出口节点</td>
					<td>本机没有选择出口节点</td>
			</tr>
			<tr>
					<td>Tailscale SSH 服务端开关</td>
					<td>本机未开启</td>
			</tr>
			<tr>
					<td>Serve / Funnel</td>
					<td>本机没有配置服务发布</td>
			</tr>
	</tbody>
</table>
<p>这份配置还很基础：<strong>已经加入网络并能看到其他节点，尚未扩展成家庭网关或网页发布入口。</strong> 状态里的“在线”并不能证明 SSH、数据库或网页端口一定可达；本次也没有做异地连接、吞吐或稳定性测试。</p>
<p>设备名、地址和账户信息没有直接贴出来。下文的 <code>work-mac</code>、<code>dev-linux</code> 都是示例名称，使用时要换成自己的设备名。</p>
<p><img alt="Tailscale 当前可见节点与 Serve、子网网关候选玩法示意" loading="lazy" src="/images/posts/tailscale-private-network-practical-guide/network-and-play.svg"></p>
<p><em>自绘拓扑示意。顶部对应本次可见的节点类型；下部是尚未配置和验证的玩法，不是实机截图或测速结果。</em></p>
<h3 id="magicdns-让连接更容易记">MagicDNS 让连接更容易记</h3>
<p>开启 MagicDNS 后，可以用设备名称代替一串数字地址。例如，目标 Linux 主机已经运行普通 SSH 服务，且网络策略允许访问时，可以这样连接：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ssh developer@dev-linux
</span></span></code></pre></div><p>名称只解决“去哪里”，不会替目标创建用户、开启 SSH 或跳过密钥认证。MagicDNS 也不会自动为子网路由后面的每台打印机、NAS、ESP32 注册名称。<a href="https://tailscale.com/docs/features/magicdns">MagicDNS</a>和<a href="https://tailscale.com/docs/how-to/connect-to-devices">连接设备指南</a>可以作为入门参考。</p>
<h2 id="先从三个能落地的用途开始">先从三个能落地的用途开始</h2>
<p>以下是可采用的用法，命令中的主机名和账户均为示例；本次没有据此修改现有机器。</p>
<h3 id="远程开发把计算和环境留在原来的机器">远程开发：把计算和环境留在原来的机器</h3>
<p>一台机器负责放代码、依赖和运行环境，另一台通过 SSH 或编辑器的 Remote SSH 连接。Tailscale 负责把两端接起来，编辑器和目标机器继续做各自的事。</p>
<p>例如，用普通 SSH 登录后检查日志、启动开发服务，或者用 <code>rsync</code> 同步一个项目目录。目标主机仍要启用 SSH，并配好账户、密钥和访问规则。</p>
<p>这里容易混淆两个名字：</p>
<ul>
<li><strong>普通 SSH 走 Tailscale 网络</strong>：沿用系统 SSH 服务和它原来的认证方式。</li>
<li><strong>Tailscale SSH</strong>：专门启用的功能，由 Tailscale 管理相应 SSH 连接的身份与授权。</li>
</ul>
<p>本机的 <code>RunSSH</code> 是关闭的，不能因此推断系统 SSH 也关闭。反过来，安装 macOS App 也不等于开启了 Tailscale SSH 服务端。官方目前将其服务端支持范围列为 Linux 和 macOS 开源 CLI 版本；普通 macOS App 用户仍可使用系统 SSH。<a href="https://tailscale.com/docs/features/tailscale-ssh">Tailscale SSH 文档</a>列出了这些边界。</p>
<h3 id="开发数据库优先建立一条明确的访问路径">开发数据库：优先建立一条明确的访问路径</h3>
<p>我本机还保留着 MySQL、Redis 和 PostgreSQL 的 Docker 开发配置。这类环境很适合讨论远程调试，但网络接通不代表数据库权限也自动准备好了。</p>
<p>一个便于理解的办法是用 SSH 隧道。假设 PostgreSQL 在 <code>work-mac</code> 上监听 <code>127.0.0.1:5432</code>，系统 SSH 已配置好，可以在访问端执行：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ssh -N -L 127.0.0.1:15432:127.0.0.1:5432 developer@work-mac
</span></span></code></pre></div><p>然后让访问端的数据库工具连接 <code>127.0.0.1:15432</code>。这里第二个 <code>127.0.0.1</code> 指向 SSH 目标机器，数据库账户与密码仍按原服务要求填写。</p>
<p>这是一条待验证的连接方案，不是本次成功连接的记录。若直接通过 Tailscale 地址访问数据库，还要分别检查服务监听地址、Docker 端口映射、主机防火墙和 tailnet 访问规则；没有必要为了“方便”就把所有数据库端口一起放开。</p>
<h3 id="手机看本地网页serve-比记住一堆端口更顺手">手机看本地网页：Serve 比记住一堆端口更顺手</h3>
<p>假设 Mac 上有一个开发页面，正在 <code>http://127.0.0.1:3000</code> 提供服务。装了 Tailscale 的手机可以通过 <strong>Serve</strong> 访问它。</p>
<p>macOS 上先按所用发行版本的说明准备命令行工具，确认终端能运行 <code>tailscale version</code>；装了图形 App，不一定已经把裸命令放进 <code>PATH</code>。<a href="https://tailscale.com/docs/reference/tailscale-cli?tab=macos">CLI 文档</a>列出了对应方式。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6272a4"># 示例：在网页所在的 Mac 上运行</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4"># 需要 tailnet 已满足相应 HTTPS 配置与访问权限</span>
</span></span><span style="display:flex;"><span>tailscale serve <span style="color:#bd93f9">3000</span>
</span></span></code></pre></div><p>命令会显示 tailnet 内的访问 URL。需要开启 HTTPS 等前置能力时，客户端会引导相应设置；本次写作没有执行这条发布命令。前台运行的示例通常可用 <code>Ctrl+C</code> 停止。</p>
<p>使用公开可信的 HTTPS 证书，还要留意：证书中的机器名和 tailnet DNS 名称会进入公开证书透明度记录，命名时不要带敏感信息。这不等于服务因此向公网开放，访问范围仍由所用入口和规则决定。<a href="https://tailscale.com/docs/how-to/set-up-https-certificates">HTTPS 说明</a>解释了这一区别。</p>
<p>这让只监听 localhost 的网页有了私有访问入口，适合手机测试页面、查看个人面板，或者访问自己的开发工具。应用原有的登录和业务权限仍要保留；被代理并不等于自动获得完整的应用鉴权。</p>
<p>macOS App 可以用 Serve 代理端口，直接分享文件或目录则有发行版本限制。因此这里使用“代理一个已经运行的 HTTP 服务”，而不是直接分享整个项目文件夹。<a href="https://tailscale.com/docs/features/tailscale-serve">Serve 说明</a>和<a href="https://tailscale.com/docs/reference/examples/serve">示例</a>有具体用法。</p>
<h2 id="设备装不了客户端就让网关带它进来">设备装不了客户端，就让网关带它进来</h2>
<p>NAS、路由器、打印机和小硬件，并不是每一种都能直接安装 Tailscale。可以分成两类：</p>
<table>
	<thead>
			<tr>
					<th>接入方式</th>
					<th>适合的设备</th>
					<th>访问时注意什么</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>设备自己运行客户端</td>
					<td>支持的平台主机、部分 NAS</td>
					<td>使用它的 Tailscale 地址或名称，连接原有服务</td>
			</tr>
			<tr>
					<td>通过子网路由器接入</td>
					<td>没有客户端、但已有局域网服务的设备</td>
					<td>访问其局域网 IP；网关、路由批准与权限需另行配置</td>
			</tr>
	</tbody>
</table>
<p>**子网路由器（subnet router）**通常是一台常开的电脑、Linux 主机或其他受支持设备。它一边加入 tailnet，一边能访问目标局域网，把特定网段的流量转发过去。配置涉及路由发布、批准、访问规则以及必要的 IP 转发；不是装好客户端就自动覆盖整个家庭网络。<a href="https://tailscale.com/docs/features/subnet-routers">子网路由文档</a>给出了完整流程。</p>
<p>这也是 NAS、媒体库和文件共享的一种接入思路。网络通道准备好后，仍然使用原来的网页、SMB 或媒体客户端和账户。Tailscale 不会自动创建共享目录，也不会把 NAS 变成同步盘；不同 NAS 平台还有各自的配置要求。<a href="https://tailscale.com/docs/use-cases/personal-or-at-home-use/access-nas-media-file-servers">官方 NAS 使用指南</a>适合继续查阅。</p>
<p>还有一个容易忽略的边界：<strong>加密隧道到子网网关结束，网关到普通局域网设备的那一段，取决于原来的应用协议。</strong> 如果终端用的是明文 HTTP，就不能说 WireGuard 一直加密到了终端芯片。<a href="https://tailscale.com/blog/how-tailscale-works">架构说明</a>解释了这种转发方式。</p>
<h3 id="和我的-esp32-小团结合可以怎么玩">和我的 ESP32 小团结合，可以怎么玩</h3>
<p>我之前写过<a href="/posts/2026/10/esp32-codex-personal-assistant/">小团的实际项目</a>。当前主要链路是 Mac 采集状态，再通过蓝牙传给 ESP32；<a href="/posts/2026/10/esp32-codex-assistant-next-ideas/">下一步构思</a>里也讨论过它还能怎样参与工作节奏。</p>
<p>结合 Tailscale，可以继续考虑两条路线：</p>
<p><strong>路线一：手机访问 Mac，再由 Mac 和小团交互。</strong> 手机加入 tailnet，打开 Mac 上新增的控制页面或接口；Mac 再通过现有蓝牙链路与小团通信。例如查看任务状态、调整提醒或切换展示内容。控制接口、应用鉴权和固件命令都需要另外实现和验证，Mac 还得保持运行并处于蓝牙可达范围。</p>
<p><strong>路线二：给有网络服务的 ESP32 提供子网访问。</strong> 如果某个 ESP32 项目本身提供了 HTTP 接口，或接入家中的 MQTT 服务，就可以研究通过子网网关访问这些局域网服务。这里的前提是它已经有对应的网络功能；当前小团的蓝牙链路不会因为安装了 Tailscale 就自动变成远程 HTTP API。</p>
<p>这两条都属于后续玩法。Tailscale 负责接通网络，设备交互仍要由具体项目完成，串口烧录、自动发现和远程唤醒也需要分别判断。</p>
<h2 id="子网路由出口节点serve-和-funnel各管哪一段">子网路由、出口节点、Serve 和 Funnel，各管哪一段</h2>
<p>这几个名字经常一起出现，可以按目的来区分：</p>
<table>
	<thead>
			<tr>
					<th>能力</th>
					<th>解决的问题</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>普通 tailnet 连接</td>
					<td>访问另一台已加入网络的设备</td>
			</tr>
			<tr>
					<td>子网路由</td>
					<td>访问网关后面某个指定局域网的设备</td>
			</tr>
			<tr>
					<td>出口节点（exit node）</td>
					<td>让客户端的普通互联网访问经另一台设备转发出去</td>
			</tr>
			<tr>
					<td>Serve</td>
					<td>把本机服务提供给 tailnet 内获准的访问者</td>
			</tr>
			<tr>
					<td>Funnel</td>
					<td>把指定本机服务提供给公网访问者</td>
			</tr>
	</tbody>
</table>
<p>访问家里的 NAS 通常不要求开启出口节点。只有想让普通上网流量经指定设备出去，才考虑配置、批准并在客户端选择 exit node；速度也会受到出口机器和网络的限制。<a href="https://tailscale.com/docs/features/exit-nodes">出口节点文档</a>说明了这一点。</p>
<p>本机“接受子网路由”虽然是开启的，但没有选择出口节点，不能把这两个设置当成“已经全局走某台服务器”。</p>
<p>Funnel 则是另一种方向：让没有加入 tailnet 的人也能访问某个服务，适合准备好鉴权的演示站或 webhook 接收端。<strong>Funnel URL 是公网入口，不能当作“只有发给朋友才看得到”的私密链接。</strong> 平台支持范围和前置条件要按当前客户端核对；本机尚未配置，本篇不把它写成已验证功能。<a href="https://tailscale.com/docs/features/tailscale-funnel">Funnel</a>与<a href="https://tailscale.com/docs/reference/funnel-vs-sharing">设备分享的区别</a>值得先看清楚。</p>
<h2 id="真正连接不上时按顺序缩小范围">真正连接不上时，按顺序缩小范围</h2>
<p>可以先用这组命令查看设备和路径。示例中的 <code>dev-linux</code> 需要替换成真实目标：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>tailscale version
</span></span><span style="display:flex;"><span>tailscale status
</span></span><span style="display:flex;"><span>tailscale ping --c<span style="color:#ff79c6">=</span><span style="color:#bd93f9">3</span> dev-linux
</span></span></code></pre></div><p><code>tailscale ping</code> 可以帮助观察连接路径；它成功，不代表目标的 SSH、数据库或网页端口一定开放。继续按这条顺序查：</p>
<ol>
<li><strong>目标是否在线。</strong> 主机休眠、关机或断网时，网络工具不能代替它提供服务。</li>
<li><strong>名称是否正确。</strong> MagicDNS 名称与局域网的 <code>.local</code> 自动发现不是同一套机制；子网后方设备可先按其 IP 排查。</li>
<li><strong>目标服务是否运行并监听正确地址。</strong> 一个只绑定远端 <code>127.0.0.1</code> 的服务，不能直接用那台机器的 Tailscale IP 访问，可以考虑 Serve 或 SSH 隧道。</li>
<li><strong>网络规则和应用认证是否允许。</strong> 检查 tailnet 策略、主机防火墙，以及应用本身的账号与权限。</li>
<li><strong>是否把连通问题和性能问题混在一起。</strong> 已经能打开服务但速度慢，再看直连/中继路径、两端带宽和设备负载。</li>
</ol>
<p>新 tailnet 的初始访问策略通常比较宽松。设备变多之后，需要按用户、设备或服务缩小权限；ACL/grants 不会自动替普通数据库和文件服务配置它们的业务账户。Tailscale SSH、Serve 身份头或专门集成的应用能力，则是需要有意配置的另一层功能。<a href="https://tailscale.com/docs/features/access-control">访问控制说明</a>可作为后续整理权限的入口。</p>
<p>对我这套配置来说，已经有了一个清楚的起点：Mac 和 Linux 节点可见，名称服务已启用。接下来可以先挑一个具体目标，例如“手机打开 Mac 的一个私有网页”，验证完整链路，再把它扩展到开发环境或小团。</p>
<p>“万物都能局域网”的实用之处，是把分散设备之间的连接变得容易组织；至于连上以后能做什么，仍然取决于我们在两端运行的服务和真正完成的交互。</p>
]]></content:encoded></item></channel></rss>