<?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>Vm on Wilson Wu</title><link>https://wilsonwu.me/zh/tags/vm/</link><description>Recent content in Vm on Wilson Wu</description><generator>Hugo</generator><language>zh-CN</language><lastBuildDate>Fri, 14 Aug 2026 00:00:00 -0400</lastBuildDate><atom:link href="https://wilsonwu.me/zh/tags/vm/index.xml" rel="self" type="application/rss+xml"/><item><title>使用 Azure 托管 PostgreSQL 降低自建数据库的安全风险</title><link>https://wilsonwu.me/zh/blog/2026/azure-managed-postgresql-vs-self-hosted-security/</link><pubDate>Fri, 14 Aug 2026 00:00:00 -0400</pubDate><guid>https://wilsonwu.me/zh/blog/2026/azure-managed-postgresql-vs-self-hosted-security/</guid><description>&lt;h2 id="问题的背景"&gt;问题的背景&lt;/h2&gt;
&lt;p&gt;&lt;img alt="PostgreSQL 安全问题" loading="lazy" src="https://wilsonwu.me/zh/blog/2026/azure-managed-postgresql-vs-self-hosted-security/1-overview.png"&gt;&lt;/p&gt;
&lt;p&gt;最近，一台部署在 Azure 上的 Linux 服务器突然出现 CPU 持续 100% 的情况。排查后发现，服务器上存在一个以 &lt;code&gt;postgres&lt;/code&gt; 用户身份运行的恶意程序，并通过 &lt;code&gt;cron&lt;/code&gt; 定时任务维持运行。&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="问题的背景">问题的背景</h2>
<p><img alt="PostgreSQL 安全问题" loading="lazy" src="/zh/blog/2026/azure-managed-postgresql-vs-self-hosted-security/1-overview.png"></p>
<p>最近，一台部署在 Azure 上的 Linux 服务器突然出现 CPU 持续 100% 的情况。排查后发现，服务器上存在一个以 <code>postgres</code> 用户身份运行的恶意程序，并通过 <code>cron</code> 定时任务维持运行。</p>
<p>这台服务器上的 PostgreSQL：</p>
<ul>
<li>没有通过 Azure NSG 向公网开放 <code>5432</code>；</li>
<li>没有被当前业务使用；</li>
<li>业务实际使用的是本机 MySQL；</li>
<li>但 PostgreSQL 服务仍然安装并运行；</li>
<li>系统中保留了 <code>postgres</code> 用户、主目录、cron 和历史脚本。</li>
</ul>
<p>这次事件说明：</p>

<blockquote>
  <p>PostgreSQL 没有向公网开放，并不代表服务器上与 PostgreSQL 相关的组件完全没有风险。</p>

</blockquote>
<p>需要特别说明的是，目前的证据只能证明恶意程序利用了 <code>postgres</code> 操作系统用户及其目录进行持久化，<strong>不能证明攻击者一定是通过 PostgreSQL 的 5432 端口进入服务器的</strong>。初始入口也可能是历史配置、SSH、Web 应用、容器、旧镜像或其他漏洞。</p>
<p>如果生产环境确实需要 PostgreSQL，但没有必须自建数据库服务器的特殊要求，建议优先使用：</p>

<blockquote>
  <p><strong>Azure Database for PostgreSQL，并通过私有网络访问，关闭公共网络入口。</strong></p>

</blockquote>
<p>这样可以减少自建数据库所带来的操作系统用户、SSH、cron、systemd、补丁、备份和主机入侵等风险。</p>
<h2 id="发现问题服务器-cpu-持续占满">发现问题：服务器 CPU 持续占满</h2>
<p>最初是在服务器上执行 <code>htop</code> 时发现异常。</p>
<p><img alt="服务器 htop 中 CPU 占用异常" loading="lazy" src="/zh/blog/2026/azure-managed-postgresql-vs-self-hosted-security/2-htop.png"></p>
<p>从截图中可以看到：</p>
<ul>
<li>服务器共有 4 个 CPU 核心；</li>
<li>4 个核心全部达到 <code>100%</code>；</li>
<li>Load Average 长期维持在 4 左右；</li>
<li>多个进程以 <code>postgres</code> 用户运行；</li>
<li>进程名称为随机字符串 <code>dzZ7yVrxv7</code>；</li>
<li>单个进程的 CPU 使用率超过 <code>200%</code>；</li>
<li>进程占用了大量内存。</li>
</ul>
<p>进一步执行：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>sudo ps -eo user,pid,ppid,lstart,etime,%cpu,%mem,stat,comm,args <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  --sort<span style="color:#f92672">=</span>-%cpu | head -20
</span></span></code></pre></div><p>其中一个主要进程如下：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>USER       PID   PPID  %CPU  %MEM  STAT  COMMAND
</span></span><span style="display:flex;"><span>postgres   9918     1   377  29.8  Ssl   dzZ7yVrxv7
</span></span></code></pre></div><p>这不是正常的 PostgreSQL 进程。</p>
<p>正常情况下，PostgreSQL 进程名称通常类似：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>postgres
</span></span><span style="display:flex;"><span>postgres: checkpointer
</span></span><span style="display:flex;"><span>postgres: background writer
</span></span><span style="display:flex;"><span>postgres: walwriter
</span></span><span style="display:flex;"><span>postgres: autovacuum launcher
</span></span></code></pre></div><p>而本次进程具有以下异常特征：</p>
<ul>
<li>名称为随机字符串；</li>
<li>以 <code>postgres</code> 用户运行；</li>
<li>CPU 使用率接近 4 个核心；</li>
<li>父进程已经变成 PID 1；</li>
<li>内存占用异常高。</li>
</ul>
<p>因此，初步判断这不是正常数据库负载，而是疑似挖矿程序或其他恶意程序。</p>
<h2 id="分析问题确认恶意程序来源">分析问题：确认恶意程序来源</h2>
<h3 id="1-检查进程真实文件">1. 检查进程真实文件</h3>
<p>Linux 进程名称可以被修改或伪装，所以不能只看 <code>COMMAND</code>，需要检查 <code>/proc/&lt;PID&gt;/exe</code>。</p>
<p>执行：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>sudo sh -c <span style="color:#e6db74">&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">echo &#34;EXE:&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">ls -l /proc/9918/exe
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">echo &#34;CWD:&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">ls -l /proc/9918/cwd
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">echo &#34;CMDLINE:&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">tr &#34;\0&#34; &#34; &#34; &lt; /proc/9918/cmdline
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">echo
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">echo &#34;CGROUP:&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">cat /proc/9918/cgroup
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&#39;</span>
</span></span></code></pre></div><p>输出如下：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>EXE:
</span></span><span style="display:flex;"><span>/proc/9918/exe -&gt; /dev/shm/gJppJl6TbG (deleted)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>CWD:
</span></span><span style="display:flex;"><span>/proc/9918/cwd -&gt; /var/lib/postgresql
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>CMDLINE:
</span></span><span style="display:flex;"><span>dzZ7yVrxv7
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>CGROUP:
</span></span><span style="display:flex;"><span>0::/system.slice/cron.service
</span></span></code></pre></div><p>这几项信息非常关键。</p>
<h3 id="可执行文件位于-devshm">可执行文件位于 <code>/dev/shm</code></h3>
<p>真实程序路径为：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>/dev/shm/gJppJl6TbG
</span></span></code></pre></div><p><code>/dev/shm</code> 是基于内存的临时文件系统。恶意程序经常将文件下载到这里，再从内存文件系统中执行，以减少在磁盘上的痕迹。</p>
<h3 id="原文件已经被删除">原文件已经被删除</h3>
<p>路径末尾显示：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>(deleted)
</span></span></code></pre></div><p>说明程序启动后，磁盘上的原文件已被删除。但由于进程仍然持有该文件，程序依然可以继续运行。</p>
<p>这种“启动后删除自身”的行为是常见的恶意程序隐藏手段。</p>
<h3 id="工作目录属于-postgresql-用户">工作目录属于 PostgreSQL 用户</h3>
<p>进程工作目录是：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>/var/lib/postgresql
</span></span></code></pre></div><p>这说明恶意程序与 <code>postgres</code> 用户的主目录有关。</p>
<h3 id="进程由-cron-启动">进程由 cron 启动</h3>
<p>cgroup 显示：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>/system.slice/cron.service
</span></span></code></pre></div><p>这说明程序是通过定时任务启动的。</p>
<h3 id="2-从运行中的进程保存样本">2. 从运行中的进程保存样本</h3>
<p>因为原始文件已经被删除，如果立即结束进程，可能无法再提取样本。因此先从 <code>/proc</code> 保存程序：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>sudo mkdir -p /root/incident
</span></span><span style="display:flex;"><span>sudo chmod <span style="color:#ae81ff">700</span> /root/incident
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>sudo cp -L /proc/9918/exe <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  /root/incident/suspect-9918.bin
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>sudo chmod <span style="color:#ae81ff">600</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  /root/incident/suspect-9918.bin
</span></span></code></pre></div><p>检查文件类型：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>sudo file /root/incident/suspect-9918.bin
</span></span></code></pre></div><p>结果如下：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>ELF 64-bit LSB shared object, x86-64,
</span></span><span style="display:flex;"><span>statically linked, no section header
</span></span></code></pre></div><p>计算 SHA-256：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>sudo sha256sum /root/incident/suspect-9918.bin
</span></span></code></pre></div><p>得到：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>d32f9eef63c496a146a6a811677dd27e337db993d6f1a4b1af145c3135d391b3
</span></span></code></pre></div><p>该文件具有以下特征：</p>
<ul>
<li>随机文件名；</li>
<li>位于 <code>/dev/shm</code>；</li>
<li>静态链接；</li>
<li>没有节区表；</li>
<li>启动后删除原文件；</li>
<li>持续占用全部 CPU。</li>
</ul>
<p>这些行为进一步证明它不是正常的 PostgreSQL 组件。</p>
<h3 id="3-检查-postgresql-用户的定时任务">3. 检查 PostgreSQL 用户的定时任务</h3>
<p>由于进程是从 <code>cron.service</code> 启动的，继续检查 <code>postgres</code> 用户的 crontab：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>sudo crontab -u postgres -l
</span></span></code></pre></div><p>发现如下任务：</p>
<pre tabindex="0"><code class="language-cron" data-lang="cron">24 * * * * /var/lib/postgresql/.dbus-XGodM5jOyL
</code></pre><p>该任务会在每小时第 24 分钟执行一次隐藏脚本：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>/var/lib/postgresql/.dbus-XGodM5jOyL
</span></span></code></pre></div><p>文件名使用了以点开头的隐藏形式，并模仿了 D-Bus 的命名方式，很容易被误认为系统文件。</p>
<p>检查 cron 日志：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>sudo journalctl -u cron.service <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  --since <span style="color:#e6db74">&#34;2026-08-15 07:15:00&#34;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  --until <span style="color:#e6db74">&#34;2026-08-15 09:15:00&#34;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  --no-pager
</span></span></code></pre></div><p>日志中可以看到：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>Aug 15 07:24:01 CRON: (postgres) CMD (/var/lib/postgresql/.dbus-XGodM5jOyL)
</span></span><span style="display:flex;"><span>Aug 15 08:24:01 CRON: (postgres) CMD (/var/lib/postgresql/.dbus-XGodM5jOyL)
</span></span></code></pre></div><p>这证明恶意脚本至少在 <code>07:24</code> 和 <code>08:24</code> 被执行过。</p>
<h3 id="4-分析恶意启动脚本">4. 分析恶意启动脚本</h3>
<p>检查文件：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>sudo file /var/lib/postgresql/.dbus-XGodM5jOyL
</span></span><span style="display:flex;"><span>sudo stat /var/lib/postgresql/.dbus-XGodM5jOyL
</span></span><span style="display:flex;"><span>sudo sha256sum /var/lib/postgresql/.dbus-XGodM5jOyL
</span></span></code></pre></div><p>结果显示它是一个 Bash 脚本：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>Bourne-Again shell script, ASCII text executable,
</span></span><span style="display:flex;"><span>with very long lines
</span></span></code></pre></div><p>对应 SHA-256：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>36b1dcf43876f55aae2b8be7dada848b41f60ec61cd4ca7ab2a1278193f286f0
</span></span></code></pre></div><p>这份脚本负责下载、启动或恢复恶意程序，也是恶意进程能够周期性出现的主要持久化机制。</p>
<p>如果只结束高 CPU 进程，而不清理这条 cron，恶意程序会在下一个小时的第 24 分钟再次出现。</p>
<h3 id="5-在用户目录中发现第二个恶意下载器">5. 在用户目录中发现第二个恶意下载器</h3>
<p>停止并卸载 PostgreSQL 后，我继续检查 <code>/var/lib/postgresql</code> 的残留文件：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>sudo find /var/lib/postgresql <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  -xdev -maxdepth <span style="color:#ae81ff">10</span> -ls
</span></span></code></pre></div><p>发现了另一个可疑文件：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>/var/lib/postgresql/.config/systemd/user/
</span></span><span style="display:flex;"><span>└── systemd-tmpfiles-cleanup/
</span></span><span style="display:flex;"><span>    └── systemd-tmpfiles-cleanup-TNWow7.sh
</span></span></code></pre></div><p>这个文件使用了：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>systemd-tmpfiles-cleanup
</span></span></code></pre></div><p>作为目录名，试图伪装成正常的 systemd 临时文件清理组件。</p>
<p>检查文件类型和哈希：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>sudo file <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  /var/lib/postgresql/.config/systemd/user/systemd-tmpfiles-cleanup/systemd-tmpfiles-cleanup-TNWow7.sh
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>sudo sha256sum <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  /var/lib/postgresql/.config/systemd/user/systemd-tmpfiles-cleanup/systemd-tmpfiles-cleanup-TNWow7.sh
</span></span></code></pre></div><p>结果为：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>Bourne-Again shell script, ASCII text executable,
</span></span><span style="display:flex;"><span>with very long lines
</span></span></code></pre></div><p>SHA-256：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>9962de5e0c68ea6e85879c5f62f2d0806360dbe186dc15e1d59abf8e4209de11
</span></span></code></pre></div><p>脚本内容经过 Base64 编码，末尾直接执行：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>base64 -d | bash
</span></span></code></pre></div><p>解码后的逻辑显示，它具备以下行为：</p>
<ul>
<li>通过 Tor、SOCKS5 或 Tor2web 连接外部地址；</li>
<li>获取服务器公网 IP；</li>
<li>收集当前用户名、主机名和内核版本；</li>
<li>读取机器 ID；</li>
<li>从外部下载其他程序；</li>
<li>在 <code>/dev/shm</code>、<code>/tmp</code>、<code>/var/tmp</code> 等目录测试和执行文件；</li>
<li>启动程序后删除临时文件。</li>
</ul>
<p>这已经不只是一个单纯的高 CPU 进程，而是一套包含下载、执行、隐藏和持久化能力的恶意程序。</p>
<h2 id="为什么-postgresql-没有开放公网端口仍然存在风险">为什么 PostgreSQL 没有开放公网端口，仍然存在风险？</h2>
<p>Azure 网络安全组当时只允许公网访问：</p>
<ul>
<li>TCP 22；</li>
<li>TCP 80；</li>
<li>TCP 443。</li>
</ul>
<p>并没有允许公网直接访问 PostgreSQL 的 <code>5432</code> 端口。</p>
<p>但检查服务器本机监听状态时发现：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>sudo ss -lntp | grep <span style="color:#e6db74">&#39;:5432&#39;</span>
</span></span></code></pre></div><p>输出为：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>LISTEN 0 244 0.0.0.0:5432 0.0.0.0:*
</span></span><span style="display:flex;"><span>LISTEN 0 244 [::]:5432    [::]:*
</span></span></code></pre></div><p>这说明 PostgreSQL 在操作系统层面监听了所有 IPv4 和 IPv6 网卡。</p>
<p>虽然 Azure NSG 没有向公网放行 <code>5432</code>，但以下风险依然存在。</p>
<h3 id="1-同一虚拟网络中的主机仍可能访问">1. 同一虚拟网络中的主机仍可能访问</h3>
<p>Azure NSG 默认存在 <code>AllowVnetInBound</code> 规则，同一虚拟网络中的其他服务器可能访问该端口。</p>
<p>如果同一 VNet 内的其他服务器被入侵，攻击者就可能通过内网继续攻击数据库服务器。</p>
<h3 id="2-本机应用或容器仍可能访问">2. 本机应用或容器仍可能访问</h3>
<p>即使公网无法连接，服务器本机运行的以下组件仍然可能访问 PostgreSQL：</p>
<ul>
<li>Web 应用；</li>
<li>Docker 容器；</li>
<li>CI/CD Agent；</li>
<li>其他被攻陷的服务；</li>
<li>本机恶意程序。</li>
</ul>
<p>网络安全组无法阻止服务器本机进程访问本机数据库。</p>
<h3 id="3-风险不只来自数据库端口">3. 风险不只来自数据库端口</h3>
<p>本次恶意程序的实际持久化位置是：</p>
<ul>
<li><code>postgres</code> 用户的 crontab；</li>
<li><code>/var/lib/postgresql</code> 用户目录；</li>
<li>用户级 systemd 配置目录；</li>
<li><code>/dev/shm</code> 临时执行目录。</li>
</ul>
<p>即使 PostgreSQL 服务停止，只要这些用户、目录和定时任务仍然存在，恶意程序就可能继续运行。</p>
<p>所以，数据库的安全边界不只是 <code>5432</code> 端口，还包括：</p>
<ul>
<li>操作系统账户；</li>
<li>数据目录；</li>
<li>用户主目录；</li>
<li>cron；</li>
<li>systemd；</li>
<li>SSH；</li>
<li>软件包；</li>
<li>配置文件；</li>
<li>历史镜像和备份。</li>
</ul>
<h3 id="4-未使用的软件往往更容易被忽略">4. 未使用的软件往往更容易被忽略</h3>
<p>这台服务器的业务实际使用 MySQL，PostgreSQL 并没有业务用途。</p>
<p>由于“不使用”，它反而容易长期处于以下状态：</p>
<ul>
<li>没有人检查配置；</li>
<li>没有人检查系统用户；</li>
<li>没有人检查数据库日志；</li>
<li>没有人检查用户的 cron；</li>
<li>没有人检查相关目录；</li>
<li>没有人确认是否仍然需要；</li>
<li>没有人关注其补丁和版本。</li>
</ul>
<p>因此：</p>

<blockquote>
  <p>未使用但仍然安装和运行的软件，并不是备用能力，而是未被管理的攻击面。</p>

</blockquote>
<h2 id="解决问题清除恶意程序并卸载-postgresql">解决问题：清除恶意程序并卸载 PostgreSQL</h2>
<h3 id="1-暂停恶意进程">1. 暂停恶意进程</h3>
<p>取证完成后，先暂停进程：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>sudo kill -STOP <span style="color:#ae81ff">9918</span>
</span></span></code></pre></div><p>检查进程状态：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>ps -p <span style="color:#ae81ff">9918</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  -o user,pid,ppid,%cpu,%mem,stat,comm,args
</span></span></code></pre></div><p>状态中出现 <code>T</code>，说明进程已暂停。</p>
<p>暂停的好处是：</p>
<ul>
<li>立即停止继续占用 CPU；</li>
<li>暂时停止潜在网络行为；</li>
<li>保留进程，便于继续调查；</li>
<li>避免在完成取证前丢失内存中的样本。</li>
</ul>
<h3 id="2-备份并删除恶意-cron">2. 备份并删除恶意 cron</h3>
<p>先保存 crontab：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>sudo crontab -u postgres -l <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  &gt; /root/incident/postgres-crontab.txt
</span></span></code></pre></div><p>然后删除：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>sudo crontab -u postgres -r
</span></span></code></pre></div><p>确认已删除：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>sudo crontab -u postgres -l
</span></span></code></pre></div><p>预期输出：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>no crontab for postgres
</span></span></code></pre></div><h3 id="3-隔离恶意启动脚本">3. 隔离恶意启动脚本</h3>
<p>先备份脚本：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>sudo cp -a <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  /var/lib/postgresql/.dbus-XGodM5jOyL <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  /root/incident/cron-payload
</span></span></code></pre></div><p>再从原位置移走：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>sudo mv <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  /var/lib/postgresql/.dbus-XGodM5jOyL <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  /root/incident/original-dbus-XGodM5jOyL
</span></span></code></pre></div><p>限制证据文件权限：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>sudo chown root:root <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  /root/incident/original-dbus-XGodM5jOyL <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  /root/incident/cron-payload
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>sudo chmod <span style="color:#ae81ff">600</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  /root/incident/original-dbus-XGodM5jOyL <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  /root/incident/cron-payload
</span></span></code></pre></div><h3 id="4-结束恶意进程">4. 结束恶意进程</h3>
<p>确认恶意 cron 和启动脚本已隔离后，结束进程：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>sudo kill -KILL <span style="color:#ae81ff">9918</span>
</span></span></code></pre></div><p>检查进程是否消失：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>ps -p <span style="color:#ae81ff">9918</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  -o user,pid,ppid,%cpu,%mem,stat,comm,args
</span></span></code></pre></div><p>随后查看系统负载：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>uptime
</span></span></code></pre></div><p>清理后的负载变为：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>load average: 0.36, 2.71, 3.68
</span></span></code></pre></div><p>1 分钟负载已经从 4 以上下降到 <code>0.36</code>。</p>
<p>5 分钟和 15 分钟负载仍然较高是正常的，因为它们反映的是一段时间内的平均值，会逐渐下降。</p>
<h3 id="5-停止并禁用-postgresql">5. 停止并禁用 PostgreSQL</h3>
<p>因为业务并不使用 PostgreSQL，所以先停止并禁止开机启动：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>sudo systemctl disable --now postgresql
</span></span></code></pre></div><p>确认服务状态：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>systemctl status postgresql --no-pager
</span></span></code></pre></div><p>确认端口不再监听：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>sudo ss -lntp | grep <span style="color:#e6db74">&#39;:5432&#39;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  <span style="color:#f92672">||</span> echo <span style="color:#e6db74">&#34;5432 端口未监听&#34;</span>
</span></span></code></pre></div><p>最终结果为：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>5432 端口未监听
</span></span></code></pre></div><h3 id="6-卸载-postgresql">6. 卸载 PostgreSQL</h3>
<p>确认业务没有受到影响后，卸载 PostgreSQL 相关软件包：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>sudo apt-get purge -y <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  postgresql <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  postgresql-14 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  postgresql-client-14 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  postgresql-client-common <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  postgresql-common <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  postgresql-contrib
</span></span></code></pre></div><p>需要注意：</p>

<blockquote>
  <p>卸载软件包不等于完成恶意程序清理。</p>

</blockquote>
<p>还应继续检查以下位置：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>/var/lib/postgresql
</span></span><span style="display:flex;"><span>/etc/postgresql
</span></span><span style="display:flex;"><span>/etc/postgresql-common
</span></span><span style="display:flex;"><span>/var/spool/cron
</span></span><span style="display:flex;"><span>/tmp
</span></span><span style="display:flex;"><span>/var/tmp
</span></span><span style="display:flex;"><span>/dev/shm
</span></span><span style="display:flex;"><span>/home/*/.config/systemd
</span></span></code></pre></div><p>本次发现的第二个恶意下载器，就是在卸载 PostgreSQL 后检查残留目录时找到的。</p>
<h3 id="7-隔离第二个恶意下载器">7. 隔离第二个恶意下载器</h3>
<p>将伪装成 systemd 清理任务的脚本移入证据目录：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>sudo mv <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  /var/lib/postgresql/.config/systemd/user/systemd-tmpfiles-cleanup/systemd-tmpfiles-cleanup-TNWow7.sh <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  /root/incident/systemd-tmpfiles-cleanup-TNWow7.sh
</span></span></code></pre></div><p>限制权限：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>sudo chown root:root <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  /root/incident/systemd-tmpfiles-cleanup-TNWow7.sh
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>sudo chmod <span style="color:#ae81ff">600</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  /root/incident/systemd-tmpfiles-cleanup-TNWow7.sh
</span></span></code></pre></div><p>然后搜索是否存在同类文件：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>sudo grep -RIlE <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#39;TNWow7|XGodM5jOyL|tor2socks|tor2web|relay\.tor2socks&#39;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  /etc <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  /var/spool/cron <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  /var/lib/postgresql <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  /tmp <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  /var/tmp <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  /dev/shm <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  /home <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  2&gt;/dev/null
</span></span></code></pre></div><h2 id="azure-网络安全配置整改">Azure 网络安全配置整改</h2>
<h3 id="1-不需要删除不存在的-5432-入站规则">1. 不需要删除不存在的 5432 入站规则</h3>
<p>Azure NSG 中原本没有允许公网访问 <code>5432</code> 的规则，所以不需要专门删除 PostgreSQL 入站规则。</p>
<p>停止并卸载 PostgreSQL 后，本机也已经不再监听 <code>5432</code>。</p>
<p>但是，不能因为 NSG 没有开放 <code>5432</code> 就认为服务器已经安全。</p>
<h3 id="2-收紧-ssh-入站范围">2. 收紧 SSH 入站范围</h3>
<p>排查过程中，SSH 日志中出现了多个公网 IP 对 <code>postgres</code> 用户进行密码尝试：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>Failed password for postgres from 172.83.83.194
</span></span><span style="display:flex;"><span>Failed password for postgres from 101.36.110.41
</span></span></code></pre></div><p>这些记录显示的是失败登录，不能说明对方已经通过 SSH 登录成功。但它们说明服务器的 SSH 正在被公网持续扫描。</p>
<p>原 Azure SSH 规则为：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>Source: Any
</span></span><span style="display:flex;"><span>Destination port: 22
</span></span><span style="display:flex;"><span>Action: Allow
</span></span></code></pre></div><p>建议改为：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>Source: 管理员固定公网 IP/32
</span></span><span style="display:flex;"><span>Destination port: 22
</span></span><span style="display:flex;"><span>Protocol: TCP
</span></span><span style="display:flex;"><span>Action: Allow
</span></span></code></pre></div><p>更安全的方式包括：</p>
<ul>
<li>Azure Bastion；</li>
<li>VPN；</li>
<li>Just-In-Time VM Access；</li>
<li>禁止 SSH 密码登录；</li>
<li>只使用 SSH Key；</li>
<li>禁止 root 远程登录；</li>
<li>配置失败登录告警。</li>
</ul>
<h3 id="3-限制服务器出站访问">3. 限制服务器出站访问</h3>
<p>恶意脚本需要连接外部地址下载程序，而当时 Azure NSG 允许任意出站访问。</p>
<p>如果业务条件允许，应限制服务器的出站范围：</p>
<ul>
<li>只允许必要的 DNS；</li>
<li>只允许必要的软件源；</li>
<li>只允许业务需要访问的 API；</li>
<li>使用 Azure Firewall 或集中式代理；</li>
<li>记录 DNS 和出站网络日志；</li>
<li>对 Tor、代理、矿池和异常域名建立告警。</li>
</ul>
<p>仅限制入站流量是不够的。恶意程序一旦进入服务器，通常还需要通过出站网络下载载荷、连接控制服务器或矿池。</p>
<h2 id="为什么建议使用-azure-托管-postgresql">为什么建议使用 Azure 托管 PostgreSQL？</h2>
<p>如果业务确实需要 PostgreSQL，建议优先使用：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>Azure Database for PostgreSQL
</span></span></code></pre></div><p>而不是直接在 Azure VM 上安装 PostgreSQL。</p>
<h3 id="1-减少操作系统层面的攻击面">1. 减少操作系统层面的攻击面</h3>
<p>在 VM 上自建 PostgreSQL，需要自行维护：</p>
<ul>
<li>Linux 操作系统；</li>
<li>PostgreSQL 软件包；</li>
<li><code>postgres</code> 系统用户；</li>
<li>SSH；</li>
<li>cron；</li>
<li>systemd；</li>
<li>数据目录权限；</li>
<li>安全补丁；</li>
<li>版本升级；</li>
<li>网络防火墙；</li>
<li>日志；</li>
<li>备份；</li>
<li>高可用；</li>
<li>恶意进程检测。</li>
</ul>
<p>使用 Azure 托管 PostgreSQL 后，用户不会直接管理数据库所在主机的 root 权限，也不会在数据库主机上创建 cron、systemd 服务或运行 <code>/dev/shm</code> 中的程序。</p>
<p>这可以显著减少本次事件中出现的主机级风险。</p>
<h3 id="2-使用私有网络关闭公网访问">2. 使用私有网络，关闭公网访问</h3>
<p>建议采用以下网络架构：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>Azure VM / App Service / AKS
</span></span><span style="display:flex;"><span>          |
</span></span><span style="display:flex;"><span>      Private Network
</span></span><span style="display:flex;"><span>          |
</span></span><span style="display:flex;"><span>Azure Database for PostgreSQL
</span></span></code></pre></div><p>数据库应配置为：</p>
<ul>
<li>禁止公共网络访问；</li>
<li>使用 Private Endpoint 或私有网络集成；</li>
<li>只允许应用所在子网访问；</li>
<li>不向公网开放数据库端口；</li>
<li>数据库管理也通过 VPN、Bastion 或受控网络完成。</li>
</ul>
<h3 id="3-自动备份和维护">3. 自动备份和维护</h3>
<p>Azure 托管 PostgreSQL 可以提供：</p>
<ul>
<li>自动备份；</li>
<li>时间点恢复；</li>
<li>高可用选项；</li>
<li>数据加密；</li>
<li>监控指标；</li>
<li>日志与告警；</li>
<li>平台维护；</li>
<li>安全更新；</li>
<li>版本支持。</li>
</ul>
<p>这些能力可以降低因长期无人维护而产生的风险。</p>
<h3 id="4-责任边界更清晰">4. 责任边界更清晰</h3>
<p>自建 PostgreSQL 经常出现这种情况：</p>

<blockquote>
  <p>为了测试临时安装，后来业务不用了，但服务、用户和目录一直留在服务器上。</p>

</blockquote>
<p>托管数据库的资源边界更加清晰：</p>
<ul>
<li>需要时创建；</li>
<li>通过私有网络连接；</li>
<li>不再使用时导出数据；</li>
<li>删除数据库实例；</li>
<li>删除访问权限和私有连接；</li>
<li>不会在业务 VM 上留下数据库系统用户和历史脚本。</li>
</ul>
<h3 id="5-托管服务并不等于绝对安全">5. 托管服务并不等于绝对安全</h3>
<p>Azure 托管 PostgreSQL 可以减少操作系统和基础设施维护风险，但仍需要正确管理：</p>
<ul>
<li>数据库账号；</li>
<li>强密码或身份认证；</li>
<li>最小权限；</li>
<li>网络访问范围；</li>
<li>SQL 注入；</li>
<li>连接字符串；</li>
<li>Azure Key Vault；</li>
<li>日志审计；</li>
<li>数据加密；</li>
<li>应用依赖；</li>
<li>凭据轮换。</li>
</ul>
<p>正确的理解是：</p>

<blockquote>
  <p>Azure 托管 PostgreSQL 缩小了需要自己负责的安全范围，但不会消除应用和账号层面的安全责任。</p>

</blockquote>
<h2 id="本次事件的-ioc">本次事件的 IOC</h2>
<p>以下指标可用于内部服务器排查。</p>
<h3 id="可疑进程及文件名">可疑进程及文件名</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>dzZ7yVrxv7
</span></span><span style="display:flex;"><span>gJppJl6TbG
</span></span><span style="display:flex;"><span>.dbus-XGodM5jOyL
</span></span><span style="display:flex;"><span>systemd-tmpfiles-cleanup-TNWow7.sh
</span></span></code></pre></div><h3 id="sha-256">SHA-256</h3>
<p>恶意 ELF 程序：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>d32f9eef63c496a146a6a811677dd27e337db993d6f1a4b1af145c3135d391b3
</span></span></code></pre></div><p>恶意 cron 启动脚本：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>36b1dcf43876f55aae2b8be7dada848b41f60ec61cd4ca7ab2a1278193f286f0
</span></span></code></pre></div><p>恶意下载器：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>9962de5e0c68ea6e85879c5f62f2d0806360dbe186dc15e1d59abf8e4209de11
</span></span></code></pre></div><h3 id="行为特征">行为特征</h3>
<ul>
<li>以 <code>postgres</code> 用户运行；</li>
<li>从 <code>/dev/shm</code> 执行随机名称文件；</li>
<li>启动后删除原文件；</li>
<li>利用用户 crontab 定时启动；</li>
<li>Base64 解码后直接交给 Bash 执行；</li>
<li>使用 Tor、SOCKS5 或 Tor2web；</li>
<li>收集公网 IP、用户名、主机名和机器 ID；</li>
<li>从外部下载其他程序；</li>
<li>在 <code>/tmp</code>、<code>/var/tmp</code> 和 <code>/dev/shm</code> 中执行程序；</li>
<li>持续占用全部 CPU；</li>
<li>伪装成 D-Bus 或 systemd 组件。</li>
</ul>
<h2 id="最终总结">最终总结</h2>
<p>本次事件从表面上看，是一次服务器 CPU 占用过高的问题。</p>
<p>但深入排查后发现，真正的问题包括：</p>
<ul>
<li>以 <code>postgres</code> 用户运行的恶意程序；</li>
<li>位于 <code>/dev/shm</code> 的随机 ELF 文件；</li>
<li><code>postgres</code> 用户的恶意 cron；</li>
<li><code>/var/lib/postgresql</code> 下的隐藏启动脚本；</li>
<li>伪装成 systemd 组件的恶意下载器；</li>
<li>可连接 Tor 和外部下载地址的恶意逻辑。</li>
</ul>
<p>最值得关注的是：</p>

<blockquote>
  <p>这台服务器没有通过 Azure NSG 向公网开放 PostgreSQL 的 5432 端口，而且业务也没有使用 PostgreSQL，但与 PostgreSQL 有关的系统用户、主目录、cron 和残留文件仍然构成了安全风险。</p>

</blockquote>
<p>当然，现有证据并不能证明初始入侵入口就是 PostgreSQL。恶意程序使用 <code>postgres</code> 用户运行，不等于攻击者一定通过 PostgreSQL 端口进入服务器。</p>
<p>但这次事件依然说明了几个重要原则。</p>
<h3 id="第一不使用的服务应立即停止和卸载">第一，不使用的服务应立即停止和卸载</h3>
<p>不要因为“没有向公网开放”就长期保留闲置服务。</p>
<p>不使用的软件应当：</p>
<ul>
<li>停止服务；</li>
<li>禁止开机启动；</li>
<li>卸载软件包；</li>
<li>检查系统用户；</li>
<li>检查 cron；</li>
<li>检查 systemd；</li>
<li>检查残留目录和脚本。</li>
</ul>
<h3 id="第二云安全组不是唯一安全边界">第二，云安全组不是唯一安全边界</h3>
<p>NSG 没有开放 <code>5432</code>，只能说明公网无法通过该规则直接连接数据库，不能防止：</p>
<ul>
<li>内网访问；</li>
<li>本机访问；</li>
<li>容器访问；</li>
<li>历史持久化；</li>
<li>用户级 cron；</li>
<li>本机恶意程序；</li>
<li>SSH 和应用漏洞。</li>
</ul>
<h3 id="第三生产-postgresql-优先使用-azure-托管服务">第三，生产 PostgreSQL 优先使用 Azure 托管服务</h3>
<p>如果没有必须自建数据库服务器的明确需求，建议使用：</p>

<blockquote>
  <p><strong>Azure Database for PostgreSQL + 私有网络 + 禁止公共访问。</strong></p>

</blockquote>
<p>这样可以减少数据库主机操作系统、SSH、系统用户、cron、systemd、补丁、备份和高可用等方面的维护风险。</p>
<h3 id="第四杀掉恶意进程不等于完成安全恢复">第四，杀掉恶意进程不等于完成安全恢复</h3>
<p>结束高 CPU 进程只解决了当前资源占用问题，还应继续：</p>
<ul>
<li>查找持久化；</li>
<li>调查入侵入口；</li>
<li>检查 SSH 成功登录记录；</li>
<li>检查 Web 应用和容器；</li>
<li>轮换服务器上的全部凭据；</li>
<li>检查同一 VNet 中的其他主机；</li>
<li>保存日志和磁盘快照；</li>
<li>必要时从可信镜像重建服务器。</li>
</ul>
<p>最终可以将这次事件概括为一句话：</p>

<blockquote>
  <p><strong>不使用的数据库不要留在服务器上；需要 PostgreSQL 时，优先使用 Azure 托管 PostgreSQL，并通过私有网络访问。</strong></p>

</blockquote>
]]></content:encoded></item><item><title>Clawdbot：让您在 Azure 上拥有一个强大的 AI 全能助手</title><link>https://wilsonwu.me/zh/blog/2026/clawdbot-on-azure/</link><pubDate>Sun, 25 Jan 2026 00:00:00 -0500</pubDate><guid>https://wilsonwu.me/zh/blog/2026/clawdbot-on-azure/</guid><description>&lt;p&gt;&lt;img alt="Clawdbot" loading="lazy" src="https://wilsonwu.me/zh/blog/2026/clawdbot-on-azure/1-clawdbot.png"&gt;&lt;/p&gt;
&lt;p&gt;随着生成式 AI 与智能代理的快速发展，自托管的智能系统正变得越来越受开发者和技术团队关注。&lt;strong&gt;Clawdbot&lt;/strong&gt; 就是这样一款开源、可自托管的个人 AI 助手，它不仅能与用户进行对话，还能执行任务、集成消息平台、提供自动化能力，并可部署在云平台上如 Azure。本文将逐步介绍 Clawdbot 的能力、架构以及如何在 &lt;strong&gt;Microsoft Azure&lt;/strong&gt; 上部署并开始使用它。&lt;/p&gt;</description><content:encoded><![CDATA[<p><img alt="Clawdbot" loading="lazy" src="/zh/blog/2026/clawdbot-on-azure/1-clawdbot.png"></p>
<p>随着生成式 AI 与智能代理的快速发展，自托管的智能系统正变得越来越受开发者和技术团队关注。<strong>Clawdbot</strong> 就是这样一款开源、可自托管的个人 AI 助手，它不仅能与用户进行对话，还能执行任务、集成消息平台、提供自动化能力，并可部署在云平台上如 Azure。本文将逐步介绍 Clawdbot 的能力、架构以及如何在 <strong>Microsoft Azure</strong> 上部署并开始使用它。</p>
<h2 id="什么是-clawdbot">什么是 Clawdbot</h2>
<p><strong>Clawdbot</strong> 是一个开源的个人 AI 助手项目，本质上是一个智能代理框架，它可以：</p>
<ul>
<li>与多种消息平台进行交互，包括 WhatsApp、Telegram、Discord、Slack、Microsoft Teams 等。</li>
<li>承担智能对话、任务执行、自动化工作流的角色，而非单纯聊天机器人。</li>
<li>支持在本地或自托管服务器上运行，数据默认保存在本地，增强隐私保护。</li>
<li>通过插件/技能扩展，实现日程提醒、文件管理、邮件处理、代码生成等功能。</li>
</ul>
<p>Clawdbot 的核心由 <strong>Gateway、Providers（平台接入）、Agents/Skills（智能任务执行单元）</strong>构成。Gateway 负责消息总线、Providers 负责与各消息平台对接，Agents/Skills 则实际执行用户请求。</p>
<h2 id="clawdbot-的主要特性">Clawdbot 的主要特性</h2>
<ol>
<li>
<p><strong>多平台消息接入</strong>
Clawdbot 支持通过连接聊天平台 API，接收来自 WhatsApp、Telegram、Discord、Signal、Slack、Microsoft Teams 等消息，并将这些消息传递给 AI 模型进行处理。</p>
</li>
<li>
<p><strong>隐私和数据控制</strong>
与云端托管的聊天 AI 不同，Clawdbot 可以运行在你自己的服务器或 VPS 上，所有对话历史、配置、凭据都可以按需存储和访问。</p>
</li>
<li>
<p><strong>自动化执行能力</strong>
除了对话，Clawdbot 还能执行具体任务，包括浏览器控制、文件操作、数据处理、发送提醒等，这使其成为一个更为复杂的个人助手平台。</p>
</li>
<li>
<p><strong>可扩展的模型支持</strong>
Clawdbot 支持多种 AI 模型提供者（如 OpenAI GPT、Anthropic Claude，甚至本地 LLM 服务如 Ollama）。</p>
</li>
</ol>
<h2 id="在-azure-vm-部署-clawdbot">在 Azure VM 部署 Clawdbot</h2>
<p>虽然 Clawdbot 支持本地运行或容器平台，但在以下场景中，<strong>Azure VM 是非常合适的部署形态</strong>：</p>
<ol>
<li>
<p><strong>完全控制运行环境</strong></p>
<ul>
<li>自由选择 OS（Ubuntu / Debian）</li>
<li>自由安装 Node.js、Docker、GPU 驱动等</li>
</ul>
</li>
<li>
<p><strong>数据与隐私可控</strong></p>
<ul>
<li>所有对话记录、日志、配置文件仅存在于 VM 内</li>
<li>不依赖第三方 PaaS</li>
</ul>
</li>
<li>
<p><strong>适合长期运行的 Agent</strong></p>
<ul>
<li>7×24 小时在线</li>
<li>可通过 systemd 或 Docker 保证自动重启</li>
</ul>
</li>
<li>
<p><strong>可扩展性良好</strong></p>
<ul>
<li>可横向扩容（多 VM + 负载均衡）</li>
<li>可纵向扩容（更大规格 CPU / GPU）</li>
</ul>
</li>
</ol>
<h2 id="整体部署架构概览">整体部署架构概览</h2>
<p>在 Azure VM 上运行 Clawdbot 的典型架构如下：</p>
<p><img alt="架构" loading="lazy" src="/zh/blog/2026/clawdbot-on-azure/2-architecture.png"></p>
<h2 id="创建-azure-vm">创建 Azure VM</h2>
<h3 id="vm-基础配置建议">VM 基础配置建议</h3>
<ul>
<li><strong>OS</strong>：Ubuntu 22.04 LTS</li>
<li><strong>规格</strong>：
<ul>
<li>轻量使用：Standard B2s / B4ms</li>
<li>多 Agent：D4s_v5 及以上</li>
</ul>
</li>
<li><strong>磁盘</strong>：至少 64 GB</li>
<li><strong>网络</strong>：
<ul>
<li>入站：22（SSH）</li>
<li>入站：3000（或自定义 Clawdbot 端口）</li>
</ul>
</li>
</ul>
<h3 id="登录-vm">登录 VM</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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 azureuser@&lt;VM_PUBLIC_IP&gt;
</span></span></code></pre></div><h2 id="安装运行时环境">安装运行时环境</h2>
<h3 id="系统依赖">系统依赖</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>sudo apt update <span style="color:#f92672">&amp;&amp;</span> sudo apt upgrade -y
</span></span><span style="display:flex;"><span>sudo apt install -y git curl build-essential
</span></span></code></pre></div><h3 id="安装-nodejs推荐-20-lts">安装 Node.js（推荐 20 LTS）</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
</span></span><span style="display:flex;"><span>sudo apt install -y nodejs
</span></span><span style="display:flex;"><span>node -v
</span></span><span style="display:flex;"><span>npm -v
</span></span></code></pre></div><h2 id="安装-clawdbot">安装 Clawdbot</h2>
<h3 id="全局-cli--本地运行目录">全局 CLI + 本地运行目录</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>sudo npm install -g clawdbot
</span></span></code></pre></div><p>创建运行目录：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>mkdir ~/clawdbot
</span></span><span style="display:flex;"><span>cd ~/clawdbot
</span></span></code></pre></div><p>初始化配置：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>clawdbot onboard
</span></span></code></pre></div><p>跟着步骤完成各类配置即可：</p>
<p><img alt="配置" loading="lazy" src="/zh/blog/2026/clawdbot-on-azure/3-onboard.png"></p>
<h2 id="启动-clawdbot">启动 Clawdbot</h2>
<h3 id="手动启动验证">手动启动验证</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>clawdbot gateway
</span></span></code></pre></div><p>确认日志中出现：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>🦞 Clawdbot 2026.1.24-3 (885167d) — I speak fluent bash, mild sarcasm, and aggressive tab-completion energy.
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>16:18:27 [heartbeat] started
</span></span><span style="display:flex;"><span>16:18:27 [gateway] agent model: openai-codex/gpt-5.1
</span></span><span style="display:flex;"><span>16:18:27 [gateway] listening on ws://127.0.0.1:18789 (PID 36888)
</span></span><span style="display:flex;"><span>16:18:27 [gateway] listening on ws://[::1]:18789
</span></span><span style="display:flex;"><span>16:18:27 [gateway] log file: \tmp\clawdbot\clawdbot-2026-01-27.log
</span></span><span style="display:flex;"><span>16:18:27 [browser/server] Browser control listening on http://127.0.0.1:18791/
</span></span><span style="display:flex;"><span>...
</span></span></code></pre></div><p>通过浏览器打开：<a href="http://127.0.0.1:18789/">http://127.0.0.1:18789/</a></p>
<p><img alt="界面" loading="lazy" src="/zh/blog/2026/clawdbot-on-azure/4-interface.png"></p>
<p>即可开始使用！</p>
<h2 id="结语">结语</h2>
<p>Clawdbot 是一款功能强大的开源 AI 个人助手，可将 AI 交互扩展到多种消息平台，并集成丰富的执行能力。通过将 Clawdbot 部署到 Azure，你可以将它打造成一个企业级、自托管的 AI 服务，提高可用性、安全性和可扩展性。上述步骤覆盖了从创建 Azure Bot 到完整部署 Clawdbot 的全流程，希望能帮助你快速搭建并运行自己的智能助理。</p>
]]></content:encoded></item><item><title>微软 TRELLIS 大语言模型：面向生产级 3D 资产生成的大模型及基于 Azure 部署使用指南</title><link>https://wilsonwu.me/zh/blog/2026/llm-microsoft-trellis-3d/</link><pubDate>Mon, 19 Jan 2026 00:00:00 -0500</pubDate><guid>https://wilsonwu.me/zh/blog/2026/llm-microsoft-trellis-3d/</guid><description>&lt;p&gt;&lt;img alt="Trellis-3D" loading="lazy" src="https://wilsonwu.me/zh/blog/2026/llm-microsoft-trellis-3d/1-trellis-3d.png"&gt;&lt;/p&gt;
&lt;p&gt;2025 年末，微软研究院发布了一个面向 3D 内容创作的开源大模型项目 &lt;strong&gt;TRELLIS&lt;/strong&gt;，并伴随学术论文&lt;a href="https://arxiv.org/abs/2412.01506"&gt;《Structured 3D Latents for Scalable and Versatile 3D Generation》&lt;/a&gt;。该项目通过统一的结构化潜在空间与先进的流模型技术，显著提升了文本/图像到 3D 资产生成的质量与灵活性，同时拓展了 3D 模型的多格式输出与编辑能力，成为当前 3D AI 模型生态中的重要技术之一。&lt;/p&gt;</description><content:encoded><![CDATA[<p><img alt="Trellis-3D" loading="lazy" src="/zh/blog/2026/llm-microsoft-trellis-3d/1-trellis-3d.png"></p>
<p>2025 年末，微软研究院发布了一个面向 3D 内容创作的开源大模型项目 <strong>TRELLIS</strong>，并伴随学术论文<a href="https://arxiv.org/abs/2412.01506">《Structured 3D Latents for Scalable and Versatile 3D Generation》</a>。该项目通过统一的结构化潜在空间与先进的流模型技术，显著提升了文本/图像到 3D 资产生成的质量与灵活性，同时拓展了 3D 模型的多格式输出与编辑能力，成为当前 3D AI 模型生态中的重要技术之一。</p>
<p>官方 Github 仓库地址：<a href="https://github.com/microsoft/TRELLIS">https://github.com/microsoft/TRELLIS</a></p>
<h2 id="trellis-是什么核心概念与架构">TRELLIS 是什么？——核心概念与架构</h2>
<p>TRELLIS 是微软构建的<strong>大型 3D 资产生成模型</strong>，支持输入文本提示或图像，并输出高质量的三维模型资产。其技术创新点主要包括：</p>
<ul>
<li><strong>统一结构化潜在表示（SLAT）</strong>：将三维信息编码为一种可扩展的结构化潜在空间表示，使模型能够以统一方式理解和生成不同表现形式的 3D 结果。</li>
<li><strong>整流流（Rectified Flow Transformers）</strong>：专为 SLAT 设计的生成骨干网络，通过适应 sparse 表示提升生成效率与质量。</li>
<li><strong>大规模训练与预训练模型</strong>：模型规模最高达<strong>约 20 亿参数</strong>，并在包含 <strong>50 万多样化三维资产的数据集</strong>上训练，具备强泛化能力。</li>
</ul>
<p>TRELLIS 不仅能生成三维几何，还能捕获复杂纹理与外观信息，使得生成的资产更贴近真实世界中可用的三维内容。</p>
<h2 id="主要功能特性">主要功能特性</h2>
<p>以下是 TRELLIS 的核心功能模块及技术亮点：</p>
<h3 id="多模态输入能力">多模态输入能力</h3>
<p>TRELLIS 支持以下输入条件：</p>
<ul>
<li><strong>文本提示（Text-to-3D）</strong>：通过自然语言描述生成三维资产（提示须清晰准确）。</li>
<li><strong>图像条件（Image-to-3D）</strong>：根据一张或多张图片生成对应三维模型。</li>
</ul>
<p>这种多模态输入支持，使 TRELLIS 适用于从概念设计到真实原型的全流程创作。</p>
<p><img alt="3D" loading="lazy" src="/zh/blog/2026/llm-microsoft-trellis-3d/2-to-3d.png"></p>
<h3 id="灵活的输出格式">灵活的输出格式</h3>
<p>根据下游需求，TRELLIS 输出包括：</p>
<ul>
<li><strong>辐射场（NeRF/Radiance Fields）</strong>：适合渲染与展示</li>
<li><strong>3D 高斯表示</strong>：一种可渲染密度表示</li>
<li><strong>传统网格 Mesh</strong>：可导出为标准 3D 文件格式（例如 GLB/OBJ）用于游戏、AR/VR 等应用</li>
</ul>
<p>这种格式灵活性是模型在实际生产环境中落地的关键能力。</p>
<h3 id="本地三维编辑与变体生成">本地三维编辑与变体生成</h3>
<p>与早期单一生成模型不同，TRELLIS 能：</p>
<ul>
<li><strong>局部编辑</strong>：在已有模型上进行细部修改</li>
<li><strong>生成变体</strong>：在保持基本结构不变的前提下，创建风格、细节不同的多版本输出</li>
</ul>
<p>这种能力特别适合迭代式 3D 内容设计。</p>
<h3 id="支持大规模数据集训练与可扩展性">支持大规模数据集训练与可扩展性</h3>
<p>TRELLIS 在包含多种来源（如 Objaverse、ABO 等）的大规模 3D 数据集上进行训练，使其具备：</p>
<ul>
<li><strong>强泛化能力</strong></li>
<li><strong>更丰富的几何与纹理表现</strong></li>
<li><strong>多任务适配能力</strong></li>
</ul>
<p>这样的数据基础是其较竞品更具优势的根本原因之一。</p>
<h2 id="与其他竞品对比优势与提升点">与其他竞品对比：优势与提升点</h2>
<p>目前市场上其他比较知名的 3D 生成模型（例如 <strong>OpenAI Shap-E</strong>、或研究型模型如 MeshGen 等）多存在以下局限：</p>
<ul>
<li>有的只能输出单一 3D 表示（如内隐函数或点云）</li>
<li>多数不支持本地编辑与多种格式输出</li>
<li>在复杂纹理、细节表现上效果有限</li>
</ul>
<p>相比之下：</p>
<h3 id="trellis-的主要优势包括">TRELLIS 的主要优势包括</h3>
<ol>
<li><strong>结构化潜在表示（SLAT）框架</strong>：支持统一的生成与解码机制，同时兼顾几何与纹理细节。</li>
<li><strong>多格式生成输出</strong>：与只生成单种数据结构的竞品相比更通用。</li>
<li><strong>本地编辑与变体生成</strong>：提升了设计迭代效率。</li>
<li><strong>大规模预训练与更丰富数据集支撑</strong>：提升了模型对稀有或复杂对象的生成能力。</li>
</ol>
<p>总的来说，TRELLIS 更偏向工业级 3D 生成工具，而不只是科研展示或单一生成模型。</p>
<h2 id="部署和使用">部署和使用</h2>
<p>目前，微软尚未在 <strong>Azure AI Foundry / Microsoft Foundry</strong> 中以 Serverless 或托管模型的形式直接提供 TRELLIS。因此，在 Azure 云环境中使用 TRELLIS 的<strong>最可行、也是最稳定的方式</strong>，是基于 <strong>Azure GPU 虚拟机（VM）</strong>进行自托管部署。</p>
<p>这种方式具备以下优势：</p>
<ul>
<li>完全控制模型版本与推理流程</li>
<li>支持大显存 GPU，适合 3D 模型生成场景</li>
<li>可与企业现有 Azure 网络、安全、存储体系无缝集成</li>
</ul>
<h3 id="1-azure-gpu-vm-规格选型建议">1. Azure GPU VM 规格选型建议</h3>
<p>TRELLIS 属于<strong>显存与算力敏感型 3D 生成模型</strong>，对 GPU 资源要求较高，推荐如下规格：</p>
<h4 id="推荐-vm-系列">推荐 VM 系列</h4>
<ul>
<li><strong>Standard_NC A100 v4</strong>（A100 80GB，首选）</li>
<li><strong>Standard_NC A100 v5</strong>（A100 80GB，新一代）</li>
<li><strong>Standard_ND H100 v5</strong>（H100，若需要更高吞吐）</li>
</ul>

<blockquote>
  <p>实践建议：</p>
<ul>
<li><strong>推理 / Demo</strong>：单卡 A100 80GB 即可</li>
<li><strong>高分辨率 / 批量生成 / 编辑任务</strong>：建议单机多卡或更高规格</li>
</ul>

</blockquote>
<h3 id="2-操作系统与基础环境">2. 操作系统与基础环境</h3>
<p>推荐环境组合如下：</p>
<ul>
<li>OS：Ubuntu 20.04 / 22.04 LTS</li>
<li>CUDA：与 VM 驱动匹配的官方版本</li>
<li>Python：3.8 或以上</li>
<li>PyTorch：官方 CUDA 版本</li>
</ul>
<p>Azure Marketplace 中可直接选择 <strong>CUDA + NVIDIA Driver 预装镜像</strong>，可显著降低环境准备成本。</p>
<h3 id="3-trellis-安装与依赖配置">3. TRELLIS 安装与依赖配置</h3>
<h4 id="拉取代码">拉取代码</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>git clone --recurse-submodules https://github.com/microsoft/TRELLIS.git
</span></span><span style="display:flex;"><span>cd TRELLIS
</span></span></code></pre></div><h4 id="创建独立-python-环境并安装依赖">创建独立 Python 环境并安装依赖</h4>
<p>官方已提供自动化脚本：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>./setup.sh --new-env --basic
</span></span></code></pre></div><p>如需支持训练、编辑或高级功能，可根据官方说明启用对应参数。</p>
<h3 id="4-基于-azure-vm-的推理运行方式">4. 基于 Azure VM 的推理运行方式</h3>
<h4 id="方式一命令行推理适合批量任务">方式一：命令行推理（适合批量任务）</h4>
<p>文本或图像驱动生成 3D 模型：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>python example_text.py <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  --prompt <span style="color:#e6db74">&#34;a futuristic electric motorcycle&#34;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  --output_dir outputs/
</span></span></code></pre></div><p>或多图像条件输入：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>python example_multi_image.py <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  --input_images images/ <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>  --output_dir outputs/
</span></span></code></pre></div><p>生成结果可直接导出为 Mesh / Radiance Field 等格式，用于后续渲染或工具链集成。</p>
<h4 id="方式二gradio-web-ui推荐用于演示与验证">方式二：Gradio Web UI（推荐用于演示与验证）</h4>
<p>TRELLIS 官方提供基于 Gradio 的 Web Demo，适合：</p>
<ul>
<li>团队内部体验</li>
<li>产品验证</li>
<li>算法效果对比</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-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>python app.py
</span></span></code></pre></div><p>然后通过 Azure VM 公网 IP 或内网负载均衡访问 Web UI。</p>
<h3 id="5-azure-上的工程化部署建议">5. Azure 上的工程化部署建议</h3>
<p>为了更好地在 Azure 上长期运行 TRELLIS，建议采用以下架构实践：</p>
<h4 id="存储解耦">存储解耦</h4>
<ul>
<li>模型权重：Azure Blob Storage</li>
<li>生成结果：Blob / Azure Files</li>
<li>日志与元数据：Azure Monitor / Log Analytics</li>
</ul>
<h4 id="服务化封装">服务化封装</h4>
<ul>
<li>使用 <strong>FastAPI / Flask</strong> 将 TRELLIS 推理封装为 HTTP API</li>
<li>GPU VM 作为推理节点</li>
<li>前端或工作流通过 REST 调用</li>
</ul>
<h4 id="安全与网络">安全与网络</h4>
<ul>
<li>VM 置于 VNet 内</li>
<li>Web UI / API 通过 Application Gateway 或内部访问</li>
<li>支持私有部署，满足企业安全要求</li>
</ul>
<h2 id="总结与展望">总结与展望</h2>
<p>微软 TRELLIS 代表了当前 3D AI 生成技术的重要进步，它在生成质量、格式多样性与本地编辑能力方面具备显著优势。对于希望自动化 3D 内容创建的开发者与设计者而言，它是一个有实力的开源方案。部署方面，虽然目前官方尚未将其作为 Foundry 的 serverless 模型，但可通过 Azure 的常规模型部署管道与 GPU 计算能力在云端高效运行 TRELLIS 推理与自定义训练。</p>
]]></content:encoded></item></channel></rss>