<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>难逃月色在人间</title>
  
  
  <link href="https://ruams.com/atom.xml" rel="self"/>
  
  <link href="https://ruams.com/"/>
  <updated>2025-08-13T09:38:00.000Z</updated>
  <id>https://ruams.com/</id>
  
  <author>
    <name>ruams</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>使用 acme.sh 自动续签谷歌 SSL 证书</title>
    <link href="https://ruams.com/posts/fc6d13cd.html"/>
    <id>https://ruams.com/posts/fc6d13cd.html</id>
    <published>2025-03-21T03:03:00.000Z</published>
    <updated>2025-08-13T09:38:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>本文主要讲述 <code>acme.sh(SSL 证书自动续签工具)</code> + <code>cloudflare(免费域名托管服务商)</code> + <code>Google Trust Services(SSL 证书颁发机构)</code> 如何使用的标准流程。</p><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>以前免费 SSL 证书时效一年的时候，一年折腾一次没啥感觉。现在所有的免费 SSL 证书时效都变成了 3 个月，折腾次数变多了之后就愈发感到困扰，所以拥有一个自动续期证书的工具就显得很重要了。这类工具常用的有 ohttps、acme.sh、certbot、Traefik（k8s 使用）。<br><a href="https://ohttps.com/">OHTTPS</a> 是我最早开始使用 SSL 证书的服务商，能够免费续迁证书，也可以 docker 部署自动更新，但是自动更新部署需要类似积分的东西，不充钱估计自动更新部署功能应该用不到两年。<br><a href="https://github.com/acmesh-official">acme.sh</a> 顾名思义是一个使用 ACME 协议的客户端工具，由 shell 编写，支持Let’s Encrypt、zerossl、google、sslcom、buypass 等常见 CA 签发机构、支持自动更新部署、支持多个 DNS 服务商的 API，应该是当前使用的最多的免费 SSL 证书签发与续签工具。<br><a href="https://certbot.eff.org/">Certbot</a> 是官方（EFF电子前哨基金会）推荐的客户端，同样是一个使用 ACME 协议的经典客户端工具。它由 python 编写，主要是支持 Let’s Encrypt 的签发、自动续签。对于常见的 DNS 服务商 API 也提供支持，同时它还可以自动识别并修改 Nginx 或 Apache 的配置文件来安装证书。<br><a href="https://github.com/traefik/traefik">Traefik</a> 是一个非常流行的云原生边缘路由器或反向代理，它深度集成了 Let’s Encrypt，可以为 k8s 集群中的后端服务提供证书自动申请和管理功能。<br>本文主要是想写在 vps 上如何部署自动申请、续签 SSL 证书的工具，Traefik 由于主要是在 k8s 里面使用这个后面发文再说。长期免费续签 SSL 证书的工具就只有 acme.sh 和 Certbot 了。为什么我最终选择了 acme.sh 呢？原因主要是有三点：</p><ul><li>acme.sh 支持更多的 CA 机构</li><li>acme.sh 支持更多的 DNS API，由于通配符证书必须要使用 DNS 验证，这一点很重要</li><li>acme.sh 完全由 shell 编写，不需要额外安装依赖</li></ul><p>以上是背景，下面开始部署教程。</p><h2 id="1-安装-acme-sh"><a href="#1-安装-acme-sh" class="headerlink" title="1. 安装 acme.sh"></a>1. 安装 acme.sh</h2><p>命令中的 <code>my@exampmle.com</code> 换成自己的邮箱</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl https://get.acme.sh | sh -s email=my@example.com</span><br></pre></td></tr></table></figure><p>这一条命令执行完成，会在你当前的家目录下面新建一个 <code>.acme.sh</code> 文件夹,后面生成的证书以及所有与之相关的文件都会在这个文件夹里面。同时还会新建一个定时任务每天0点自动检测证书有效期，如果临近过期则会自动发起更新证书的操作。</p><p>执行这条命令加载环境变量，让 acme.sh 命令能被识别</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">source</span> ~/.bashrc</span><br></pre></td></tr></table></figure><h2 id="2-DNS-记录验证准备"><a href="#2-DNS-记录验证准备" class="headerlink" title="2. DNS 记录验证准备"></a>2. DNS 记录验证准备</h2><p>本文指的 SSL 证书都是为域名颁发的，因为现在为 ipv4 颁发 SSL 证书也是很常见的一个操作，所以这里提一下以免混淆！对于域名的 SSL 证书在颁发之前必须要做域名所有权验证，常见的验证方式有两种。</p><ul><li>为需要颁发证书的域名添加指定的 DNS 记录进行验证</li><li>在需要颁发证书的域名网站的根目录，添加指定文件进行验证<br>我这里因为是要给通配符域名颁发证书，所以只能选择使用 DNS 记录进行验证，我的域名免费托管在 cloudflare 上而 acme.sh 可以调用 cloudflare 的 api 进行自动化部署，下面说明如何操作。</li></ul><h3 id="2-1-创建-APi-令牌"><a href="#2-1-创建-APi-令牌" class="headerlink" title="2.1 创建 APi 令牌"></a>2.1 创建 APi 令牌</h3><p>登录 cloudflare 后选择需要操作的域名，假如我有一个域是 <code>hello.com</code>，后面都以这个域名来描述。<br><img src= "/img/load.gif" data-lazy-src="https://image.ruams.com/%E8%87%AA%E5%8A%A8%E7%BB%AD%E7%AD%BE-1.webp" style="zoom:50%" /><br>选择域之后，往下看到右边的 API 标题那里记录自己的 <code>区域ID</code>，然后点击 <code>获取您的API令牌</code> ,然后再点击 <code>创建令牌</code> 并选择 <code>编辑区域 DNS </code><br><img src= "/img/load.gif" data-lazy-src="https://image.ruams.com/%E8%87%AA%E5%8A%A8%E7%BB%AD%E7%AD%BE-2.webp" style="zoom:50%" /><br>给区域编辑权限，<code>特定区域</code> 选择 <code>hello.com</code>, 同时建议为令牌的使用指定来源 ip 以加强安全<br><img src= "/img/load.gif" data-lazy-src="https://image.ruams.com/%E8%87%AA%E5%8A%A8%E7%BB%AD%E7%AD%BE-3.webp" style="zoom:50%" /><br>令牌创建完成后会获得一个 key 并记录下来。</p><h3 id="2-2-添加环境变量"><a href="#2-2-添加环境变量" class="headerlink" title="2.2 添加环境变量"></a>2.2 添加环境变量</h3><p>下面的命令中 <code>CF_Token </code> 换成你完成创建令牌后获得的 key，<code>CF_Zone_ID</code> 后面的内容换成你的 <code>区域 ID</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">export</span> CF_Token=<span class="string">&quot;xxxxxxxxxxxxxxxxxxxxxxxxx&quot;</span></span><br><span class="line"><span class="built_in">export</span> CF_Zone_ID=<span class="string">&quot;xxxxxxxxxxxxxxxxxxxxxxx&quot;</span></span><br></pre></td></tr></table></figure><p>这一步是为后面 acme.sh 调用 API 添加 DNS 记录进行验证做准备的，在使用 acme.sh 第一次申请证书的时候会被 acme.sh 自动保存，后面证书过期自动续签的时候就不需要人为干预了。</p><h2 id="3-Google-API-权限配置"><a href="#3-Google-API-权限配置" class="headerlink" title="3.Google API 权限配置"></a>3.Google API 权限配置</h2><p>acme.sh 本身是支持多个 CA 机构的，默认使用的是 zerossl，其次用的最多的是 let’s encrypt，不过我一般偏好 google（无他，个人偏好）</p><h3 id="3-1登录-GCP-控制台"><a href="#3-1登录-GCP-控制台" class="headerlink" title="3.1登录 GCP 控制台"></a>3.1登录 GCP 控制台</h3><p><a href="https://console.cloud.google.com/apis/library/publicca.googleapis.com">GCP控制台</a> 点击登录，选择右上角的激活 cloud shell。</p><p>额外说下，这里登录 GCP 控制台的账号跟上面第一步安装 acme.sh 的时候邮箱账号是没有任何关联的。<br>第一个步骤里面的邮箱账号是注册 ACME 账号的，仅用于向 CA 机构说明身份和接收通知；<br>这里登录 GCP 控制台的账号是用来向谷歌获取颁发证书的授权的，两者无任何关联关系！</p><h3 id="3-2启用-publicca"><a href="#3-2启用-publicca" class="headerlink" title="3.2启用 publicca"></a>3.2启用 publicca</h3><p>在 cloud shell 里面执行命令启用 publicca</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gcloud services <span class="built_in">enable</span> publicca.googleapis.com</span><br></pre></td></tr></table></figure><h3 id="3-3获取-EAB-密钥-ID-和-HMAC"><a href="#3-3获取-EAB-密钥-ID-和-HMAC" class="headerlink" title="3.3获取 EAB 密钥 ID 和 HMAC"></a>3.3获取 EAB 密钥 ID 和 HMAC</h3><p>记录下获取到的密钥和 HMAC 值</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gcloud publicca external-account-keys create</span><br></pre></td></tr></table></figure><h2 id="4-颁发证书"><a href="#4-颁发证书" class="headerlink" title="4.颁发证书"></a>4.颁发证书</h2><h3 id="4-1-申请证书"><a href="#4-1-申请证书" class="headerlink" title="4.1 申请证书"></a>4.1 申请证书</h3><p>在自己部署 <code>acme.sh</code> 的 vps 上执行命令申请证书</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">acme.sh --issue --dns dns_cf  -d hello.com -d <span class="string">&#x27;*.hello.com&#x27;</span> --keylength 2048 --dnssleep 120</span><br></pre></td></tr></table></figure><ul><li><code>--keylength 2048</code> 指定使用 RSA 加密算法，长度为 2048</li><li><code>-d hello.com -d &#39;*.hello.com&#39;</code> 指定要申请证书的域名。（这里要注意 <code>*.hello.com </code> 是不包含 hello.com 这个根域名的，如果你有直接使用 hello.com 访问加密的站点则必须要额外加上 <code>-d hello.com</code>）</li><li><code>--dns dns_cf</code> 指定 dns 服务商为 cloudflare</li><li><code>--issue</code> 核心命令，它的作用是向一个证书颁发机构（CA）发起请求并获取一个新的SSL证书</li><li><code>--dnssleep 120</code> DNS 指定记录检查等待时间为 120 秒。默认是 20 秒，如果指定多个域名建议调整长一点</li></ul><h3 id="4-2安装证书"><a href="#4-2安装证书" class="headerlink" title="4.2安装证书"></a>4.2安装证书</h3><p>这一步也可以不使用下面的命令，安装证书本质上的操作就是把生成的证书、密钥文件复制到指定的路径，自己手动复制完全没问题的。<br>不过相对于手动操作，使用命令安装证书有个好处是后面自动更新证书的时候，acme.sh 会在证书更新完成后自动的执行这里相同的安装命令以达成自动更新本地证书的目的，这样就不用在证书续签之后再手动复制了!</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">acme.sh --install-cert -d hello.com --fullchain-file /etc/pki/nginx/fullchain.cer --key-file       /etc/pki/nginx/fullchain.pem --reloadcmd      <span class="string">&quot;systemctl reload nginx.service&quot;</span></span><br></pre></td></tr></table></figure><ul><li><code>fullchain.cer</code> 是我习惯用来保存证书的文件</li><li><code>fullchain.pem</code> 是我习惯用来保存密钥的文件</li><li><code>--reloadcmd</code> 因为我 vps 上是用 nginx 部署的 web 服务，所以这里可以顺便指定本地证书替换完成后 reload 的操作</li></ul><h2 id="5-结束"><a href="#5-结束" class="headerlink" title="5.结束"></a>5.结束</h2><p>全程大概五分钟不到，一次操作终身免于证书更新的困扰！</p><p>补充一个关于常见证书文件后缀相关的小知识：</p><ul><li><code>.key</code> 私钥 (Private Key)文件。不应该给任何人看的核心机密。对应 Nginx 里面的 <code>ssl_certificate_key</code></li><li><code>.pem</code> 通用后缀。文件里可以包含私钥、服务器证书、中间证书、根证书，或任意组合。必须根据上下文或文件名来判断其内容</li><li><code>.crt</code> 公钥证书 (Certificate)文件。通常只包含一个公钥证书（服务器证书或中间证书），一般不包含私钥。与 <code>.cer</code> 常常可以互换使用</li><li><code>.cer</code> 公钥证书 (Certificate)文件。和 <code>.crt</code> 基本一样，也是代表公钥证书。<code>acme.sh</code> 默认生成 <code>.cer</code>，而Windows系统也常用 <code>.cer</code></li><li><code>.csr</code> 证书签名请求 (Certificate Signing Request)文件。通常会把域名信息和公钥打包在这个文件里提交给CA，请求CA用他们的私钥来“签名”这个请求，从而生成 SSL 证书</li></ul>]]></content>
    
    
    <summary type="html">在vps上使用acme.sh调用cloudflare dns api自动申请谷歌免费SSL证书</summary>
    
    
    
    <category term="linux" scheme="https://ruams.com/categories/linux/"/>
    
    
    <category term="linux" scheme="https://ruams.com/tags/linux/"/>
    
  </entry>
  
  <entry>
    <title>Debian12部署k8s集群</title>
    <link href="https://ruams.com/posts/47e13348.html"/>
    <id>https://ruams.com/posts/47e13348.html</id>
    <published>2025-02-14T01:03:00.000Z</published>
    <updated>2025-02-14T13:37:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>本文的主要记录了如何利用手头空余的 vps 服务器建立一个 k8s 集群以供实验学习。目前我是有三台空余的 vps 服务器，刚好可以搭建一个小的 k8s 集群。集群采用 <code>1*master + 2*work</code> 的方式。<br>因为 k8s 集群搭建本身并不复杂，所以本篇文章也主要是记录下来每一步操作。在开始阅读此篇文章之前强烈建议先阅读我之前写的<a href="https://ruams.com/posts/972d30c.html">跨云k8s组网方案讨论</a>这篇文章，里面完整记录了关于我这个小环境组网方案的新路历程。</p><h2 id="1-前言"><a href="#1-前言" class="headerlink" title="1. 前言"></a>1. 前言</h2><ul><li>目标: 搭建一个安全、稳定、符合官方标准的K8s学习与实验环境。</li><li>操作系统版本： Debian12</li><li>k8s 版本： 1.33.4</li><li>CRI 容器运行时： containerd v1.6.20</li><li>CNI 网络插件： Calico v3.30.3</li><li>服务暴露： Cloudflare Tunnel</li></ul><h2 id="2-基础配置"><a href="#2-基础配置" class="headerlink" title="2. 基础配置"></a>2. 基础配置</h2><p>此部分所有 master、work 节点都需要操作。</p><h3 id="2-1-基本工具安装"><a href="#2-1-基本工具安装" class="headerlink" title="2.1 基本工具安装"></a>2.1 基本工具安装</h3><p>更新系统并安装必要工具：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">apt update</span><br><span class="line">apt upgrade -y</span><br><span class="line">apt install -y apt-transport-https ca-certificates curl gpg bash-completion</span><br></pre></td></tr></table></figure><h3 id="2-2-关闭-swap"><a href="#2-2-关闭-swap" class="headerlink" title="2.2 关闭 swap"></a>2.2 关闭 swap</h3><p>k8s 建议关闭 swap分区:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">swapoff -a</span><br><span class="line">vim /etc/fstab <span class="comment"># 编辑fstab文件，注释掉 swap 那一行</span></span><br></pre></td></tr></table></figure><h3 id="2-3-安装-WireGuard"><a href="#2-3-安装-WireGuard" class="headerlink" title="2.3 安装 WireGuard"></a>2.3 安装 WireGuard</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">apt install wireguard -y</span><br></pre></td></tr></table></figure><h3 id="2-4-生成密钥对"><a href="#2-4-生成密钥对" class="headerlink" title="2.4 生成密钥对"></a>2.4 生成密钥对</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> /etc/wireguard</span><br><span class="line">wg genkey | <span class="built_in">tee</span> privatekey | wg pubkey &gt; publickey</span><br><span class="line"><span class="built_in">chmod</span> 600 privatekey</span><br><span class="line"><span class="built_in">chmod</span> 600 publickey</span><br></pre></td></tr></table></figure><h3 id="2-5-配置-WireGuard"><a href="#2-5-配置-WireGuard" class="headerlink" title="2.5 配置 WireGuard"></a>2.5 配置 WireGuard</h3><p>关于 WireGUard 更详细的内容解析，可以查看我之前写的这篇文章 <a href="https://ruams.com/posts/a37af523.html">WireGuard原理解析与生产实践</a><br>编辑 <code>/etc/wireguard/wg0.conf</code>，写入以下内容：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">[Interface]</span><br><span class="line"># 本机的私钥</span><br><span class="line">PrivateKey = xxxxxxxxxxxx</span><br><span class="line"># 本机在私有组网中的IP地址</span><br><span class="line">Address = 10.66.66.1/24</span><br><span class="line"># 默认监听端口（UDP协议）</span><br><span class="line">ListenPort = 51820</span><br><span class="line"># 统一设置 MTU</span><br><span class="line">MTU = 1420</span><br><span class="line"></span><br><span class="line"># 一号工作节点的 Peer</span><br><span class="line">[Peer]</span><br><span class="line"># 一号工作节点的公钥</span><br><span class="line">PublicKey = xxxxxxxxxxxx</span><br><span class="line"># 一号工作节点的公网IP和端口</span><br><span class="line">Endpoint = x.x.x.x:51820</span><br><span class="line"># 允许使用 WireGuard 接口通信的网段。这里注意下除了自定义的私网网段，还需要加上POD的网段。因为我的组网方案里POD是用Calico BGP模式通信的。</span><br><span class="line">AllowedIPs = 10.66.66.0/24,10.244.0.0/16</span><br><span class="line"># 保持连接，25秒发一次心跳，最佳建议值</span><br><span class="line">PersistentKeepalive = 25</span><br><span class="line"></span><br><span class="line"># 二号工作节点的 Peer</span><br><span class="line">[Peer]</span><br><span class="line"># 二号工作节点的公钥</span><br><span class="line">PublicKey = xxxxxxxxxxxx</span><br><span class="line"># 二号工作节点的公网IP和端口</span><br><span class="line">Endpoint = x.x.x.x:51820</span><br><span class="line"># 允许使用 WireGuard 接口通信的网段。这里注意下除了自定义的私网网段，还需要加上POD的网段。因为我的组网方案里POD是用Calico BGP模式通信的。</span><br><span class="line">AllowedIPs = 10.66.66.0/24,10.244.0.0/16</span><br><span class="line"># 保持连接，25秒发一次心跳，最佳建议值</span><br><span class="line">PersistentKeepalive = 25</span><br></pre></td></tr></table></figure><p>上面这个配置文件是以 master 节点为例编写的，其他两个节点也使用同样的格式配置，只不过变换下 Peer 部分 为其余两个主机、本机部分按照本节点配置。</p><h3 id="2-6-启动与验证"><a href="#2-6-启动与验证" class="headerlink" title="2.6 启动与验证"></a>2.6 启动与验证</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 启动并设置开机自启</span></span><br><span class="line">wg-quick up wg0</span><br><span class="line">systemctl <span class="built_in">enable</span> wg-quick@wg0</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看状态</span></span><br><span class="line">wg show</span><br><span class="line"></span><br><span class="line"><span class="comment"># 验证网络</span></span><br><span class="line">ping 10.66.66.1</span><br><span class="line">ping 10.66.66.2</span><br><span class="line">ping 10.66.66.3</span><br></pre></td></tr></table></figure><h3 id="2-7-参数与模块配置"><a href="#2-7-参数与模块配置" class="headerlink" title="2.7 参数与模块配置"></a>2.7 参数与模块配置</h3><p>k8s需要特定的内核模块和系统参数来支持容器和网络,同时我希望 kube-proxy 使用 ipvs 模式所以需要做如下操作：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安装 ipvs</span></span><br><span class="line">apt-get install -y ipset ipvsadm</span><br><span class="line"></span><br><span class="line"><span class="comment"># 配置启动自加载 ipvs 所需模块</span></span><br><span class="line"><span class="built_in">cat</span> &lt;&lt;<span class="string">EOF | tee /etc/modules-load.d/k8s-ipvs.conf</span></span><br><span class="line"><span class="string">ip_vs</span></span><br><span class="line"><span class="string">ip_vs_rr</span></span><br><span class="line"><span class="string">ip_vs_wrr</span></span><br><span class="line"><span class="string">ip_vs_sh</span></span><br><span class="line"><span class="string">nf_conntrack</span></span><br><span class="line"><span class="string">EOF</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 手动加载 ipvs 相关模块</span></span><br><span class="line">modprobe ip_vs</span><br><span class="line">modprobe ip_vs_rr</span><br><span class="line">modprobe ip_vs_wrr</span><br><span class="line">modprobe ip_vs_sh</span><br><span class="line">modprobe nf_conntrack</span><br><span class="line"></span><br><span class="line"><span class="comment"># 配置启动自加载容器运行时所需模块</span></span><br><span class="line"><span class="built_in">cat</span> &lt;&lt;<span class="string">EOF | tee /etc/modules-load.d/k8s.conf</span></span><br><span class="line"><span class="string">overlay</span></span><br><span class="line"><span class="string">br_netfilter</span></span><br><span class="line"><span class="string">EOF</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 手动加载容器运行时相关模块</span></span><br><span class="line">modprobe overlay</span><br><span class="line">modprobe br_netfilter</span><br><span class="line"></span><br><span class="line"><span class="comment"># 配置启动自加载内核参数</span></span><br><span class="line"><span class="built_in">cat</span> &lt;&lt;<span class="string">EOF | tee /etc/sysctl.d/k8s.conf</span></span><br><span class="line"><span class="string">net.bridge.bridge-nf-call-iptables  = 1</span></span><br><span class="line"><span class="string">net.bridge.bridge-nf-call-ip6tables = 1</span></span><br><span class="line"><span class="string">net.ipv4.ip_forward                 = 1</span></span><br><span class="line"><span class="string">EOF</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 配置生效</span></span><br><span class="line">sysctl --system</span><br></pre></td></tr></table></figure><h3 id="2-8-安装容器运行时"><a href="#2-8-安装容器运行时" class="headerlink" title="2.8 安装容器运行时"></a>2.8 安装容器运行时</h3><p>安装 containerd ：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">apt-get install -y containerd</span><br></pre></td></tr></table></figure><p>生成默认配置文件并修改 cgroup 驱动为 systemd：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 生成配置文件</span></span><br><span class="line"><span class="built_in">mkdir</span> -p /etc/containerd</span><br><span class="line">containerd config default | <span class="built_in">tee</span> /etc/containerd/config.toml</span><br><span class="line"></span><br><span class="line"><span class="comment"># 修改cgroup驱动为systemd，与kubelet保持一致</span></span><br><span class="line">sed -i <span class="string">&#x27;s/SystemdCgroup = false/SystemdCgroup = true/&#x27;</span> /etc/containerd/config.toml</span><br><span class="line"></span><br><span class="line"><span class="comment"># 重启 containerd</span></span><br><span class="line">systemctl restart containerd</span><br></pre></td></tr></table></figure><p>替换 containerd 为国内镜像源（可选，但位于国内的服务器必须要操作），配置方法可以按照这篇文章来 <a href="https://ruams.com/posts/3ed18a31.html">containerd配置国内镜像加速</a></p><h3 id="2-9-安装-k8s-组件"><a href="#2-9-安装-k8s-组件" class="headerlink" title="2.9 安装 k8s 组件"></a>2.9 安装 k8s 组件</h3><p>添加 k8s 官方 GPG 密钥和 APT 仓库：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Google的仓库在国内无法访问，这里建议使用阿里云的镜像</span></span><br><span class="line">curl -fsSL https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.33/deb/Release.key | gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&#x27;deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.33/deb/ /&#x27;</span> | <span class="built_in">tee</span> /etc/apt/sources.list.d/kubernetes.list</span><br></pre></td></tr></table></figure><p>安装指定版本的 k8s 组件：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">apt-get update</span><br><span class="line">apt-get install -y kubelet=1.33.4-1.1 kubeadm=1.33.4-1.1 kubectl=1.33.4-1.1</span><br><span class="line"></span><br><span class="line"><span class="comment"># 锁定版本，防止意外升级</span></span><br><span class="line">apt-mark hold kubelet kubeadm kubectl</span><br></pre></td></tr></table></figure><p>k8s 集群创建的时候默认是通过路由来判断 internal ip 的，所以在使用 WireGuard 组建私有网络之后，k8s 并不会直接识别使用私网 IP，所以这里需要手动指定 kubelet 的 internal ip：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">vim /usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf</span><br><span class="line"></span><br><span class="line"><span class="comment"># 在最后一行添加 --node-ip=x.x.x.x 这里的 x.x.x.x 替换成各个节点实际的 WireGuard 私网 IP</span></span><br><span class="line"><span class="comment"># 以我的 master 节点为例，最后一行修改为：</span></span><br><span class="line">ExecStart=/usr/bin/kubelet <span class="variable">$KUBELET_KUBECONFIG_ARGS</span> <span class="variable">$KUBELET_CONFIG_ARGS</span> <span class="variable">$KUBELET_KUBEADM_ARGS</span> <span class="variable">$KUBELET_EXTRA_ARGS</span> --node-ip=10.66.66.1</span><br></pre></td></tr></table></figure><p>重启 kubelet：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">systemctl daemon-reload</span><br><span class="line">systemctl <span class="built_in">enable</span> kubelet</span><br><span class="line">systemctl restart kubelet</span><br></pre></td></tr></table></figure><h2 id="3-master-配置"><a href="#3-master-配置" class="headerlink" title="3. master 配置"></a>3. master 配置</h2><p>以下步骤仅在 master 节点上配置</p><h3 id="3-1-创建-k8s-集群"><a href="#3-1-创建-k8s-集群" class="headerlink" title="3.1 创建 k8s 集群"></a>3.1 创建 k8s 集群</h3><p>执行 <code>kubeadm init</code> 命令创建集群：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 我的 master 节点配置的 WireGuard IP 是 10.66.66.1、POD 网段是 10.244.0.0/16</span></span><br><span class="line">kubeadm init \</span><br><span class="line">  --kubernetes-version=v1.33.4 \</span><br><span class="line">  --apiserver-advertise-address=10.66.66.1 \</span><br><span class="line">  --pod-network-cidr=10.244.0.0/16 \</span><br><span class="line">  --ignore-preflight-errors=Mem</span><br><span class="line"></span><br><span class="line"><span class="comment"># --apiserver-advertise-address: 这个参数非常重要它指明其他节点要通过 WireGuard 组建的私有网络来访问 API Server</span></span><br><span class="line"><span class="comment"># --ignore-preflight-errors=Mem: 添加这个参数是因为我的 master 节点内存很小只有 2GB ，不加会提示内存不足</span></span><br></pre></td></tr></table></figure><p>初始化拉取镜像会花费一定时间，如果网络条件不好可能要等待5分钟左右，待命令执行完成后可以看到类似如下提示：</p> <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">Your Kubernetes control-plane has initialized successfully!</span><br><span class="line"></span><br><span class="line">To start using your cluster, you need to run the following as a regular user:</span><br><span class="line"></span><br><span class="line">  mkdir -p $HOME/.kube</span><br><span class="line">  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config</span><br><span class="line">  sudo chown $(id -u):$(id -g) $HOME/.kube/config</span><br><span class="line"></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line">Then you can join any number of worker nodes by running the following on each as root:</span><br><span class="line"></span><br><span class="line">kubeadm join 10.66.66.1:6443 --token abcdef.1234567890abcdef \</span><br><span class="line">        --discovery-token-ca-cert-hash sha256:1234...</span><br></pre></td></tr></table></figure><p>记录上面屏幕提示中 <code>kubeadm join</code> 这段命令，后续工作节点将通过此条命令加入新建的 k8s 集群中</p><p>按照提示配置：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">mkdir</span> -p <span class="variable">$HOME</span>/.kube</span><br><span class="line"><span class="built_in">cp</span> -i /etc/kubernetes/admin.conf <span class="variable">$HOME</span>/.kube/config</span><br><span class="line"><span class="built_in">chown</span> $(<span class="built_in">id</span> -u):$(<span class="built_in">id</span> -g) <span class="variable">$HOME</span>/.kube/config</span><br></pre></td></tr></table></figure><p>配置命令补全：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">echo</span> <span class="string">&#x27;source &lt;(kubectl completion bash)&#x27;</span> &gt;&gt; ~/.bashrc</span><br><span class="line"><span class="built_in">source</span> ~/.bashrc</span><br></pre></td></tr></table></figure><p>修改 kube-proxy 为 ipvs 模式：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kubectl edit configmap kube-proxy -n kube-system</span><br></pre></td></tr></table></figure><p>将 <code>mode: &quot;&quot;</code> 修改为 <code>mode: &quot;ipvs&quot;</code>，操作完成之后再执行命令删除老的 POD 让它重新生成：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kubectl delete pod -l k8s-app=kube-proxy -n kube-system</span><br></pre></td></tr></table></figure><h3 id="3-2-配置-CNI-网络插件"><a href="#3-2-配置-CNI-网络插件" class="headerlink" title="3.2 配置 CNI 网络插件"></a>3.2 配置 CNI 网络插件</h3><p>在前面跨云 k8s 组网方案中已经说明了使用到的 CNI 插件是 Calico，网络模式配置为 BGP。</p><p>安装 Calico：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.30.3/manifests/calico.yaml</span><br></pre></td></tr></table></figure><p>等待几分钟，让 Calico 的 Pod 启动即可</p><p>修改 calico 网络模式：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">kubectl edit ippool default-ipv4-ippool</span><br><span class="line"><span class="comment"># 将 ipipMode: Always 修改为 ipipMode: Never</span></span><br><span class="line"><span class="comment"># 手动重启 calico-node</span></span><br><span class="line">kubectl rollout restart daemonset calico-node -n kube-system</span><br></pre></td></tr></table></figure><p>下载 calicoctl 工具：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">wget https://github.com/projectcalico/calico/releases/download/v3.30.3/calicoctl-linux-amd64 -O calicoctl</span><br><span class="line"><span class="built_in">chmod</span> +x calicoctl</span><br></pre></td></tr></table></figure><h2 id="4-防火墙配置"><a href="#4-防火墙配置" class="headerlink" title="4. 防火墙配置"></a>4. 防火墙配置</h2><p>如果你的 vps 云厂商默认启用了安全组限制，需要打开 WireGuard 的监听端口的访问权限。</p><p>注意： 这里不需要再为 apiserver、etcd、kubelet 端口打开访问权限，因为前面已经做了 wg 组网。</p><p>因此，所有节点安全组上都仅需要却保对集群内其他节点的公网地址放开 <code>51820/UDP</code></p><h2 id="5-加入工作节点"><a href="#5-加入工作节点" class="headerlink" title="5. 加入工作节点"></a>5. 加入工作节点</h2><p>此部分内容仅在两个工作节点上进行</p><p>在两个工作节点上使用 root 权限执行 3.1 章节中记录下的 kubeadm join 命令即可加入集群：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">kubeadm <span class="built_in">join</span> 10.66.66.1:6443 --token abcdef.1234567890abcdef \</span><br><span class="line">        --discovery-token-ca-cert-hash sha256:1234...</span><br></pre></td></tr></table></figure><p>执行成功，屏幕会打印 This node has joined the cluster</p><p>在 master 节点上执行以下命令，检查节点状态：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kubectl get nodes -o wide</span><br></pre></td></tr></table></figure><p>正常情况下所有节点的状态都会是 Ready 并且 INTERNAL-IP 应该显示的是 WireGuard 组网时配置的私有 IP。<br>在第四步工作节点加入集群的时候工作节点会拉取 kube-proxy 和 calico-node 的镜像并部署，这一步也有可能会因为网络不好导致进度缓慢，所以如果节点是 NotReady 可以先等下再看。</p><p>检查 Pod 状态：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kubectl get pods -A -o wide</span><br></pre></td></tr></table></figure><p>确保 calico-node, coredns, kube-proxy 等所有 Pod 都处于 Running 状态。</p><p>使用 calicoctl 工具检查 BGP 是否建立成功：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./calicoctl node status</span><br></pre></td></tr></table></figure><p>输出结果里面每一个节点都应该是 Established 才正常。</p><h2 id="6-暴露服务"><a href="#6-暴露服务" class="headerlink" title="6. 暴露服务"></a>6. 暴露服务</h2><h3 id="6-1-部署-ingress"><a href="#6-1-部署-ingress" class="headerlink" title="6.1 部署 ingress"></a>6.1 部署 ingress</h3><p>在 master 上执行以下命令部署 ingress：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.13.2/deploy/static/provider/baremetal/deploy.yaml</span><br></pre></td></tr></table></figure><h3 id="6-2-创建-tunnel"><a href="#6-2-创建-tunnel" class="headerlink" title="6.2 创建 tunnel"></a>6.2 创建 tunnel</h3><p>登录 cloudflare 仪表盘，点击左侧面板进入 ZeroTrust 页面，随后依次点击 “网络”-“tunnels”-“创建隧道”-“选择 cloudflared”-“自定义名称”-“保存”-“环境选择 Docker”-“点击复制命令”<br>随后记录下–token 后面的一长串字符，例如我的是 <code>eyJhxxxxxxxxxxxxxxxxxxxxVdyJ9</code></p><h3 id="6-3-创建-secret"><a href="#6-3-创建-secret" class="headerlink" title="6.3 创建 secret"></a>6.3 创建 secret</h3><p>在 k8s 中通常使用 secret 安全保存各类 token、密码等内容：<br>新建 <code>cloudflare-tunnel-secret.yaml</code>，写入以下内容：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">apiVersion:</span> <span class="string">v1</span></span><br><span class="line"><span class="attr">kind:</span> <span class="string">Secret</span></span><br><span class="line"><span class="attr">metadata:</span></span><br><span class="line">  <span class="attr">name:</span> <span class="string">tunnel-token-secret</span> <span class="comment"># 自定义的名字，后面要用到</span></span><br><span class="line">  <span class="attr">namespace:</span> <span class="string">default</span></span><br><span class="line"><span class="attr">stringData:</span></span><br><span class="line">  <span class="attr">token:</span> <span class="string">&quot;&lt;尖括号内的这部分文字替换成上面复制出来的Token&gt;&quot;</span></span><br></pre></td></tr></table></figure><p>创建 secret：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kubectl apply -f cloudflare-tunnel-secret.yaml</span><br></pre></td></tr></table></figure><h3 id="6-4-创建-cloudflare-deployment"><a href="#6-4-创建-cloudflare-deployment" class="headerlink" title="6.4 创建 cloudflare deployment"></a>6.4 创建 cloudflare deployment</h3><p>上一步的 secret 创建好之后即可进一步新建 deployment，新建 <code>cloudflare-deployment.yaml</code> 文件并写入以下内容：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">apiVersion:</span> <span class="string">apps/v1</span></span><br><span class="line"><span class="attr">kind:</span> <span class="string">Deployment</span></span><br><span class="line"><span class="attr">metadata:</span></span><br><span class="line">  <span class="attr">name:</span> <span class="string">cloudflared</span></span><br><span class="line">  <span class="attr">namespace:</span> <span class="string">default</span></span><br><span class="line">  <span class="attr">labels:</span></span><br><span class="line">    <span class="attr">app:</span> <span class="string">cloudflared</span></span><br><span class="line"><span class="attr">spec:</span></span><br><span class="line">  <span class="attr">replicas:</span> <span class="number">2</span> <span class="comment"># 2个副本高可用</span></span><br><span class="line">  <span class="attr">selector:</span></span><br><span class="line">    <span class="attr">matchLabels:</span></span><br><span class="line">      <span class="attr">app:</span> <span class="string">cloudflared</span></span><br><span class="line">  <span class="attr">template:</span></span><br><span class="line">    <span class="attr">metadata:</span></span><br><span class="line">      <span class="attr">labels:</span></span><br><span class="line">        <span class="attr">app:</span> <span class="string">cloudflared</span></span><br><span class="line">    <span class="attr">spec:</span></span><br><span class="line">      <span class="attr">containers:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">cloudflared</span></span><br><span class="line">        <span class="attr">image:</span> <span class="string">cloudflare/cloudflared:latest</span></span><br><span class="line">        <span class="attr">args:</span></span><br><span class="line">          <span class="bullet">-</span> <span class="string">tunnel</span></span><br><span class="line">          <span class="bullet">-</span> <span class="string">--no-autoupdate</span></span><br><span class="line">          <span class="bullet">-</span> <span class="string">run</span></span><br><span class="line">          <span class="bullet">-</span> <span class="string">--token</span></span><br><span class="line">          <span class="bullet">-</span> <span class="string">$(TUNNEL_TOKEN)</span></span><br><span class="line">        <span class="attr">env:</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">TUNNEL_TOKEN</span></span><br><span class="line">          <span class="attr">valueFrom:</span></span><br><span class="line">            <span class="attr">secretKeyRef:</span></span><br><span class="line">              <span class="comment"># 下面这行 name 的值必须和 5.3 章节中创建的 name 值保持一致</span></span><br><span class="line">              <span class="attr">name:</span> <span class="string">tunnel-token-secret</span></span><br><span class="line">              <span class="attr">key:</span> <span class="string">token</span></span><br></pre></td></tr></table></figure><p>创建 deployment：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kubectl apply -f cloudflare-deployment.yaml</span><br></pre></td></tr></table></figure><p>创建完成之后检查 cloudflare 的 pod 状态运行正常即可。</p><h3 id="6-5-创建测试用-web-页面"><a href="#6-5-创建测试用-web-页面" class="headerlink" title="6.5 创建测试用 web 页面"></a>6.5 创建测试用 web 页面</h3><p>新建一个 <code>hello-deployment.yaml</code> 文件，并写入以下内容：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">apiVersion:</span> <span class="string">apps/v1</span></span><br><span class="line"><span class="attr">kind:</span> <span class="string">Deployment</span></span><br><span class="line"><span class="attr">metadata:</span></span><br><span class="line">  <span class="attr">name:</span> <span class="string">hello-app</span></span><br><span class="line">  <span class="attr">namespace:</span> <span class="string">default</span></span><br><span class="line"><span class="attr">spec:</span></span><br><span class="line">  <span class="attr">replicas:</span> <span class="number">2</span> <span class="comment"># 2个副本高可用</span></span><br><span class="line">  <span class="attr">selector:</span></span><br><span class="line">    <span class="attr">matchLabels:</span></span><br><span class="line">      <span class="attr">app:</span> <span class="string">hello-app</span></span><br><span class="line">  <span class="attr">template:</span></span><br><span class="line">    <span class="attr">metadata:</span></span><br><span class="line">      <span class="attr">labels:</span></span><br><span class="line">        <span class="attr">app:</span> <span class="string">hello-app</span></span><br><span class="line">    <span class="attr">spec:</span></span><br><span class="line">      <span class="attr">containers:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">hello-app</span></span><br><span class="line">        <span class="attr">image:</span> <span class="string">nginxdemos/hello</span> <span class="comment"># 惯用的用于演示的 ng 镜像</span></span><br><span class="line">        <span class="attr">ports:</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">containerPort:</span> <span class="number">80</span></span><br></pre></td></tr></table></figure><p>创建 deployment：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kubectl apply -f hello-deployment.yaml</span><br></pre></td></tr></table></figure><p>新建一个 <code>hello-service.yaml</code> 文件，并写入以下内容：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">apiVersion:</span> <span class="string">v1</span></span><br><span class="line"><span class="attr">kind:</span> <span class="string">Service</span></span><br><span class="line"><span class="attr">metadata:</span></span><br><span class="line">  <span class="attr">name:</span> <span class="string">hello-app-service</span></span><br><span class="line">  <span class="attr">namespace:</span> <span class="string">default</span></span><br><span class="line"><span class="attr">spec:</span></span><br><span class="line">  <span class="attr">type:</span> <span class="string">ClusterIP</span> <span class="comment"># 只在集群内部暴露服务</span></span><br><span class="line">  <span class="attr">selector:</span></span><br><span class="line">    <span class="attr">app:</span> <span class="string">hello-app</span></span><br><span class="line">  <span class="attr">ports:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="attr">protocol:</span> <span class="string">TCP</span></span><br><span class="line">      <span class="attr">port:</span> <span class="number">80</span> <span class="comment"># service 监听端口</span></span><br><span class="line">      <span class="attr">targetPort:</span> <span class="number">80</span> <span class="comment"># 流量转发到容器的端口</span></span><br></pre></td></tr></table></figure><p>创建 service:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kubectl apply -f hello-service.yaml</span><br></pre></td></tr></table></figure><p>检查 deployment、pod、service 是否正常：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kubectl get deployment,service,pods -n default</span><br></pre></td></tr></table></figure><p>新建 <code>hello-app-ingress.yaml</code> 文件，并写入以下内容：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">apiVersion:</span> <span class="string">networking.k8s.io/v1</span></span><br><span class="line"><span class="attr">kind:</span> <span class="string">Ingress</span></span><br><span class="line"><span class="attr">metadata:</span></span><br><span class="line">  <span class="attr">name:</span> <span class="string">hello-app-ingress</span></span><br><span class="line">  <span class="attr">namespace:</span> <span class="string">default</span></span><br><span class="line"><span class="attr">spec:</span></span><br><span class="line">  <span class="attr">ingressClassName:</span> <span class="string">&quot;nginx&quot;</span></span><br><span class="line">  <span class="attr">rules:</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">host:</span> <span class="string">&quot;hello.com&quot;</span> <span class="comment"># 替换为自己的域名，例如 blog.xxx.com、test.xxx.com</span></span><br><span class="line">    <span class="attr">http:</span></span><br><span class="line">      <span class="attr">paths:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">path:</span> <span class="string">/</span></span><br><span class="line">        <span class="attr">pathType:</span> <span class="string">Prefix</span></span><br><span class="line">        <span class="attr">backend:</span></span><br><span class="line">          <span class="attr">service:</span></span><br><span class="line">            <span class="attr">name:</span> <span class="string">hello-app-service</span> <span class="comment"># 流量转发到 hello-app-service</span></span><br><span class="line">            <span class="attr">port:</span></span><br><span class="line">              <span class="attr">number:</span> <span class="number">80</span></span><br></pre></td></tr></table></figure><p>创建 ingress：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kubectl apply -f hello-app-ingress.yaml</span><br></pre></td></tr></table></figure><h3 id="6-6-配置域名和路由"><a href="#6-6-配置域名和路由" class="headerlink" title="6.6 配置域名和路由"></a>6.6 配置域名和路由</h3><p>这里以 blog.hello.com 域名为例。<br>回到之前打开的 cloudflare tunnel 页面，选择“下一步”，子域栏填 <code>hello</code>，域栏选择托管在 cloudflare 上的 <code>hello.com</code>，路径栏留空，服务选择 HTTP，URL 栏填 <code>http://ingress-nginx-controller.ingress-nginx</code>,最后点击保存完成配置。</p><h2 id="7-验证"><a href="#7-验证" class="headerlink" title="7. 验证"></a>7. 验证</h2><p>浏览器访问 <code>https://blog.hello.com</code> 并勾选 <code>Auto Refresh</code>，应该可以看到每秒自动刷新的 nginx 图标页面，并且会显示 pod 的 ip</p><p>高可用测试：<br>轮流关闭工作节点主机，可以看到页面仍然能够正常访问，且 IP 会对应的变成剩余工作节点上的那个 pod ip。</p><p>后续新上线一个服务，就要创建 deployment-service-ingress-web 上跳转 tunnel。<br>因为我是有个域名完全拿来在这个环境做测试的，所以我的 tunnel 上面配置的是 <code>*</code> 通配符跳转，tunnel 相当于仅作为连接器。而 ingress 负责实现实验学习所需要的复杂的流量路由,同时这样后面也就不用在网页上调整 cloudflare 了。<br>如果你不需要用 ingress 来做这种复杂的流量路由实验，则完全可以去除本文中 ingress 相关的章节。用 tunnel 既当连接器又当路由分发器。比如直接跳过创建 ingress 部分，同样按照文中创建完成 hello-app 的 deployment 和 service 之后。在 tunnel 页面依然选择 HTTP 类型，URL 改成你 service 对应的 cluster ip 即可。</p>]]></content>
    
    
    <summary type="html">在跨云、跨地区的vps节点间使用debian12操作系统部署k8s1.33集群，并使用cloudflare tunnel对外暴露服务。</summary>
    
    
    
    <category term="k8s" scheme="https://ruams.com/categories/k8s/"/>
    
    
    <category term="k8s" scheme="https://ruams.com/tags/k8s/"/>
    
  </entry>
  
  <entry>
    <title>跨云k8s组网方案讨论</title>
    <link href="https://ruams.com/posts/972d30c.html"/>
    <id>https://ruams.com/posts/972d30c.html</id>
    <published>2025-02-13T00:41:00.000Z</published>
    <updated>2025-02-13T11:02:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>因客观条件限制，我打算用手头上3~4个 vps 来搭建一个 k8s 集群用于学习和实验，所以就有了这篇文章。本文主要是探讨一个三&#x2F;四节点(单 master)的跨云跨国云主机在搭建 k8s 集群时应该采用什么样的网络架构，在阅读这篇文章之前默认你已经有了 k8s 相关的基础理论知识，关于具体的搭建流程后面会写一篇新的文章。</p><h2 id="1-私有网络"><a href="#1-私有网络" class="headerlink" title="1. 私有网络"></a>1. 私有网络</h2><p>为什么首先提到的是私有网络？<br>结论放在前面：安全！即便是我个人测试用的这里也要使用私有网络</p><p>那么为什么不能直接使用各个云主机的公网 ip 地址来搭建 k8s 集群呢？因为直接使用公网 ip 地址搭建 k8s 集群存在极大的安全隐患：</p><ol><li>k8s 的控制平面是整个集群的大脑，<code>kube-apiserver</code> 作为其入口，若直接暴露于公网那么你懂的，这将会面对永无休止的恶意扫描和攻击。尽管 apiserver 拥有完善的认证授权机制（TLS、RBAC），但将核心管理端口暴露在互联网上，本身就极大地增加了攻击面，违反了最小权限和纵深防御的安全原则。etcd 的端口（2379&#x2F;2380）若有任何配置不当，则可能导致整个集群数据被泄露或篡改，后果不堪设想。</li><li>集群内部的流量如果直接通过公网进行通信，那么 k8s 原生的 <code>NetworkPolicy</code> 就很难有用武之地了。网络策略依赖于 CNI 插件对 pod ip 的识别和控制，公网流量使得这种精细化的内部访问控制会变得异常复杂和不可靠。</li><li>如果使用的 CNI 插件用的是 BGP 等明文协议，这相当于对外公布了你集群所有的路由信息，仅是从信息泄露这个层面来看就是不可接受的。</li></ol><p>综上所述，为所有节点构建一个统一的、私有的“第二层”网络平面、让所有集群内部的通信（包括控制平面和数据平面）都发生在这个可信的私有网络之上是十分必要的！</p><h3 id="k8s-集群内通信机制"><a href="#k8s-集群内通信机制" class="headerlink" title="k8s 集群内通信机制"></a>k8s 集群内通信机制</h3><p>在深入组网方案之前，我们有必要先回忆下 k8s 集群内部的通信模型。<br>首先我们都知道 k8s 集群的通信主要围绕 <code>kube-apiserver</code> 展开，它以 HTTPS API 的形式提供服务：</p><ul><li>控制平面内部通信：etcd、kube-controller-manager、kube-scheduler 与 kube-apiserver 之间的通信，均通过 TLS 加密。etcd 尤其敏感，通常只监听在回环地址或一个受信任的私有网络接口上。</li><li>控制平面与工作节点通信：</li><li>kube-apiserver 到 kubelet：apiserver 主动连接 kubelet 的 10250 端口，用于执行 kubectl exec&#x2F;logs&#x2F;port-forward 等命令,此连接需要经过 kubelet 的认证和授权。</li><li>kubelet 到 kube-apiserver：kubelet 作为客户端，主动连接 kube-apiserver 的 6443 端口，上报节点状态、Pod 状态，并接收指令，这是最主要的通信路径。</li></ul><h2 id="2-组网工具"><a href="#2-组网工具" class="headerlink" title="2. 组网工具"></a>2. 组网工具</h2><p>市面上有多种成熟的组网工具，如 ZeroTier、Tailscale、WireGuard 等等，我这里使用的是原生的 WireGuard，关于 WireGuard我之前也写了一篇相对详细的文章： <a href="https://ruams.com/posts/a37af523.html">WireGuard原理解析与生产实践</a><br>几种组网工具说明以及为何会选择使用 WireGuard，原因如下：</p><ul><li><strong>ZeroTier</strong>：功能强大，通过自有的全球根服务器网络实现复杂的 NAT 穿越和多路径路由，配置简单。但其协议私有，且核心网络依赖于中心化的控制器</li><li><strong>Tailscale</strong>：基于 WireGuard 构建，极大地简化了密钥交换和节点管理。它引入了一个中心化的协调服务器来管理公钥、ACL 和 IP 分配等。最大的特点是易用，but Tailscale 不支持自定义组网网段，并且使用了 <code>100.64.0.0/10</code> 这个为运营商级 NAT 保留的地址段。这本身并不是个问题，问题在于奇妙的阿里云服务器内部也使用了这个地址段。如果你也有阿里云的服务器那就有可能会产生冲突导致路由问题，这里我不具体展开了，搜索关键词会有很多文章</li><li><strong>WireGuard</strong>：一个现代化、高性能的 VPN 协议，从 5.6 版本内核开始已被并入 Linux 内核主线，市面上也有很多基于它构建的组网工具，主要缺点是没有自带的管理工具，在多节点全连接网状拓扑下扩展配置就很麻烦</li></ul><table><thead><tr><th align="left">特性</th><th align="left">WireGuard</th><th align="left">Tailscale</th><th align="left">ZeroTier</th></tr></thead><tbody><tr><td align="left"><strong>核心协议</strong></td><td align="left">WireGuard</td><td align="left">WireGuard</td><td align="left">自有协议</td></tr><tr><td align="left"><strong>性能</strong></td><td align="left">极高 (内核态)</td><td align="left">很高 (基于 WireGuard)</td><td align="left">良好</td></tr><tr><td align="left"><strong>配置复杂度</strong></td><td align="left">中等 (手动管理密钥)</td><td align="left">极低 (自动化)</td><td align="left">低</td></tr><tr><td align="left"><strong>网络自定义</strong></td><td align="left">完全自定义</td><td align="left">有限 (固定网段)</td><td align="left">较高</td></tr><tr><td align="left"><strong>中心化依赖</strong></td><td align="left">无 (纯 P2P)</td><td align="left">有 (协调服务器)</td><td align="left">有 (根服务器)</td></tr></tbody></table><p>我的需求场景: 一个稳定、高性能、且网络配置完全可控的底层。<br>3~4个节点的 wg 配置不会太麻烦，对比 Tailscle 和 ZeroTier 原生 wg 的网络稳定性实测要更优秀，所以最终选择 WireGuard 进行节点间的组网。</p><h3 id="节点间组网规划"><a href="#节点间组网规划" class="headerlink" title="节点间组网规划"></a>节点间组网规划</h3><ol><li><p><strong>网络拓扑</strong>：对于三到四个节点的小规模集群，最理想的拓扑显然是 <strong>全连接网状网络</strong>。每个节点都与其他所有节点直接建立 WireGuard 隧道。这样任意两个节点间的通信都只需要一跳，延迟最低，且没有单点故障。</p></li><li><p><strong>网段选择</strong>：</p><ul><li>必须在 RFC 1918 定义的私有地址空间中选择：<code>10.0.0.0/8</code>, <code>172.16.0.0/12</code>, <code>192.168.0.0/16</code></li><li>为了最大程度地避免与节点所在云环境的内网、本地开发环境的局域网或未来可能接入的其他网络产生冲突，一个最佳实践是选择一个相对冷门的网段。例如，不直接使用 <code>10.0.0.0/24</code> 或 <code>192.168.1.0/24</code>，而是选择像 <code>10.66.66.0/24</code> 这样的网段</li><li>对于3-4个节点，建议直接使用 <code>/24</code> 这种标准的 C 类子网便于记忆和管理，当然选择一个小的子网（如 <code>/29</code>）地址也完全够用</li></ul></li></ol><h2 id="3-CNI-插件"><a href="#3-CNI-插件" class="headerlink" title="3. CNI 插件"></a>3. CNI 插件</h2><p>上面我们讨论了 k8s 集群中 <strong>Node-to-Node</strong> 的通信问题。接下来需要解决 k8s 中更加核心的 <strong>Pod-to-Pod</strong> 通信问题。</p><p>首先要澄清一个事实，也是常见的疑惑。 K8s 官方仅提出了个网络规范模型，<strong>并不提供具体实现</strong>。这个模型的核心原则是：</p><ol><li>集群中每个 Pod 都拥有一个唯一的、可路由的 IP 地址</li><li>任何节点上的 Pod 都可以直接与任何其他节点上的 Pod 通信，无需 NAT</li><li>节点上的代理（如 <code>kubelet</code>）可以与该节点上的所有 Pod 通信。</li></ol><p>CNI 插件则是这个模型的具体实现，k8s 会通过用户选择安装的 CNI 插件来实现创建虚拟网络设备、配置 IP、设置路由等操作。</p><h3 id="常见的-CNI-插件"><a href="#常见的-CNI-插件" class="headerlink" title="常见的 CNI 插件"></a>常见的 CNI 插件</h3><p>市面上有众多 CNI 插件，我们选取几个主流的进行对比：</p><table><thead><tr><th align="left">CNI 插件</th><th align="left">核心技术</th><th align="left">网络模式</th><th align="left">网络策略</th><th align="left">性能</th><th align="left">适用场景</th></tr></thead><tbody><tr><td align="left"><strong>Flannel</strong></td><td align="left">VXLAN &#x2F; host-gw</td><td align="left">Overlay</td><td align="left">不支持 (需配合其他)</td><td align="left">中等</td><td align="left">简单、快速部署，功能要求不高的场景</td></tr><tr><td align="left"><strong>Calico</strong></td><td align="left">BGP &#x2F; IPIP &#x2F; VXLAN</td><td align="left">Overlay &#x2F; Underlay(BGP)</td><td align="left">强，功能丰富</td><td align="left">高 (BGP 模式)</td><td align="left">对网络策略和性能有高要求的生产环境</td></tr><tr><td align="left"><strong>Cilium</strong></td><td align="left">eBPF</td><td align="left">Overlay &#x2F; Underlay</td><td align="left">极强 (基于身份&#x2F;API)</td><td align="left">极高</td><td align="left">云原生、微服务、高性能和可观察性场景</td></tr></tbody></table><p>**我这里选择使用 Calico 这个 CNI 插件并使用 BGP 网络模式，原因如下：</p><ul><li>避免双重封装： Flannel 通常使用 VXLAN 封装 Pod 流量。如果将其运行在已经是 WireGuard 隧道的网络上，就会产生 <code>Pod流量 -&gt; VXLAN封装 -&gt; WireGuard封装 -&gt; 公网</code> 的双重封装，带来额外的性能开销（MTU 问题、CPU 消耗）。</li><li>利用底层网络： Calico 的 BGP 模式将每个 K8s 节点变成一个 BGP aS(自治系统)。节点之间通过 BGP 协议交换各自负责的 Pod CIDR 的路由信息。这意味着，当 Node1 上的一个 Pod 要访问 Node2 上的 Pod 时，Node1 的内核路由表会明确地知道，目标 Pod IP 的下一跳是 Node2 的节点 IP。</li><li>与 WireGuard 的协同： 我们可以让 Calico 的 BGP Peering 直接在 WireGuard 的私有网络接口（如 <code>wg0</code>）上进行。这样，路由信息和 Pod 数据流量都将通过加密的 WireGuard 隧道传输，既实现了路由的高效宣告，又保证了数据的安全性。整个数据路径是：<code>Pod流量 -&gt; WireGuard封装 -&gt; 公网</code>，只有一层封装，性能最优。</li></ul><p>下面的 Mermaid 图展示了 Calico BGP over WireGuard 的数据流：</p><pre><code class="highlight mermaid">sequenceDiagram    participant Pod1 on Node1    participant Node1 Kernel    participant WG Tunnel on Node1    participant WG Tunnel on Node2    participant Node2 Kernel    participant Pod2 on Node2    Pod1-&gt;&gt;Node1 Kernel: Send packet to Pod2 IP    Note over Node1 Kernel: Routing Table Lookup&lt;br/&gt;Destination: Pod2&#x27;s CIDR&lt;br/&gt;Next Hop: Node2&#x27;s WG IP (10.20.30.2)    Node1 Kernel-&gt;&gt;WG Tunnel on Node1: Forward packet to wg0 interface    WG Tunnel on Node1-&gt;&gt;WG Tunnel on Node2: Encapsulate &amp; send over Internet    WG Tunnel on Node2-&gt;&gt;Node2 Kernel: Decapsulate &amp; receive packet    Note over Node2 Kernel: Packet arrives on wg0&lt;br/&gt;Destination: Pod2 IP    Node2 Kernel-&gt;&gt;Pod2 on Node2: Deliver packet</code></pre><h3 id="Pod-网段规划"><a href="#Pod-网段规划" class="headerlink" title="Pod 网段规划"></a>Pod 网段规划</h3><p>我这里就用默认的 <code>10.244.0.0/16</code>，默认就是一个极大的值，而且我觉得也没有调小的必要性。<br>如果你想要自定义个网段一个至关重要的点： Pod 网段（<code>--pod-network-cidr</code>）绝对不能与节点 WireGuard 网络的网段重叠，当然如果按照我前面的规划，节点间 wg 组网使用 <code>10.66.66.0/24</code> 则即可与 Pod 的默认网段分开。</p><ul><li>K8s 的 <code>kube-controller-manager</code> 会从你指定的 Pod 网段中为每个节点分配一个小子网（例如，<code>--pod-network-cidr=10.244.0.0/16</code>，那么 Node1 可能分到 <code>10.244.0.0/24</code>，Node2 分到 <code>10.244.1.0/24</code>）</li><li>Calico 会将这些子网的路由信息通过 BGP 在节点间广播</li><li>如果 Pod 网段与节点网络重叠，将导致严重的路由冲突</li></ul><p>推荐的规划是：</p><ul><li>节点网络: <code>10.66.66.0/24</code></li><li>Pod 网络: <code>10.244.0.0/16</code> (默认，<code>--pod-network-cidr</code> 指定 )</li><li>Service: <code>10.96.0.0/12</code> (kubeadm 默认值，通常无需修改)</li></ul><h2 id="4-服务暴露"><a href="#4-服务暴露" class="headerlink" title="4. 服务暴露"></a>4. 服务暴露</h2><p>上面已经解决了节点和 Pod 的通信，现在我们还需要考虑集群内部服务间的通信以及如何将服务安全地对外暴露。</p><h3 id="kube-proxy-模式"><a href="#kube-proxy-模式" class="headerlink" title="kube-proxy 模式"></a>kube-proxy 模式</h3><p>毕竟是讨论 k8s 集群的网络设计，这里顺带也提下 <code>kube-proxy</code> 吧。</p><p><code>kube-proxy</code> 是实现 k8s 集群中 service 的关键组件，它负责将发送到 Service ClusterIP 的流量负载均衡到后端的 Pods。它主要有两种工作模式：</p><ul><li>iptables： 默认采用的模式。<code>kube-proxy</code> 会为每个 Service 和 Endpoint 创建大量的 iptables 规则。当 Service 数量巨大时，iptables 规则链会变得非常长，数据包在内核中进行规则匹配的路径也会变长，导致性能下降。其查找算法的时间复杂度是 O(n)，其中 n 是规则数量</li><li>IPVS (IP Virtual Server)： IPVS 是 Linux 内核中专门用于负载均衡的模块，内建于 LVS (Linux Virtual Server) 项目。它使用哈希表来存储 Service 和 Real Server (Pod) 的映射关系，查找效率极高，时间复杂度为 O(1)。此外，IPVS 支持更丰富的负载均衡算法（轮询、最少连接、加权等）</li></ul><p>对于有一定规模或对性能有要求的 k8s 集群，强烈推荐使用 IPVS 模式。虽然在我的 3 节点小集群中实测性能差异并不明显，但我就是想要换成 ipvs 哈哈哈。<br>修改的方法也很简单，确保 ipvs 模块已经加载并且在 <code>kube-proxy</code> 的 ConfigMap (<code>kube-proxy-cm</code>) 中将 <code>mode</code> 字段从 <code>&quot;&quot;</code> (或 <code>&quot;iptables&quot;</code>) 修改为 <code>&quot;ipvs&quot;</code> 即可。</p><h3 id="服务暴露方案"><a href="#服务暴露方案" class="headerlink" title="服务暴露方案"></a>服务暴露方案</h3><p>K8s 默认提供的服务暴露方式(service 类型)：</p><ul><li>ClusterIP： 默认类型。为 Service 分配一个只能在集群内部访问的虚拟 IP。节点间可通过这个 IP 访问服务，但无法从外部访问</li><li>NodePort： 在 <code>ClusterIP</code> 的基础上，在每个节点上都开放一个固定的静态端口（默认范围 <code>30000-32767</code>）。任何发送到 <code>&lt;NodeIP&gt;:&lt;NodePort&gt;</code> 的流量都会被转发到该 Service。在生产环境中，它通常不直接对最终用户暴露，而是作为上游负载均衡器（如 F5, Nginx）的目标</li><li>LoadBalancer： 在 <code>NodePort</code> 的基础上，请求云服务商创建一个外部负载均衡器，并将流量导向所有节点的 <code>NodePort</code>。这是云上环境最常用的生产级暴露方式，但它依赖于云厂商的服务，在我们的跨云、自建环境中<strong>不可用</strong>。</li><li>ExternalName： 这个比较特别它不转发流量，而是将 Service 名称映射到指定的 DNS 名称。适用于集群内的应用需要通过固定名称访问外部服务的场景</li></ul><p>对于我的跨云环境，<code>LoadBalancer</code> 首先就被排除了，其次 <code>ExternalName</code> 不适用测试、日常使用场景，<code>ClusterIP</code> 无法在外网直接使用，看起来就只能使用 <code>NodePort</code> 了，但是这也有个问题，使用 <code>NodePort</code> 意味着我访问的时候还必须要用 <code>ip:端口</code> 的形式，这看起来令人不爽。我渴望一个统一的入口或者说反向代理吧！<br>习惯了使用因为我个人测试用也是通过域名去访问，而我的域名托管在 cloudflare，cloudflare 的免费版不支持给同一个域名解析到不同的 A 记录。</p><p>针对这个问题，自然就会想到生产环境常用的 <code>ingress</code> 了。</p><p>简单介绍下 <code>ingress</code>,我们一般提到的 <code>ingress</code> 指的是 <code>ingress</code> + <code>ingress controller</code>。<br>首先 <code>Ingress</code> 它不是一种 service 类型，它是一个独立的 API 对象，它定义了从集群外部访问内部 Service 的规则，主要针对 HTTP&#x2F;HTTPS 流量。<br><code>Ingress Controller</code> 则是实现这些规则的“大脑”，它是一个运行在集群内的反向代理程序，持续监听 Ingress 对象的变化，并动态更新自身的路由配置。<br>对于我当前的环境选择使用 <code>ingress</code> 可以让多个 service 可以共享同一个 Ingress Controller 和同一个外部入口，这很好。</p><p>但是依然存在一个问题。Ingress 没有解决一个根本问题： 外部流量最初应该发送到哪里？ Ingress Controller 本身也需要被暴露。在我的跨云环境中，有以下几种传统思路：</p><ul><li>NodePort + DNS 轮询： 将 Ingress Controller 的 service 设置为 <code>NodePort</code> 类型。这样，三个节点的公网 IP 都会监听在同一个端口上。随后，我们可以为域名（如 <code>hello.example.com</code>）配置三条 A 记录，分别指向这三个公网 IP。可惜的是我的域名托管在 cloudflare，cloudflare 的免费版不支持给同一个域名解析到不同的 A 记录</li><li>NodePort + 专业 DNS 服务： 使用支持健康检查的 DNS 服务（常见的有 AWS Route, Google Cloud DNS 或 Cloudflare 的付费套餐）。这些服务可以定期探测 <code>NodePort</code> 的可用性，并动态地从 DNS 解析结果中移除故障节点的 IP。可惜的是都不免费！</li><li>自建外部负载均衡器： 在三台 K8s 节点之外，再部署一组高可用的负载均衡器（HAProxy + Keepalived），由它们来接收所有流量并分发到 K8s 节点的 <code>NodePort</code>。这应该是最可用的方案了，但是仍然会有一定的额外开销。</li></ul><p>我这里最终方案是妥协使用 <code>Cloudflare Tunnel</code> 来免费解决问题，当然代价是大陆访问存在网络负优化。</p><p>Cloudflare Tunnel (原名 Argo Tunnel) 是一个非常巧妙的工具。它的工作原理如下：</p><ol><li>在 Kubernetes 集群内部署一个 <code>cloudflared</code> 连接器（通常是 Deployment）</li><li>这个连接器会主动向 Cloudflare 的边缘网络发起一个出站连接隧道</li><li>然后你可以在 Cloudflare 的 DNS 配置中，将你的域名（如 <code>hello.example.com</code>）指向这个 Tunnel</li><li>当外部用户访问 <code>hello.example.com</code> 时，请求会到达 Cloudflare 的边缘节点。Cloudflare 通过已经建立的隧道，将流量安全地转发到集群内的 <code>cloudflared</code> 连接器上</li><li><code>cloudflared</code> 连接器再将流量转发给集群内部的 Ingress Controller Service</li></ol><p>这个方案的优势在于：</p><ul><li>零公网暴露： 因为 cf 使用隧道连接到集群内部，所以我们的服务就可以使用上面提到的 <code>ClusterIP</code> 方式仅对集群内暴露，这样任何一台 K8s 节点都不需要暴露任何公网端口，所有入站流量都通过 Cloudflare 的加密隧道进入，安全性极高。</li><li>原生高可用： <code>cloudflared</code> 的 Deployment 可以有多个副本，它们会自动连接到 Cloudflare 边缘，实现负载均衡和高可用。Cloudflare 会自动处理到健康隧道的流量路由。</li><li>简化网络配置： 无需处理公网 IP 变化、DDNS、防火墙端口开放等繁琐事宜。</li></ul><p>这个方案的核心缺点在于： 网络对大陆不友好，部分省份延迟可能达到4-5秒。</p><p>最后还有一个问题，既然已经决定使用 cloudflare tunnel 了，那么前面说的 ingress 还需要继续使用吗？</p><p>答案是仍然推荐使用 ingress，看下面两张图就明白了。</p><ul><li>方案一： 为每个服务创建一个 Tunnel。在这个方案中需要在 Cloudflare 那边配置三条独立的 Tunnel，并且为每个 Tunnel 配置一个域名，让它们分别指向 Kubernetes 内部对应的服务</li><li>方案二：使用单一 Tunnel + Ingress。在这个方案中，只需要在 Cloudflare 那边配置一个 Tunnel。这个 Tunnel 的目标是集群内的唯一入口——Ingress Controller，所有的域名都先解析到这个 Tunnel 再由 ingress 分发到不同的服务中。</li></ul><p>方案一示例：</p><pre><code class="highlight mermaid">graph TD    subgraph &quot;用户端&quot;        User[&lt;fa:fa-user&gt; 用户]    end    subgraph &quot;Cloudflare 全球网络&quot;        CF_Edge[&lt;fa:fa-globe&gt; Cloudflare Edge]        User -- &quot;访问 web01.example.com&quot; --&gt; CF_Edge        User -- &quot;访问 web02.example.com&quot; --&gt; CF_Edge        User -- &quot;访问 web03.example.com&quot; --&gt; CF_Edge    end    subgraph &quot;Kubernetes 集群内部&quot;        subgraph &quot;网络入口&quot;            Cloudflared[&lt;fa:fa-shield-halved&gt; cloudflared Pods]        end                subgraph &quot;应用服务 (L4)&quot;            Svc1[&lt;fa:fa-server&gt; web01-svc]            Svc2[&lt;fa:fa-server&gt; web02-svc]            Svc3[&lt;fa:fa-server&gt; web03-svc]        end        subgraph &quot;应用Pods (分布在3个节点上)&quot;            Pod1A[&lt;fa:fa-cube&gt; web01-pod-a]            Pod1B[&lt;fa:fa-cube&gt; web01-pod-b]            Pod1C[&lt;fa:fa-cube&gt; web01-pod-c]                        Pod2A[&lt;fa:fa-cube&gt; web02-pod-a]            Pod2B[&lt;fa:fa-cube&gt; web02-pod-b]            Pod2C[&lt;fa:fa-cube&gt; web02-pod-c]            Pod3A[&lt;fa:fa-cube&gt; web03-pod-a]            Pod3B[&lt;fa:fa-cube&gt; web03-pod-b]            Pod3C[&lt;fa:fa-cube&gt; web03-pod-c]        end        CF_Edge -- &quot;Tunnel for web01&quot; --&gt; Cloudflared        CF_Edge -- &quot;Tunnel for web02&quot; --&gt; Cloudflared        CF_Edge -- &quot;Tunnel for web03&quot; --&gt; Cloudflared        Cloudflared -- &quot;转发给 web01-svc&quot; --&gt; Svc1        Cloudflared -- &quot;转发给 web02-svc&quot; --&gt; Svc2        Cloudflared -- &quot;转发给 web03-svc&quot; --&gt; Svc3                Svc1 --&gt; Pod1A &amp; Pod1B &amp; Pod1C        Svc2 --&gt; Pod2A &amp; Pod2B &amp; Pod2C        Svc3 --&gt; Pod3A &amp; Pod3B &amp; Pod3C    end        style User fill:#cde4ff    style CF_Edge fill:#ffb366</code></pre><p>方案二示例：</p><pre><code class="highlight mermaid">graph TD    subgraph &quot;用户端&quot;        User[&lt;fa:fa-user&gt; 用户]    end    subgraph &quot;Cloudflare 全球网络&quot;        CF_Edge[&lt;fa:fa-globe&gt; Cloudflare Edge]        User -- &quot;访问 web01/02/03.example.com&quot; --&gt; CF_Edge    end    subgraph &quot;Kubernetes 集群内部&quot;        subgraph &quot;统一网络入口&quot;            Cloudflared[&lt;fa:fa-shield-halved&gt; cloudflared Pods]        end                subgraph &quot;应用路由层 (L7)&quot;            IngressSvc[&lt;fa:fa-network-wired&gt; Ingress Controller Service]            IngressPod[&lt;fa:fa-map-signs&gt; Ingress Controller Pod]        end                subgraph &quot;应用服务 (L4)&quot;            Svc1[&lt;fa:fa-server&gt; web01-svc]            Svc2[&lt;fa:fa-server&gt; web02-svc]            Svc3[&lt;fa:fa-server&gt; web03-svc]        end        subgraph &quot;应用Pods (分布在3个节点上)&quot;            Pod1A[&lt;fa:fa-cube&gt; web01-pod-a]            Pod1B[&lt;fa:fa-cube&gt; web01-pod-b]            Pod1C[&lt;fa:fa-cube&gt; web01-pod-c]                        Pod2A[&lt;fa:fa-cube&gt; web02-pod-a]            Pod2B[&lt;fa:fa-cube&gt; web02-pod-b]            Pod2C[&lt;fa:fa-cube&gt; web02-pod-c]            Pod3A[&lt;fa:fa-cube&gt; web03-pod-a]            Pod3B[&lt;fa:fa-cube&gt; web03-pod-b]            Pod3C[&lt;fa:fa-cube&gt; web03-pod-c]        end        CF_Edge -- &quot;统一入口 Tunnel&quot; --&gt; Cloudflared        Cloudflared -- &quot;所有流量都转发给 Ingress&quot; --&gt; IngressSvc        IngressSvc --&gt; IngressPod        IngressPod -- &quot;读取Ingress规则&lt;br/&gt;if host == web01.example.com&quot; --&gt; Svc1        IngressPod -- &quot;if host == web02.example.com&quot; --&gt; Svc2        IngressPod -- &quot;if host == web03.example.com&quot; --&gt; Svc3                Svc1 --&gt; Pod1A &amp; Pod1B &amp; Pod1C        Svc2 --&gt; Pod2A &amp; Pod2B &amp; Pod2C        Svc3 --&gt; Pod3A &amp; Pod3B &amp; Pod3C    end        style User fill:#cde4ff    style CF_Edge fill:#ffb366    style IngressPod fill:#99ff99</code></pre><h2 id="5-总结"><a href="#5-总结" class="headerlink" title="5. 总结"></a>5. 总结</h2><ul><li>节点间使用原生 <code>WireGuard</code> 全连接网状组网</li><li>节点网段使用 <code>10.66.66.0/24</code></li><li>CNI 插件使用 <code>Calico</code>，网络模式采用 BGP</li><li>Pod 网段使用默认的 <code>10.244.0.0/16</code></li><li>Service 网段使用默认的 <code>10.96.0.0/12</code></li><li>采用 <code>ClusterIP</code> 仅对集群内暴露服务</li><li>部署 <code>ingress</code> 统一服务入口</li><li>使用 <code>cloudflare tunnel</code> 安全引入外部流量到集群内部</li></ul><p>结合实际情况最终还是给出了这样一套不算完美的跨云、跨地域 k8s 集群网络设计思路，对于小型的实验环境(至少对于我)应该算是够用了。</p>]]></content>
    
    
    <summary type="html">在跨云、跨地区的vps节点间如何设计一套合理的k8s组网方案，包含完整的讨论过程和设计思路</summary>
    
    
    
    <category term="k8s" scheme="https://ruams.com/categories/k8s/"/>
    
    
    <category term="k8s" scheme="https://ruams.com/tags/k8s/"/>
    
  </entry>
  
  <entry>
    <title>WireGuard原理解析与生产实践</title>
    <link href="https://ruams.com/posts/a37af523.html"/>
    <id>https://ruams.com/posts/a37af523.html</id>
    <published>2025-02-10T08:21:00.000Z</published>
    <updated>2025-02-11T06:08:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="1-简介"><a href="#1-简介" class="headerlink" title="1. 简介"></a>1. 简介</h2><p>在今天这个云计算、远程办公和物联网设备无处不在的分布式时代里，异地组网需求变的越来越多，实现异地组网的工具也凸显其重要性。WireGuard 就是当前最合适的工具之一。本篇文章将会深入讲解 WireGuard 原理、快速配置和进阶配置方案，后面如果有机会也会再介绍基于 WireGuard 实现的一些其他工具（Tailscale 之流）,不过对于我个人来说我使用 wg 其实是用来为3节点的 k8s 集群做组网的，所以我个人还是更加偏向于原生的 wg 使用而非其他工具。</p><h2 id="2-WireGuard-的哲学和机制"><a href="#2-WireGuard-的哲学和机制" class="headerlink" title="2. WireGuard 的哲学和机制"></a>2. WireGuard 的哲学和机制</h2><h3 id="2-1-设计哲学"><a href="#2-1-设计哲学" class="headerlink" title="2.1 设计哲学"></a>2.1 设计哲学</h3><p>WireGuard 不提供加密算法的选择，而是直接内建了一套被认为是当前最安全且高效的密码学原语组合。</p><ul><li>密钥交换 (ECDH): Curve25519，现代椭圆曲线算法的最佳标准之一</li><li>对称加密: ChaCha20，在缺乏 AES 硬件加速的 CPU（如 ARM）上性能卓越，同时具备极高的安全性</li><li>消息认证 (MAC): Poly1305，与 ChaCha20构成 AEAD（认证加密），确保数据完整性和真实性</li><li>哈希: BLAKE2s，性能和安全性均超越 SHA-2&#x2F;SHA-3</li><li>哈希表键: SipHash24，有效抵御哈希碰撞攻击 (Hash-DoS)</li><li>握手协议: 基于Noise Protocol Framework (Noise_IK)，提供前向保密、身份验证和抗重放攻击能力</li></ul><p>这种设计的直接好处是消除了因配置错误导致的安全漏洞，极大地降低了部署和审计的复杂性。</p><h3 id="2-2-核心机制"><a href="#2-2-核心机制" class="headerlink" title="2.2 核心机制"></a>2.2 核心机制</h3><p>传统的 VPN 依赖复杂的策略或 ACL 来决定哪些流量进入隧道，而 WireGuard 则采用一种极为优雅的机制，即： 通过将对等（Peer）的公钥与其允许的IP地址列表进行强绑定，构建出一张“密码学路由表”。</p><p>当一个数据包到达 <code>wg</code> 接口时：</p><ol><li>内核查找该数据包的目标 IP 地址属于哪个 Peer 的 <code>AllowedIPs</code> 范围</li><li>一旦匹配，内核就使用与该 Peer 公钥关联的对称会话密钥对数据包进行加密3.  反之，当收到一个加密数据包时，内核通过其身份信息（一个静态的 key index）找到对应的解密密钥，解密后验证其源 IP 是否在该 Peer 的 AllowedIPs 列表中。如果不在，数据包将被直接丢弃。</li></ol><p><code>AllowedIPs</code> 因此扮演了路由表和防火墙ACL的双重角色，机制极其简单高效！</p><h2 id="3-WireGuard-流量路径"><a href="#3-WireGuard-流量路径" class="headerlink" title="3. WireGuard 流量路径"></a>3. WireGuard 流量路径</h2><hr><h3 id="3-1-WireGuard-拓扑"><a href="#3-1-WireGuard-拓扑" class="headerlink" title="3.1 WireGuard 拓扑"></a>3.1 WireGuard 拓扑</h3><p>WireGuard 的核心设计: 所有节点皆为 Peer。这赋予了其无与伦比的灵活性，使其能够通过简单的配置文件，构建出多种网络拓扑以适应不同的业务场景。本节具体讲解 WireGuard 组网拓扑的四个场景。</p><h4 id="3-1-1-点对点"><a href="#3-1-1-点对点" class="headerlink" title="3.1.1 点对点"></a>3.1.1 点对点</h4><p>这是最基础的拓扑，也是所有复杂拓扑的基础。它仅连接两个节点，创建一个简单、安全的加密通道。</p><ul><li>结构: <code>[节点 A]</code> &lt;–&gt; <code>[节点 B]</code></li><li>配置特点:<ul><li>节点 A 的配置文件中只有一个 <code>[Peer]</code> 区块，指向节点 B</li><li>节点 B 的配置文件中也只有一个 <code>[Peer]</code> 区块，指向节点 A</li></ul></li><li>适用场景:<ul><li>安全地连接两台重要的服务器（例如，应用服务器与数据库服务器）</li><li>为单个开发人员提供对其远程开发环境的专线访问</li></ul></li></ul><h4 id="3-1-2-中心辐射型-星型"><a href="#3-1-2-中心辐射型-星型" class="headerlink" title="3.1.2 中心辐射型&#x2F;星型"></a>3.1.2 中心辐射型&#x2F;星型</h4><p>这是最常见的一种拓扑，一个中心节点作为网络的枢纽，所有其他分支节点都只与中心节点连接</p><ul><li>结构: 中心节点连接到其他所有节点，其他节点之间没有连接</li><li>配置特点:<ul><li>中心节点: 配置文件中有多个 <code>[Peer]</code> 区块，分别对应每一个分支节点。通常拥有固定的公网 IP 并监听端口</li><li>分支节点: 配置文件中只有一个 <code>[Peer]</code> 区块，指向中心节点。通常 <code>Endpoint</code> 指向中心节点的公网地址</li></ul></li><li>流量路径: 任何两个分支节点之间的通信，都必须经过中心节点进行中转。例如，A 要访问 B，流量路径是 <code>A -&gt; 中心节点 -&gt; B</code></li><li>优势:<ul><li>配置简单: 新增节点只需在中心节点上添加一个 Peer，并在新节点上配置指向中心节点即可</li><li>集中管理与审计: 所有流量都经过中心节点，便于统一管理安全策略、进行流量监控和日志审计</li></ul></li><li>劣势:<ul><li>单点故障: 中心节点一旦宕机，整个网络瘫痪</li><li>性能瓶颈: 所有分支间的流量都汇集于中心，可能造成中心节点的带宽和 CPU 瓶颈</li><li>延迟增加: 分支间的通信需要绕行中心节点，延迟较高</li></ul></li></ul><h4 id="3-1-3-全连接网状"><a href="#3-1-3-全连接网状" class="headerlink" title="3.1.3 全连接网状"></a>3.1.3 全连接网状</h4><p>网络中的每一个节点都与其他所有节点直接建立连接</p><ul><li>结构: 每个节点都像星型拓扑中的中心节点，与其他所有节点互为 Peer</li><li>配置特点:<ul><li>一个包含 N 个节点的网络，每个节点的配置文件中都必须有 <code>N-1</code> 个 <code>Peer</code></li></ul></li><li>流量路径: 任何两个节点之间都存在直接路径</li><li>优势:<ul><li>高可用性: 任意一个节点的故障不会影响其他节点之间的通信</li><li>最低延迟: 节点间通信无需中转，性能最优</li></ul></li><li>劣势:<ul><li>配置复杂: 配置量随着节点数量的增加呈指数级增长。对于多节点的组网，手动配置令人绝望，必须依赖自动化脚本或上层管理平台（如 Netmaker 等）。</li></ul></li></ul><h4 id="3-1-4-部分连接网状"><a href="#3-1-4-部分连接网状" class="headerlink" title="3.1.4 部分连接网状"></a>3.1.4 部分连接网状</h4><p>这是一种介于星型和全连接网状之间的混合拓扑。网络中的关键节点之间相互直连，而非关键节点则可能只连接到部分核心节点。</p><ul><li>结构: 按需连接，形成一个不规则的网状</li><li>配置特点: 每个节点的 Peer 数量根据其在网络中的角色和需求而定</li><li>适用场景:<ul><li>在多集群场景下，让各个集群的核心网关互相直连，而每个集群内部的服务器则与本地网关连接</li><li>性能敏感的应用服务器之间建立直接连接，普通服务器则通过一个或多个集中网关通信</li></ul></li></ul><h4 id="3-1-5-简单总结"><a href="#3-1-5-简单总结" class="headerlink" title="3.1.5 简单总结"></a>3.1.5 简单总结</h4><table><thead><tr><th align="left">拓扑类型</th><th align="left">配置复杂度</th><th align="left">可靠性</th><th align="left">性能&#x2F;延迟</th><th align="left">适用场景</th></tr></thead><tbody><tr><td align="left">点对点</td><td align="left">极低</td><td align="left">-</td><td align="left">最高</td><td align="left">两个节点间的安全通道</td></tr><tr><td align="left">星型</td><td align="left">低</td><td align="left">低 (单点故障)</td><td align="left">差 (需中转)</td><td align="left">远程办公、分支机构互联</td></tr><tr><td align="left">全连接网状</td><td align="left">极高</td><td align="left">最高</td><td align="left">最优</td><td align="left">少节点、高性能、高可用集群</td></tr><tr><td align="left">部分连接网状</td><td align="left">中等</td><td align="left">中到高</td><td align="left">中到优</td><td align="left">复杂、分层的企业网络</td></tr></tbody></table><h3 id="3-1-场景设定"><a href="#3-1-场景设定" class="headerlink" title="3.1 场景设定"></a>3.1 场景设定</h3><p>这里以一个具体的案例来说明在 wg 组网环境下，数据包的流量路径。在描述案例之前必须要说明的是：从 WireGuard 的设计、架构、拓扑等层面来看，它是完全没有客户端和服务端之分的，每一个运行WireGuard的节点，无论其配置如何，在技术上都是一个完全对等的 “对等体（Peer）”，它们遵循相同的协议，拥有相同的功能。后文中提到的服务端和客户端都是不准确（严格说就是错误）的描述，用这个说法的原因是仅仅是方便 “角色定位”,请注意这个细节。</p><p>前提: Host A 与 Host B 之间已经完成了首次握手，并已协商出会话所需的对称加密密钥</p><ul><li>Host A:<ul><li>物理网卡 <code>eth0</code> IP: <code>192.168.1.10</code></li><li>虚拟网卡 <code>wg0</code> IP: <code>10.0.0.1</code></li><li>动作: <code>curl</code> Host B</li></ul></li><li>Host B:<ul><li>物理网卡 <code>eth0</code> IP: <code>192.168.1.20</code></li><li>虚拟网卡 <code>wg0</code> IP: <code>10.0.0.2</code></li><li>动作: Nginx 已监听 <code>10.0.0.2:80</code></li></ul></li></ul><h3 id="3-2-出站流量-Host-A"><a href="#3-2-出站流量-Host-A" class="headerlink" title="3.2 出站流量(Host A)"></a>3.2 出站流量(Host A)</h3><h4 id="3-2-1-阶段一"><a href="#3-2-1-阶段一" class="headerlink" title="3.2.1 阶段一"></a>3.2.1 阶段一</h4><p>用户空间 -&gt; 内核协议栈 (Host A):</p><ol><li><p>应用层 (Userspace): 用户在 Host A 上执行命令 <code>curl http://10.0.0.2</code>。<code>curl</code> 程序通过 <code>socket()</code>、<code>connect()</code>、<code>write()</code> 等标准 POSIX 系统调用(syscall)，请求操作系统建立一个到 <code>10.0.0.2:80</code> 的 TCP 连接并发送 HTTP GET请求</p></li><li><p>内核协议栈 (Kernel Space - TCP&#x2F;IP Stack):</p><ul><li>内核的 TCP 模块接收到请求，开始构建 TCP 段（Segment）。首先是三次握手的 <code>SYN</code> 包，随后是承载 HTTP 数据的 <code>PSH, ACK</code> 包</li><li>IP 模块将 TCP 段封装成 IP 数据包（Packet）。此刻，这个 “内部数据包” 的样子是：<ul><li>IP Header: <code>Source IP: 10.0.0.1</code>, <code>Destination IP: 10.0.0.2</code></li><li>TCP Header: <code>Source Port: &lt;随机端口&gt;</code>, <code>Destination Port: 80</code>, <code>Payload: &quot;GET / HTTP/1.1...&quot;</code></li></ul></li></ul></li></ol><h4 id="3-2-2-阶段二"><a href="#3-2-2-阶段二" class="headerlink" title="3.2.2 阶段二"></a>3.2.2 阶段二</h4><p>内核路由 -&gt; WireGuard 加密 (Host A):</p><ol><li><p>路由决策 (Routing): 内核需要决定从哪个网络接口发送这个 IP 包。它会查询路由表 (<code>ip route show</code>)，发现一条类似 <code>10.0.0.0/24 dev wg0</code> 的规则。因此内核决定将此包交给 <code>wg0</code> 虚拟接口处理</p></li><li><p>WireGuard 核心处理 (<code>wg_xmit</code>):</p></li></ol><ul><li>数据包进入 <code>wg0</code> 接口的发送队列，触发 WireGuard 内核模块的 <code>wg_xmit()</code> 函数</li><li>密码学密钥路由 (Cryptokey Routing): 这是 WireGuard 的灵魂。此模块识别到数据包的目标 IP: <code>10.0.0.2</code>, 接着会遍历其对等（Peer）列表，查找哪个 Peer 的 <code>AllowedIPs</code> 配置包含了 <code>10.0.0.2</code>。最终会找到 Host B 的条目</li><li>内核态加密 (Encryption): WireGuard 模块从与 Host B 的会话中取出预先协商好的对称密钥，使用 <code>ChaCha20-Poly1305</code> 算法对 整个内部 IP 数据包 （从 IP 头到 TCP 数据结束）进行加密</li></ul><h4 id="3-2-3-阶段三"><a href="#3-2-3-阶段三" class="headerlink" title="3.2.3 阶段三"></a>3.2.3 阶段三</h4><p>UDP 封装 -&gt; 物理网络 (Host A):</p><ol><li>UDP 封装 (Encapsulation):</li></ol><ul><li>加密后的数据块成为一个新的 “外部数据包” 的 Payload</li><li>WireGuard 模块为其添加一个简短的 WireGuard 头部（包含密钥索引等信息）</li><li>内核网络栈为其添加 UDP 头部。源端口由操作系统分配，目标端口是 Host B 配置中指定的 <code>Endpoint</code> 端口 <code>51820</code> (默认)</li><li>最后添加外部 IP 头部。源 IP 是物理网卡 IP <code>192.168.1.10</code>，目标 IP 是 Host B 的 Endpoint IP <code>192.168.1.20</code></li><li>此刻即将在物理网络上传输的数据包结构是：<br>  Outer IP Header: <code>Source IP: 192.168.1.10</code>, <code>Destination IP: 192.168.1.20</code><br>  Outer UDP Header: <code>Source Port: &lt;随机&gt;</code>, <code>Destination Port: 51820</code><br>  WireGuard Header<br>  Encrypted Payload: (加密后的 <code>[内部IP包]</code> )</li></ul><ol><li>物理发送: 这个完整的外部 UDP 包根据主路由表，通过物理网卡 <code>eth0</code> 发送出去，并在其外面包上一层以太网帧头</li></ol><h3 id="3-3-入站流量-Host-B"><a href="#3-3-入站流量-Host-B" class="headerlink" title="3.3 入站流量(Host B)"></a>3.3 入站流量(Host B)</h3><h4 id="3-3-1-阶段一"><a href="#3-3-1-阶段一" class="headerlink" title="3.3.1 阶段一"></a>3.3.1 阶段一</h4><p>物理网络 -&gt; WireGuard 解密 (Host B):</p><ol><li><p>物理接收: Host B 的 <code>eth0</code> 网卡接收到以太网帧，剥离帧头后，将外部 IP 包递交给内核</p></li><li><p>内核协议栈 (Kernel Space - IP&#x2F;UDP Stack): 内核 IP 模块检查 IP 头，发现协议是 UDP。UDP 模块检查目标端口是 <code>51820</code> (默认)，发现 WireGuard 模块已经注册监听此端口。于是，该 UDP 包的 Payload 被 直接递交给 WireGuard 模块处理</p></li><li><p>WireGuard 核心处理 (<code>wg_packet_receive</code>):</p></li></ol><ul><li>WireGuard 模块接收到数据。它读取 WireGuard 头部，识别出这是来自 Peer A 的数据包</li><li>内核态解密与验证: 模块使用与 Peer A 关联的对称会话密钥进行解密，并用 <code>Poly1305</code> 验证数据的完整性和真实性</li><li>解密成功，内部 IP 数据包被还原</li></ul><h4 id="3-3-2-阶段二"><a href="#3-3-2-阶段二" class="headerlink" title="3.3.2 阶段二"></a>3.3.2 阶段二</h4><p>ACL 校验 -&gt; 内核协议栈 -&gt; 用户空间 (Host B)</p><ol><li>ACL 校验:</li></ol><ul><li>WireGuard 模块会检查还原后的内部 IP 包的源地址 <code>10.0.0.1</code></li><li>核对 Peer A 的配置，确认 <code>10.0.0.1</code> 是否在其 <code>AllowedIPs</code> (<code>10.0.0.1/32</code>) 范围内</li><li>匹配成功，数据包被接受。如果不匹配，数据包将被静默丢弃</li></ul><ol start="2"><li>重新注入协议栈:</li></ol><ul><li>通过验证的内部 IP 包通过 <code>netif_rx()</code>（或 <code>gro_receive()</code>）函数会被注入到 <code>wg0</code> 虚拟接口的接收队列</li><li>对于内核的其他部分来说，这个过程是透明的。它看起来就像 <code>wg0</code> 网卡“凭空”收到了一个源地址为 <code>10.0.0.1</code> 的普通 IP包。</li></ul><ol start="3"><li>内核协议栈处理:</li></ol><ul><li>内核的 IP&#x2F;TCP 栈开始处理这个“新”收到的 IP 包。它看到目标是 <code>10.0.0.2:80</code></li><li>TCP 模块处理 TCP 协议状态机。</li><li>内核发现 Nginx 进程正在监听此端口，于是将 HTTP GET 请求数据从内核缓冲区拷贝到 Nginx 进程的用户空间缓冲区，并唤醒 Nginx 工作进程</li></ul><ol start="4"><li>应用层: Nginx 收到 HTTP 请求，开始处理并准备 HTTP 响应</li></ol><h3 id="3-4-返回流量（Host-B）"><a href="#3-4-返回流量（Host-B）" class="headerlink" title="3.4 返回流量（Host B）"></a>3.4 返回流量（Host B）</h3><p>流量返回的流程路径与上述完全对称，只是源和目的颠倒，这里快速描述一下：</p><ol><li>Host B (Nginx): 生成 HTTP 200 OK 响应。</li><li>Host B (Kernel): 创建 内部 IP 包: <code>[IP_H(src=10.0.0.2, dst=10.0.0.1)][TCP_H(data=HTTP Response)]</code>。</li><li>Host B (Routing): 路由决策指向 <code>wg0</code> 接口。</li><li>Host B (WireGuard): <code>wg_xmit</code> 触发，根据目标 <code>10.0.0.1</code> 找到 Peer A，加密整个内部 IP 包。</li><li>Host B (Encapsulation): 封装成 外部 UDP 包: <code>[IP_H(src=192.168.1.20, dst=192.168.1.10)][UDP_H][...]</code>。</li><li>Host B (Egress): 通过 <code>eth0</code> 发送。</li><li>Host A (Ingress &amp; Decryption): <code>eth0</code> 收到 UDP 包，递交 WireGuard 模块解密，还原出内部 IP 包。</li><li>Host A (ACL &amp; Re-injection): 校验源 IP <code>10.0.0.2</code> 是否在 Peer B 的 <code>AllowedIPs</code> 内，校验通过后，将包注入 <code>wg0</code>。</li><li>Host A (Kernel &amp; Userspace): 内核 TCP&#x2F;IP 栈处理响应包，将数据交给 <code>curl</code> 进程。</li><li>Host A (curl): <code>curl</code> 收到完整的 HTTP 响应，将其打印到标准输出，流程结束。</li></ol><h2 id="4-适用场景"><a href="#4-适用场景" class="headerlink" title="4. 适用场景"></a>4. 适用场景</h2><h3 id="4-1-核心适用场景"><a href="#4-1-核心适用场景" class="headerlink" title="4.1 核心适用场景"></a>4.1 核心适用场景</h3><ul><li>将分散的设备（服务器、电脑、IoT设备）连接成一个逻辑上统一的私有网络</li><li>在不可信的物理网络（公共互联网、公有云）之上创建一个可信私有网络平面</li><li>对网络访问进行强身份认证（基于密钥），而不是弱认证（基于IP地址）</li></ul><h3 id="4-2-不建议使用场景"><a href="#4-2-不建议使用场景" class="headerlink" title="4.2 不建议使用场景"></a>4.2 不建议使用场景</h3><p>以下场景虽然都有解决（妥协）方案，但是仍然强烈不建议去使用 WireGuard，没有必要为了使用而使用。</p><ol><li>需要动态路由协议: WireGuard 本身是静态的点对点隧道，不广播路由信息<ul><li>解决方案: 结合 BGP 守护进程 (如 BIRD, FRR)。让 BGP 在 WireGuard 隧道之上运行，动态交换路由，构建复杂的大规模网络。</li></ul></li><li>网络环境只允许 TCP: WireGuard 只使用 UDP，在某些严格限制或 UDP 丢包率极高的网络中可能无法工作<ul><li>解决方案: 使用 OpenVPN over TCP 作为备选，或使用 <code>udp2raw</code> 等工具将 UDP 流量伪装成 TCP</li></ul></li><li>需要二层(L2)隧道: WireGuard 工作在三层(L3)，无法传输 ARP、DHCP 等二层广播流量<ul><li>解决方案: 使用 VXLAN over WireGuard 或 GRE over WireGuard 的组合，先用 L2协议封装，再用 WireGuard 加密传输</li></ul></li></ol><h2 id="5-快速配置"><a href="#5-快速配置" class="headerlink" title="5. 快速配置"></a>5. 快速配置</h2><p>WireGuard 各节点配置条目基本都一样，这里仅以两个节点互联(全连接)为例说明。</p><h3 id="5-1-安装命令行工具（所有组网节点）"><a href="#5-1-安装命令行工具（所有组网节点）" class="headerlink" title="5.1 安装命令行工具（所有组网节点）"></a>5.1 安装命令行工具（所有组网节点）</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Ubuntu</span></span><br><span class="line">apt update &amp;&amp; apt install wireguard</span><br><span class="line"></span><br><span class="line"><span class="comment"># RHEL</span></span><br><span class="line">dnf install epel-release -y</span><br><span class="line">dnf install wireguard-tools</span><br></pre></td></tr></table></figure><h3 id="5-2-生成密钥（所有组网节点）"><a href="#5-2-生成密钥（所有组网节点）" class="headerlink" title="5.2 生成密钥（所有组网节点）"></a>5.2 生成密钥（所有组网节点）</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> /etc/wireguard</span><br><span class="line">wg genkey | <span class="built_in">tee</span> privatekey | wg pubkey &gt; publickey</span><br><span class="line"><span class="built_in">chmod</span> 600 privatekey</span><br><span class="line"><span class="built_in">chmod</span> 600 publickey</span><br></pre></td></tr></table></figure><h3 id="5-3-节点-A"><a href="#5-3-节点-A" class="headerlink" title="5.3 节点 A"></a>5.3 节点 A</h3><p><code>/etc/wireguard/wg0.conf</code> 详解：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">[Interface]</span><br><span class="line"><span class="comment"># 本端私钥</span></span><br><span class="line">PrivateKey = xxxxxxxxxxxx</span><br><span class="line"><span class="comment"># 本端在组网中的IP地址</span></span><br><span class="line">Address = 10.66.66.1/24</span><br><span class="line"><span class="comment"># 监听UDP端口，每个节点都建议配置上</span></span><br><span class="line">ListenPort = 51820</span><br><span class="line"><span class="comment"># 统一设置 MTU</span></span><br><span class="line">MTU = 1420</span><br><span class="line"></span><br><span class="line">[Peer]</span><br><span class="line"><span class="comment"># 对端的公钥</span></span><br><span class="line">PublicKey = xxxxxxxxxxxx</span><br><span class="line"><span class="comment"># 对端的公网IP和端口</span></span><br><span class="line">Endpoint = hostB.ip:51820</span><br><span class="line"><span class="comment"># 对端的组网中的IP地址，也可以用掩码写一个网段</span></span><br><span class="line">AllowedIPs = 10.66.66.0/24</span><br><span class="line"><span class="comment"># 保持连接，25秒发一次心跳，最佳建议值</span></span><br><span class="line">PersistentKeepalive = 25</span><br></pre></td></tr></table></figure><h3 id="5-4-节点-B"><a href="#5-4-节点-B" class="headerlink" title="5.4 节点 B"></a>5.4 节点 B</h3><p><code>/etc/wireguard/wg0.conf</code> 和节点 A 配置文件基本一致：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">[Interface]</span><br><span class="line"><span class="comment"># 本端私钥</span></span><br><span class="line">PrivateKey = xxxxxxxxxxxxx</span><br><span class="line"><span class="comment"># 本端在组网中的IP地址</span></span><br><span class="line">Address = 10.66.66.2/24</span><br><span class="line"><span class="comment"># 监听UDP端口，每个节点都建议配置上</span></span><br><span class="line">ListenPort = 51820</span><br><span class="line"><span class="comment"># 统一设置 MTU</span></span><br><span class="line">MTU = 1420</span><br><span class="line"></span><br><span class="line">[Peer]</span><br><span class="line"><span class="comment"># 对端的公钥</span></span><br><span class="line">PublicKey = xxxxxxxxxxxx</span><br><span class="line"><span class="comment"># 对端的公网IP和端口</span></span><br><span class="line">Endpoint = hostA.ip:51820</span><br><span class="line"><span class="comment"># 对端的组网中的IP地址，也可以用掩码写一个网段</span></span><br><span class="line">AllowedIPs = 10.66.66.0/24</span><br><span class="line"><span class="comment"># 保持连接，25秒发一次心跳，最佳建议值</span></span><br><span class="line">PersistentKeepalive = 25</span><br></pre></td></tr></table></figure><h3 id="5-5-启动并验证"><a href="#5-5-启动并验证" class="headerlink" title="5.5 启动并验证"></a>5.5 启动并验证</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 启动并设置开机自启</span></span><br><span class="line"><span class="built_in">sudo</span> wg-quick up wg0</span><br><span class="line"><span class="built_in">sudo</span> systemctl <span class="built_in">enable</span> wg-quick@wg0</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看状态</span></span><br><span class="line"><span class="built_in">sudo</span> wg show</span><br><span class="line"><span class="comment"># 验证网络</span></span><br><span class="line">ping 10.66.66.1</span><br><span class="line">ping 10.66.66.2</span><br></pre></td></tr></table></figure><h2 id="6-注意事项"><a href="#6-注意事项" class="headerlink" title="6. 注意事项"></a>6. 注意事项</h2><h3 id="6-1-MTU-MSS-配置"><a href="#6-1-MTU-MSS-配置" class="headerlink" title="6.1 MTU&#x2F;MSS 配置"></a>6.1 MTU&#x2F;MSS 配置</h3><p>问题: WireGuard 会增加约60-80字节的头部开销。不调整 MTU，可能会存在大数据包通过隧道时被分片影响性能或者直接被丢弃<br>解决方案:<br>1、调整 MTU： 在 <code>wg</code> 接口上设置 <code>MTU = 物理接口MTU - 80</code>。例如，物理接口 <code>eth0</code> MTU 为1500，则 <code>wg0</code> 接口 MTU 应设为1420<br>2、MSS 钳制： <code>MTU= MSS + TCP头部 (20字节) + IP头部 (20字节)</code>,MSS钳制的核心思想是在TCP三次握手的过程中，修改双方通告的MSS值，强制它们使用一个更小的MSS，从而确保后续生成的TCP数据包加上IP和TCP头部后，不会超过我们设定的隧道MTU。在防火墙 <code>PostUp</code> 规则中添加 MSS 钳制规则</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># wg0.conf</span></span><br><span class="line">[Interface]</span><br><span class="line">MTU = 1420</span><br><span class="line">PostUp = iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -o %i -j TCPMSS --set-mss 1380</span><br><span class="line">PostDown = iptables -t mangle -D FORWARD -p tcp --tcp-flags SYN,RST SYN -o %i -j TCPMSS --set-mss 1380</span><br></pre></td></tr></table></figure><h3 id="6-2-NAT与防火墙穿透"><a href="#6-2-NAT与防火墙穿透" class="headerlink" title="6.2 NAT与防火墙穿透"></a>6.2 NAT与防火墙穿透</h3><ul><li><code>ListenPort</code>: 确保服务端防火墙放行该UDP端口</li><li><code>PersistentKeepalive</code>: 在客户端或位于NAT后的Peer上设置（如20秒），定期发送“心跳包”，以保持NAT会话和状态防火墙的连接跟踪条目活跃</li></ul><h3 id="6-3-密钥安全管理"><a href="#6-3-密钥安全管理" class="headerlink" title="6.3 密钥安全管理"></a>6.3 密钥安全管理</h3><ul><li>在任何环境&#x2F;程序中私钥都是唯一身份凭证，绝不可泄露</li><li>建立密钥轮换制度，例如每6-12个月更换一次密钥对</li></ul><h3 id="6-4-路由与DNS泄漏"><a href="#6-4-路由与DNS泄漏" class="headerlink" title="6.4 路由与DNS泄漏"></a>6.4 路由与DNS泄漏</h3><ul><li>路由黑洞: <code>AllowedIPs</code> 配置必须精确，错误的配置会导致流量无法路由或被丢弃</li><li>DNS泄漏: 当客户端 <code>AllowedIPs</code> 设为 <code>0.0.0.0/0, ::/0</code> 以接管所有流量时，必须确保客户端的DNS解析器也指向隧道内的DNS服务器（例如，通过 <code>wg-quick</code> 的 <code>DNS</code> 配置项），否则DNS查询将绕过隧道，存在暴露隐私的安全风险</li></ul><h2 id="7-进阶优化"><a href="#7-进阶优化" class="headerlink" title="7. 进阶优化"></a>7. 进阶优化</h2><p>以下优化为可选项，对大部分的普通 VPS&#x2F;家里云 玩家来说性能提升不大，但是在高负载环境、高性能服务器之间会有明显的性能改善。</p><h3 id="7-1-启用多队列-Multi-Queue"><a href="#7-1-启用多队列-Multi-Queue" class="headerlink" title="7.1 启用多队列 (Multi-Queue)"></a>7.1 启用多队列 (Multi-Queue)</h3><p>WireGuard 会为每个队列创建一个独立的加密&#x2F;解密工作线程，并将其绑定到不同的 CPU 核心上，实现并行处理。 WireGuard 原生支持多队列，可将数据包处理压力分散到多个CPU核心，配置方法如下：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查看当前接口支持的最大队列数</span></span><br><span class="line">ethtool -l wg0</span><br><span class="line"><span class="comment"># 将wg0的收发队列设置为4个 (假设CPU有4核以上)</span></span><br><span class="line"><span class="built_in">sudo</span> ethtool -L wg0 combined 4 </span><br></pre></td></tr></table></figure><h3 id="7-2-调整内核网络缓冲区"><a href="#7-2-调整内核网络缓冲区" class="headerlink" title="7.2 调整内核网络缓冲区"></a>7.2 调整内核网络缓冲区</h3><p>7.2-7.4 这部分是 linux 内核的调优项目不仅仅是针对 WireGuard 的使用。<br>在高吞吐量场景下， linux 内核默认的 socket 缓冲区可能成为瓶颈,增大 TCP&#x2F;UDP 的读写缓冲区上限，允许 WireGuard 在处理突发流量时有更多的缓冲空间，减少丢包风险</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># /etc/sysctl.conf</span></span><br><span class="line">net.core.rmem_max = 20971520 <span class="comment"># 20MB</span></span><br><span class="line">net.core.wmem_max = 20971520</span><br><span class="line"><span class="comment"># 生效</span></span><br><span class="line">sysctl -p</span><br></pre></td></tr></table></figure><h3 id="7-3-设置中断亲和性-IRQ-Affinity"><a href="#7-3-设置中断亲和性-IRQ-Affinity" class="headerlink" title="7.3 设置中断亲和性 (IRQ Affinity)"></a>7.3 设置中断亲和性 (IRQ Affinity)</h3><p>将数据包接收(eth0)和加解密(wg0)分别固定在不同的 CPU 核心上，最大化地利用 CPU 缓存，减少跨核调度开销，避免资源争抢。同时建议将处理网卡中断和处理 WireGuard 加解密任务的CPU核心绑定在同一个NUMA节点上，以避免跨节点内存访问带来的延迟</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1. 查找wg和eth0的中断号(IRQ)</span></span><br><span class="line"><span class="built_in">cat</span> /proc/interrupts | grep -E <span class="string">&#x27;wg|eth0&#x27;</span></span><br><span class="line"><span class="comment"># 2. 假设eth0的队列中断号为30-33, wg0的为34-37</span></span><br><span class="line"><span class="comment"># 绑定eth0的队列到CPU 0-3</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> &#123;0..3&#125;; <span class="keyword">do</span> <span class="built_in">echo</span> <span class="variable">$i</span> &gt; /proc/irq/$((<span class="number">30</span>+i))/smp_affinity_list; <span class="keyword">done</span></span><br><span class="line"><span class="comment"># 绑定wg0的队列到CPU 4-7</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> &#123;0..3&#125;; <span class="keyword">do</span> <span class="built_in">echo</span> $((<span class="number">4</span>+i)) &gt; /proc/irq/$((<span class="number">34</span>+i))/smp_affinity_list; <span class="keyword">done</span></span><br></pre></td></tr></table></figure><h3 id="7-4-开启-GRO-GSO-通用接收-发送卸载"><a href="#7-4-开启-GRO-GSO-通用接收-发送卸载" class="headerlink" title="7.4 开启 GRO&#x2F;GSO (通用接收&#x2F;发送卸载)"></a>7.4 开启 GRO&#x2F;GSO (通用接收&#x2F;发送卸载)</h3><p>GSO 允许 WireGuard 在加密前将多个小包聚合成一个大包；GRO 则是在物理网卡层面将收到的多个相关小包聚合成大包再交给上层处理。这能极大减少内核处理数据包的次数，降低 CPU 负载</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 一般默认是开启的，使用以下命令可以确认</span></span><br><span class="line">ethtool -k eth0 | grep <span class="string">&#x27;generic-receive-offload&#x27;</span></span><br><span class="line">ethtool -k wg0 | grep <span class="string">&#x27;generic-send-offload&#x27;</span></span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">WireGuard原理深度解析与生产配置实践及优化</summary>
    
    
    
    <category term="linux" scheme="https://ruams.com/categories/linux/"/>
    
    
    <category term="linux" scheme="https://ruams.com/tags/linux/"/>
    
    <category term="WireGuard" scheme="https://ruams.com/tags/WireGuard/"/>
    
  </entry>
  
  <entry>
    <title>containerd配置国内镜像加速</title>
    <link href="https://ruams.com/posts/3ed18a31.html"/>
    <id>https://ruams.com/posts/3ed18a31.html</id>
    <published>2024-11-06T01:48:00.000Z</published>
    <updated>2024-11-06T02:16:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>因为新版本 k8s 底层容器运行时换成了 containerd，所以记录一下镜像加速配置方法顺便学习下这个看着让人一脸问号的 containerd<br>相较于传统 Docker 镜像加速配置方法，containerd 的配置实在是略显麻烦啊…</p><h2 id="1-containerd-简介"><a href="#1-containerd-简介" class="headerlink" title="1. containerd 简介"></a>1. containerd 简介</h2><h3 id="1-1-Kubernetes-CRI-与-containerd"><a href="#1-1-Kubernetes-CRI-与-containerd" class="headerlink" title="1.1 Kubernetes CRI 与 containerd"></a>1.1 Kubernetes CRI 与 containerd</h3><p>在早期的 K8s 版本中，<code>kubelet</code> 组件直接与 Docker Daemon 交互来管理容器。但社区考虑扩展性为了使 K8s 能够支更多样的容器运行时（如 rkt, Kata Containers），社区推出了容器运行时接口(Container Runtime Interface, 即CRI)。CRI 是一套标准的、基于 gRPC 的 API 规范，它解耦了 <code>kubelet</code> 与具体容器运行时之间的强依赖关系。</p><p><code>containerd</code> 正是 CRI 规范的一种标准实现工具。经过近两年发展 <code>kubelet</code> 已经不再与 Docker Daemon 对话，而是通过 CRI 接口，向 <code>containerd</code> 发出指令，如“拉取这个镜像”、“创建 Pod 沙箱”、“启动容器”等。</p><p>工作流程如下：</p><img src= "/img/load.gif" data-lazy-src="https://image.ruams.com/containerd.webp" style="zoom:50%" /><p>从图中可见，当 K8s 需要部署一个 Pod 时：</p><ol><li><code>kubelet</code> 接收到指令，分析 Pod 定义中所需的容器镜像</li><li><code>kubelet</code> 通过 CRI 的 <code>PullImage</code> RPC 调用，请求 <code>containerd</code> 确保该镜像存在于本地</li><li><code>containerd</code> 检查本地镜像存储。如果镜像不存在，它将负责根据自身的配置从远程镜像仓库拉取镜像</li></ol><p>这里的关键点在于，执行镜像拉取操作的直接负责人是 <code>containerd</code>，因此，加速配置必须在 <code>containerd</code> 层面进行，才能对 K8s 集群生效。</p><h3 id="1-2-需要加速的镜像"><a href="#1-2-需要加速的镜像" class="headerlink" title="1.2 需要加速的镜像"></a>1.2 需要加速的镜像</h3><ul><li><code>registry.k8s.io</code>: K8s 官方镜像仓库，存放着 k8s 核心组件的容器镜像</li><li><code>docker.io</code>: 全球最大的公共镜像中心，平时使用到的大部分容器镜像都存在里面</li><li><code>gcr.io</code>: Google 镜像仓库，存放 Google 相关的一些容器镜像</li><li><code>quay.io</code>: RedHat 运营的镜像仓库，也有许多核心开源项目的镜像存在里面</li><li><code>ghcr.io</code>: GitHub 运营的镜像仓库，有很多开源项目的镜像也会选择存放在里面</li></ul><p>在国内节点部署、使用 k8s 的时候，前面两个镜像必须要配置加速，否则必然是会镜像拉取失败的，剩下几个按需配置即可。</p><h2 id="2-配置解析"><a href="#2-配置解析" class="headerlink" title="2. 配置解析"></a>2. 配置解析</h2><p>认识一下这个看着比 Docker 麻烦很多的配置文件。</p><h3 id="2-1-config-toml-的结构"><a href="#2-1-config-toml-的结构" class="headerlink" title="2.1 config.toml 的结构"></a>2.1 <code>config.toml</code> 的结构</h3><p><code>containerd</code> 的主配置文件位于 <code>/etc/containerd/config.toml</code>，采用 <code>TOML</code> 格式。TOML 格式具有清晰的层级结构和良好的可读性，现在在云原生项目中出现的频率确实是明显多了很多！</p><p><code>containerd</code> 配置文件的整体架构是 “插件化” 形式, 文件里面也是众多插件的配置条目。CRI 服务本身也是其众多插件中的一个，其官方名称为 <code>io.containerd.grpc.v1.cri</code>。因此所有与 CRI 相关的配置，都必须放置在这个插件的命名空间下。</p><p>具体来看下这个配置项：</p><p><code>[plugins.&quot;io.containerd.grpc.v1.cri&quot;.registry.mirrors.&quot;docker.io&quot;]</code></p><ul><li><code>[plugins.&quot;...&quot;]</code>：表示这是一个插件配置区块</li><li><code>&quot;io.containerd.grpc.v1.cri&quot;</code>：指定了配置的目标是 CRI 插件。</li><li><code>.registry</code>：表示我们正在配置该插件的 <code>registry</code>（镜像仓库）相关功能</li><li><code>.mirrors</code>：进一步指定是配置镜像的 <code>mirrors</code>（这里即是我们的镜像加速器）</li><li><code>.&quot;docker.io&quot;</code>：明确了此配置块只针对 <code>docker.io</code> 这个上游仓库生效</li></ul><p>官方背书： 每个配置项都拥有唯一的、不会产生冲突的路径、具有清晰的配置导向，也为未来新增更多功能插件而不会破坏现有结构提供了保障<br>在我看来： 真是挺麻烦的！</p><h3 id="2-2-进一步分析"><a href="#2-2-进一步分析" class="headerlink" title="2.2 进一步分析"></a>2.2 进一步分析</h3><p>关键配置区块:</p><figure class="highlight toml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">[plugins.&quot;io.containerd.grpc.v1.cri&quot;.registry.mirrors.&quot;docker.io&quot;]</span></span><br><span class="line">  <span class="attr">endpoint</span> = [<span class="string">&quot;https://mirror1.com&quot;</span>, <span class="string">&quot;https://mirror2.com&quot;</span>]</span><br></pre></td></tr></table></figure><p>简单搂一眼这个部分源码，该配置对应的源码定义在 <code>containerd/pkg/cri/apis/config/config.go</code> 中：</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Registry is registry settings &quot;plugins.cri.registry&quot;</span></span><br><span class="line"><span class="keyword">type</span> Registry <span class="keyword">struct</span> &#123;</span><br><span class="line">    <span class="comment">// ... 省略 ...</span></span><br><span class="line">Mirrors <span class="keyword">map</span>[<span class="type">string</span>]Mirror <span class="string">`toml:&quot;mirrors&quot;`</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Mirror contains the configuration for a registry mirror.</span></span><br><span class="line"><span class="keyword">type</span> Mirror <span class="keyword">struct</span> &#123;</span><br><span class="line"><span class="comment">// Endpoint specifies the endpoints of a mirror. The scheme part is optional.</span></span><br><span class="line"><span class="comment">// When the scheme is not specified, &quot;https://&quot;&quot; is used.</span></span><br><span class="line"><span class="comment">// The endpoint will be tried in order.</span></span><br><span class="line">Endpoint []<span class="type">string</span> <span class="string">`toml:&quot;endpoint&quot;`</span></span><br><span class="line">    <span class="comment">// ... 省略 ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>从源码中我们可以得到几个关键信息：</p><ol><li><code>Mirrors</code> 是一个 <code>map[string]Mirror</code> 类型：<code>key</code>（即 <code>&quot;docker.io&quot;</code>）是原始仓库地址，<code>value</code> 是 <code>Mirror</code> 结构体。从这里可以看出我们可以为不同仓库配置不同加速镜像源</li><li><code>Endpoint</code> 是一个 <code>[]string</code> 类型（字符串切片）： 这表明 <code>containerd</code> 原生即支持配置多个 <code>endpoint</code></li><li>源码注释明确指出 “The endpoint will be tried in order.”： <code>containerd</code> 会按照数组中定义的顺序，依次尝试连接 <code>endpoint</code> 来拉取镜像。一旦其中一个成功，它就会停止尝试并开始下载。</li></ol><h2 id="3-快速配置"><a href="#3-快速配置" class="headerlink" title="3. 快速配置"></a>3. 快速配置</h2><p>上面都是顺带简单了解下 containerd 的内容，现在回到镜像加速源具体配置方法</p><h3 id="3-1-生成默认配置"><a href="#3-1-生成默认配置" class="headerlink" title="3.1 生成默认配置"></a>3.1 生成默认配置</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">mkdir</span> -p /etc/containerd</span><br><span class="line"></span><br><span class="line"><span class="comment"># 生成默认配置文件</span></span><br><span class="line">containerd config default | <span class="built_in">sudo</span> <span class="built_in">tee</span> /etc/containerd/config.toml</span><br></pre></td></tr></table></figure><h3 id="3-2-配置方法"><a href="#3-2-配置方法" class="headerlink" title="3.2 配置方法"></a>3.2 配置方法</h3><p>编辑 <code>/etc/containerd/config.toml</code> 文件，找到 <code>[plugins.&quot;io.containerd.grpc.v1.cri&quot;.registry.mirrors]</code> 部分，替换补充以下内容。这是一个涵盖了绝大部分场景的推荐配置：</p><figure class="highlight toml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># /etc/containerd/config.toml</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 确保使用 v2 版本的配置格式</span></span><br><span class="line"><span class="attr">version</span> = <span class="number">2</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ... 省略其他配置 ...</span></span><br><span class="line"></span><br><span class="line"><span class="section">[plugins.&quot;io.containerd.grpc.v1.cri&quot;.registry]</span></span><br><span class="line">  <span class="comment"># config_path不再是推荐的主要配置方式，直接在下面定义mirrors更清晰</span></span><br><span class="line">  <span class="attr">config_path</span> = <span class="string">&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">  <span class="section">[plugins.&quot;io.containerd.grpc.v1.cri&quot;.registry.mirrors]</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 1. k8s 官方镜像 -- registry.k8s.io</span></span><br><span class="line">    <span class="comment"># 必须配置，这是K8s核心组件的来源</span></span><br><span class="line">    <span class="section">[plugins.&quot;io.containerd.grpc.v1.cri&quot;.registry.mirrors.&quot;registry.k8s.io&quot;]</span></span><br><span class="line">      <span class="attr">endpoint</span> = [<span class="string">&quot;https://k8s.m.daocloud.io&quot;</span>]</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 2. DockerHub -- docker.io</span></span><br><span class="line">    <span class="comment"># 必须配置，绝大部分应用镜像的来源，配置多个作为冗余避免爱发电暴毙</span></span><br><span class="line">    <span class="section">[plugins.&quot;io.containerd.grpc.v1.cri&quot;.registry.mirrors.&quot;docker.io&quot;]</span></span><br><span class="line">      <span class="attr">endpoint</span> = [</span><br><span class="line">        <span class="string">&quot;https://docker.1ms.run&quot;</span>,</span><br><span class="line">        <span class="string">&quot;https://hub-mirror.c.163.com&quot;</span>,</span><br><span class="line">        <span class="string">&quot;https://mirror.baidubce.com&quot;</span>,</span><br><span class="line">        <span class="string">&quot;https://docker.m.daocloud.io&quot;</span></span><br><span class="line">      ]</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 3. gcr.io</span></span><br><span class="line">    <span class="comment"># 按需可选配置</span></span><br><span class="line">    <span class="section">[plugins.&quot;io.containerd.grpc.v1.cri&quot;.registry.mirrors.&quot;gcr.io&quot;]</span></span><br><span class="line">      <span class="attr">endpoint</span> = [<span class="string">&quot;gcr.proxy.ustclug.org&quot;</span>]</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 4. quay.io</span></span><br><span class="line">    <span class="comment"># 按需可选配置</span></span><br><span class="line">    <span class="section">[plugins.&quot;io.containerd.grpc.v1.cri&quot;.registry.mirrors.&quot;quay.io&quot;]</span></span><br><span class="line">      <span class="attr">endpoint</span> = [<span class="string">&quot;quay.proxy.ustclug.org&quot;</span>]</span><br><span class="line">      </span><br><span class="line">    <span class="comment"># 5. ghcr.io</span></span><br><span class="line">    <span class="comment"># 按需可选配置</span></span><br><span class="line">    <span class="section">[plugins.&quot;io.containerd.grpc.v1.cri&quot;.registry.mirrors.&quot;ghcr.io&quot;]</span></span><br><span class="line">      <span class="attr">endpoint</span> = [<span class="string">&quot;ghcr.proxy.ustclug.org&quot;</span>]</span><br></pre></td></tr></table></figure><p>额外说明：</p><p>镜像源选择: 上述配置的镜像仅为个人设置且当前时间点(2024-11-06)可用的，有些镜像是爱发电的，不保证哪天会突然暴毙！如已有失效的自行更改就好</p><h3 id="3-3-验证"><a href="#3-3-验证" class="headerlink" title="3.3 验证"></a>3.3 验证</h3><p>修改配置后，必须重启 <code>containerd</code> 才能使其加载新配置</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">systemctl daemon-reload</span><br><span class="line">systemctl restart containerd</span><br></pre></td></tr></table></figure><p>这里注意下验证配置是否生效，不能使用 <code>docker pull</code>，而应该使用 <code>crictl</code> 工具。<code>crictl</code> 是一个专门用于与 CRI 兼容的容器运行时进行交互的命令行工具</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 拉取一个docker.io的镜像进行测试</span></span><br><span class="line">crictl pull busybox</span><br></pre></td></tr></table></figure><p>如果命令能够快速成功返回，并且没有出现网络超时错误，说明配置已正确生效</p>]]></content>
    
    
    <summary type="html">containerd配置解析以及在国内节点部署k8s的时候，为containerd配置国内镜像加速源</summary>
    
    
    
    <category term="k8s" scheme="https://ruams.com/categories/k8s/"/>
    
    
    <category term="k8s" scheme="https://ruams.com/tags/k8s/"/>
    
  </entry>
  
  <entry>
    <title>Linux 中没有 telnet 等工具如何快速检测端口连通性</title>
    <link href="https://ruams.com/posts/671f32e2.html"/>
    <id>https://ruams.com/posts/671f32e2.html</id>
    <published>2022-12-27T16:00:00.000Z</published>
    <updated>2022-12-27T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>在 Linux 环境中测试本机与远程主机端口的网络连通性时我们一般都会使用 telnet 和 nc 进行测试，即使在 telnet 和 nc 工具都没有的前提下也可以使用类似 wget 和 curl 等工具变相实现端口测试的目的。然而最近发现在容器中会有更恶劣的条件，即 telnet nc curl wget nmap socat 等能够想到的工具全都没有，这种情况下似乎没有任何办法了。不过没多久在和几个搞安全的同学聊天的时候意外了解到了反弹 shell 这个操作，由此发现还可以使用 bash 自带的 <code>/dev/tcp</code> 和 <code>/dev/udp</code> 来解决这个问题！</p><h3 id="什么是-dev-tcp-和-dev-udp"><a href="#什么是-dev-tcp-和-dev-udp" class="headerlink" title="什么是 /dev/tcp 和 /dev/udp"></a>什么是 <code>/dev/tcp</code> 和 <code>/dev/udp</code></h3><p>我们都或多或少的听说过在 linux 中 “一切皆文件“，这两个便是 linux 中的特殊文件，可以通过它们使用 TCP 或 UDP 协议与外界（本机也行）进行网络通信。也就是说，我们可以直接向目标主机的指定端口发送数据，而不需要任何额外的程序。下面说明如何使用它们。</p><h3 id="测试-TCP-端口连通性"><a href="#测试-TCP-端口连通性" class="headerlink" title="测试 TCP 端口连通性"></a>测试 TCP 端口连通性</h3><p>首先，打开你的终端，输入以下命令来测试 TCP 连接：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">echo</span> hello &gt; /dev/tcp/目标主机/IP/端口</span><br></pre></td></tr></table></figure><p>举个例子，假设你想测试与 <code>192.168.1.10</code> 的 80 端口的连接，可以这样写：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">echo</span> hello &gt; /dev/tcp/192.168.1.10/80</span><br></pre></td></tr></table></figure><p>接下来，你可以根据返回检查连接状态：</p><ul><li>如果连接成功，终端不会显示任何输出，并且状态码为 0。</li><li>如果连接失败，终端会给出错误信息例如 <code>refused</code> 或 <code>no route to host</code></li><li>如果连接超时，终端则会一直没反应，一般超过三秒没任何动静就可以认为是 timeout 了</li></ul><h3 id="测试-UDP-端口连通性"><a href="#测试-UDP-端口连通性" class="headerlink" title="测试 UDP 端口连通性"></a>测试 UDP 端口连通性</h3><p>UDP 的测试稍微复杂一点，因为它是无连接的协议，不能像 TCP 那样直接使用 <code>echo</code> 后根据返回值进行判断，即便发送后没有任何返回，这也不一定意味着连接失败。但是我们可以到目标主机上启动一个 udp 端口，然后在源端使用 <code>/dev/udp</code> 的方法给该端口发送数据，如果网络是正常连通的则在目标主机上可以收到源端发送的数据。<br>比如，在源端使用：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">echo</span> <span class="string">&quot;hello world&quot;</span> &gt; /dev/udp/192.168.1.30/4888</span><br></pre></td></tr></table></figure><p>那么在网络连通的情况下，目标主机 <code>192.168.1.30</code> 的 4888 端口是可以收到源端发送的字符 <code>hello world</code> 的！</p>]]></content>
    
    
    <summary type="html">在没有telnet、curl、wget、nmap等网络工具的情况下，如何在 Linux 系统中利用 /dev/tcp 和 /dev/udp 进行端口连通性测试。</summary>
    
    
    
    <category term="linux" scheme="https://ruams.com/categories/linux/"/>
    
    
    <category term="linux" scheme="https://ruams.com/tags/linux/"/>
    
    <category term="巧思妙计" scheme="https://ruams.com/tags/%E5%B7%A7%E6%80%9D%E5%A6%99%E8%AE%A1/"/>
    
  </entry>
  
  <entry>
    <title>如何恢复被误删除的 /boot 文件</title>
    <link href="https://ruams.com/posts/bdee7e49.html"/>
    <id>https://ruams.com/posts/bdee7e49.html</id>
    <published>2022-12-17T16:00:00.000Z</published>
    <updated>2022-12-17T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="boot-文件全部删除后如何恢复？"><a href="#boot-文件全部删除后如何恢复？" class="headerlink" title="&#x2F;boot 文件全部删除后如何恢复？"></a>&#x2F;boot 文件全部删除后如何恢复？</h2><p>&#x2F;boot 文件夹下除了 grub2 目录之外其他文件均可由 kernel-3.10.0-xxx 安装后生成。</p><h4 id="1-首先生成系统启动所需-vmlinux-和-initramfs"><a href="#1-首先生成系统启动所需-vmlinux-和-initramfs" class="headerlink" title="1.首先生成系统启动所需 vmlinux 和 initramfs"></a>1.首先生成系统启动所需 vmlinux 和 initramfs</h4><p>挂镜像进 troubleshoot - rescue system - 1 - shell</p><p>cp &#x2F;run&#x2F;install&#x2F;repo&#x2F;Packages&#x2F;kernel-3.10.0-xxx.rpm &#x2F;mnt&#x2F;sysimage&#x2F;boot<br>chroot &#x2F;mnt&#x2F;sysimage<br>rpm -ivh kernel-3.10.0-xxx.rpm –force</p><h4 id="2-生成-grub-引导"><a href="#2-生成-grub-引导" class="headerlink" title="2.生成 grub 引导"></a>2.生成 grub 引导</h4><p>生成&#x2F;boot&#x2F;grub2 相关目录:<br>grub2-install &#x2F;dev&#x2F;sda</p><p>生成引导配置文件：<br>grub2-mkconfig -o &#x2F;boot&#x2F;grub2&#x2F;grub.cfg</p><h4 id="3-设置引导选项"><a href="#3-设置引导选项" class="headerlink" title="3.设置引导选项"></a>3.设置引导选项</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">awk -F\&#x27; <span class="string">&#x27;$1==&quot;menuentry &quot; &#123;print $2&#125;&#x27;</span> /etc/grub2.cfg</span><br></pre></td></tr></table></figure><p>grub2-set-default “xxx”<br>grub2-editenv list</p>]]></content>
    
    
    <summary type="html">Centos7 中误删除了 /boot 文件夹如何进行恢复</summary>
    
    
    
    <category term="linux" scheme="https://ruams.com/categories/linux/"/>
    
    
    <category term="linux" scheme="https://ruams.com/tags/linux/"/>
    
  </entry>
  
  <entry>
    <title>使用 iptables 重定向指定来源 ip 访问本机 22 端口的流量</title>
    <link href="https://ruams.com/posts/a0da3ec3.html"/>
    <id>https://ruams.com/posts/a0da3ec3.html</id>
    <published>2022-11-17T16:00:00.000Z</published>
    <updated>2022-11-17T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>命令如下：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">iptables -t nat -A PREROUTING -p tcp -s 123.60.99.6 --dport 22 -j REDIRECT --to-port 48763</span><br></pre></td></tr></table></figure><p>将源地址访问 22 端口的流量重定向到 48763 不影响其他端口，可以避免不安全的 ssh 端口访问，亦或实现蒙混安全漏洞扫描的目的。</p>]]></content>
    
    
    <summary type="html">使用 iptables 重定向指定来源 ip 访问本机 22 端口的流量</summary>
    
    
    
    <category term="linux" scheme="https://ruams.com/categories/linux/"/>
    
    
    <category term="linux" scheme="https://ruams.com/tags/linux/"/>
    
    <category term="巧思妙计" scheme="https://ruams.com/tags/%E5%B7%A7%E6%80%9D%E5%A6%99%E8%AE%A1/"/>
    
  </entry>
  
  <entry>
    <title>每月指定时间赋权 sudo 其余时间自动回收</title>
    <link href="https://ruams.com/posts/20bc0216.html"/>
    <id>https://ruams.com/posts/20bc0216.html</id>
    <published>2022-09-27T16:00:00.000Z</published>
    <updated>2022-09-27T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>每月26-28日为用户配置sudo权限，其余时间回收 sudo</p><p>脚本 <code>sudo_access.sh</code> 如下</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取当前日期的日</span></span><br><span class="line">day=$(<span class="built_in">date</span> +%d)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 检查当前日期是否在26-28之间</span></span><br><span class="line"><span class="keyword">if</span> [[ <span class="variable">$day</span> -ge 26 &amp;&amp; <span class="variable">$day</span> -le 28 ]]; <span class="keyword">then</span></span><br><span class="line">    <span class="comment"># 赋予sudo权限</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;&lt;用户名&gt; ALL=(ALL) ALL&quot;</span> &gt;&gt; /etc/sudoers.d/sudo_access</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">    <span class="comment"># 回收sudo权限</span></span><br><span class="line">    sed -i <span class="string">&quot;/&lt;用户名&gt; ALL=(ALL) ALL/d&quot;</span> /etc/sudoers.d/sudo_access</span><br><span class="line"><span class="keyword">fi</span></span><br></pre></td></tr></table></figure><p>添加定时任务每日检查</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> <span class="built_in">mv</span> sudo_access.sh /etc/cron.daily/</span><br><span class="line"></span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">chmod</span> +x /etc/cron.daily/sudo_access.sh</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">为用户配置每月指定日期范围内赋权 sudo 其余时间自动回收</summary>
    
    
    
    <category term="linux" scheme="https://ruams.com/categories/linux/"/>
    
    
    <category term="linux" scheme="https://ruams.com/tags/linux/"/>
    
    <category term="巧思妙计" scheme="https://ruams.com/tags/%E5%B7%A7%E6%80%9D%E5%A6%99%E8%AE%A1/"/>
    
  </entry>
  
  <entry>
    <title>sudo 权限绕过和缓冲区溢出漏洞修复</title>
    <link href="https://ruams.com/posts/c90d3579.html"/>
    <id>https://ruams.com/posts/c90d3579.html</id>
    <published>2022-09-10T16:00:00.000Z</published>
    <updated>2022-09-10T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="详细描述"><a href="#详细描述" class="headerlink" title="详细描述"></a>详细描述</h2><h3 id="sudo权限绕过-CVE-2019-14287"><a href="#sudo权限绕过-CVE-2019-14287" class="headerlink" title="sudo权限绕过(CVE-2019-14287)"></a>sudo权限绕过(CVE-2019-14287)</h3><p>Sudo 中存在权限绕过漏洞，当 sudo 配置为允许用户通过 Runas 规范中定义的 ALL 关键字来以任意用户身份运行命令的话，那么攻击者将有可能通过制定用户 ID -1 或 4294967295 来以 root 权限执行恶意命令。这可以允许具有足够 sudo 特权的用户以 root 身份运行命令，即使 Runas 规范明确禁止 root 访问。</p><h3 id="缓冲区溢出漏洞-CVE-2021-3156"><a href="#缓冲区溢出漏洞-CVE-2021-3156" class="headerlink" title="缓冲区溢出漏洞(CVE-2021-3156)"></a>缓冲区溢出漏洞(CVE-2021-3156)</h3><p>Sudo 存在缓存区溢出漏洞，该漏洞由于 Sudo 错误地在参数中转义了反斜杠导致堆缓冲区溢出，允许攻击者通过 sudoedit -s 和以单个反斜杠字符结尾的命令行参数将权限提升为 root。并非所有存在漏洞的 Sudo 版本都能利用成功，glibc&gt;&#x3D;2.27 只能说明漏洞利用难度较小，低版本的 glibc 也能利用成功，只是漏洞利用难度增加。</p><h2 id="解决办法"><a href="#解决办法" class="headerlink" title="解决办法"></a>解决办法</h2><p>升级 sudo 版本<br>以下升级版本包仅针对 rhel7 版本</p><h3 id="CVE-2019-14287"><a href="#CVE-2019-14287" class="headerlink" title="CVE-2019-14287"></a>CVE-2019-14287</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">sudo-1.8.23-4.el7_7.1.x86_64.rpm           SHA-256: 7d831c2183201785fe11fbc353f123776bf2e24af76b26716c7b8a6c47f431cd</span><br><span class="line"></span><br><span class="line">sudo-debuginfo-1.8.23-4.el7_7.1.x86_64.rpm SHA-256: d98c0d8b596984c38c11e6d0c80693d4631dc0623a1a60cc902821e066a23f03</span><br><span class="line"></span><br><span class="line">sudo-devel-1.8.23-4.el7_7.1.x86_64.rpm     SHA-256: 6029de2bd54377f059af74a5367eec5b195a9454c7c6c8c31731dab43ac7b86d</span><br></pre></td></tr></table></figure><h3 id="CVE-2021-3156"><a href="#CVE-2021-3156" class="headerlink" title="CVE-2021-3156"></a>CVE-2021-3156</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">sudo-1.8.23-10.el7_9.1.x86_64.rpm             SHA-256: ce4dcf37c40b87c206d7b936811e6f62d7432082dbe15cf513b74c372504b6e5</span><br><span class="line"></span><br><span class="line">sudo-debuginfo-1.8.23-10.el7_9.1.x86_64.rpm   SHA-256: 561fa8a3eb3ba59bbaf31b9454031dfa65418bf9de50102b3659c7f70c6b3356</span><br><span class="line"></span><br><span class="line">sudo-devel-1.8.23-10.el7_9.1.x86_64.rpm       SHA-256: 9a26b6ccfc6b67d94130bf699835b5a056b346a8c51ba33a47f09d4907dc59c1</span><br></pre></td></tr></table></figure><p>综合以上，rhel7 应升级 sudo 版本至 <font color="#ff0000">sudo-1.8.23-10.el7_9.1.x86_64.rpm</font></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[[rpm包下载]]</span><br></pre></td></tr></table></figure><h2 id="风险级别"><a href="#风险级别" class="headerlink" title="风险级别"></a>风险级别</h2><p>高危</p><h2 id="责任归属"><a href="#责任归属" class="headerlink" title="责任归属"></a>责任归属</h2><p>系统管理员</p><h2 id="加固风险"><a href="#加固风险" class="headerlink" title="加固风险"></a>加固风险</h2><p>低风险操作，无需重启服务，只可能影响配置了 sudo 权限的用户使用 sudo</p><h2 id="链接"><a href="#链接" class="headerlink" title="链接"></a>链接</h2><p><a href="https://access.redhat.com/security/cve/CVE-2019-14287">CVE-2019-14287- Red Hat Customer Portal</a></p>]]></content>
    
    
    <summary type="html">OpenSSH 用户枚举漏洞(CVE-2018-15473) 修复</summary>
    
    
    
    <category term="linux" scheme="https://ruams.com/categories/linux/"/>
    
    
    <category term="linux" scheme="https://ruams.com/tags/linux/"/>
    
    <category term="安全漏洞" scheme="https://ruams.com/tags/%E5%AE%89%E5%85%A8%E6%BC%8F%E6%B4%9E/"/>
    
  </entry>
  
  <entry>
    <title>Polkit 本地权限提升漏洞 CVE-2021-4034 修复</title>
    <link href="https://ruams.com/posts/3a0c9a28.html"/>
    <id>https://ruams.com/posts/3a0c9a28.html</id>
    <published>2022-08-22T16:00:00.000Z</published>
    <updated>2022-08-22T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="详细描述"><a href="#详细描述" class="headerlink" title="详细描述"></a>详细描述</h2><p>polkit 是一个在类 Unix 操作系统中控制系统范围权限的组件。通过定义和审核权限规则，实现不同优先级进程间的通讯。<br>polkit 存在本地权限提升漏洞，由于 pkexec 无法正确处理调用参数计数，攻击者可利用该漏洞通过精心设计环境变量诱导 pkexec 执行任意代码，具有低权限的攻击者可以利用此漏洞绕过 pkexec 自带的安全保护措施，获取目标机器的 ROOT 权限。</p><h2 id="解决办法"><a href="#解决办法" class="headerlink" title="解决办法"></a>解决办法</h2><ul><li>根据官方修复建议升级 polkit 版本</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">polkit-0.112-26.el7_9.1.x86_64.rpm            SHA-256: 7f4cd400e1cf20c30290641befa5a900fe425070e9573866f242f9acc3bda048</span><br><span class="line"></span><br><span class="line">polkit-debuginfo-0.112-26.el7_9.1.x86_64.rpm  SHA-256: aa3d5197c9cd4598c7162fb1a5df04437552acfc02fe14143ce8e8132f807df0</span><br><span class="line"></span><br><span class="line">polkit-devel-0.112-26.el7_9.1.x86_64.rpm      SHA-256: 1c5179227026b66d92fbfe14b35449db482e70b2cc64906a01d57882124c44bc</span><br><span class="line"></span><br><span class="line">polkit-docs-0.112-26.el7_9.1.noarch.rpm       SHA-256: 6a8da2909d06acc1763fcacb82068442425b3a87f13295469670897327fb5944</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[[rpm包下载]]</span><br></pre></td></tr></table></figure><h2 id="风险级别"><a href="#风险级别" class="headerlink" title="风险级别"></a>风险级别</h2><p><font color=#f20707>高风险</font></p><h2 id="责任归属"><a href="#责任归属" class="headerlink" title="责任归属"></a>责任归属</h2><p>系统管理员</p><h2 id="加固风险"><a href="#加固风险" class="headerlink" title="加固风险"></a>加固风险</h2><p>低风险操作，无需重启服务</p><h2 id="链接"><a href="#链接" class="headerlink" title="链接"></a>链接</h2><p><a href="https://access.redhat.com/security/cve/cve-2021-4034">CVE-2021-4034- Red Hat Customer Portal</a><br><a href="https://access.redhat.com/errata/RHSA-2022:0274">RHEL-7-polkit修复链接</a></p>]]></content>
    
    
    <summary type="html">polkit cve 漏洞 CVE-2021-4034 本地权限提升修复</summary>
    
    
    
    <category term="linux" scheme="https://ruams.com/categories/linux/"/>
    
    
    <category term="linux" scheme="https://ruams.com/tags/linux/"/>
    
    <category term="安全漏洞" scheme="https://ruams.com/tags/%E5%AE%89%E5%85%A8%E6%BC%8F%E6%B4%9E/"/>
    
  </entry>
  
  <entry>
    <title>OpenSSH 用户枚举漏洞修复</title>
    <link href="https://ruams.com/posts/245ba87b.html"/>
    <id>https://ruams.com/posts/245ba87b.html</id>
    <published>2022-08-22T16:00:00.000Z</published>
    <updated>2022-08-22T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="详细描述"><a href="#详细描述" class="headerlink" title="详细描述"></a>详细描述</h2><p>OpenSSH（OpenBSD Secure Shell）是 OpenBSD 计划组所维护的一套用于安全访问远程计算机的连接工具。该工具是 SSH 协议的开源实现，支持对所有的传输进行加密，可有效阻止窃听、连接劫持以及其他网络级的攻击。 OpenSSH 7.7 及之前版本中存在用户枚举漏洞，该漏洞源于程序会对有效的和无效的用户身份验证请求发出不同的响应。攻击者可通过发送特制的请求利用该漏洞枚举用户名称。</p><h2 id="解决办法"><a href="#解决办法" class="headerlink" title="解决办法"></a>解决办法</h2><p>厂商补丁：<br>应用如下补丁可以修复此漏洞，需要重新编译<br><a href="https://github.com/openbsd/src/commit/779974d35b4859c07bc3cb8a12c74b43b0a7d1e0">https://github.com/openbsd/src/commit/779974d35b4859c07bc3cb8a12c74b43b0a7d1e0</a><br>OpenSSH<br>新版本OpenSSH-7.8已经修复这个安全问题，请到厂商的主页下载：<br>链接：<br><a href="http://www.openssh.com/">http://www.openssh.com/</a><br><a href="http://www.openssh.com/portable.html">http://www.openssh.com/portable.html</a></p><blockquote><p>[!info]+ 提示<br>一般都直接连带编译升级 openssh 和 openssl 到最新版</p></blockquote><h2 id="风险级别"><a href="#风险级别" class="headerlink" title="风险级别"></a>风险级别</h2><p>中风险</p><h2 id="责任归属"><a href="#责任归属" class="headerlink" title="责任归属"></a>责任归属</h2><p>系统管理员</p><h2 id="加固风险"><a href="#加固风险" class="headerlink" title="加固风险"></a>加固风险</h2><p>中风险，如无 saltstack 和管理控制台等界面加固失败会导致无法登录机器；<br>升级 openssh 可能会导致客户端使用低版本 jsch 连接服务端报错；<br>升级 openssh 可能会导致 sftp 无法使用;<br>由于需要连带升级 openssl 可能会在升级失败的情况下造成 openssl 故障无法使用。</p><h2 id="链接"><a href="#链接" class="headerlink" title="链接"></a>链接</h2><p><a href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-15473">CVE-2018-15473</a><br><a href="https://access.redhat.com/security/cve/cve-2018-15473">CVE-2018-15473- Red Hat Customer Portal</a></p><h2 id="详细方案"><a href="#详细方案" class="headerlink" title="详细方案"></a>详细方案</h2><blockquote><p>[!info]+ 提示<br>使用 shell 脚本升级到最新版本 openssh 及其对应版本的 openssl<br>当前以最新版 openssh9.0 和 openssl1.1.1o 为例<br>在漏洞机器上执行升级脚本（前提升级需要的 openssh 和 openssl 源码包已经放到指定位置）</p></blockquote><p>[[openssh9.0升级脚本]]</p>]]></content>
    
    
    <summary type="html">OpenSSH 用户枚举漏洞(CVE-2018-15473) 修复</summary>
    
    
    
    <category term="linux" scheme="https://ruams.com/categories/linux/"/>
    
    
    <category term="linux" scheme="https://ruams.com/tags/linux/"/>
    
    <category term="安全漏洞" scheme="https://ruams.com/tags/%E5%AE%89%E5%85%A8%E6%BC%8F%E6%B4%9E/"/>
    
  </entry>
  
  <entry>
    <title>Nfs showmount 漏洞 CVE-1999-0554 修复</title>
    <link href="https://ruams.com/posts/dc4ca284.html"/>
    <id>https://ruams.com/posts/dc4ca284.html</id>
    <published>2022-08-16T16:00:00.000Z</published>
    <updated>2022-08-16T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="详细描述"><a href="#详细描述" class="headerlink" title="详细描述"></a>详细描述</h2><p>可以对目标主机进行”showmount -e”操作，此操作将泄露目标主机大量敏感信息，比如目录结构。更糟糕的是，如果访问控制不严的话，攻击者有可能直接访问到目标主机上的数据。</p><h2 id="解决办法"><a href="#解决办法" class="headerlink" title="解决办法"></a>解决办法</h2><ul><li>限制可以获取 NFS 输出列表的 IP 和用户。 </li><li>除非绝对必要，请关闭NFS服务、MOUNTD。</li></ul><h2 id="风险级别"><a href="#风险级别" class="headerlink" title="风险级别"></a>风险级别</h2><p><font color=#f20707>高风险</font></p><h2 id="责任归属"><a href="#责任归属" class="headerlink" title="责任归属"></a>责任归属</h2><p>系统管理员</p><h2 id="加固风险"><a href="#加固风险" class="headerlink" title="加固风险"></a>加固风险</h2><p>低风险操作，无需重启服务，只影响 showmout -e 查询服务端共享目录并<font color= #f20707 >不会影响 nfs 服务正常挂载和使用</font></p><h2 id="链接"><a href="#链接" class="headerlink" title="链接"></a>链接</h2><p><a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-1999-0554">CVE - CVE-1999-0554 (mitre.org)</a></p><hr><h2 id="详细方案"><a href="#详细方案" class="headerlink" title="详细方案"></a>详细方案</h2><h3 id="1-配置文件限制"><a href="#1-配置文件限制" class="headerlink" title="1. 配置文件限制"></a>1. 配置文件限制</h3><p>对 NFS 共享服务器的 <code>/etc/hosts.allow</code> 和 <code>/etc/hosts.deny</code> 进行配置<br>由于各系统版本的 nfs mount 服务不同，所以分为以下三种配置方式：</p><h4 id="1-1-centos7"><a href="#1-1-centos7" class="headerlink" title="1.1 centos7"></a>1.1 centos7</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">vim /etc/hosts.allow</span><br><span class="line">mountd:134.64.   # 允许134.64.x.x 网段访问mountd服务，如果是mountd:134.则为允许134.x.x.x访问mountd服务，多个ip可用逗号隔开</span><br><span class="line"></span><br><span class="line">vim /etc/hosts.deny</span><br><span class="line">mountd:all</span><br></pre></td></tr></table></figure><h4 id="1-2-centos6"><a href="#1-2-centos6" class="headerlink" title="1.2 centos6"></a>1.2 centos6</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">vim /etc/hosts.allow</span><br><span class="line">rpcbind: 134.:allow</span><br><span class="line"></span><br><span class="line">vim /etc/hosts.deny</span><br><span class="line">rpcbind:ALL:deny</span><br></pre></td></tr></table></figure><h4 id="1-3-centos5"><a href="#1-3-centos5" class="headerlink" title="1.3 centos5"></a>1.3 centos5</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">vim /etc/hosts.allow</span><br><span class="line">Portmap:134.:allow</span><br><span class="line"></span><br><span class="line">vim /etc/hosts.deny</span><br><span class="line">Portmap:ALL:deny</span><br></pre></td></tr></table></figure><h3 id="2-iptables-限制"><a href="#2-iptables-限制" class="headerlink" title="2 iptables 限制"></a>2 iptables 限制</h3><p>使用 iptables 白名单对 nfs 服务端口（默认 111, 2049）进行限制。</p><p>end</p><hr><blockquote><p>[!info]+ 提示<br> 推荐使用第一种方法，无业务影响无需更改防火墙配置</p></blockquote>]]></content>
    
    
    <summary type="html">nfs cve 漏洞 showmount 信息泄露(CVE-1999-0554) 修复</summary>
    
    
    
    <category term="linux" scheme="https://ruams.com/categories/linux/"/>
    
    
    <category term="linux" scheme="https://ruams.com/tags/linux/"/>
    
    <category term="安全漏洞" scheme="https://ruams.com/tags/%E5%AE%89%E5%85%A8%E6%BC%8F%E6%B4%9E/"/>
    
  </entry>
  
  <entry>
    <title>Redhat6.5 编译升级 glibc 至 2.17 版本</title>
    <link href="https://ruams.com/posts/7667849c.html"/>
    <id>https://ruams.com/posts/7667849c.html</id>
    <published>2021-09-14T16:00:00.000Z</published>
    <updated>2021-09-14T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>[!example] 环境<br>操作系统： RedHat6.5<br>GLIBC： 2.17<br>RedHat6.5 默认 glibc 版本为 2.12，需要本地编译安装升级至 2.17 版本。</p></blockquote><h3 id="1-下载离线包"><a href="#1-下载离线包" class="headerlink" title="1.下载离线包"></a>1.下载离线包</h3><p>glibc-2.17.tar.gz</p><h3 id="2-编译安装"><a href="#2-编译安装" class="headerlink" title="2.编译安装"></a>2.编译安装</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">tar xzf glibc-2.17.tar.gz</span><br><span class="line"><span class="built_in">cd</span> glibc-2.17</span><br><span class="line"><span class="comment"># 必须要新建目录进行 configure</span></span><br><span class="line"><span class="built_in">mkdir</span> build</span><br><span class="line"><span class="built_in">cd</span> build</span><br><span class="line"><span class="comment"># 下面路径不要修改</span></span><br><span class="line">../configure --prefix=/usr --disable-profile --enable-add-ons --with-headers=/usr/include --with-binutils=/usr/bin</span><br><span class="line">make -j20</span><br><span class="line">make install</span><br></pre></td></tr></table></figure><h3 id="3-验证"><a href="#3-验证" class="headerlink" title="3.验证"></a>3.验证</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">strings /lib64/libc.so.6 | grep GLIBC</span><br></pre></td></tr></table></figure><p>完成</p><blockquote><p>[!danger] libc 故障补救措施</p></blockquote><p>错误一： 其他方法覆盖了 libc.so.6 这个软链接导致系统指令不可用<br>错误信息： <code>ls: error while loading shared libraries: __vdso_time: invalid mode for dlopen(): Invalid argument</code></p><p>适用于仅覆盖了软链接的场景：<br><code>ln -sf /usr/local/glibc-2.17/lib/lib-2.17.so /lib64/libc.so.6</code></p><p>恢复方法：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">export</span> LD_LIBRARY_PATH=/usr/lib64:/usr/local/lib64:/usr/local/glibc-2.17</span><br><span class="line">ldconfig</span><br></pre></td></tr></table></figure><hr><blockquote><p>[!danger] 升级后 locale 报 warnning<br>错误信息：warning: setlocale: LC_CTYPE: cannot change locale (en_US.UTF-8): No such file or directory</p></blockquote><p>恢复方法：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">ls</span> -l /lib64/libc.so.6</span><br><span class="line"><span class="comment"># 上条命令可以查看到指向的glibc具体版本</span></span><br><span class="line">strings /lib64/glibc-2.17.so | grep locale-archive</span><br><span class="line"><span class="comment"># 上条命令可以看到locale-archive的目录</span></span><br><span class="line"><span class="built_in">ln</span> -s /usr/lib/locale/locale-archive /usr/local/glibc-2.14/lib/locale/locale-archive</span><br><span class="line"><span class="comment"># 上条命令重新建立locale链接</span></span><br><span class="line">此时locale应已恢复正常</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">redhat6.5 升级glibc至2.17</summary>
    
    
    
    <category term="linux" scheme="https://ruams.com/categories/linux/"/>
    
    
    <category term="linux" scheme="https://ruams.com/tags/linux/"/>
    
  </entry>
  
</feed>
