<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Soul Of Free Loop &#187; 网络技术</title>
	<atom:link href="https://zohead.com/archives/category/technology/network-tech/feed" rel="self" type="application/rss+xml" />
	<link>https://zohead.com</link>
	<description>Uranus Zhou&#039;s Blog</description>
	<lastBuildDate>Sat, 19 Jul 2025 15:42:46 +0000</lastBuildDate>
	<language>zh-CN</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.8</generator>
	<item>
		<title>使用kbpbot实现Keybase静态文件托管</title>
		<link>https://zohead.com/archives/kbpbot-keybase/</link>
		<comments>https://zohead.com/archives/kbpbot-keybase/#comments</comments>
		<pubDate>Sat, 22 Jul 2023 14:13:19 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[网络技术]]></category>
		<category><![CDATA[Git]]></category>
		<category><![CDATA[KBFS]]></category>
		<category><![CDATA[kbpbot]]></category>
		<category><![CDATA[Keybase]]></category>
		<category><![CDATA[rsync]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1884</guid>
		<description><![CDATA[关于 Keybase.pub 我之前写过一篇介绍 Keybase 加密网络服务体验 的文章，里面提到 Keybase 对于用户的公共目录提供了 Keybase.pub 这个静态文件托管网站，方便其它用户访问 Keybase KBFS 里保存的公共文件。 所以之前我都会把我的 MDwiki 知识库 也同步更新到我的 Keybase 下的 wiki 公共目录，这样就可以把我的 nocwat.keybase.pub 作为一个备用的 Wiki 网站，如果我的 VPS 服务器无法访问，也可以通过 Keybase.pub 访问 Wiki 知识库。 然而最近我才后知后觉地发现原来 Keybase 从今年 3 [&#8230;]]]></description>
				<content:encoded><![CDATA[<h2 id="about-keybase-pub">关于 Keybase.pub</h2>
<p>我之前写过一篇介绍 <a href="https://zohead.com/archives/keybase/">Keybase 加密网络服务体验</a> 的文章，里面提到 Keybase 对于用户的公共目录提供了 Keybase.pub 这个静态文件托管网站，方便其它用户访问 Keybase KBFS 里保存的公共文件。</p>
<p>所以之前我都会把我的 <a href="https://mdwiki.zohead.com/">MDwiki 知识库</a> 也同步更新到我的 Keybase 下的 <strong>wiki</strong> 公共目录，这样就可以把我的 <code>nocwat.keybase.pub</code> 作为一个备用的 Wiki 网站，如果我的 VPS 服务器无法访问，也可以通过 Keybase.pub 访问 Wiki 知识库。</p>
<p>然而最近我才后知后觉地发现原来 Keybase 从今年 3 月份开始就已经关闭了 Keybase.pub 静态文件托管服务，理由则是使用这项服务的用户数量太少，因此也就需要考虑新的 Keybase 静态文件托管方案了。</p>
<h2 id="kbpbot">kbpbot</h2>
<p>我稍微看了一下 Keybase Book 中关于 <a href="https://book.keybase.io/sites">Kebyase Sites</a> 的介绍文章，里面关于 Keybase.pub 的介绍还没有更新，不过还好目前 Keybase 提供的 <a href="https://keybase.io/kbpbot">kbpbot</a> 机器人还是可以正常使用的。</p>
<p>kbpbot 机器人原本是为了支持用户使用自定义域名发布网站而开发的，为自定义域名配置正确的 <strong>CNAME</strong> 和 <strong>TXT</strong> 记录之后，访问域名时 kbpbot 就可以读取指定的 Keybase 目录中的文件，然后将静态文件内容返回给访问者。</p>
<p>由此也可以看出，kbpbot 也必须有访问对应的 Keybase 目录的权限，自定义域名静态文件托管才能正常工作。得益于 Keybase KBFS 文件系统的设计，公共目录、私有目录、团队目录都可以给 kbpbot 赋予访问权限：</p>
<blockquote>
<p><strong>提示</strong></p>
<p>KBFS 文件系统中的所有文件都会自动签名加密，如果需要了解更多关于 KBFS 的使用方式，可以参考 Keybase 官方的 <a href="https://book.keybase.io/docs/files">Introducing the Keybase Filesystem</a> 这篇文章。</p>
</blockquote>
<ul>
<li>
<p><strong>公共目录</strong>：</p>
<p>kbpbot 默认就可以访问用户的所有公共目录，不需要专门配置，例如我的 Wiki 知识库的 KBFS 路径就是：<code>/keybase/public/nocwat/wiki</code>；</p>
</li>
<li>
<p><strong>私有目录</strong>：</p>
<p>用户只需要在私有目录下建立一个自己和 kbpbot 能访问的子目录，这里多个 Keybase 用户名使用逗号隔开即可，例如：<code>/keybase/private/yourname,kbpbot/XXX</code>；</p>
</li>
<li>
<p><strong>团队目录</strong>：</p>
<p>团队目录则更加简单，将 kbpbot 作为读者加入你的团队，然后使用团队目录路径，例如：<code>/keybase/team/yourteam/XXX</code>。</p>
</li>
</ul>
<p>然后是最重要的自定义域名配置，按照 Keybase Sites 文章里介绍的方式添加对应的 DNS 记录即可。例如我的 MDwiki 知识库使用的是 <a href="https://wiki.vp8.win/">wiki.vp8.win</a> 自定义域名，为 kbpbot 添加如下 DNS 记录：</p>
<ul>
<li>
<p><strong>CNAME</strong> 记录：</p>
<p>名称为 <code>wiki</code>，记录值为：<code>kbp.keybaseapi.com</code>；</p>
</li>
<li>
<p><strong>TXT</strong> 记录：</p>
<p>名称为 <code>_keybase_pages.wiki</code>，记录值为：<code>kbp=/keybase/public/nocwat/wiki</code>。</p>
</li>
</ul>
<p>其中比较关键的就是 <strong>TXT</strong> 记录了，<code>kbp=XXX</code> 记录值就是用来告诉 kbpbot 机器人，用户访问此域名时需要访问什么 KBFS 目录。如果你用的是私有目录，把 <strong>TXT</strong> 记录值换成类似于 <code>kbp=/keybase/private/yourname,kbpbot/XXX</code> 就可以了。</p>
<p>域名配置完成生效之后，就可以通过自定义域名访问你的静态站点了。由于 kbpbot 还支持自动申请和更新 Let’s Encrypt HTTPS 证书，用户访问站点时将会自动使用 HTTPS 访问，完全不需要维护 HTTPS 证书之类的，这一点还是非常方便的。</p>
<p>另外 kbpbot 甚至还支持使用 Keybase Git 代码仓库来访问静态文件，只是不支持私有代码仓库（只有自己才能访问私有 Git 仓库），需要使用团队仓库，同样也要将 kbpbot 作为读者加入你的团队。</p>
<p>例如团队名称还是上面的 <code>yourteam</code>，如果建立一个 <code>teamwiki</code> 仓库，就可以使用 <code>keybase://team/yourteam/teamwiki.git</code> 这种地址来 clone 仓库了，然后存放一些静态文件内容并提交。</p>
<p>最后我们把 Git 仓库对应的域名 <strong>TXT</strong> 记录值修改为：<code>kbp=git@keybase:team/yourteam/teamwiki</code>，用户就可以通过域名 HTTPS 访问静态站点了。</p>
<h2 id="sync-kbfs">同步 KBFS</h2>
<p>我的 Wiki 主站是放在目前博客的 VPS 服务器上运行的，整个 Wiki 知识库目录通过 <a href="https://www.resilio.com/individuals/">Resilio Sync</a> 与本地的 Windows / Linux 电脑进行同步，本地使用 Markdown 编辑器修改 Wiki 内容后就可以自动更新到 VPS 服务器上。</p>
<p>Keybase KBFS 对应的 Wiki 目录，我则是直接在 Linux 上使用 <code>rsync</code> 命令将同步过的本地 Wiki 目录更新到 Keybase，好处就是可以基于 <code>rsync</code> 的同步机制自动只更新差异的文件，这里需要先启动 Keybase 应用程序哦：</p>
<pre class="brush: bash; title: ; notranslate">
~# rsync -ruv --exclude=notes.sqlite --exclude=.sync ~/Documents/wiki/ /run/user/$(id -u)/keybase/kbfs/public/nocwat/wiki/
</pre>
<p><code>rsync</code> 的 <code>--exclude</code> 参数可以忽略不需要同步的文件，这里我指定 <code>notes.sqlite</code> 忽略 Markdown 编辑器使用的数据库文件，并指定 <code>.sync</code> 忽略 Resilio Sync 使用的内部同步目录。</p>
<p>Linux 系统上 rsync 的目标目录是：<code>/run/user/$(id -u)/keybase/kbfs</code>，与当前本地用户的用户 ID 有关；Windows 系统如果 Keybase 客户端启用了在资源管理器中显示 KBFS 盘符，rsync 的目标目录可能就是这样：<code>K:\</code>（实际的 KBFS 盘符可能并不是 K 盘）。</p>
<h2 id="summary">总结</h2>
<p>我的 MDwiki 知识库在 Keybase 上的备用网站 <a href="https://wiki.vp8.win/">wiki.vp8.win</a> 目前已经正常运行一段时间了，而且 Keybase 的静态文件托管服务在国内访问也很正常，并没有出现被墙之类的问题。</p>
<p>由于 kbpbot 除了 Keybase 公共、私有和团队 KBFS 目录，还支持使用 Git 仓库，我们也可以将类似 Hexo 这种静态博客通过 Keybase Git 来管理，本地修改构建之后提交到仓库更新，这样小伙伴们通过 Keybase 来托管静态博客也还是比较省事的。</p>
<p>虽然 Keybase 被 Zoom 收购之后发展势头不太好，更新节奏也变慢了，但还是希望 kbpbot 的静态文件托管服务能继续维持下去，毕竟 Keybase 提供的各项加密网络服务还是非常出色的，祝大家玩的开心哦。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/kbpbot-keybase/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Keybase加密网络服务初步体验</title>
		<link>https://zohead.com/archives/keybase/</link>
		<comments>https://zohead.com/archives/keybase/#comments</comments>
		<pubDate>Sun, 27 Oct 2019 04:00:52 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[网络技术]]></category>
		<category><![CDATA[Git]]></category>
		<category><![CDATA[gpg]]></category>
		<category><![CDATA[KBFS]]></category>
		<category><![CDATA[Keybase]]></category>
		<category><![CDATA[PGP]]></category>
		<category><![CDATA[Stellar]]></category>
		<category><![CDATA[XLM]]></category>
		<category><![CDATA[加密]]></category>
		<category><![CDATA[加密货币]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1602</guid>
		<description><![CDATA[关于 Keybase 体验之前需要先了解 Keybase 到底是啥，维基百科上对 Keybase 的定义是基于 PGP 技术的社交网络平台，可以将用户的身份映射到公钥，反之亦然。最常见的用法就是 Keybase 做为公共的 PGP key server。Keybase 可以对用户的 Twitter、GitHub、Reddit 等社交网络账户提供身份验证功能。 做为开源项目，Keybase 提供的服务已经远不止这些了，除了在社交网络身份验证之外增加了域名、网站管理员以及比特币和 Zcash 加密货币地址的验证功能，目前还提供了不少扩展功能： 端对端加密聊天通讯； 类似 Slack 的加密团队聊天 [&#8230;]]]></description>
				<content:encoded><![CDATA[<h2 id="about-keybase">关于 Keybase</h2>
<p>体验之前需要先了解 <a href="https://keybase.io/" target="_blank">Keybase</a> 到底是啥，维基百科上对 Keybase 的定义是基于 PGP 技术的社交网络平台，可以将用户的身份映射到公钥，反之亦然。最常见的用法就是 Keybase 做为公共的 PGP key server。Keybase 可以对用户的 Twitter、GitHub、Reddit 等社交网络账户提供身份验证功能。</p>
<p>做为开源项目，Keybase 提供的服务已经远不止这些了，除了在社交网络身份验证之外增加了域名、网站管理员以及比特币和 Zcash 加密货币地址的验证功能，目前还提供了不少扩展功能：</p>
<ul>
<li>端对端加密聊天通讯；</li>
<li>类似 Slack 的加密团队聊天和协作；</li>
<li>KBFS 加密文件存储服务，支持公共文件和私有文件，公共文件支持静态文件托管服务；</li>
<li>集成 Stellar（XLM）钱包（恒星币）功能，支持创建和导入 Stellar 账户，可以通过 Keybase 发送 XLM 给其它用户；</li>
<li>加密 Git 代码仓库托管服务，用户可以创建自己的私有仓库。</li>
</ul>
<h2 id="pgp-encrypt">PGP 加密</h2>
<p>还是从最基本的 PGP 加密说起，在 Keybase 官网注册账户并安装客户端之后，用户可以把自己的 PGP 公钥上传到 Keybase。Linux 下我一般使用系统自带的 gpg（GnuPG）来管理密钥，gpg 的知识网上有很多这里就不做详细介绍了，Keybase 做为公共 PGP key server 还是比较好用的。</p>
<p>例如，我的 Keybase 用户名是 <strong><a href="https://keybase.io/nocwat" target="_blank">nocwat</a></strong>，欢迎大家添加好友，我已经把自己的 PGP 公钥上传到 Keybase 了，如果你需要在 Linux 系统里导入我的公钥就很简单：</p>
<pre class="brush: bash; title: ; notranslate">
~$ curl https://keybase.io/nocwat/pgp_keys.asc | gpg --import
</pre>
<p>如果你已经安装了 Keybase 客户端，也可以这样导入：</p>
<pre class="brush: bash; title: ; notranslate">
~$ keybase pgp pull nocwat
</pre>
<p>导入公钥之后，就可以用 gpg 再做加密、解密、签名、验证之类的操作了。</p>
<p>当然 Keybase 客户端也是直接支持 PGP 的这些操作了，例如：</p>
<pre class="brush: bash; title: ; notranslate">
~$ keybase encrypt -i input -o output user
</pre>
<blockquote>
<p><strong>注意</strong></p>
<p>Keybase 可能觉得用户直接使用 PGP 的门槛太高太复杂，又自己搞了一套新的认证机制，可以参考这里：</p>
<p><a href="https://keybase.io/blog/keybase-new-key-model" target="_blank">Keybase’s New Key Model</a></p>
<p>新的认证机制使用 Keybase 设备密钥（device key）和 paper key（非常重要，可以更方便地认证 Keybase 设备），至于新的密钥系统则是基于 <a href="http://nacl.cr.yp.to/" target="_blank">NaCl</a> 新开发出来的 <a href="https://saltpack.org/" target="_blank">SaltPack</a>，具体可以看看 <a href="https://keybase.io/docs/crypto/overview" target="_blank">Keybase Crypto Documents</a>。</p>
</blockquote>
<p>对于 Keybase 这套新的认证机制，我不太好评价其优劣点。Keybase 用户的设备列表是所有用户都可见的，例如我的 <a href="https://keybase.io/nocwat/devices" target="_blank">Keybase devices</a>，这里包含了所有已认证的设备和 paper key。</p>
<p>在 <a href="https://keybase.io/nocwat/graph" target="_blank">Keybase graph</a> 里甚至还能看到 PGP key、device key、paper key 及其它已认证项目的对应关系图。从 graph 图里就能看到目前我还是最信赖自己的 PGP 密钥的。</p>
<blockquote>
<p><strong>提示</strong></p>
<p>Keybase 也提供 PGP 私钥托管保存功能，Keybase 声称 PGP 私钥必须通过用户的 passphrase 才能解密，解密是通过浏览器客户端本地完成，服务器并不会保存 passphrase。这个托管功能并不是强制的，上传与否要看你是否足够信任 Keybase 了。</p>
</blockquote>
<p>另外 Keybase 网页版也有一个 <strong><a href="https://keybase.io/encrypt" target="_blank">PGP Encrypt</a></strong> 页面提供 PGP 加解密等功能：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737371721/keybase-pgp.png" alt="Keybase PGP Encrypt"></p>
<p>PGP 私钥托管之后确实会方便不少，如果你的 PGP 私钥跟我一样没有上传到 Keybase 上托管，那这个页面只能使用 Encrypt 加密发送消息和 Verify 功能，Decrypt 和 Sign 功能必须通过 PGP 或 Keybase 客户端来完成。</p>
<h2 id="encrypt-im">加密聊天通讯</h2>
<p>Keybase 端对端的加密聊天功能需要安装客户端才能使用，与其它聊天软件不同，即使其他人只有 Twitter、GitHub 等社交网络账户还没有注册 Keybase，你也可以通过 Keybase 发送消息给他们（直接指定社交网络账户名，而不需要知道 Keybase 账户名），待他们通过 Keybase 认证这些账户之后就能收到之前的消息。</p>
<p>你甚至可以直接通过 Keybase 发送消息给 PGP 公钥所有者，如果对方的 Keybase 账户添加验证了此 PGP 公钥就能收到消息。</p>
<p>Keybase 聊天的消息目前是基于 device key 和 paper key 进行加密的，而不是 PGP 密钥，Keybase 给出的解释是无需单独管理 PGP 密钥更有利于非技术人员使用。</p>
<p>Keybase 命令行 <code>keybase chat</code> 也可以进行简单的聊天，而且聊天功能还支持 JSON API 接口，可以很方便地实现聊天机器人之类的功能。</p>
<p>另外 Keybase 团队聊天同样也是端对端加密的，具体可以看看 <a href="https://keybase.io/docs/teams/crypto" target="_blank">Teams Crypto</a> 的介绍。由于 Keybase 代码是开源的，因此总体看起来 Keybase 似乎是一个比 Telegram 安全性更高的即时通讯软件。</p>
<h2 id="kbfs-storage">KBFS 文件存储</h2>
<p>KBFS（Keybase 文件系统）的介绍可以参考 <a href="https://keybase.io/docs/kbfs" target="_blank">Introducing the Keybase filesystem</a>，用户存入的所有文件都会自动签名加密。与聊天功能类似，所有 KBFS 数据都是基于 device key 和 paper key 进行加密的，而不是 PGP 密钥，因此 paper key 特别需要注意备份不能丢失。</p>
<p>KBFS 需要安装 Keybase 客户端才可以使用，Linux 下是基于 FUSE 实现的，Windows 则是基于常见的 Dokan 用户态文件系统了：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737371495/keybase-dokan.jpg" alt="Keybase Windows Dokan Library"></p>
<p>Keybase 客户端的 Files 里可以直接看到 private、public 和 team 三个顶层目录，分别对应用户私有、用户公共和团队文件：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737371494/keybase-files.jpg" alt="KBFS 文件系统"></p>
<p>我的私有文件在 Linux 上的访问路径是类似这样：<code>/keybase/private/nocwat</code>，Windows 上则是：<code>k:\private\nocwat</code>，公共目录则是：<code>/keybase/public/nocwat</code>。</p>
<p>你也可以创建一个只有几个特定用户能访问的私有目录，类似这样：</p>
<pre class="brush: plain; title: ; notranslate">
/keybase/private/nocwat,xxx,yyy
</pre>
<p>对于公共目录，Keybase 还提供了 <a href="https://keybase.pub/" target="_blank">Keybase.pub</a> 网站方便其它用户访问使用，例如我的公共目录地址就是：<a href="https://keybase.pub/nocwat/" target="_blank"><code>https://keybase.pub/nocwat/</code></a>。</p>
<p>Keybase.pub 还提供了静态文件托管网站功能，例如你可以直接访问下面的地址查看我放到公共目录的 Wiki 知识库：<a href="https://nocwat.keybase.pub/wiki/" target="_blank"><code>https://nocwat.keybase.pub/wiki/</code></a>。其实这个地址就相当于访问我的 <code>/keybase/public/nocwat/wiki</code> 公共目录。</p>
<p>目前 Keybase 给每个用户提供了 250 GB 的 KBFS 存储空间，目前 KBFS 用户量还不是很多可能存在不稳定的问题，最好注意备份数据。</p>
<p>KBFS 文件系统可以直接看到空间使用情况：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737371722/keybase-kbfs.png" alt="KBFS 空间使用"></p>
<h2 id="stellar-wallet">Stellar 钱包</h2>
<p>Stellar 是基于 Ripple 修改而来的支付网络系统，用户使用 Lumen（XLM）作为基础货币，可以通过 XLM 转账任意一种货币（可以是各国法定货币或者其它加密货币），其交易确认速度也非常快，有关恒星币的详细内容还请自行了解哦。</p>
<p>Stellar Lumens 加密货币（恒星币）的钱包功能同样必须安装客户端才能使用，目前所有 Keybase 用户都可以使用：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737371721/keybase-wallet.jpg" alt="Keybase Stellar 钱包"></p>
<p>Keybase 将聊天功能和钱包功能进行整合，你可以直接通过 Keybase 用户名而不是很长的 Stellar 钱包地址进行转账操作，当然也可以通过 Keybase 客户端转账给其它未使用 Keybase 的 Stellar 钱包地址。</p>
<p>目前 Keybase 正在搞 Stellar 空投活动，一共会发送 20 亿个 Lumens，只要 Keybase 账户的条件合格就可以每个月领到 Lumens，详情请参考 <a href="https://keybase.io/airdrop" target="_blank">The Big Stellar Space Drop</a> 页面的介绍。</p>
<h2 id="git-repo">Git 代码仓库</h2>
<p>不得不说 Keybase 新推出的 Git 私有仓库托管服务才是我使用的原因，你可以通过 Keybase 客户端建立自己的私有仓库，也可以为团队建立代码仓库。Keybase 为每个用户和团队提供了 100 GB 的仓库存储空间，实际已经足够用了。Git 代码仓库同样是端对端加密的，只有自己或团队的密钥才能解密。</p>
<p>Keybase 的仓库托管服务是基于 Git 的 remote helpers 功能来实现的，因此仓库地址和常见的 GitHub 不太一样。例如下面是我建立的一个名为 <code>ssicd</code> 的私有代码仓库：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737371498/keybase-git.jpg" alt="Keybase Git 代码仓库"></p>
<p>使用 Keybase Git 仓库的机器上必须安装 Keybase 客户端（通过 <code>git-remote-keybase</code> 程序来实现），我就可以通过命令行来 clone 仓库：</p>
<pre class="brush: bash; title: ; notranslate">
~$ git clone keybase://private/nocwat/ssicd.git
</pre>
<p>接下来的使用方法就和其它 Git 仓库没什么区别了，只是 push 等操作时需要使 Keybase 客户端保持运行状态。</p>
<p>另外还有一个小窍门就是 Keybase Git 代码仓库中的文件是可以直接通过 KBFS 进行访问的，这样无需 clone 整个仓库就能下载其中的某个文件，例如上面我的私有仓库目录路径就是：</p>
<pre class="brush: plain; title: ; notranslate">
/keybase/private/nocwat/.kbfs_autogit/ssicd
</pre>
<p>路径中的 <code>.kbfs_autogit</code> 是表示 Git 仓库的特殊目录名，不过需要注意直接列举 <code>/keybase/private/{user}</code> 并不能看到这个目录，需要在目录路径中特别指定。</p>
<p>你甚至可以通过 KBFS 直接访问仓库的某个分支，路径形式如下：</p>
<pre class="brush: plain; title: ; notranslate">
/keybase/private/nocwat/.kbfs_autogit/ssicd/.kbfs_autogit_branch_develop
</pre>
<p>路径中的 <code>.kbfs_autogit_branch_</code> 后面跟 Git 分支名称即可。</p>
<p>你如果想直接访问具体某个 commit 的数据，也可以指定 KBFS 路径：</p>
<pre class="brush: plain; title: ; notranslate">
/keybase/private/nocwat/.kbfs_autogit/ssicd/.kbfs_autogit_commit_XXXXXX
</pre>
<p>路径中的 <code>.kbfs_autogit_commit_</code> 后面跟 commit 的完整 hash 值。</p>
<h2 id="summary">总结</h2>
<p>Keybase 做为一个开源项目，基于 PGP 技术做了很多身份验证之外的扩展和改进，增加了很多加密相关的服务，例如端对端加密的聊天、协作、存储、钱包、Git 仓库等功能确实降低了用户使用门槛。我这段时间用下来还是比较方便的，不过国内某些网络下 Keybase 可能访问速度很慢或者干脆被墙了。</p>
<p>不过 Keybase 设计了新的认证机制而不是直接使用成熟的 PGP 可能会引起一些争议，另外可能由于集成了太多功能导致 Keybase 客户端稍显臃肿，现在 Windows 客户端安装包体积就已经有 180 多 MB 了。其实我倒希望 Keybase 能发布一个体积更小的纯命令行版本，或者能可选安装图形程序（我可能也只需要图形界面的聊天功能）。</p>
<p>目前 Keybase 我使用最多的场景就是 PGP key server、KBFS 文件存储和私有 Git 代码仓库功能了，由于基本没有添加好友，端对端加密聊天功能基本没有用到。</p>
<p>最后我写这篇文章时 Stellar（XLM）价格为 $0.06（0.000006 BTC）还非常低廉，哈哈，欢迎大家通过 Keybase 用户名 <strong><a href="https://keybase.io/nocwat/sigchain#2e5759ea8c98e03c36c49b04282dbef7728a75d39c2b10a31ffe118761a098c022" target="_blank">nocwat</a></strong> 或者 <strong><a href="https://steexp.com/account/GBJZINB72KUDGZQSGJP6BQCQHBNQWXBAS6VQ2H4VTAZRPK5MRCP6DSR3" target="_blank">Stellar 钱包地址</a></strong> 给我打币。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/keybase/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Linux下使用tcpkill工具重置NFS连接</title>
		<link>https://zohead.com/archives/tcpkill-nfs/</link>
		<comments>https://zohead.com/archives/tcpkill-nfs/#comments</comments>
		<pubDate>Wed, 14 Sep 2016 18:00:14 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[网络技术]]></category>
		<category><![CDATA[Dsniff]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[NFS]]></category>
		<category><![CDATA[RST]]></category>
		<category><![CDATA[TCP]]></category>
		<category><![CDATA[tcpkill]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1272</guid>
		<description><![CDATA[有些时候在 Linux 系统中使用 NFS 挂载远程共享（使用默认的 TCP 方式）之后，偶尔会因为网络异常出现 NFS 连接出错的问题，这种情况下使用任何 ls 或者 df 等等常用的命令对 NFS 挂载目录进行简单的查看操作都可能卡顿几十秒乃至几分钟的时间。 此时如果在别的 Linux 客户机系统上又是可以正常访问 NFS 共享的，而 NFS 服务器端考虑到有多个客户机正在使用不方便直接重启服务，一般只能等待 NFS 挂载连接恢复正常或者重启客户端系统，这样还是很麻烦的，为此我专门找了个使用 tcpkill 命令重置 NFS 连接的方法分享给大家。 tcpkill 命令属于 Dsniff  [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>有些时候在 Linux 系统中使用 NFS 挂载远程共享（使用默认的 TCP 方式）之后，偶尔会因为网络异常出现 NFS 连接出错的问题，这种情况下使用任何 ls 或者 df 等等常用的命令对 NFS 挂载目录进行简单的查看操作都可能卡顿几十秒乃至几分钟的时间。</p>
<p>此时如果在别的 Linux 客户机系统上又是可以正常访问 NFS 共享的，而 NFS 服务器端考虑到有多个客户机正在使用不方便直接重启服务，一般只能等待 NFS 挂载连接恢复正常或者重启客户端系统，这样还是很麻烦的，为此我专门找了个使用 tcpkill 命令重置 NFS 连接的方法分享给大家。</p>
<p>tcpkill 命令属于 Dsniff 嗅探工具包，Dsniff 本身是一个高级的网络嗅探器，Dsniff 可以将制造的数据包注入到网络，一般 Linux 系统中可以找到对应的 dsniff 软件包进行安装，我测试使用的 CentOS 6.1 64 位系统使用的下面的 RPM 安装包：</p>
<p><a href="http://rpm.repo.onapp.com/ramdisk-hv/centos6/dsniff/">http://rpm.repo.onapp.com/ramdisk-hv/centos6/dsniff/</a></p>
<p>tcpkill 的工作原理是利用 libpcap 库监控符合过滤条件的 TCP 连接并等待，当该 TCP 连接上有数据传输时就会被 tcpkill 感知到，不过作为应用程序的 tcpkill 当然不能直接关闭 TCP 连接，此时就会构造一条 RST 报文发回去直接导致 TCP 连接异常关闭（其实 TCP RST 就是 GFW 对敏感网页进行屏蔽的老手段之一哈）。</p>
<p>对于 NFS 共享使用的 TCP 连接而言，通过 tcpkill 发送 RST 报文导致连接异常关闭之后，NFS 客户端就能知道原有的连接不可用，会自动重新开启新的 TCP 连接，这样就能达到我们需要的重置 NFS 连接的效果。</p>
<p>下面具体实验看看，首先在 NFS 服务器上使用 <code>netstat</code> 命令确认所有 TCP 方式挂载的 NFS 连接，这里使用 NFS 服务的 <code>2049</code> 端口进行过滤：</p>
<pre class="brush: bash; title: ; notranslate">
~ # netstat -anp 2&gt;/dev/null | grep 2049
tcp        0      0 0.0.0.0:2049            0.0.0.0:*               LISTEN      -
tcp        0      0 192.168.1.56:2049       192.168.1.52:953        ESTABLISHED -
tcp        0      0 192.168.1.56:2049       192.168.1.129:824       ESTABLISHED -
udp        0      0 0.0.0.0:2049            0.0.0.0:*                           -
</pre>
<p>可以看到 NFS 服务器上有 192.168.1.52 和 192.168.1.129 这两个客户端正在连接，看看 tcpkill 命令的用法：</p>
<pre class="brush: bash; title: ; notranslate">
~ # tcpkill [-i interface] [-1...9] expression
</pre>
<p>tcpkill 的 <code>-i</code> 参数指定绑定哪个网卡，<code>expression</code> 就是 tcpdump 的过滤表达式参数，具体如何使用可以参考 tcpdump 的帮助信息：</p>
<p><a href="http://www.tcpdump.org/tcpdump_man.html">http://www.tcpdump.org/tcpdump_man.html</a></p>
<p>我们就以关闭上面的 192.168.1.52 客户端使用的 NFS 连接为例：</p>
<pre class="brush: bash; title: ; notranslate">
~ # tcpkill -i eth0 host 192.168.1.52 and port 2049
tcpkill: listening on eth0 [host 192.168.1.52 and port 2049]
</pre>
<p>tcpkill 命令启动之后会一直等待新的数据传输，我们就可以在 NFS 客户端上随便运行 ls 或者 df 等命令查看 NFS 共享文件夹就可以让系统发送新的 NFS 数据（仍然是有问题的 NFS 连接所以可能会卡住），此时服务器端的 tcpkill 命令就有反应了：</p>
<pre class="brush: bash; title: ; notranslate">
~ # tcpkill -i eth0 host 192.168.1.52 and port 2049
tcpkill: listening on eth0 [host 192.168.1.52 and port 2049]
192.168.1.52:953 &gt; 192.168.1.56:2049: R 1715581713:1715581713(0) win 0
192.168.1.52:953 &gt; 192.168.1.56:2049: R 1715581896:1715581896(0) win 0
192.168.1.52:953 &gt; 192.168.1.56:2049: R 1715582262:1715582262(0) win 0
192.168.1.56:2049 &gt; 192.168.1.52:953: R 624540755:624540755(0) win 0
192.168.1.56:2049 &gt; 192.168.1.52:953: R 624540919:624540919(0) win 0
192.168.1.56:2049 &gt; 192.168.1.52:953: R 624541247:624541247(0) win 0
192.168.1.52:953 &gt; 192.168.1.56:2049: R 1715581801:1715581801(0) win 0
192.168.1.52:953 &gt; 192.168.1.56:2049: R 1715581984:1715581984(0) win 0
192.168.1.52:953 &gt; 192.168.1.56:2049: R 1715582350:1715582350(0) win 0
</pre>
<p>可以看到 tcpkill 已经发送了 RST 包导致 NFS 客户端重新连接，如果客户端的 NFS 共享操作已经恢复正常了，就可以退出 tcpkill 命令了。</p>
<p>从上面的例子我们也会发现 tcpkill 默认是只能“关闭”活跃有数据传输的 TCP 连接的；对于非活跃的连接我们可能需要主动发送 SYN 包，并根据返回的 TCP 序列号构造新的 RST 包再次发送以达到关闭连接的效果，看起来已经有网友实现了一个支持关闭非活跃连接的 tcpkill 程序，有兴趣的朋友也可以关注看看哦。最后祝大家中秋佳节玩的开心 ^_^。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/tcpkill-nfs/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>在Linux容器中使用ZeroTier P2P VPN</title>
		<link>https://zohead.com/archives/zerotier-container/</link>
		<comments>https://zohead.com/archives/zerotier-container/#comments</comments>
		<pubDate>Wed, 31 Aug 2016 16:57:17 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Docker]]></category>
		<category><![CDATA[网络技术]]></category>
		<category><![CDATA[Codeanywhere]]></category>
		<category><![CDATA[P2P]]></category>
		<category><![CDATA[SSH]]></category>
		<category><![CDATA[TAP]]></category>
		<category><![CDATA[TUN]]></category>
		<category><![CDATA[VPN]]></category>
		<category><![CDATA[ZeroTier]]></category>
		<category><![CDATA[容器]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1266</guid>
		<description><![CDATA[关于 ZeroTier P2P VPN P2P VPN 和我们平常使用的 PPTP、OpenVPN 等 VPN 的不同之处在于其只负责将两个或多个主机通过点对点的方式进行连接，不需要中心服务器支持，多个主机之间通过 STUN 打洞或者 TURN 中继等方式进行连接，比较适合不同网络间的多个主机进行直接连接，不像 PPTP 这种在国内用的最多的就是拿来爬墙的。 目前使用的比较多的包括 n2n、tinc 和本文要介绍的 ZeroTier 等几种免费开源的 P2P VPN 软件。 n2n 由于已经没人继续开发支持了所以不予考虑，我在对比了 tinc 和 ZeroTier 之后发现 ZeroTier  [&#8230;]]]></description>
				<content:encoded><![CDATA[<h2 id="关于-zerotier-p2p-vpn">关于 ZeroTier P2P VPN</h2>
<p>P2P VPN 和我们平常使用的 PPTP、OpenVPN 等 VPN 的不同之处在于其只负责将两个或多个主机通过点对点的方式进行连接，不需要中心服务器支持，多个主机之间通过 <a href="https://en.wikipedia.org/wiki/STUN">STUN</a> 打洞或者 <a href="https://en.wikipedia.org/wiki/Traversal_Using_Relays_around_NAT">TURN</a> 中继等方式进行连接，比较适合不同网络间的多个主机进行直接连接，不像 PPTP 这种在国内用的最多的就是拿来爬墙的。</p>
<p>目前使用的比较多的包括 n2n、tinc 和本文要介绍的 ZeroTier 等几种免费开源的 P2P VPN 软件。</p>
<p>n2n 由于已经没人继续开发支持了所以不予考虑，我在对比了 tinc 和 ZeroTier 之后发现 ZeroTier 虽然看起来性能比 tinc 略差一点，但其客户端配置步骤非常简单，不需要像 tinc 那样要在多个主机分别手工生成配置文件，而且 ZeroTier 自己也自带了几个不同国家区域的中继服务器方便普通用户使用，另外一点就是本文要介绍的 ZeroTier 支持在 Linux 容器中使用，因此平时我主要使用 ZeroTier，tinc 用作辅助。</p>
<p>ZeroTier 的虚拟网络可以包含多个处于相同或不同物理网络里的主机，每个虚拟网络都有一个唯一的网络 ID，多个运行 ZeroTier 服务的主机加入同一个 ZeroTier 网络 ID 就可以构成一个虚拟网络。虚拟网络都是由 ZeroTier Controller 控制器来管理的，默认可以使用 ZeroTier 提供的控制器来管理，用户也可以自行搭建控制器来实现完全自己管理 ZeroTier 网络。</p>
<p>另外 ZeroTier 有一个特殊的 <code>8056c2e21c000001</code> 网络 ID 表示全球公共 ZeroTier 网络，如果只是想测试连接网络的话也可以加入这个公共网络；但实际使用中为了安全性考虑建议最好在 ZeroTier 网站上注册一个账户并新建你自己的 P2P VPN 网络用于管理。</p>
<p>有关 ZeroTier P2P VPN 的详细介绍可以参考其官网：</p>
<p><a href="https://www.zerotier.com/">https://www.zerotier.com/</a></p>
<h2 id="容器中使用-zerotier-的问题">容器中使用 ZeroTier 的问题</h2>
<p>简单了解 ZeroTier P2P VPN 主机端的工作机制之后我就发现一个问题，其依赖的 TUN/TAP 驱动（n2n 和 tinc VPN 也同样依赖）在很多 Linux 容器系统中是无法使用的。</p>
<p>我使用的 OpenVZ VPS 容器还好还可以在控制面板里开启 TUN/TAP 支持，而对于现在非常流行的 Docker 容器（例如之前我用到的 IBM Bluemix 容器）就比较麻烦了，Docker 容器在运行时必须包含 <code>SYS_NET_ADMIN</code> 特权才能正常执行 TUN/TAP 虚拟网卡设备的创建操作（Docker 容器中同样也不能使用 OpenVPN、OpenConnect 等依赖 TUN/TAP 驱动的程序），但目前的容器服务厂商基本上不会以特权方式运行容器。</p>
<p>ZeroTier 为此提出的解决方案就是 Network Containers 模式，通过实现一个轻量级的应用层 TCP/IP API 来 hook 现有应用程序的 Socket API，这样可以使现有的程序也能使用 ZeroTier 技术连接其它主机。</p>
<blockquote>
<p>最近 ZeroTier 已经将 Network Containers 模式更名为 ZeroTier SDK 并使用了单独的版本库，开发者也可以选择将 ZeroTier SDK 直接编译进自己的应用程序以方便直接使用。有关容器模式的详细介绍可以参考官方介绍文章：<a href="https://www.zerotier.com/product-netcon.shtml">ZeroTier Network Containers</a>。</p>
</blockquote>
<h2 id="关于-codeanywhere-容器">关于 Codeanywhere 容器</h2>
<p>这里我使用 <a href="https://codeanywhere.com/">Codeanywhere</a> 虚拟机来测试 ZeroTier Network Containers，Codeanywhere 作为一个云端 IDE 平台还是比较好用的，其提供的免费版本的容器支持：</p>
<ul>
<li>2GB 磁盘空间；</li>
<li>256MB RAM (+ 512 MB 交换空间)；</li>
<li>sudo 支持；</li>
<li>支持 SSH 登录（免费版本的容器每次启动时 SSH 端口不固定）；</li>
<li>支持访问所有 HTTP 和 Websocket 端口（默认 1024 - 9999）。</li>
</ul>
<p>问题是 Codeanywhere 免费版本的虚拟机不支持固定 IP 地址，也不支持域名绑定或者固定子域名，每次虚拟机启动之后分配到的都是一个超长的子域名，例如你看到的子域名可能就是下面的形式：</p>
<pre><code>preview.7reszf59tfv18aor9x7odheaio340a4ih8tcn8fvndzvkj4i.box.codeanywhere.com
</code></pre>
<p>这样实际开发测试起来还是有点不太方便。</p>
<p>当然 Codeanywhere 也是不提供 TUN/TAP 支持的，这样如果 ZeroTier 的容器模式可以在 Codeanywhere 虚拟机内正常使用，我就可以直接在自己的电脑上直接使用虚拟的 P2P VPN 节点 IP 地址来访问 Codeanywhere 虚拟机了，可以省掉那一长串恶心的子域名了。</p>
<h2 id="使用-zerotier-network-containers">使用 ZeroTier Network Containers</h2>
<h3 id="编译安装-zerotier-network-containers">编译安装 ZeroTier Network Containers</h3>
<p>首先创建一个新的 Codeanywhere 虚拟机，建议使用 <code>C/C++ Development Stack</code> 类型方便后续编译安装 ZeroTier 程序，另外操作系统最好选择 Centos 6.5 64 位，如果使用 Ubuntu 系统可能存在连接不上 P2P VPN 网络的问题。</p>
<p>在 Codeanywhere 虚拟机中使用 git 命令检出 ZeroTier 官方版本库，检出之后运行 <code>make netcon</code> 命令就可以编译出 ZeroTier Network Containers 模式需要的文件了：</p>
<pre class="brush: bash; title: ; notranslate">
[cabox@box-codeanywhere ZeroTierOne]$ ls                                           
AUTHORS.md   examples                 netcon        ui
LICENSE.txt  ext                      node          version.h
Makefile     include                  objects.mk    windows
README.md    java                     one.cpp       world
artwork      libzerotierintercept.so  osdep         zerotier-cli
attic        make-freebsd.mk          selftest.cpp  zerotier-idtool
cluster-geo  make-linux.mk            service       zerotier-netcon-service
controller   make-mac.mk              tcp-proxy
</pre>
<p>其中最重要的就是 <code>zerotier-netcon-service</code> 服务和 <code>libzerotierintercept.so</code> 以及 netcon 目录中的 <code>liblwip.so</code> 应用层 Socket API 库文件了，接着可以先将 ZeroTier Network Container 模式程序和有关库文件安装到 <code>/var/lib/zerotier-one</code> 目录中：</p>
<pre class="brush: bash; title: ; notranslate">
[cabox@box-codeanywhere ZeroTierOne]$ mkdir -m /var/lib/zerotier-one
[cabox@box-codeanywhere ZeroTierOne]$ cp -ra zerotier-* /var/lib/zerotier-one
[cabox@box-codeanywhere ZeroTierOne]$ cp libzerotierintercept.so netcon/liblwip.so /var/lib/zerotier-one
</pre>
<h3 id="将容器主机加入-zerotier-虚拟网络">将容器主机加入 ZeroTier 虚拟网络</h3>
<p>下面就可以在 Codeanywhere 虚拟机中进行测试了，首先启动 Network Container 模式下的 ZeroTier 主服务程序：</p>
<pre class="brush: bash; title: ; notranslate">
[cabox@box-codeanywhere workspace]$ /var/lib/zerotier-one/zerotier-netcon-service -d
</pre>
<p>服务启动成功之后稍等片刻，运行 <code>zerotier-cli</code> 命令加入现有的 ZeroTier 虚拟网络，下面命令行中的 <code>565799d8f61077e9</code> 就是我在 ZeroTier 网站上新创建的虚拟网络 ID：</p>
<pre class="brush: bash; title: ; notranslate">
[cabox@box-codeanywhere workspace]$ /var/lib/zerotier-one/zerotier-cli join 565799d8f61077e9
</pre>
<blockquote>
<p><strong>提示</strong></p>
<p>如果你加入的是 <code>8056c2e21c000001</code> 这个全球公共 ZeroTier 虚拟网络那就可以省掉下面这一段落的配置步骤了。</p>
</blockquote>
<p>join 操作完成之后需要在 ZeroTier 网站后台虚拟网络管理界面中先允许新的主机加入此网络，接着可以为此主机配置虚拟网络上的 IP 地址、子网掩码之类的，具体配置操作步骤还算比较简单基本都可以在 <a href="https://my.zerotier.com/network">ZeroTier Central</a> 网站后台中完成。</p>
<p>配置完成之后就可以在容器中继续运行 <code>zerotier-cli</code> 命令查询该主机上 ZeroTier 虚拟网络状态：</p>
<pre class="brush: bash; title: ; notranslate">
[cabox@box-codeanywhere workspace]$ /var/lib/zerotier-one/zerotier-cli listnetworks
200 listnetworks &lt;nwid&gt; &lt;name&gt; &lt;mac&gt; &lt;status&gt; &lt;type&gt; &lt;dev&gt; &lt;ZT assigned ips&gt;
200 listnetworks 565799d8f61077e9 zohead-network fa:d9:cf:a8:b2:40 OK PRIVATE /var/lib/zerotier-one/nc_565799d8f61077e9 10.10.8.4/24
</pre>
<p>可以看到该容器中已经正确连接到我建立的私有 ZeroTier 虚拟网络了，而且分配到了一个 <code>10.10.8.4</code> 的 IP 地址，子网掩码为 255.255.255.0（对应 24）。</p>
<h3 id="通过虚拟网络访问容器主机">通过虚拟网络访问容器主机</h3>
<p>此时需要特别注意的是容器中运行的程序还不能直接使用 ZeroTier 虚拟网络与其它主机进行连接，必须通过预加载 Network Container 库文件的方式运行程序才可以在容器系统中正确访问 ZeroTier 虚拟网络，这里我们以 SSH 服务器为例，首先可以新运行一个 <code>sshd</code> 服务程序：</p>
<pre class="brush: bash; title: ; notranslate">
[cabox@box-codeanywhere workspace]$ sudo ZT_NC_NETWORK=/var/lib/zerotier-one/nc_565799d8f61077e9 LD_PRELOAD=/var/lib/zerotier-one/libzerotierintercept.so /usr/sbin/sshd -p 27001
</pre>
<p>这样可以新运行一个 <code>sshd</code> 服务程序，并绑定新的端口（例如上面例子中的 27001 端口）防止和系统默认的 22 端口冲突，<code>ZT_NC_NETWORK</code> 变量指定需要使用的 ZeroTier 虚拟网络路径（默认为：<code>/var/lib/zerotier-one/nc_网络ID</code>），<code>LD_PRELOAD</code> 是必须指定的，否则新运行的 <code>sshd</code> 程序将无法访问 ZeroTier 虚拟网络。</p>
<p>新的 <code>sshd</code> 服务启动成功之后，我们就可以在测试的 Windows、Linux 等主机上运行 ZeroTier 客户端，加入同样的虚拟网络并配置虚拟网络 IP 地址（例如我的 Windows 机器 ZeroTier 网络 IP 地址是 <code>10.10.8.3</code>）及子网掩码。</p>
<p>最后我们可以在 Windows 主机上 ping 测试 Codeanywhere 容器的虚拟网络 IP 地址了，如果通讯正常的话就可以通过 ssh 客户端工具直接连接登录 Codeanywhere 容器系统了，这样就顺利绕过了 Codeanywhere 的随机子域名限制。</p>
<h2 id="后记">后记</h2>
<p>如果需要在 Codeanywhere 容器系统启动时自动连接 ZeroTier 虚拟网络并开启独立的 <code>sshd</code> 服务程序，那可以将类似下面的两句命令加到 <code>/etc/rc.local</code> 文件末尾：</p>
<pre class="brush: bash; title: ; notranslate">
/var/lib/zerotier-one/zerotier-netcon-service -d
echo &quot;ZT_NC_NETWORK=/var/lib/zerotier-one/nc_565799d8f61077e9 LD_PRELOAD=/var/lib/zerotier-one/libzerotierintercept.so /usr/sbin/sshd -p 27001&quot; | at now + 2 minutes
</pre>
<p>第二句命令表示在 ZeroTier Network Container 服务程序启动两分钟之后再启动单独的 <code>sshd</code> 服务程序，以使 ZeroTier 服务程序在等待的时间内能正确连接虚拟网络。</p>
<p>本文主要介绍 ZeroTier 程序里不太常见的 Network Container 容器模式，这对于越来越多的 Docker 容器系统而言能绕过 TUN/TAP 限制还是有点用处的，如果读者发现了有关 ZeroTier P2P VPN 的更多好玩法，欢迎提出交流哦，祝大家玩的开心～～～。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/zerotier-container/feed/</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>考虑开启SRI防止七牛CDN HTTPS劫持</title>
		<link>https://zohead.com/archives/qiniu-https-tamper/</link>
		<comments>https://zohead.com/archives/qiniu-https-tamper/#comments</comments>
		<pubDate>Fri, 27 May 2016 14:48:28 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[HTTPS]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[CDN]]></category>
		<category><![CDATA[https]]></category>
		<category><![CDATA[SRI]]></category>
		<category><![CDATA[七牛]]></category>
		<category><![CDATA[云存储]]></category>
		<category><![CDATA[劫持]]></category>
		<category><![CDATA[证书]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1237</guid>
		<description><![CDATA[最近我在使用 Android 上的 Chrome 浏览器访问博客页面时发现一个奇怪的问题：博客页面底部有一个悬浮的叉，但又没有显示任何实际的内容。赶紧用 Chromebook 打开博客网页，将 User Agent 切换成 Android Chrome，这时可以看到网页里无端多了一个 iframe，该 iframe 地址为 http://dbcpm.com/locate_1/jiwei_MBpt.html，如下图所示： 由于我确定博客 VPS 后台并没有被入侵，因此初步估计是网页被万恶的运营商给劫持了，但又一想我的博客已经启用了全站 HTTPS，按说不会轻易遇到这种问题了。马上看看 Chrom [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>最近我在使用 Android 上的 Chrome 浏览器访问博客页面时发现一个奇怪的问题：博客页面底部有一个悬浮的叉，但又没有显示任何实际的内容。赶紧用 Chromebook 打开博客网页，将 User Agent 切换成 Android Chrome，这时可以看到网页里无端多了一个 iframe，该 iframe 地址为 <code>http://dbcpm.com/locate_1/jiwei_MBpt.html</code>，如下图所示：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442960/tampered-page.png" alt="博客被劫持效果" title=""></p>
<p>由于我确定博客 VPS 后台并没有被入侵，因此初步估计是网页被万恶的运营商给劫持了，但又一想我的博客已经启用了全站 HTTPS，按说不会轻易遇到这种问题了。马上看看 Chrome 浏览器上的地址栏标志，果然没有小绿锁了，浏览器控制台里也有 Mixed Content 报错：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442846/mixed-content-error.png" alt="Mixed Content 报错" title=""></p>
<p>可以看到浏览器拒绝加载 HTTP 的 JavaScript 文件，接着看看七牛 HTTPS 地址返回的数据是否正确：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442957/tampered-js.png" alt="七牛 CDN HTTPS 错误数据" title=""></p>
<p>这下立马发现返回的 JavaScript 代码不对了，开头是插入了 HTTP 形式的 JavaScript 文件地址，后面则看起来明显是广告一类的程序代码了。</p>
<p>难道是运营商做了 DNS 劫持导致我使用的七牛 CDN HTTPS 域名 <code>dn-zohead.qbox.me</code> 解析到了不正确的地址？如果是这样按说应该会出现 SSL 证书错误的，而且运营商只是为了插入广告代码而去搞 TLS 中间人攻击之类的感觉也不太合理。</p>
<p>考虑到另一种可能就是七牛 CDN 在从我的博客 VPS 地址回源的时候由于 DNS 污染之类的问题请求到了错误的数据，导致 HTTPS CDN 返回的数据也不对，这样还是登录到七牛后台管理，查看被劫持的 main.js 文件内容是否正确，下载下来对比却发现和我的博客源站内容是一致的，并没有被污染。</p>
<p>多次测试之后我发现如果再刷新页面七牛 HTTPS 劫持的问题可能又没了，而且在新标签页中打开被劫持的 HTTPS JavaScript 路径又能返回正确的数据了，这个时候通过 Chrome 调试工具看到的 HTTP 请求响应结果是这样的：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442862/normal-https-reponse.png" alt="正常七牛 CDN HTTPS 请求" title=""></p>
<p>而被劫持的 HTTP 请求响应结果则是：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442957/tampered-https-response.png" alt="被劫持的七牛 CDN HTTPS 请求" title=""></p>
<p>可以看到返回的 HTTP 头信息完全不同，而且多次刷新之后发现被劫持的 HTTPS 数据基本都来自 <code>211.142.22.13</code> 这台七牛的 CDN 服务器。查询之后发现这个 IP 地址是山西移动的（刚好我使用的是移动宽带），看起来只要浏览器是从 <code>211.142.22.13</code> 这台 CDN 服务器请求数据很可能得到的是被劫持的 JavaScript 代码，而如果七牛的 qbox.me 域名解析到的是其它 CDN 服务器地址则数据可能是正常的。</p>
<p>为了更好的重现这个问题，我们可以在 Linux 下先修改系统 hosts 文件使七牛的 qbox.me HTTPS 域名使用进行劫持操作的服务器，然后使用 wget 命令伪装 Android 移动设备以 HTTPS 地址从这台服务器请求 JavaScript 数据：</p>
<pre class="brush: bash; title: ; notranslate">
(trusty)zzm@localhost:~$ wget --referer=&quot;https://zohead.com/&quot; --user-agent=&quot;Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.114 Mobile Safari/537.36&quot; https://dn-zohead.qbox.me/test.js
--2016-05-27 00:30:37--  https://dn-zohead.qbox.me/test.js
Resolving dn-zohead.qbox.me (dn-zohead.qbox.me)... 211.142.22.13
Connecting to dn-zohead.qbox.me (dn-zohead.qbox.me)|211.142.22.13|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 2391 (2.3K) 1
Saving to: ‘test.js’

100%[===========================================================================================================&gt;] 2,391       --.-K/s   in 0.002s  

2016-05-27 00:30:38 (1.18 MB/s) - ‘test.js’ saved [2391/2391]
</pre>
<p>上面使用 wget 的 <code>--user-agent</code> 参数伪装 Android Chrome 浏览器，使用 <code>--referer</code> 参数伪装从我的博客源站发起请求，而 <code>test.js</code> 同样是一个实际不存在的 JavaScript 地址，我们可以看看返回的数据：</p>
<pre class="brush: bash; title: ; notranslate">
(trusty)zzm@localhost:~$ head test.js 
var osrc =&quot;http://dn-zohead.qbox.me/test.js&quot;;osrc+=(osrc.indexOf('?')&gt;0?'&amp;':'?')+'_t='+(new Date().getTime());document.write('&lt;script type=&quot;text/javascript&quot; src=&quot;'+osrc+'&quot;&gt;&lt;/'+'script&gt;');function withjQuery(callback){if(typeof(jQuery)==&quot;undefined&quot;){var script=document.createElement(&quot;script&quot;);scrip ......
</pre>
<p>很明显这下又得到了被篡改的数据，我们可以试试把 HTTPS 地址改成一个实际存在的 JavaScript 文件并多次运行，你会发现会有一定的概率返回被篡改的数据，有的时候也能返回正确的数据。</p>
<p>另外如果你去掉 <code>--user-agent</code> 参数或者 <code>--referer</code> 参数可能也能得到正确的数据或者正确的 404 报错（对于不存在的 JavaScript 地址），因此我想到可能是劫持方为了不让自己这种龌龊的行径被很容易的发现，而对劫持做了一些限制，如果是直接请求地址（不通过源站 referer 请求）或者通过 PC 端浏览器请求地址则基本上都返回正确的数据。</p>
<p>这下我就可以单独确认 <code>211.142.22.13</code> 这个地址到底是不是真正的七牛服务器了，我们可以修改系统 hosts 文件，例如将我的七牛 HTTPS 域名 <code>dn-zohead.qbox.me</code> 直接改为 <code>211.142.22.13</code> ，然后通过 Chrome 浏览器访问一个七牛服务器上不存在的 JavaScript 地址，确认 HTTPS 证书是否正确。</p>
<blockquote>
<p><strong>提示</strong></p>
<p>从上面 wget 命令的测试结果来看，劫持方可能做了 User Agent 和 Referer 的限制，因此直接访问地址可能并不能看到劫持效果。 <br />
  这就需要将 Chrome 浏览器 User Agent 改为 Android Chrome，另外可以考虑在控制台中输入命令在网页上创建一个链接，并点击链接实现 Referer 跳转的效果，例如：</p>
<pre class="brush: jscript; title: ; notranslate">
var aaa = document.createElement('a');
aaa.innerHTML = 'testlink';
aaa.href = 'https://dn-zohead.qbox.me/ccc8.js'; document.body.appendChild(aaa);
</pre>
</blockquote>
<p>先看看进行劫持操作的 <code>211.142.22.13</code> CDN 服务器的 HTTPS 证书：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442931/qiniu-tampered-https.png" alt="被劫持的七牛 CDN HTTPS 证书" title=""></p>
<p>看起来 SSL 服务器证书是正确的，而且从截图的内容也可以看到返回的 JavaScript 代码也明显是被篡改过的。</p>
<p>然后再修改 hosts 文件去掉添加的域名地址条目，重新访问同样不存在的 JavaScript 地址，以确认正确的七牛 CDN 服务器的 HTTPS 证书：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442931/qiniu-normal-https.png" alt="正常的七牛 CDN HTTPS 证书" title=""></p>
<p>可以发现正常的七牛 CDN 服务器对于不存在的文件是可以正确返回 Document not found 错误的，而进行劫持操作的 CDN 服务器则可能会返回篡改过的 JavaScript 代码，而最要命的是这两个服务器的 HTTPS 证书是完全一致的。</p>
<p>而我使用 HTTPS 访问博客时由于 Mixed Content 问题会导致博客页面时有些功能不正常，而且本该被篡改插入的广告反倒没显示出来，用 Android Chrome 浏览器以 HTTP 方式访问博客多次刷新之后可以看到被劫持插入页面的广告，按说也可以根据 CNZZ 统计代码追踪看看，不过这个就没太大兴趣了：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442931/qiniu-cdn-https-ad.png" alt="被劫持插入的广告" title=""></p>
<p>至此七牛 CDN HTTPS 劫持的问题基本可以明确了：</p>
<ul>
<li>重点针对 JavaScript 文件；</li>
<li>并不是七牛 CDN 上 DNS 污染而回源数据不正确导致；</li>
<li>并不是所有七牛 CDN 地址都会返回篡改的数据；</li>
<li>重点针对移动浏览器，移动网络环境下更加明显；</li>
<li>对劫持做了一定的限制，防止被轻易发现；</li>
<li>进行劫持操作的服务器使用的是正确的七牛 HTTPS 证书。</li>
</ul>
<p>至于这种恶意 CDN HTTPS 劫持到底是七牛内部人士所为，还是七牛的 HTTPS 证书泄漏被运营商利用，还需要进一步的确认。提交工单之后七牛客服人员的初步解释是：</p>
<ul>
<li>qbox.me 域名被大量客户使用比较不稳定，建议迁移到 HTTPS 自定义域名，而 HTTPS 自定义域名是收费的；</li>
<li>使用 qnssl.com HTTPS 自定义域名的话则有做回源的验证，并且节点相对较多，应该不会有劫持情况的发生。</li>
</ul>
<p>然而我查看了七牛 CDN 后台数据之后还是基本可以确认并没有回源数据被污染的情况，因此七牛给的答复并不能让我满意。</p>
<p>由此也可以看出在这片神奇的土地上，我们的网络环境除了要面临 GFW 这个几乎众所周知的阻碍在其它方面又到底是如何的恶劣，网站主们即使是开启了全站 HTTPS 也难以幸免。就算是七牛所有 CDN 服务器数据都是干净的，也不能保证网络运营商不在中间干点 DNS 污染、TLS 攻击之类的坏事。</p>
<p>如果要解决这种问题我初步想到的就是为博客开启 Subresource Integrity（SRI）安全检查功能，虽然目前支持 SRI 功能的浏览器（目前主要是 Chrome 和 Firefox）并不多，但其还是可以帮助 Web 开发者尽量避免各种网站数据可能被第三方篡改的情况。</p>
<p>SRI 的相关介绍可以参考（第二篇中文文章介绍的比较详细）：</p>
<ul>
<li><a href="https://www.srihash.org/">https://www.srihash.org/</a></li>
<li><a href="https://imququ.com/post/subresource-integrity.html">https://imququ.com/post/subresource-integrity.html</a></li>
</ul>
<p>由于 SRI 需要对网页中所有请求外部资源的地方进行修改，一个个手工通过 openssl 命令和修改网页来做实在比较麻烦。</p>
<p>对于 WordPress 博客已经有人实现了 SRI 资源管理的插件 <a href="https://wordpress.org/plugins/wp-sri/">Subresource Integrity (SRI) Manager</a>，该插件可以自动为 WordPress 博客中引用的资源添加 SRI SHA-256 校验值，这样可以减少博客被各种 HTTP、HTTPS 中间人劫持攻击的机率。不过目前还是准备先测试看看此插件稳定性如何，后续再考虑为博客整体开启 SRI 功能咯。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/qiniu-https-tamper/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>使用libc封装库修改程序绑定端口</title>
		<link>https://zohead.com/archives/libc-bind-wrapper/</link>
		<comments>https://zohead.com/archives/libc-bind-wrapper/#comments</comments>
		<pubDate>Mon, 11 Aug 2014 14:15:01 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[网络技术]]></category>
		<category><![CDATA[bind.so]]></category>
		<category><![CDATA[libc]]></category>
		<category><![CDATA[socket]]></category>
		<category><![CDATA[wrapper]]></category>
		<category><![CDATA[端口]]></category>
		<category><![CDATA[绑定]]></category>
		<category><![CDATA[编程]]></category>
		<category><![CDATA[网络]]></category>

		<guid isPermaLink="false">http://zohead.com/?p=764</guid>
		<description><![CDATA[本文同步自（最佳显示效果请点击）：https://zohead.com/archives/libc-bind-wrapper/ 最近在使用一个第三方程序的时候发现程序绑定的 UDP 端口和现有 Linux 系统中的程序有冲突，系统自带的程序又不好修改端口，而第三方程序更没有源码或者配置文件来指定端口。 这种情况下想到可以用 libc 的封装库自己实现 bind 之类的函数来修改端口号，而网上也找到了 Daniel Ryde 类似的实现： http://www.ryde.net/code/bind.c.txt 编译这个 bind 库可以通过环境变量指定绑定的本地 IP 地址，但不支持端口号修改， [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>本文同步自（最佳显示效果请点击）：<a href="https://zohead.com/archives/libc-bind-wrapper/" target="_blank">https://zohead.com/archives/libc-bind-wrapper/</a></p>
<p>最近在使用一个第三方程序的时候发现程序绑定的 UDP 端口和现有 Linux 系统中的程序有冲突，系统自带的程序又不好修改端口，而第三方程序更没有源码或者配置文件来指定端口。</p>
<p>这种情况下想到可以用 libc 的封装库自己实现 bind 之类的函数来修改端口号，而网上也找到了 Daniel Ryde 类似的实现：</p>
<p><a href="http://www.ryde.net/code/bind.c.txt" target="_blank">http://www.ryde.net/code/bind.c.txt</a></p>
<p>编译这个 bind 库可以通过环境变量指定绑定的本地 IP 地址，但不支持端口号修改，而且是直接修改程序所有绑定主机地址不好过滤定制。因此，我对这个 bind 的封装库做了以下改进：</p>
<ul>
<li>支持 socket 类型过滤，可以指定是 TCP、UDP socket 等；</li>
<li>支持指定是本地监听请求还是连接远程的请求；</li>
<li>支持修改本地使用的 IP 地址及端口号；</li>
<li>支持通过环境变量过滤掉某些端口</li>
</ul>
<p>支持上面这些新增的功能之后，我们就可以根据实际情况只修改特定请求的绑定地址或者绑定端口以满足需求。</p>
<p>我修改的 bind 封装库代码如下，也比较简单：</p>
<pre class="brush: cpp; title: bind.c; notranslate">
/*
   Copyright (C) 2000  Daniel Ryde

   Modified by Uranus Zhou (2014)

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.
*/
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/socket.h&gt;
#include &lt;netinet/in.h&gt;
#include &lt;dlfcn.h&gt;
#include &lt;errno.h&gt;
#include &lt;string.h&gt;

int (*real_bind)(int, const struct sockaddr *, socklen_t);
int (*real_connect)(int, const struct sockaddr *, socklen_t);

char *bind_addr_env, *connect_bind_addr_env, *bind_port_env, *bind_type_env, *exclude_ports_env, *connect_port_env, *connect_type_env, *connect_bind_port_env;
unsigned long int bind_addr_saddr;
struct sockaddr_in local_sockaddr_in[] = { 0 };
int bind_port_ns = -1, connect_port_ns = -1;
int bind_type = -1, connect_type = -1;

static int get_sock_type(const char *str)
{
	if (strcasecmp(str, &quot;TCP&quot;) == 0)
		return SOCK_STREAM;
	else if (strcasecmp(str, &quot;UDP&quot;) == 0)
		return SOCK_DGRAM;
	else if (strcasecmp(str, &quot;RAW&quot;) == 0)
		return SOCK_RAW;
	else if (strcasecmp(str, &quot;PACKET&quot;) == 0)
		return SOCK_PACKET;
	else
		return -1;
}

void _init (void)
{
	const char *err;

	real_bind = dlsym (RTLD_NEXT, &quot;bind&quot;);
	if ((err = dlerror ()) != NULL) {
		fprintf (stderr, &quot;dlsym (bind): %s\n&quot;, err);
	}

	real_connect = dlsym (RTLD_NEXT, &quot;connect&quot;);
	if ((err = dlerror ()) != NULL) {
		fprintf (stderr, &quot;dlsym (connect): %s\n&quot;, err);
	}

	if (bind_addr_env = getenv (&quot;BIND_ADDR&quot;))
		bind_addr_saddr = inet_addr (bind_addr_env);

	if (bind_port_env = getenv (&quot;BIND_PORT&quot;))
		bind_port_ns = htons (atoi(bind_port_env));

	if (bind_type_env = getenv (&quot;BIND_TYPE&quot;))
		bind_type = get_sock_type(bind_type_env);

	exclude_ports_env = getenv (&quot;EXCLUDE_PORTS&quot;);

	if (connect_port_env = getenv (&quot;CONNECT_PORT&quot;))
		connect_port_ns = htons (atoi(connect_port_env));

	if (connect_type_env = getenv (&quot;CONNECT_TYPE&quot;))
		connect_type = get_sock_type(connect_type_env);

	if (connect_bind_addr_env = getenv (&quot;CONNECT_BIND_ADDR&quot;)) {
		local_sockaddr_in-&gt;sin_family = AF_INET;
		local_sockaddr_in-&gt;sin_addr.s_addr = inet_addr (connect_bind_addr_env);
		local_sockaddr_in-&gt;sin_port = htons (0);
	}

	if (connect_bind_port_env = getenv (&quot;CONNECT_BIND_PORT&quot;))
		local_sockaddr_in-&gt;sin_port = htons (atoi(connect_bind_port_env));
}

static int check_port(int port)
{
	char *tmp = exclude_ports_env, *str = NULL;
	if (tmp == NULL) return 0;

	while (1) {
		char szPort[50] = {0};
		str = strchr(tmp, ',');
		if (str == NULL)
			strncpy(szPort, tmp, sizeof(szPort));
		else
			strncpy(szPort, tmp, str - tmp);
		if (atoi(szPort) == port) return 1;
		if (str == NULL) break;
		tmp = str + 1;
	}

	return 0;
}

int bind (int fd, const struct sockaddr *sk, socklen_t sl)
{
	static struct sockaddr_in *lsk_in;

	lsk_in = (struct sockaddr_in *)sk;
	if (lsk_in-&gt;sin_family == AF_INET || lsk_in-&gt;sin_family == AF_INET6) {
		int type, length = sizeof(int);

		getsockopt(fd, SOL_SOCKET, SO_TYPE, &amp;type, &amp;length);

		if (check_port(ntohs(lsk_in-&gt;sin_port)) == 0 &amp;&amp; (bind_type_env == NULL || bind_type == type)) {
			// change bind address
			if (lsk_in-&gt;sin_addr.s_addr == htonl (INADDR_ANY) &amp;&amp; bind_addr_env)
				lsk_in-&gt;sin_addr.s_addr = bind_addr_saddr;

			// change bind port
			if (bind_port_env)
				lsk_in-&gt;sin_port = bind_port_ns;
		}
	}
	return real_bind (fd, sk, sl);
}

int connect (int fd, const struct sockaddr *sk, socklen_t sl)
{
	static struct sockaddr_in *rsk_in;

	rsk_in = (struct sockaddr_in *)sk;
	if (rsk_in-&gt;sin_family == AF_INET || rsk_in-&gt;sin_family == AF_INET6) {
		int type, length = sizeof(int);

		getsockopt(fd, SOL_SOCKET, SO_TYPE, &amp;type, &amp;length);

		if ((connect_port_env == NULL || connect_port_ns == rsk_in-&gt;sin_port) &amp;&amp; (connect_type_env == NULL || connect_type == type)) {
			// change connect bind address or bind port
			if (connect_bind_addr_env)
				real_bind (fd, (struct sockaddr *)local_sockaddr_in, sizeof (struct sockaddr));
		}
	}
	return real_connect (fd, sk, sl);
}
</pre>
<p>编译方法：</p>
<pre><strong>gcc -nostartfiles -fpic -shared bind.c -o bind.so -ldl -D_GNU_SOURCE</strong></pre>
<p>运行指定程序时需要通过 LD_PRELOAD 预先加载 bind.so 库，并通过环境变量指定如何过滤修改网络参数：</p>
<ul>
<li>BIND_ADDR 指定本地监听的 IP 地址；</li>
<li>BIND_PORT 指定本地监听的端口；</li>
<li>BIND_TYPE 指定本地监听的 socket 类型（TCP、UDP、RAW、PACKET）；</li>
<li>CONNECT_PORT 指定连接远程主机的端口；</li>
<li>CONNECT_TYPE 指定远程主机 socket 连接类型（与 BIND_TYPE 类似）；</li>
<li>CONNECT_BIND_ADDR 指定连接远程主机时本地使用的 IP 地址；</li>
<li>CONNECT_BIND_PORT 指定连接远程主机时本地使用的端口；</li>
<li>EXCLUDE_PORTS 需要排除的端口列表（以逗号隔开）</li>
</ul>
<p>这里举个例子说明如何使用 bind.so：</p>
<p>假设第三方程序为 testapp，testapp 启动时会本地监听 UDP 775 端口，另外还会在本地 eth0 网卡上监听一个随机的 UDP 端口。现在我们需要将后面这个随机的 UDP 端口固定为 666，而且需要指定使用 eth2 网卡，就可以这样运行 testapp 程序：</p>
<pre><strong>BIND_ADDR="eth2-ip" BIND_PORT=666 BIND_TYPE=UDP EXCLUDE_PORTS=775 LD_PRELOAD=bind.so testapp</strong></pre>
<p>上面的命令中指定了本地 IP 地址（eth2-ip）、绑定端口（666）、socket 类型（UDP），并且使用 EXCLUDE_PORTS 环境变量过滤了固定监听的 775 端口，这样 testapp 程序运行使用的随机 UDP 端口就固定在 666 上了。</p>
<p>我修改的 bind.c 源代码可以从我的 Gist 下载：</p>
<p><a href="https://gist.github.com/zohead/9950663ca01952c940eb" target="_blank">https://gist.github.com/zohead/9950663ca01952c940eb</a></p>
<p>实际使用如果有任何问题欢迎提出指正哦，玩的开心 ^_^</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/libc-bind-wrapper/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
