<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>首页 on vpn</title>
    <link>https://cncfstack.com/p/vpn/</link>
    <description>Recent content in 首页 on vpn</description>
    <generator>Hugo</generator>
    <language>en</language>
    <lastBuildDate>Fri, 20 Oct 2023 10:00:00 +0800</lastBuildDate>
    <atom:link href="https://cncfstack.com/p/vpn/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>HelmChart 仓库</title>
      <link>https://cncfstack.com/p/vpn/helmchart/</link>
      <pubDate>Fri, 20 Oct 2023 10:00:00 +0800</pubDate>
      <guid>https://cncfstack.com/p/vpn/helmchart/</guid>
      <description>&lt;div id=&#34;helmchart-container&#34;&gt;&#xA;  &lt;div class=&#34;charts-container&#34;&gt;&#xA;    &lt;div class=&#34;loading&#34;&gt;加载中...&lt;/div&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div class=&#34;pagination-wrapper&#34;&gt;&#xA;    &lt;div class=&#34;pagination-container&#34;&gt;&#xA;      &lt;!-- 分页控件将通过JavaScript动态生成 --&gt;&#xA;    &lt;/div&gt;&#xA;  &lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;style&gt;&#xA;.helmchart-user-index {&#xA;  padding: 10px;&#xA;}&#xA;&#xA;.charts-container {&#xA;  min-height: 400px;&#xA;  /* 在显示数据时，顶部的导航高度 */&#xA;  margin-top: 80px;&#xA;}&#xA;&#xA;.no-data {&#xA;  padding: 40px 0;&#xA;  text-align: center;&#xA;}&#xA;&#xA;.chart-grid {&#xA;  display: grid;&#xA;  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));&#xA;  gap: 20px;&#xA;  margin-bottom: 30px;&#xA;}&#xA;&#xA;.chart-item {&#xA;  cursor: pointer;&#xA;  transition: all 0.3s ease;&#xA;  border: 1.5px solid #ebeef5;&#xA;  display: flex;&#xA;  flex-direction: column;&#xA;  background: #fff;&#xA;  border-radius: 4px;&#xA;  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);&#xA;}&#xA;&#xA;.chart-item:hover {&#xA;  transform: translateY(-4px);&#xA;  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);&#xA;}&#xA;&#xA;.chart-content {&#xA;  padding: 20px;&#xA;  display: flex;&#xA;  flex-direction: column;&#xA;  flex: 1;&#xA;}&#xA;&#xA;.chart-main {&#xA;  flex: 1;&#xA;}&#xA;&#xA;.chart-header {&#xA;  display: flex;&#xA;  align-items: flex-start;&#xA;  margin-bottom: 15px;&#xA;  justify-content: space-between;&#xA;}&#xA;&#xA;.header-tag {&#xA;  flex-shrink: 0;&#xA;  margin-left: 10px;&#xA;  margin-top: 5px;&#xA;}&#xA;&#xA;.chart-info {&#xA;  flex: 1;&#xA;  min-width: 0;&#xA;}&#xA;&#xA;.chart-name {&#xA;  margin: 0 0 5px 0;&#xA;  font-size: 18px;&#xA;  font-weight: 600;&#xA;  color: #303133;&#xA;  white-space: nowrap;&#xA;  overflow: hidden;&#xA;  text-overflow: ellipsis;&#xA;}&#xA;&#xA;.chart-repourl {&#xA;  font-size: 12px;&#xA;  color: gray;&#xA;  margin: 0;&#xA;  overflow: hidden;&#xA;  text-overflow: ellipsis;&#xA;}&#xA;&#xA;.chart-description {&#xA;  margin-bottom: 15px;&#xA;  font-size: 14px;&#xA;  color: #606266;&#xA;  line-height: 1.5;&#xA;  display: -webkit-box;&#xA;  -webkit-box-orient: vertical;&#xA;  overflow: hidden;&#xA;  text-align: justify;&#xA;  word-wrap: break-word;&#xA;  word-break: break-word;&#xA;}&#xA;&#xA;.description-text {&#xA;  margin: 0;&#xA;  display: -webkit-box;&#xA;  -webkit-box-orient: vertical;&#xA;  -webkit-line-clamp: 4;&#xA;  overflow: hidden;&#xA;  text-overflow: ellipsis;&#xA;}&#xA;&#xA;.chart-meta {&#xA;  display: flex;&#xA;  justify-content: space-between;&#xA;  align-items: center;&#xA;  border-top: 1px solid #eee;&#xA;  padding-top: 15px;&#xA;  margin-top: auto;&#xA;}&#xA;&#xA;.chart-sync-period {&#xA;  font-size: 12px;&#xA;  color: #909399;&#xA;}&#xA;&#xA;.pagination-wrapper {&#xA;  display: flex;&#xA;  justify-content: flex-end;&#xA;  padding: 20px 0;&#xA;}&#xA;&#xA;.pagination-container {&#xA;  display: flex;&#xA;  align-items: center;&#xA;  gap: 10px;&#xA;}&#xA;&#xA;.pagination-info {&#xA;  font-size: 14px;&#xA;  color: #606266;&#xA;}&#xA;&#xA;.pagination-controls {&#xA;  display: flex;&#xA;  gap: 5px;&#xA;}&#xA;&#xA;.pagination-btn {&#xA;  padding: 5px 10px;&#xA;  border: 1px solid #dcdfe6;&#xA;  background: #fff;&#xA;  border-radius: 4px;&#xA;  cursor: pointer;&#xA;  font-size: 14px;&#xA;}&#xA;&#xA;.pagination-btn:hover {&#xA;  background: #ecf5ff;&#xA;  color: #409eff;&#xA;}&#xA;&#xA;.pagination-btn.disabled {&#xA;  color: #c0c4cc;&#xA;  cursor: not-allowed;&#xA;  background: #fff;&#xA;}&#xA;&#xA;.pagination-btn.disabled:hover {&#xA;  background: #fff;&#xA;}&#xA;&#xA;.pagination-btn.active {&#xA;  background: #409eff;&#xA;  color: #fff;&#xA;  border-color: #409eff;&#xA;}&#xA;&#xA;.page-sizes {&#xA;  margin-right: 10px;&#xA;}&#xA;&#xA;.page-sizes select {&#xA;  padding: 5px;&#xA;  border: 1px solid #dcdfe6;&#xA;  border-radius: 4px;&#xA;}&#xA;&#xA;.loading, .error {&#xA;  text-align: center;&#xA;  padding: 40px 0;&#xA;  font-size: 16px;&#xA;}&#xA;&#xA;.error {&#xA;  color: #f56565;&#xA;}&#xA;&#xA;.el-tag {&#xA;  background-color: #ecf5ff;&#xA;  border-color: #d9ecff;&#xA;  color: #409eff;&#xA;  padding: 0 10px;&#xA;  height: 24px;&#xA;  line-height: 22px;&#xA;  font-size: 12px;&#xA;  border-width: 1px;&#xA;  border-style: solid;&#xA;  border-radius: 4px;&#xA;  box-sizing: border-box;&#xA;  white-space: nowrap;&#xA;}&#xA;&#xA;@media (max-width: 768px) {&#xA;  .chart-grid {&#xA;    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));&#xA;    gap: 15px;&#xA;  }&#xA;}&#xA;&#xA;@media (max-width: 480px) {&#xA;  .chart-grid {&#xA;    grid-template-columns: 1fr;&#xA;  }&#xA;  &#xA;  .chart-header {&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: center;&#xA;  }&#xA;  &#xA;  .header-tag {&#xA;    margin-top: 10px;&#xA;  }&#xA;}&#xA;&lt;/style&gt;&#xA;&lt;script&gt;&#xA;document.addEventListener(&#39;DOMContentLoaded&#39;, function() {&#xA;  // API端点URL&#xA;  const API_HELMCHART_URL = &#39;https://server.cs.cncfstack.com/api/v1/resources/helmchart/repo&#39;;&#xA;  &#xA;  // 数据状态&#xA;  let loading = false;&#xA;  let charts = [];&#xA;  &#xA;  // 分页相关&#xA;  let currentPage = 1;&#xA;  let pageSize = 50;&#xA;  let totalCharts = 0;&#xA;  &#xA;  // 搜索关键词&#xA;  let searchKeyword = &#39;vpn&#39;;&#xA;  &#xA;  // 格式化日期&#xA;  function formatDate(dateString) {&#xA;    const date = new Date(dateString);&#xA;    return date.toLocaleDateString(&#39;zh-CN&#39;);&#xA;  }&#xA;  &#xA;  // 提取主机名&#xA;  function getOriginalHostname(url) {&#xA;    const match = url.match(/^https?:\/\/([^\/]+)/i);&#xA;    return match ? match[1] : &#39;&#39;;&#xA;  }&#xA;  &#xA;  // 跳转到详情页&#xA;  function goToDetail(repo_url, chart_name) {&#xA;    const repo_host = getOriginalHostname(repo_url);&#xA;    window.open(`https://cncfstack.com/c/${repo_host}/${chart_name}`, &#39;_blank&#39;);&#xA;  }&#xA;  &#xA;  // 渲染图表网格&#xA;  function renderChartGrid(chartsData) {&#xA;    const container = document.querySelector(&#39;.charts-container&#39;);&#xA;    &#xA;    if (!chartsData || chartsData.length === 0) {&#xA;      container.innerHTML = `&#xA;        &lt;div class=&#34;no-data&#34;&gt;&#xA;          &lt;p&gt;暂无 HelmChart 数据&lt;/p&gt;</description>
    </item>
    <item>
      <title>镜像仓库</title>
      <link>https://cncfstack.com/p/vpn/image/</link>
      <pubDate>Fri, 20 Oct 2023 10:00:00 +0800</pubDate>
      <guid>https://cncfstack.com/p/vpn/image/</guid>
      <description>&lt;div id=&#34;image-grid-container&#34;&gt;&lt;/div&gt;&#xA;&lt;script&gt;&#xA;document.addEventListener(&#39;DOMContentLoaded&#39;, function() {&#xA;  // API端点URL（需要根据实际情况调整）&#xA;  const API_IMAGE_REPO_URL = &#39;https://server.cs.cncfstack.com/api/v1/image/repo&#39;; // 需要替换为实际的API地址&#xA;  &#xA;  // 格式化日期&#xA;  function formatDate(dateString) {&#xA;    const date = new Date(dateString);&#xA;    return date.toLocaleDateString(&#39;zh-CN&#39;);&#xA;  }&#xA;&#xA;  // 渲染镜像卡片&#xA;  function renderImageGrid(reposData) {&#xA;    const container = document.getElementById(&#39;image-grid-container&#39;);&#xA;    &#xA;    if (!container) {&#xA;      console.error(&#39;未找到ID为image-grid-container的元素&#39;);&#xA;      return;&#xA;    }&#xA;&#xA;    if (!reposData || reposData.length === 0) {&#xA;      container.innerHTML = `&#xA;        &lt;div class=&#34;no-images&#34;&gt;&#xA;          &lt;p&gt;暂无镜像数据&lt;/p&gt;&#xA;        &lt;/div&gt;&#xA;      `;&#xA;      return;&#xA;    }&#xA;&#xA;    let gridHTML = &#39;&lt;div class=&#34;image-grid&#34;&gt;&#39;;&#xA;    &#xA;    reposData.forEach(repo =&gt; {&#xA;      gridHTML += `&#xA;        &lt;div class=&#34;image-card&#34; onclick=&#34;viewRepoDetail(&#39;${repo.host_vendor}&#39;, &#39;${repo.image_repo}&#39;)&#34;&gt;&#xA;          &lt;div class=&#34;app-card-content&#34;&gt;&#xA;            &lt;div class=&#34;app-card-header&#34;&gt;&#xA;              &lt;div class=&#34;app-card-info&#34;&gt;&#xA;                &lt;h2 class=&#34;app-card-name&#34;&gt;${repo.image_repo}&lt;/h2&gt;&#xA;                &lt;p class=&#34;image-repo&#34;&gt;${repo.host_vendor}&lt;/p&gt;&#xA;              &lt;/div&gt;&#xA;              &lt;div class=&#34;header-tag&#34;&gt;&#xA;                &lt;span class=&#34;image-repo-tag&#34;&gt;镜像&lt;/span&gt;&#xA;              &lt;/div&gt;&#xA;            &lt;/div&gt;&#xA;            &#xA;            &lt;div class=&#34;app-card-description&#34;&gt;&#xA;              &lt;p class=&#34;app-card-description-text&#34;&gt;${repo.description || &#39;暂无描述&#39;}&lt;/p&gt;&#xA;            &lt;/div&gt;&#xA;            &#xA;            &lt;div class=&#34;app-card-image-meta&#34;&gt;&#xA;              &lt;span class=&#34;app-card-meta&#34;&gt;上次同步 ${formatDate(repo.sync_at)}&lt;/span&gt;&#xA;            &lt;/div&gt;&#xA;          &lt;/div&gt;&#xA;        &lt;/div&gt;&#xA;      `;&#xA;    });&#xA;    &#xA;    gridHTML += &#39;&lt;/div&gt;&#39;;&#xA;    container.innerHTML = gridHTML;&#xA;  }&#xA;&#xA;  // 查看仓库详情&#xA;  window.viewRepoDetail = function(hostVendor, imageRepo) {&#xA;    const name = `${hostVendor}/${imageRepo}`;&#xA;    window.open(`https://cncfstack.com/i/${name}`, &#39;_blank&#39;);&#xA;  };&#xA;&#xA;  // 从API加载数据&#xA;  async function loadData() {&#xA;    try {&#xA;      // 显示加载状态&#xA;      const container = document.getElementById(&#39;image-grid-container&#39;);&#xA;      container.innerHTML = &#39;&lt;div class=&#34;loading&#34;&gt;加载中...&lt;/div&gt;&#39;;&#xA;      &#xA;      // 发起API请求&#xA;      const response = await fetch(`${API_IMAGE_REPO_URL}?page=1&amp;page_size=50&amp;ready_check=true&amp;search_colume=all&amp;search_keyword=vpn`);&#xA;      &#xA;      if (!response.ok) {&#xA;        throw new Error(`HTTP error! status: ${response.status}`);&#xA;      }&#xA;      &#xA;      const result = await response.json();&#xA;      &#xA;      // 正确解析API响应数据&#xA;      let repos = [];&#xA;      if (result &amp;&amp; result.data) {&#xA;        // 如果data是一个数组，直接使用&#xA;        if (Array.isArray(result.data)) {&#xA;          repos = result.data;&#xA;        } &#xA;        // 如果data包含data字段且是数组，使用该字段&#xA;        else if (result.data.data &amp;&amp; Array.isArray(result.data.data)) {&#xA;          repos = result.data.data;&#xA;        }&#xA;      }&#xA;      &#xA;      // 渲染数据&#xA;      renderImageGrid(repos);&#xA;    } catch (error) {&#xA;      console.error(&#39;获取仓库列表失败:&#39;, error);&#xA;      const container = document.getElementById(&#39;image-grid-container&#39;);&#xA;      container.innerHTML = &#39;&lt;div class=&#34;error&#34;&gt;获取数据失败，请稍后重试&lt;/div&gt;&#39;;&#xA;    }&#xA;  }&#xA;&#xA;  // 初始化渲染&#xA;  loadData();&#xA;});&#xA;&lt;/script&gt;&#xA;&lt;style&gt;&#xA;/* image-grid.css */&#xA;.image-grid {&#xA;  display: grid;&#xA;  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));&#xA;  gap: 20px;&#xA;  margin-bottom: 30px;&#xA;  margin-top: 80px;&#xA;  padding: 20px;&#xA;}&#xA;&#xA;/* .image-card {&#xA;  cursor: pointer;&#xA;  transition: all 0.3s ease;&#xA;  border-radius: 4px;&#xA;  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);&#xA;  background: #fff;&#xA;} */&#xA;&#xA;.image-card {&#xA;  cursor: pointer;&#xA;  transition: all 0.3s ease;&#xA;  border-radius: 4px;&#xA;  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);&#xA;  background: #fff;&#xA;  /* 添加最大宽度限制 */&#xA;  max-width: 100%;&#xA;  width: 100%;&#xA;}&#xA;&#xA;.image-card:hover {&#xA;  transform: translateY(-4px);&#xA;  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.1);&#xA;}&#xA;&#xA;.app-card-content {&#xA;  padding: 20px;&#xA;  display: flex;&#xA;  flex-direction: column;&#xA;  flex: 1;&#xA;}&#xA;&#xA;.app-card-header {&#xA;  display: flex;&#xA;  align-items: flex-start;&#xA;  margin-bottom: 15px;&#xA;  justify-content: space-between;&#xA;}&#xA;&#xA;.header-tag {&#xA;  flex-shrink: 0;&#xA;  margin-left: 10px;&#xA;  margin-top: 5px;&#xA;}&#xA;&#xA;.app-card-info {&#xA;  flex: 1;&#xA;  min-width: 0;&#xA;}&#xA;&#xA;/* .app-card-name {&#xA;  margin: 0 0 5px 0;&#xA;  font-size: 18px;&#xA;  font-weight: 600;&#xA;  color: #303133;&#xA;  white-space: nowrap;&#xA;  overflow: hidden;&#xA;  text-overflow: ellipsis;&#xA;} */&#xA;&#xA;.app-card-name {&#xA;  margin: 0 0 5px 0;&#xA;  font-size: 18px;&#xA;  font-weight: 600;&#xA;  color: #303133;&#xA;  /* 修改以下属性以支持长文本换行 */&#xA;  white-space: normal;&#xA;  overflow: hidden;&#xA;  text-overflow: ellipsis;&#xA;  word-wrap: break-word;&#xA;  word-break: break-word;&#xA;  display: -webkit-box;&#xA;  -webkit-line-clamp: 2; /* 限制显示2行 */&#xA;  -webkit-box-orient: vertical;&#xA;}&#xA;&#xA;.image-repo {&#xA;  margin: 0;&#xA;  font-size: 13px;&#xA;  color: #606266;&#xA;  overflow: hidden;&#xA;  text-overflow: ellipsis;&#xA;}&#xA;&#xA;.app-card-description {&#xA;  margin-bottom: 15px;&#xA;  font-size: 14px;&#xA;  color: #606266;&#xA;  line-height: 1.5;&#xA;  display: -webkit-box;&#xA;  -webkit-box-orient: vertical;&#xA;  overflow: hidden;&#xA;  text-align: justify;&#xA;  min-height: 60px;&#xA;  word-wrap: break-word;&#xA;  word-break: break-word;&#xA;}&#xA;&#xA;.app-card-description-text {&#xA;  margin: 0;&#xA;  display: -webkit-box;&#xA;  -webkit-box-orient: vertical;&#xA;  overflow: hidden;&#xA;  text-overflow: ellipsis;&#xA;}&#xA;&#xA;.app-card-image-meta {&#xA;  display: flex;&#xA;  justify-content: space-between;&#xA;  align-items: center;&#xA;  border-top: 1px solid #eee;&#xA;  padding-top: 15px;&#xA;  margin-top: auto;&#xA;}&#xA;&#xA;.app-card-meta {&#xA;  font-size: 12px;&#xA;  color: #909399;&#xA;}&#xA;&#xA;.no-images {&#xA;  grid-column: 1 / -1;&#xA;  text-align: center;&#xA;  padding: 50px 0;&#xA;}&#xA;&#xA;.loading, .error {&#xA;  grid-column: 1 / -1;&#xA;  text-align: center;&#xA;  padding: 50px 0;&#xA;  font-size: 16px;&#xA;}&#xA;&#xA;.error {&#xA;  color: #f56565;&#xA;}&#xA;&#xA;.image-repo-tag {&#xA;  background-color: #ecf5ff;&#xA;  border-color: #d9ecff;&#xA;  color: #409eff;&#xA;  padding: 0 10px;&#xA;  height: 24px;&#xA;  line-height: 22px;&#xA;  font-size: 12px;&#xA;  border-width: 1px;&#xA;  border-style: solid;&#xA;  border-radius: 4px;&#xA;  box-sizing: border-box;&#xA;  white-space: nowrap;&#xA;}&#xA;&#xA;@media (max-width: 768px) {&#xA;  .image-grid {&#xA;    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));&#xA;    gap: 15px;&#xA;  }&#xA;}&#xA;/* &#xA;@media (max-width: 480px) {&#xA;  .image-grid {&#xA;    grid-template-columns: 1fr;&#xA;  }&#xA;  &#xA;  .app-card-header {&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: center;&#xA;  }&#xA;} */&#xA;&#xA;@media (max-width: 480px) {&#xA;  .image-grid {&#xA;    grid-template-columns: 1fr;&#xA;    padding: 10px;&#xA;  }&#xA;  &#xA;  .app-card-header {&#xA;    flex-direction: column;&#xA;    align-items: center;&#xA;    text-align: center;&#xA;  }&#xA;  &#xA;  .app-card-content {&#xA;    padding: 15px;&#xA;  }&#xA;  &#xA;  .app-card-name {&#xA;    font-size: 16px;&#xA;    -webkit-line-clamp: 3; /* 移动端允许显示更多行 */&#xA;  }&#xA;}&#xA;&lt;/style&gt;</description>
    </item>
    <item>
      <title>Logo</title>
      <link>https://cncfstack.com/p/vpn/logo/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://cncfstack.com/p/vpn/logo/</guid>
      <description>&lt;div id=&#34;dynamic-content&#34;&gt;&#xA;  &lt;p&gt;正在加载动态内容...&lt;/p&gt;&#xA;&lt;/div&gt;&#xA;&lt;style&gt;&#xA;/* 保留原有样式 */&#xA;.image-list-container {&#xA;  max-width: 1200px;&#xA;  margin: 2rem auto;&#xA;  padding: 0 1rem;&#xA;}&#xA;&#xA;.image-list-title {&#xA;  text-align: center;&#xA;  margin-bottom: 2rem;&#xA;  color: #333;&#xA;  font-size: 1.8rem;&#xA;}&#xA;&#xA;.image-grid {&#xA;  display: grid;&#xA;  grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));&#xA;  gap: 1.5rem;&#xA;}&#xA;&#xA;.image-card {&#xA;  border: 1px solid #e1e8ed;&#xA;  border-radius: 8px;&#xA;  overflow: hidden;&#xA;  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);&#xA;  transition: transform 0.3s ease, box-shadow 0.3s ease;&#xA;  background: white;&#xA;  cursor: pointer;&#xA;}&#xA;&#xA;.image-card:hover {&#xA;  transform: translateY(-5px);&#xA;  box-shadow: 0 5px 20px rgba(0, 0, 0, 0.15);&#xA;}&#xA;&#xA;.card-link {&#xA;  text-decoration: none;&#xA;  color: inherit;&#xA;}&#xA;&#xA;.card-header {&#xA;  background-color: #f5f8fa;&#xA;  padding: 1rem;&#xA;  border-bottom: 1px solid #e1e8ed;&#xA;}&#xA;&#xA;.card-title {&#xA;  margin: 0;&#xA;  font-size: 1.2rem;&#xA;  color: #1da1f2;&#xA;  font-weight: 600;&#xA;}&#xA;&#xA;.card-body {&#xA;  padding: 1rem;&#xA;}&#xA;&#xA;.vendor-info {&#xA;  display: flex;&#xA;  align-items: center;&#xA;  margin-bottom: 0.8rem;&#xA;}&#xA;&#xA;.vendor-icon {&#xA;  width: 32px;&#xA;  height: 32px;&#xA;  background-color: #1da1f2;&#xA;  border-radius: 50%;&#xA;  display: flex;&#xA;  align-items: center;&#xA;  justify-content: center;&#xA;  color: white;&#xA;  font-weight: bold;&#xA;  margin-right: 0.8rem;&#xA;}&#xA;&#xA;.vendor-name {&#xA;  font-weight: 500;&#xA;  color: #333;&#xA;}&#xA;&#xA;.repo-info {&#xA;  background-color: #f8f9fa;&#xA;  padding: 0.8rem;&#xA;  border-radius: 4px;&#xA;  font-family: &#39;Courier New&#39;, monospace;&#xA;  font-size: 0.9rem;&#xA;  color: #333;&#xA;  word-break: break-all;&#xA;  margin-bottom: 0;&#xA;}&#xA;&#xA;.error-message {&#xA;  text-align: center;&#xA;  color: #e02d2d;&#xA;  padding: 2rem;&#xA;  font-size: 1.1rem;&#xA;}&#xA;&#xA;.loading-message {&#xA;  text-align: center;&#xA;  padding: 2rem;&#xA;  font-size: 1.1rem;&#xA;  color: #657786;&#xA;}&#xA;&#xA;@media (max-width: 768px) {&#xA;  .image-grid {&#xA;    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));&#xA;    gap: 1rem;&#xA;  }&#xA;  &#xA;  .image-list-title {&#xA;    font-size: 1.5rem;&#xA;  }&#xA;}&#xA;&#xA;/* 新增Logo展示样式 */&#xA;.logo-repo-container {&#xA;  padding: 20px;&#xA;  max-width: 1400px;&#xA;  margin: 80px auto;&#xA;}&#xA;&#xA;.logo-grid {&#xA;  display: grid;&#xA;  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));&#xA;  gap: 20px;&#xA;  margin-bottom: 20px;&#xA;}&#xA;&#xA;.logo-card {&#xA;  cursor: pointer;&#xA;  transition: all 0.3s ease;&#xA;  border-radius: 8px;&#xA;  overflow: hidden;&#xA;  background: white;&#xA;  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);&#xA;  position: relative;&#xA;}&#xA;&#xA;.logo-card:hover {&#xA;  transform: translateY(-5px);&#xA;  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15);&#xA;}&#xA;&#xA;.logo-image-container {&#xA;  height: 160px;&#xA;  overflow: hidden;&#xA;  display: flex;&#xA;  align-items: center;&#xA;  justify-content: center;&#xA;  background-color: #f0f2f5;&#xA;  position: relative;&#xA;}&#xA;&#xA;.logo-image {&#xA;  max-width: 100%;&#xA;  max-height: 100%;&#xA;  object-fit: contain;&#xA;}&#xA;&#xA;.image-repo-tag {&#xA;  position: absolute;&#xA;  top: 8px;&#xA;  right: 8px;&#xA;  background-color: #ecf5ff;&#xA;  color: #409eff;&#xA;  padding: 3px 8px;&#xA;  border-radius: 4px;&#xA;  font-size: 12px;&#xA;}&#xA;&#xA;.image-error {&#xA;  display: flex;&#xA;  flex-direction: column;&#xA;  align-items: center;&#xA;  justify-content: center;&#xA;  height: 100%;&#xA;  color: #909399;&#xA;  font-size: 14px;&#xA;}&#xA;&#xA;.pagination-container {&#xA;  display: flex;&#xA;  justify-content: center;&#xA;  padding: 20px 0;&#xA;}&#xA;&#xA;.el-pagination {&#xA;  display: flex;&#xA;  align-items: center;&#xA;  gap: 10px;&#xA;  flex-wrap: wrap;&#xA;  justify-content: center;&#xA;}&#xA;&#xA;.pagination-info {&#xA;  font-size: 14px;&#xA;  color: #606266;&#xA;}&#xA;&#xA;.pagination-sizes {&#xA;  display: flex;&#xA;  align-items: center;&#xA;  gap: 5px;&#xA;}&#xA;&#xA;.page-size-select {&#xA;  padding: 5px;&#xA;  border: 1px solid #dcdfe6;&#xA;  border-radius: 4px;&#xA;}&#xA;&#xA;.pagination-controls {&#xA;  display: flex;&#xA;  align-items: center;&#xA;  gap: 5px;&#xA;}&#xA;&#xA;.pagination-btn {&#xA;  padding: 5px 10px;&#xA;  border: 1px solid #dcdfe6;&#xA;  background: white;&#xA;  border-radius: 4px;&#xA;  cursor: pointer;&#xA;}&#xA;&#xA;.pagination-btn.disabled {&#xA;  color: #c0c4cc;&#xA;  cursor: not-allowed;&#xA;}&#xA;&#xA;.pagination-btn.active {&#xA;  background-color: #409eff;&#xA;  color: white;&#xA;  border-color: #409eff;&#xA;}&#xA;&#xA;.pagination-jump {&#xA;  display: flex;&#xA;  align-items: center;&#xA;  gap: 5px;&#xA;  font-size: 14px;&#xA;  color: #606266;&#xA;}&#xA;&#xA;.pagination-input {&#xA;  width: 50px;&#xA;  padding: 5px;&#xA;  border: 1px solid #dcdfe6;&#xA;  border-radius: 4px;&#xA;}&#xA;&#xA;.pagination-go-btn {&#xA;  padding: 5px 10px;&#xA;  border: 1px solid #409eff;&#xA;  background: white;&#xA;  color: #409eff;&#xA;  border-radius: 4px;&#xA;  cursor: pointer;&#xA;}&#xA;&#xA;.drawer-overlay {&#xA;  position: fixed;&#xA;  top: 0;&#xA;  right: 0;&#xA;  bottom: 0;&#xA;  left: 0;&#xA;  background-color: rgba(0, 0, 0, 0.5);&#xA;  z-index: 1000;&#xA;  display: none;&#xA;}&#xA;&#xA;.drawer-overlay.visible {&#xA;  display: block;&#xA;}&#xA;&#xA;.detail-drawer {&#xA;  position: fixed;&#xA;  top: 0;&#xA;  right: 0;&#xA;  bottom: 0;&#xA;  width: 50%;&#xA;  background: white;&#xA;  z-index: 1001;&#xA;  transform: translateX(100%);&#xA;  transition: transform 0.3s ease;&#xA;  display: flex;&#xA;  flex-direction: column;&#xA;  overflow: hidden;&#xA;}&#xA;&#xA;.detail-drawer.open {&#xA;  transform: translateX(0);&#xA;}&#xA;&#xA;.drawer-header {&#xA;  padding: 15px 20px;&#xA;  border-bottom: 1px solid #eee;&#xA;  display: flex;&#xA;  justify-content: space-between;&#xA;  align-items: center;&#xA;}&#xA;&#xA;.drawer-title {&#xA;  font-size: 18px;&#xA;  font-weight: 600;&#xA;  margin: 0;&#xA;}&#xA;&#xA;.close-btn {&#xA;  background: none;&#xA;  border: none;&#xA;  font-size: 20px;&#xA;  cursor: pointer;&#xA;  color: #909399;&#xA;}&#xA;&#xA;.drawer-content {&#xA;  flex: 1;&#xA;  overflow-y: auto;&#xA;  padding: 20px;&#xA;  display: flex;&#xA;  flex-direction: column;&#xA;  gap: 20px;&#xA;}&#xA;&#xA;.detail-image-container {&#xA;  height: 370px;&#xA;  display: flex;&#xA;  align-items: center;&#xA;  justify-content: center;&#xA;  background-color: #f0f2f5;&#xA;  border-radius: 8px;&#xA;  overflow: hidden;&#xA;}&#xA;&#xA;.detail-image {&#xA;  max-width: 100%;&#xA;  max-height: 100%;&#xA;  object-fit: contain;&#xA;}&#xA;&#xA;.detail-info {&#xA;  flex: 1;&#xA;}&#xA;&#xA;.detail-actions {&#xA;  margin-top: 20px;&#xA;  text-align: center;&#xA;}&#xA;&#xA;.btn-download {&#xA;  background-color: #409eff;&#xA;  color: white;&#xA;  border: none;&#xA;  border-radius: 4px;&#xA;  padding: 12px 30px;&#xA;  font-size: 16px;&#xA;  cursor: pointer;&#xA;  display: inline-flex;&#xA;  align-items: center;&#xA;  gap: 5px;&#xA;}&#xA;&#xA;.btn-download:hover {&#xA;  background-color: #337ecc;&#xA;}&#xA;&#xA;.btn-download.loading {&#xA;  opacity: 0.8;&#xA;  cursor: not-allowed;&#xA;}&#xA;&#xA;.descriptions {&#xA;  border: 1px solid #ebeef5;&#xA;  border-radius: 4px;&#xA;  overflow: hidden;&#xA;}&#xA;&#xA;.desc-row {&#xA;  display: flex;&#xA;  border-bottom: 1px solid #ebeef5;&#xA;}&#xA;&#xA;.desc-row:last-child {&#xA;  border-bottom: none;&#xA;}&#xA;&#xA;.desc-label {&#xA;  width: 30%;&#xA;  padding: 12px 15px;&#xA;  background-color: #fafafa;&#xA;  color: #606266;&#xA;  font-size: 14px;&#xA;}&#xA;&#xA;.desc-content {&#xA;  flex: 1;&#xA;  padding: 12px 15px;&#xA;  color: #303133;&#xA;  font-size: 14px;&#xA;}&#xA;&#xA;.loading-indicator {&#xA;  text-align: center;&#xA;  padding: 20px;&#xA;  color: #606266;&#xA;}&#xA;&#xA;.error-message {&#xA;  text-align: center;&#xA;  padding: 20px;&#xA;  color: #f56c6c;&#xA;}&#xA;&#xA;.svg-container {&#xA;  width: 100%;&#xA;  height: 100%;&#xA;  display: flex;&#xA;  align-items: center;&#xA;  justify-content: center;&#xA;}&#xA;&#xA;.svg-image {&#xA;  width: 80%;&#xA;  height: 80%;&#xA;}&#xA;&#xA;@media (max-width: 768px) {&#xA;  .logo-grid {&#xA;    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));&#xA;    gap: 10px;&#xA;  }&#xA;  &#xA;  .detail-drawer {&#xA;    width: 100%;&#xA;  }&#xA;}&#xA;&lt;/style&gt;&#xA;&lt;script&gt;&#xA;// 全局变量&#xA;let tableData = [];&#xA;let currentPage = 1;&#xA;let pageSize = 48;&#xA;let totalTasks = 0;&#xA;let searchKeyword = &#39;vpn&#39;;&#xA;let currentFileData = null;&#xA;let totalLogo = 0;&#xA;let downloadLoadingStatus = false;&#xA;&#xA;// DOM元素引用&#xA;const dynamicContent = document.getElementById(&#39;dynamic-content&#39;);&#xA;&#xA;// 初始化&#xA;document.addEventListener(&#39;DOMContentLoaded&#39;, function() {&#xA;  fetchData();&#xA;});&#xA;&#xA;// 格式化文件大小&#xA;function formatFileSize(size) {&#xA;  if (size &lt; 1024) {&#xA;    return size + &#39;B&#39;;&#xA;  } else if (size &lt; 1024 * 1024) {&#xA;    return (size / 1024).toFixed(2) + &#39;KB&#39;;&#xA;  } else if (size &lt; 1024 * 1024 * 1024) {&#xA;    return (size / (1024 * 1024)).toFixed(2) + &#39;MB&#39;;&#xA;  } else {&#xA;    return (size / (1024 * 1024 * 1024)).toFixed(2) + &#39;GB&#39;;&#xA;  }&#xA;}&#xA;&#xA;// 格式化时间&#xA;function formatTime(time) {&#xA;  if (!time) return &#39;&#39;;&#xA;  return new Date(time).toLocaleString(&#39;zh-CN&#39;);&#xA;}&#xA;&#xA;// 获取预览图片URL&#xA;function getPreviewImageUrl(url) {&#xA;  if (!url) return &#39;&#39;;&#xA;  // 将文件后缀的最后一个点替换为 _thumbn.&#xA;  url = url.replace(/\.\w+$/, &#39;_thumb$&amp;&#39;);&#xA;  // 替换http://为https://&#xA;  url = url.replace(&#39;http://&#39;, &#39;https://&#39;);&#xA;  return url.replace(&#39;https://&#39;, &#39;https://file.cncfstack.com/&#39;);&#xA;}&#xA;&#xA;function getSvgUrl(url) {&#xA;  if (!url) return &#39;&#39;;&#xA;  url = url.replace(&#39;http://&#39;, &#39;https://&#39;);&#xA;  return url.replace(&#39;https://&#39;, &#39;https://file.cncfstack.com/&#39;);&#xA;}&#xA;&#xA;// 处理分页大小变化&#xA;function handleSizeChange(event) {&#xA;  pageSize = parseInt(event.target.value);&#xA;  currentPage = 1;&#xA;  fetchData();&#xA;}&#xA;&#xA;// 处理当前页变化&#xA;function handleCurrentChange(page) {&#xA;  currentPage = page;&#xA;  fetchData();&#xA;}&#xA;&#xA;// 获取任务列表&#xA;function fetchData() {&#xA;  const params = new URLSearchParams({&#xA;    page: currentPage,&#xA;    page_size: pageSize,&#xA;    sync_status: &#39;success&#39;,&#xA;    order_by: &#39;created_at DESC&#39;,&#xA;    file_type: &#39;logo&#39;&#xA;  });&#xA;  &#xA;  if (searchKeyword) {&#xA;    params.append(&#39;search_keyword&#39;, searchKeyword);&#xA;  }&#xA;  &#xA;  const url = `https://server.cs.cncfstack.com/api/v1/resources/file/filesync?${params.toString()}`;&#xA;  &#xA;  fetch(url)&#xA;    .then(response =&gt; response.json())&#xA;    .then(data =&gt; {&#xA;      tableData = data.data || [];&#xA;      totalTasks = data.total || 0;&#xA;      renderPage();&#xA;    })&#xA;    .catch(error =&gt; {&#xA;      console.error(&#39;获取Logo文件失败:&#39;, error);&#xA;      dynamicContent.innerHTML = `&#xA;        &lt;div class=&#34;error-message&#34;&gt;&#xA;          &lt;p&gt;获取Logo文件失败，请稍后重试。&lt;/p&gt;</description>
    </item>
    <item>
      <title>OpenVPN服务端安装</title>
      <link>https://cncfstack.com/p/vpn/docs/ops/vpn-install3/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://cncfstack.com/p/vpn/docs/ops/vpn-install3/</guid>
      <description>&lt;h1 id=&#34;vpn软件下载&#34;&gt;vpn软件下载&lt;a class=&#34;td-heading-self-link&#34; href=&#34;#vpn%e8%bd%af%e4%bb%b6%e4%b8%8b%e8%bd%bd&#34; aria-label=&#34;Heading self-link&#34;&gt;&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;https://github.com/OpenVPN/openvpn/releases/download/v2.6.14/openvpn-2.6.14.tar.gz&#xA;https://github.com/OpenVPN/easy-rsa/releases/download/v3.2.4/EasyRSA-3.2.4.tgz&#xA;&#xA;https://file.cncfstack.com/github.com/OpenVPN/easy-rsa/releases/download/v3.2.4/EasyRSA-3.2.4.tgz&#xA;https://file.cncfstack.com/github.com/OpenVPN/openvpn/releases/download/v2.6.14/openvpn-2.6.14.tar.gz&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&#34;依赖软件包安装&#34;&gt;依赖软件包安装&lt;a class=&#34;td-heading-self-link&#34; href=&#34;#%e4%be%9d%e8%b5%96%e8%bd%af%e4%bb%b6%e5%8c%85%e5%ae%89%e8%a3%85&#34; aria-label=&#34;Heading self-link&#34;&gt;&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;yum install -y openssl-devel lzo lzo-devel pam pam-devel automake pkgconfig  autoconf libtool  libnl3-devel  libcap-ng-devel pkg-config  lz4-devel lz4-libs&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&#34;编译安装openvpn&#34;&gt;编译安装openVPN&lt;a class=&#34;td-heading-self-link&#34; href=&#34;#%e7%bc%96%e8%af%91%e5%ae%89%e8%a3%85openvpn&#34; aria-label=&#34;Heading self-link&#34;&gt;&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;tar xf openvpn-2.6.14.tar.gz &amp;amp;&amp;amp; cd openvpn-2.4.6&#xA;autoreconf -i -v -f&#xA;./configure --prefix=/usr/local/openvpn/&#xA;make&#xA;make install&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&#34;初始化准备&#34;&gt;初始化准备&lt;a class=&#34;td-heading-self-link&#34; href=&#34;#%e5%88%9d%e5%a7%8b%e5%8c%96%e5%87%86%e5%a4%87&#34; aria-label=&#34;Heading self-link&#34;&gt;&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;mkdir -p /usr/local/openvpn/etc/pki&#xA;echo &amp;#39;net.ipv4.ip_forward = 1&amp;#39; &amp;gt;&amp;gt; /etc/sysctl.conf&#xA;sysctl -p&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&#34;安装easyrsa&#34;&gt;安装EasyRSA&lt;a class=&#34;td-heading-self-link&#34; href=&#34;#%e5%ae%89%e8%a3%85easyrsa&#34; aria-label=&#34;Heading self-link&#34;&gt;&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;tar xf EasyRSA-3.2.4.tgz -C /usr/local/openvpn&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&#34;创建vars变量文件&#34;&gt;创建vars变量文件&lt;a class=&#34;td-heading-self-link&#34; href=&#34;#%e5%88%9b%e5%bb%bavars%e5%8f%98%e9%87%8f%e6%96%87%e4%bb%b6&#34; aria-label=&#34;Heading self-link&#34;&gt;&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;cd /usr/local/openvpn/EasyRSA-*&#xA;&#xA;cat &amp;gt; vars &amp;lt;&amp;lt;EOF&#xA;set_var EASYRSA_REQ_COUNTRY &amp;#34;CN&amp;#34;&#xA;set_var EASYRSA_REQ_PROVINCE &amp;#34;ZheJiang&amp;#34;&#xA;set_var EASYRSA_REQ_CITY &amp;#34;HangZhou&amp;#34;&#xA;set_var EASYRSA_REQ_ORG &amp;#34;OpenVPN CA&amp;#34;&#xA;set_var EASYRSA_REQ_EMAIL &amp;#34;zhaowenyu@cncfstack.com&amp;#34;&#xA;set_var EASYRSA_REQ_OU &amp;#34;MyVPN&amp;#34;&#xA;EOF&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&#34;初始化证书目录&#34;&gt;初始化证书目录&lt;a class=&#34;td-heading-self-link&#34; href=&#34;#%e5%88%9d%e5%a7%8b%e5%8c%96%e8%af%81%e4%b9%a6%e7%9b%ae%e5%bd%95&#34; aria-label=&#34;Heading self-link&#34;&gt;&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;./easyrsa init-pki&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&#34;创建ca证书&#34;&gt;创建ca证书&lt;a class=&#34;td-heading-self-link&#34; href=&#34;#%e5%88%9b%e5%bb%baca%e8%af%81%e4%b9%a6&#34; aria-label=&#34;Heading self-link&#34;&gt;&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;p&gt;在创建CA证书，为防止私钥丢失后非法获取私钥内容，&lt;code&gt;pass phrase&lt;/code&gt;需要输入2次私钥加密的密码。在使用CA签发证书时，需要使用ca证书加密密码。&lt;/p&gt;</description>
    </item>
    <item>
      <title>代码仓库</title>
      <link>https://cncfstack.com/p/vpn/code/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://cncfstack.com/p/vpn/code/</guid>
      <description>&lt;script type=&#34;text/javascript&#34;&gt;&#xA;    function resizeIframe(iframe) {&#xA;      iframe.height = iframe.contentWindow.document.body.scrollHeight + &#34;px&#34;;&#xA;    }&#xA;  &lt;/script&gt;  &#xA;&#xA;&lt;iframe id=&#34;iframe-id&#34; class=&#34;iframe-container&#34; src=&#34;https://gitea.cncfstack.com/vpn&#34; width=&#34;100%&#34; name=&#34;iframe-name&#34; style=&#34;min-height:98vh; border:none;&#34; onload=&#34;resizeIframe(this)&#34; referrerpolicy=&#34;no-referrer&#34;&gt;&#xA;  &lt;p&gt;Your browser can not display embedded frames. You can access the embedded page via the following link: &lt;a href=&#34;https://gitea.cncfstack.com/vpn&#34;&gt;https://gitea.cncfstack.com/vpn&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/iframe&gt;&#xA;&#xA;&lt;style&gt;&#xA;.iframe-container {&#xA;  margin-top: 15px;&#xA;}&#xA;&lt;/style&gt;</description>
    </item>
    <item>
      <title>使用网易脚本搭建</title>
      <link>https://cncfstack.com/p/vpn/docs/ops/vpn-install-163/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://cncfstack.com/p/vpn/docs/ops/vpn-install-163/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://www.163yun.com/help/documents/125681593272684544&#34;&gt;https://www.163yun.com/help/documents/125681593272684544&lt;/a&gt;&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&#xA;#!/bin/bash&#xA;# OpenVPN road warrior installer for Debian, Ubuntu and CentOS&#xA;&#xA;# This script will work on Debian, Ubuntu, CentOS and probably other distros&#xA;# of the same families, although no support is offered for them. It isn&amp;#39;t&#xA;# bulletproof but it will probably work if you simply want to setup a VPN on&#xA;# your Debian/Ubuntu/CentOS box. It has been designed to be as unobtrusive and&#xA;# universal as possible.&#xA;&#xA;&#xA;# Detect Debian users running the script with &amp;#34;sh&amp;#34; instead of bash&#xA;if readlink /proc/$$/exe | grep -qs &amp;#34;dash&amp;#34;; then&#xA;    echo &amp;#34;This script needs to be run with bash, not sh&amp;#34;&#xA;    exit 1&#xA;fi&#xA;&#xA;if [[ &amp;#34;$EUID&amp;#34; -ne 0 ]]; then&#xA;    echo &amp;#34;Sorry, you need to run this as root&amp;#34;&#xA;    exit 2&#xA;fi&#xA;&#xA;if [[ ! -e /dev/net/tun ]]; then&#xA;    echo &amp;#34;The TUN device is not available&#xA;You need to enable TUN before running this script&amp;#34;&#xA;    exit 3&#xA;fi&#xA;&#xA;if grep -qs &amp;#34;CentOS release 5&amp;#34; &amp;#34;/etc/redhat-release&amp;#34;; then&#xA;    echo &amp;#34;CentOS 5 is too old and not supported&amp;#34;&#xA;    exit 4&#xA;fi&#xA;if [[ -e /etc/debian_version ]]; then&#xA;    OS=debian&#xA;    GROUPNAME=nogroup&#xA;    RCLOCAL=&amp;#39;/etc/rc.local&amp;#39;&#xA;elif [[ -e /etc/centos-release || -e /etc/redhat-release ]]; then&#xA;    OS=centos&#xA;    GROUPNAME=nobody&#xA;    RCLOCAL=&amp;#39;/etc/rc.d/rc.local&amp;#39;&#xA;else&#xA;    echo &amp;#34;Looks like you aren&amp;#39;t running this installer on Debian, Ubuntu or CentOS&amp;#34;&#xA;    exit 5&#xA;fi&#xA;&#xA;newclient () {&#xA;    # Generates the custom client.ovpn&#xA;    cp /etc/openvpn/client-common.txt ~/$1.ovpn&#xA;    echo &amp;#34;&amp;lt;ca&amp;gt;&amp;#34; &amp;gt;&amp;gt; ~/$1.ovpn&#xA;    cat /etc/openvpn/easy-rsa/pki/ca.crt &amp;gt;&amp;gt; ~/$1.ovpn&#xA;    echo &amp;#34;&amp;lt;/ca&amp;gt;&amp;#34; &amp;gt;&amp;gt; ~/$1.ovpn&#xA;    echo &amp;#34;&amp;lt;cert&amp;gt;&amp;#34; &amp;gt;&amp;gt; ~/$1.ovpn&#xA;    cat /etc/openvpn/easy-rsa/pki/issued/$1.crt &amp;gt;&amp;gt; ~/$1.ovpn&#xA;    echo &amp;#34;&amp;lt;/cert&amp;gt;&amp;#34; &amp;gt;&amp;gt; ~/$1.ovpn&#xA;    echo &amp;#34;&amp;lt;key&amp;gt;&amp;#34; &amp;gt;&amp;gt; ~/$1.ovpn&#xA;    cat /etc/openvpn/easy-rsa/pki/private/$1.key &amp;gt;&amp;gt; ~/$1.ovpn&#xA;    echo &amp;#34;&amp;lt;/key&amp;gt;&amp;#34; &amp;gt;&amp;gt; ~/$1.ovpn&#xA;    echo &amp;#34;&amp;lt;tls-auth&amp;gt;&amp;#34; &amp;gt;&amp;gt; ~/$1.ovpn&#xA;    cat /etc/openvpn/ta.key &amp;gt;&amp;gt; ~/$1.ovpn&#xA;    echo &amp;#34;&amp;lt;/tls-auth&amp;gt;&amp;#34; &amp;gt;&amp;gt; ~/$1.ovpn&#xA;}&#xA;&#xA;# Try to get our IP from the system and fallback to the Internet.&#xA;# I do this to make the script compatible with NATed servers (lowendspirit.com)&#xA;# and to avoid getting an IPv6.&#xA;IP=$(ip addr | grep &amp;#39;inet&amp;#39; | grep -v inet6 | grep -vE &amp;#39;127\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}&amp;#39; | grep -o -E &amp;#39;[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}&amp;#39; | head -1)&#xA;VPCSUBNETDEFAULT=$(route -n | grep eth0 | grep 255.255| awk &amp;#39;{print $1,$3}&amp;#39; | tail -n 1| awk &amp;#39;{print $1}&amp;#39;)&#xA;VPCMASKDEFAULT=$(route -n | grep eth0 | grep 255.255| awk &amp;#39;{print $1,$3}&amp;#39; | tail -n 1| awk &amp;#39;{print $2}&amp;#39;)&#xA;&#xA;if [[ &amp;#34;$IP&amp;#34; = &amp;#34;&amp;#34; ]]; then&#xA;        IP=$(wget -4qO- &amp;#34;http://whatismyip.akamai.com/&amp;#34;)&#xA;fi&#xA;&#xA;if [[ -e /etc/openvpn/server.conf ]]; then&#xA;    while :&#xA;    do&#xA;    clear&#xA;        echo &amp;#34;Looks like OpenVPN is already installed&amp;#34;&#xA;        echo &amp;#34;&amp;#34;&#xA;        echo &amp;#34;What do you want to do?&amp;#34;&#xA;        echo &amp;#34;   1) Add a new user&amp;#34;&#xA;        echo &amp;#34;   2) Revoke an existing user&amp;#34;&#xA;        echo &amp;#34;   3) Remove OpenVPN&amp;#34;&#xA;        echo &amp;#34;   4) Exit&amp;#34;&#xA;        read -p &amp;#34;Select an option [1-4]: &amp;#34; option&#xA;        case $option in&#xA;            1)&#xA;            echo &amp;#34;&amp;#34;&#xA;            echo &amp;#34;Tell me a name for the client certificate&amp;#34;&#xA;            echo &amp;#34;Please, use one word only, no special characters&amp;#34;&#xA;            read -p &amp;#34;Client name: &amp;#34; -e -i netease-b CLIENT&#xA;            cd /etc/openvpn/easy-rsa/&#xA;            ./easyrsa build-client-full $CLIENT nopass&#xA;            # Generates the custom client.ovpn&#xA;            newclient &amp;#34;$CLIENT&amp;#34;&#xA;            echo &amp;#34;&amp;#34;&#xA;            echo &amp;#34;Client $CLIENT added, configuration is available at&amp;#34; ~/&amp;#34;$CLIENT.ovpn&amp;#34;&#xA;            exit&#xA;            ;;&#xA;            2)&#xA;            # This option could be documented a bit better and maybe even be simplimplified&#xA;            # ...but what can I say, I want some sleep too&#xA;            NUMBEROFCLIENTS=$(tail -n +2 /etc/openvpn/easy-rsa/pki/index.txt | grep -c &amp;#34;^V&amp;#34;)&#xA;            if [[ &amp;#34;$NUMBEROFCLIENTS&amp;#34; = &amp;#39;0&amp;#39; ]]; then&#xA;                echo &amp;#34;&amp;#34;&#xA;                echo &amp;#34;You have no existing clients!&amp;#34;&#xA;                exit 6&#xA;            fi&#xA;            echo &amp;#34;&amp;#34;&#xA;            echo &amp;#34;Select the existing client certificate you want to revoke&amp;#34;&#xA;            tail -n +2 /etc/openvpn/easy-rsa/pki/index.txt | grep &amp;#34;^V&amp;#34; | cut -d &amp;#39;=&amp;#39; -f 2 | nl -s &amp;#39;) &amp;#39;&#xA;            if [[ &amp;#34;$NUMBEROFCLIENTS&amp;#34; = &amp;#39;1&amp;#39; ]]; then&#xA;                read -p &amp;#34;Select one client [1]: &amp;#34; CLIENTNUMBER&#xA;            else&#xA;                read -p &amp;#34;Select one client [1-$NUMBEROFCLIENTS]: &amp;#34; CLIENTNUMBER&#xA;            fi&#xA;            CLIENT=$(tail -n +2 /etc/openvpn/easy-rsa/pki/index.txt | grep &amp;#34;^V&amp;#34; | cut -d &amp;#39;=&amp;#39; -f 2 | sed -n &amp;#34;$CLIENTNUMBER&amp;#34;p)&#xA;            cd /etc/openvpn/easy-rsa/&#xA;            ./easyrsa --batch revoke $CLIENT&#xA;            EASYRSA_CRL_DAYS=3650 ./easyrsa gen-crl&#xA;            rm -rf pki/reqs/$CLIENT.req&#xA;            rm -rf pki/private/$CLIENT.key&#xA;            rm -rf pki/issued/$CLIENT.crt&#xA;            rm -rf /etc/openvpn/crl.pem&#xA;            cp /etc/openvpn/easy-rsa/pki/crl.pem /etc/openvpn/crl.pem&#xA;            # CRL is read with each client connection, when OpenVPN is dropped to nobody&#xA;            chown nobody:$GROUPNAME /etc/openvpn/crl.pem&#xA;            echo &amp;#34;&amp;#34;&#xA;            echo &amp;#34;Certificate for client $CLIENT revoked&amp;#34;&#xA;            exit&#xA;            ;;&#xA;            3)&#xA;            echo &amp;#34;&amp;#34;&#xA;            read -p &amp;#34;Do you really want to remove OpenVPN? [y/n]: &amp;#34; -e -i n REMOVE&#xA;            if [[ &amp;#34;$REMOVE&amp;#34; = &amp;#39;y&amp;#39; ]]; then&#xA;                PORT=$(grep &amp;#39;^port &amp;#39; /etc/openvpn/server.conf | cut -d &amp;#34; &amp;#34; -f 2)&#xA;                PROTOCOL=$(grep &amp;#39;^proto &amp;#39; /etc/openvpn/server.conf | cut -d &amp;#34; &amp;#34; -f 2)&#xA;                if pgrep firewalld; then&#xA;                    IP=$(firewall-cmd --direct --get-rules ipv4 nat POSTROUTING | grep &amp;#39;\-s 10.8.0.0/24 &amp;#39;&amp;#34;&amp;#39;&amp;#34;&amp;#39;!&amp;#39;&amp;#34;&amp;#39;&amp;#34;&amp;#39; -d 10.8.0.0/24 -j SNAT --to &amp;#39; | cut -d &amp;#34; &amp;#34; -f 10)&#xA;                    # Using both permanent and not permanent rules to avoid a firewalld reload.&#xA;                    firewall-cmd --zone=public --remove-port=$PORT/$PROTOCOL&#xA;                    firewall-cmd --zone=trusted --remove-source=10.8.0.0/24&#xA;                    firewall-cmd --permanent --zone=public --remove-port=$PORT/$PROTOCOL&#xA;                    firewall-cmd --permanent --zone=trusted --remove-source=10.8.0.0/24&#xA;                    firewall-cmd --direct --remove-rule ipv4 nat POSTROUTING 0 -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to $IP&#xA;                    firewall-cmd --permanent --direct --remove-rule ipv4 nat POSTROUTING 0 -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to $IP&#xA;                else&#xA;                    IP=$(grep &amp;#39;iptables -t nat -A POSTROUTING -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to &amp;#39; $RCLOCAL | cut -d &amp;#34; &amp;#34; -f 14)&#xA;                    iptables -t nat -D POSTROUTING -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to $IP&#xA;                    sed -i &amp;#39;/iptables -t nat -A POSTROUTING -s 10.8.0.0\/24 ! -d 10.8.0.0\/24 -j SNAT --to /d&amp;#39; $RCLOCAL&#xA;                    if iptables -L -n | grep -qE &amp;#39;^ACCEPT&amp;#39;; then&#xA;                        iptables -D INPUT -p $PROTOCOL --dport $PORT -j ACCEPT&#xA;                        iptables -D FORWARD -s 10.8.0.0/24 -j ACCEPT&#xA;                        iptables -D FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT&#xA;                        sed -i &amp;#34;/iptables -I INPUT -p $PROTOCOL --dport $PORT -j ACCEPT/d&amp;#34; $RCLOCAL&#xA;                        sed -i &amp;#34;/iptables -I FORWARD -s 10.8.0.0\/24 -j ACCEPT/d&amp;#34; $RCLOCAL&#xA;                        sed -i &amp;#34;/iptables -I FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT/d&amp;#34; $RCLOCAL&#xA;                    fi&#xA;                fi&#xA;                if hash sestatus 2&amp;gt;/dev/null; then&#xA;                    if sestatus | grep &amp;#34;Current mode&amp;#34; | grep -qs &amp;#34;enforcing&amp;#34;; then&#xA;                        if [[ &amp;#34;$PORT&amp;#34; != &amp;#39;1194&amp;#39; || &amp;#34;$PROTOCOL&amp;#34; = &amp;#39;tcp&amp;#39; ]]; then&#xA;                            semanage port -d -t openvpn_port_t -p $PROTOCOL $PORT&#xA;                        fi&#xA;                    fi&#xA;                fi&#xA;                if [[ &amp;#34;$OS&amp;#34; = &amp;#39;debian&amp;#39; ]]; then&#xA;                    apt-get remove --purge -y openvpn&#xA;                else&#xA;                    yum remove openvpn -y&#xA;                fi&#xA;                rm -rf /etc/openvpn&#xA;                echo &amp;#34;&amp;#34;&#xA;                echo &amp;#34;OpenVPN removed!&amp;#34;&#xA;            else&#xA;                echo &amp;#34;&amp;#34;&#xA;                echo &amp;#34;Removal aborted!&amp;#34;&#xA;            fi&#xA;            exit&#xA;            ;;&#xA;            4) exit;;&#xA;        esac&#xA;    done&#xA;else&#xA;    clear&#xA;    echo &amp;#39;Welcome to this quick OpenVPN &amp;#34;road warrior&amp;#34; installer&amp;#39;&#xA;    echo &amp;#34;&amp;#34;&#xA;    # OpenVPN setup and first user creation&#xA;    echo &amp;#34;I need to ask you a few questions before starting the setup&amp;#34;&#xA;    echo &amp;#34;You can leave the default options and just press enter if you are ok with them&amp;#34;&#xA;    echo &amp;#34;&amp;#34;&#xA;    echo &amp;#34;First I need to know the IPv4 address of the network interface you want OpenVPN&amp;#34;&#xA;    echo &amp;#34;listening to.&amp;#34;&#xA;    read -p &amp;#34;IP address: &amp;#34; -e -i $IP IP&#xA;    echo &amp;#34;&amp;#34;    echo &amp;#34;Which protocol do you want for OpenVPN connections?&amp;#34;&#xA;&#xA;    echo &amp;#34;   1) UDP (recommended)&amp;#34;&#xA;    echo &amp;#34;   2) TCP&amp;#34;&#xA;    read -p &amp;#34;Protocol [1-2]: &amp;#34; -e -i 1 PROTOCOL&#xA;    case $PROTOCOL in&#xA;        1)&#xA;        PROTOCOL=udp&#xA;        ;;&#xA;        2)&#xA;        PROTOCOL=tcp&#xA;        ;;&#xA;    esac&#xA;    echo &amp;#34;&amp;#34;&#xA;    echo &amp;#34;What port do you want OpenVPN listening to?&amp;#34;&#xA;    read -p &amp;#34;Port: &amp;#34; -e -i 1194 PORT&#xA;    echo &amp;#34;&amp;#34;&#xA;    echo &amp;#34;what VPC subnet?&amp;#34;&#xA;    read -p &amp;#34;VPC subnet(E.g 172.16.0.0): &amp;#34; -e -i $VPCSUBNETDEFAULT  VPCSUBNET&#xA;    echo &amp;#34;&amp;#34;&#xA;    echo &amp;#34;what VPC mask?&amp;#34;&#xA;    read -p &amp;#34;VPC mask(E.g 255.255.0.0): &amp;#34; -e -i $VPCMASKDEFAULT  VPCMASK&#xA;    echo &amp;#34;&amp;#34;&#xA;    # echo &amp;#34;Which DNS do you want to use with the VPN?&amp;#34;&#xA;    # echo &amp;#34;   1) Current system resolvers&amp;#34;&#xA;    # echo &amp;#34;   2) Google&amp;#34;&#xA;    # echo &amp;#34;   3) OpenDNS&amp;#34;&#xA;    # echo &amp;#34;   4) NTT&amp;#34;&#xA;    # echo &amp;#34;   5) Hurricane Electric&amp;#34;&#xA;    # echo &amp;#34;   6) Verisign&amp;#34;&#xA;    # read -p &amp;#34;DNS [1-6]: &amp;#34; -e -i 1 DNS&#xA;    # echo &amp;#34;&amp;#34;&#xA;    echo &amp;#34;Finally, tell me your name for the client certificate&amp;#34;&#xA;    echo &amp;#34;Please, use one word only, no special characters&amp;#34;&#xA;    read -p &amp;#34;Client name: &amp;#34; -e -i netease-b CLIENT&#xA;    echo &amp;#34;&amp;#34;&#xA;    echo &amp;#34;Okay, that was all I needed. We are ready to setup your OpenVPN server now&amp;#34;&#xA;    read -n1 -r -p &amp;#34;Press any key to continue...&amp;#34;&#xA;    if [[ &amp;#34;$OS&amp;#34; = &amp;#39;debian&amp;#39; ]]; then&#xA;        apt-get update&#xA;        apt-get install openvpn iptables openssl ca-certificates -y&#xA;    else&#xA;        # Else, the distro is CentOS&#xA;        yum install epel-release -y&#xA;        yum install openvpn iptables openssl wget ca-certificates -y&#xA;    fi&#xA;    # An old version of easy-rsa was available by default in some openvpn packages&#xA;    if [[ -d /etc/openvpn/easy-rsa/ ]]; then&#xA;        rm -rf /etc/openvpn/easy-rsa/&#xA;    fi&#xA;    # Get easy-rsa&#xA;    wget -O ~/EasyRSA-3.0.4.tgz &amp;#34;https://nos-public.nos-eastchina1.126.net/EasyRSA-3.0.4.tgz&amp;#34;&#xA;    tar xzf ~/EasyRSA-3.0.4.tgz -C ~/&#xA;    mv ~/EasyRSA-3.0.4/ /etc/openvpn/&#xA;    mv /etc/openvpn/EasyRSA-3.0.4/ /etc/openvpn/easy-rsa/&#xA;    chown -R root:root /etc/openvpn/easy-rsa/&#xA;    rm -rf ~/EasyRSA-3.0.4.tgz&#xA;    cd /etc/openvpn/easy-rsa/&#xA;    # Create the PKI, set up the CA, the DH params and the server + client certificates&#xA;    ./easyrsa init-pki&#xA;    ./easyrsa --batch build-ca nopass&#xA;    ./easyrsa gen-dh&#xA;    ./easyrsa build-server-full server nopass&#xA;    ./easyrsa build-client-full $CLIENT nopass&#xA;    EASYRSA_CRL_DAYS=3650 ./easyrsa gen-crl&#xA;    # Move the stuff we need&#xA;    cp pki/ca.crt pki/private/ca.key pki/dh.pem pki/issued/server.crt pki/private/server.key pki/crl.pem /etc/openvpn&#xA;    # CRL is read with each client connection, when OpenVPN is dropped to nobody&#xA;    chown nobody:$GROUPNAME /etc/openvpn/crl.pem&#xA;    # Generate key for tls-auth&#xA;    openvpn --genkey --secret /etc/openvpn/ta.key&#xA;    # Generate server.conf&#xA;    echo &amp;#34;port $PORT&#xA;proto $PROTOCOL&#xA;dev tun&#xA;sndbuf 0&#xA;rcvbuf 0&#xA;ca ca.crt&#xA;cert server.crt&#xA;key server.key&#xA;dh dh.pem&#xA;auth SHA512&#xA;tls-auth ta.key 0&#xA;topology subnet&#xA;server 10.8.0.0 255.255.255.0&#xA;ifconfig-pool-persist ipp.txt&amp;#34; &amp;gt; /etc/openvpn/server.conf&#xA;    echo &amp;#34;push \&amp;#34;route $VPCSUBNET $VPCMASK vpn_gateway\&amp;#34;&amp;#34; &amp;gt;&amp;gt; /etc/openvpn/server.conf&#xA;    echo &amp;#34;keepalive 10 120&#xA;cipher AES-256-CBC&#xA;comp-lzo&#xA;user nobody&#xA;group $GROUPNAME&#xA;persist-key&#xA;duplicate-cn&#xA;max-clients 10&#xA;persist-tun&#xA;status openvpn-status.log&#xA;verb 3&#xA;crl-verify crl.pem&amp;#34; &amp;gt;&amp;gt; /etc/openvpn/server.conf&#xA;    # Enable net.ipv4.ip_forward for the system&#xA;    sed -i &amp;#39;/\&amp;lt;net.ipv4.ip_forward\&amp;gt;/c\net.ipv4.ip_forward=1&amp;#39; /etc/sysctl.conf&#xA;    if ! grep -q &amp;#34;\&amp;lt;net.ipv4.ip_forward\&amp;gt;&amp;#34; /etc/sysctl.conf; then&#xA;        echo &amp;#39;net.ipv4.ip_forward=1&amp;#39; &amp;gt;&amp;gt; /etc/sysctl.conf&#xA;    fi&#xA;    # Avoid an unneeded reboot&#xA;    echo 1 &amp;gt; /proc/sys/net/ipv4/ip_forward&#xA;    if pgrep firewalld; then&#xA;        # Using both permanent and not permanent rules to avoid a firewalld&#xA;        # reload.&#xA;        # We don&amp;#39;t use --add-service=openvpn because that would only work with&#xA;        # the default port and protocol.&#xA;        firewall-cmd --zone=public --add-port=$PORT/$PROTOCOL&#xA;        firewall-cmd --zone=trusted --add-source=10.8.0.0/24&#xA;        firewall-cmd --permanent --zone=public --add-port=$PORT/$PROTOCOL&#xA;        firewall-cmd --permanent --zone=trusted --add-source=10.8.0.0/24&#xA;        # Set NAT for the VPN subnet&#xA;        firewall-cmd --direct --add-rule ipv4 nat POSTROUTING 0 -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to $IP&#xA;        firewall-cmd --permanent --direct --add-rule ipv4 nat POSTROUTING 0 -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to $IP&#xA;    else&#xA;        # Needed to use rc.local with some systemd distros&#xA;        if [[ &amp;#34;$OS&amp;#34; = &amp;#39;debian&amp;#39; &amp;amp;&amp;amp; ! -e $RCLOCAL ]]; then&#xA;            echo &amp;#39;#!/bin/sh -e&#xA;exit 0&amp;#39; &amp;gt; $RCLOCAL&#xA;        fi&#xA;        chmod +x $RCLOCAL&#xA;        # Set NAT for the VPN subnet&#xA;        iptables -t nat -A POSTROUTING -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to $IP&#xA;        sed -i &amp;#34;1 a\iptables -t nat -A POSTROUTING -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to $IP&amp;#34; $RCLOCAL&#xA;        if iptables -L -n | grep -qE &amp;#39;^(REJECT|DROP)&amp;#39;; then&#xA;            # If iptables has at least one REJECT rule, we asume this is needed.&#xA;            # Not the best approach but I can&amp;#39;t think of other and this shouldn&amp;#39;t&#xA;            # cause problems.&#xA;            iptables -I INPUT -p $PROTOCOL --dport $PORT -j ACCEPT&#xA;            iptables -I FORWARD -s 10.8.0.0/24 -j ACCEPT&#xA;            iptables -I FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT&#xA;            sed -i &amp;#34;1 a\iptables -I INPUT -p $PROTOCOL --dport $PORT -j ACCEPT&amp;#34; $RCLOCAL&#xA;            sed -i &amp;#34;1 a\iptables -I FORWARD -s 10.8.0.0/24 -j ACCEPT&amp;#34; $RCLOCAL&#xA;            sed -i &amp;#34;1 a\iptables -I FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT&amp;#34; $RCLOCAL&#xA;        fi&#xA;    fi&#xA;    # If SELinux is enabled and a custom port or TCP was selected, we need this&#xA;    if hash sestatus 2&amp;gt;/dev/null; then&#xA;        if sestatus | grep &amp;#34;Current mode&amp;#34; | grep -qs &amp;#34;enforcing&amp;#34;; then&#xA;            if [[ &amp;#34;$PORT&amp;#34; != &amp;#39;1194&amp;#39; || &amp;#34;$PROTOCOL&amp;#34; = &amp;#39;tcp&amp;#39; ]]; then&#xA;                # semanage isn&amp;#39;t available in CentOS 6 by default&#xA;                if ! hash semanage 2&amp;gt;/dev/null; then&#xA;                    yum install policycoreutils-python -y&#xA;                fi&#xA;                semanage port -a -t openvpn_port_t -p $PROTOCOL $PORT&#xA;            fi&#xA;        fi&#xA;    fi&#xA;    # And finally, restart OpenVPN&#xA;    if [[ &amp;#34;$OS&amp;#34; = &amp;#39;debian&amp;#39; ]]; then&#xA;        # Little hack to check for systemd&#xA;        if pgrep systemd-journal; then&#xA;            systemctl restart openvpn@server.service&#xA;        else&#xA;            /etc/init.d/openvpn restart&#xA;        fi&#xA;    else&#xA;        if pgrep systemd-journal; then&#xA;            systemctl restart openvpn@server.service&#xA;            systemctl enable openvpn@server.service&#xA;        else&#xA;            service openvpn restart&#xA;            chkconfig openvpn on&#xA;        fi&#xA;    fi&#xA;    # Try to detect a NATed connection and ask about it to potential LowEndSpirit users&#xA;    EXTERNALIP=$(wget -4qO- &amp;#34;http://whatismyip.akamai.com/&amp;#34;)&#xA;    if [[ &amp;#34;$IP&amp;#34; != &amp;#34;$EXTERNALIP&amp;#34; ]]; then&#xA;        echo &amp;#34;&amp;#34;&#xA;        echo &amp;#34;Looks like your server is behind a NAT!&amp;#34;&#xA;        echo &amp;#34;&amp;#34;&#xA;        echo &amp;#34;If your server is NATed (e.g. LowEndSpirit), I need to know the external IP&amp;#34;&#xA;        echo &amp;#34;If that&amp;#39;s not the case, just ignore this and leave the next field blank&amp;#34;&#xA;        read -p &amp;#34;External IP: &amp;#34; -e -i $EXTERNALIP USEREXTERNALIP&#xA;        if [[ &amp;#34;$USEREXTERNALIP&amp;#34; != &amp;#34;&amp;#34; ]]; then&#xA;            IP=$USEREXTERNALIP&#xA;        fi&#xA;    fi&#xA;    # client-common.txt is created so we have a template to add further users later&#xA;    echo &amp;#34;client&#xA;dev tun&#xA;proto $PROTOCOL&#xA;sndbuf 0&#xA;rcvbuf 0&#xA;remote $IP $PORT&#xA;resolv-retry infinite&#xA;nobind&#xA;max-clients 10&#xA;persist-key&#xA;persist-tun&#xA;remote-cert-tls server&#xA;auth SHA512&#xA;cipher AES-256-CBC&#xA;comp-lzo&#xA;setenv opt block-outside-dns&#xA;key-direction 1&#xA;auth-nocache&#xA;verb 3&amp;#34; &amp;gt; /etc/openvpn/client-common.txt&#xA;    # Generates the custom client.ovpn&#xA;    newclient &amp;#34;$CLIENT&amp;#34;&#xA;    echo &amp;#34;&amp;#34;&#xA;    echo &amp;#34;Finished!&amp;#34;&#xA;    echo &amp;#34;&amp;#34;&#xA;    echo &amp;#34;Your client configuration is available at&amp;#34; ~/&amp;#34;$CLIENT.ovpn&amp;#34;&#xA;    echo &amp;#34;If you want to add more clients, you simply need to run this script again!&amp;#34;&#xA;fi&#xA;&lt;/code&gt;&lt;/pre&gt;</description>
    </item>
    <item>
      <title>博客</title>
      <link>https://cncfstack.com/p/vpn/news/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://cncfstack.com/p/vpn/news/</guid>
      <description>&lt;div id=&#34;blog-app&#34;&gt;&#xA;  &lt;!-- 加载状态 --&gt;&#xA;  &lt;div id=&#34;loading-state&#34; class=&#34;loading-state&#34;&gt;&#xA;    &lt;div class=&#34;loading-spinner&#34;&gt;&lt;/div&gt;&#xA;    &lt;p&gt;文档加载中...&lt;/p&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;iframe &#xA;    id=&#34;blog-iframe&#34;&#xA;    src=&#34;&#34;&#xA;    class=&#34;full-width-iframe&#34;&#xA;    scrolling=&#34;auto&#34;&#xA;    onload=&#34;onIframeLoad()&#34;&#xA;  &gt;&lt;/iframe&gt;&#xA;&lt;/div&gt;&#xA;&lt;style&gt;&#xA;/* 原有样式保持不变 */&#xA;.image-list-container {&#xA;  max-width: 1200px;&#xA;  margin: 2rem auto;&#xA;  padding: 0 1rem;&#xA;}&#xA;&#xA;.image-list-title {&#xA;  text-align: center;&#xA;  margin-bottom: 2rem;&#xA;  color: #333;&#xA;  font-size: 1.8rem;&#xA;}&#xA;&#xA;.image-grid {&#xA;  display: grid;&#xA;  grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));&#xA;  gap: 1.5rem;&#xA;}&#xA;&#xA;.image-card {&#xA;  border: 1px solid #e1e8ed;&#xA;  border-radius: 8px;&#xA;  overflow: hidden;&#xA;  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);&#xA;  transition: transform 0.3s ease, box-shadow 0.3s ease;&#xA;  background: white;&#xA;  cursor: pointer;&#xA;}&#xA;&#xA;.image-card:hover {&#xA;  transform: translateY(-5px);&#xA;  box-shadow: 0 5px 20px rgba(0, 0, 0, 0.15);&#xA;}&#xA;&#xA;.card-link {&#xA;  text-decoration: none;&#xA;  color: inherit;&#xA;}&#xA;&#xA;.card-header {&#xA;  background-color: #f5f8fa;&#xA;  padding: 1rem;&#xA;  border-bottom: 1px solid #e1e8ed;&#xA;}&#xA;&#xA;.card-title {&#xA;  margin: 0;&#xA;  font-size: 1.2rem;&#xA;  color: #1da1f2;&#xA;  font-weight: 600;&#xA;}&#xA;&#xA;.card-body {&#xA;  padding: 1rem;&#xA;}&#xA;&#xA;.vendor-info {&#xA;  display: flex;&#xA;  align-items: center;&#xA;  margin-bottom: 0.8rem;&#xA;}&#xA;&#xA;.vendor-icon {&#xA;  width: 32px;&#xA;  height: 32px;&#xA;  background-color: #1da1f2;&#xA;  border-radius: 50%;&#xA;  display: flex;&#xA;  align-items: center;&#xA;  justify-content: center;&#xA;  color: white;&#xA;  font-weight: bold;&#xA;  margin-right: 0.8rem;&#xA;}&#xA;&#xA;.vendor-name {&#xA;  font-weight: 500;&#xA;  color: #333;&#xA;}&#xA;&#xA;.repo-info {&#xA;  background-color: #f8f9fa;&#xA;  padding: 0.8rem;&#xA;  border-radius: 4px;&#xA;  font-family: &#39;Courier New&#39;, monospace;&#xA;  font-size: 0.9rem;&#xA;  color: #333;&#xA;  word-break: break-all;&#xA;  margin-bottom: 0;&#xA;}&#xA;&#xA;.error-message {&#xA;  text-align: center;&#xA;  color: #e02d2d;&#xA;  padding: 2rem;&#xA;  font-size: 1.1rem;&#xA;}&#xA;&#xA;.loading-message {&#xA;  text-align: center;&#xA;  padding: 2rem;&#xA;  font-size: 1.1rem;&#xA;  color: #657786;&#xA;}&#xA;&#xA;@media (max-width: 768px) {&#xA;  .image-grid {&#xA;    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));&#xA;    gap: 1rem;&#xA;  }&#xA;  &#xA;  .image-list-title {&#xA;    font-size: 1.5rem;&#xA;  }&#xA;}&#xA;&#xA;/* 新增博客页面样式 */&#xA;.blog-page {&#xA;  margin-top: -60px;&#xA;  width: 100%;&#xA;}&#xA;&#xA;.full-width-iframe {&#xA;  width: 100%;&#xA;  border: none;&#xA;  overflow: hidden;&#xA;  min-height: 600px;&#xA;}&#xA;&#xA;.loading-spinner {&#xA;  width: 40px;&#xA;  height: 40px;&#xA;  border: 4px solid #e9ecef;&#xA;  border-top: 4px solid #007bff;&#xA;  border-radius: 50%;&#xA;  animation: spin 1s linear infinite;&#xA;  margin: 0 auto;&#xA;}&#xA;&#xA;@keyframes spin {&#xA;  0% { transform: rotate(0deg); }&#xA;  100% { transform: rotate(360deg); }&#xA;}&#xA;&#xA;.loading-state {&#xA;  text-align: center;&#xA;  padding: 2rem;&#xA;}&#xA;&lt;/style&gt;&#xA;&lt;script&gt;&#xA;// 初始化 iframe URL&#xA;document.addEventListener(&#39;DOMContentLoaded&#39;, function() {&#xA;  const iframe = document.getElementById(&#39;blog-iframe&#39;);&#xA;  const currentYear = new Date().getFullYear();&#xA;  iframe.src = `https://cncfstack.com/b/tags/vpn`;&#xA;  // 设置初始高度&#xA;  iframe.style.height = window.innerHeight + &#39;px&#39;;&#xA;  &#xA;  // 添加 visibilitychange 事件监听器&#xA;  document.addEventListener(&#39;visibilitychange&#39;, handleVisibilityChange);&#xA;});&#xA;&#xA;function hideInternalNav() {&#xA;  const iframe = document.getElementById(&#39;blog-iframe&#39;);&#xA;  if (!iframe) return;&#xA;&#xA;  try {&#xA;    const iframeDoc = iframe.contentDocument;&#xA;    if (iframeDoc) {&#xA;      // 隐藏导航栏元素&#xA;      const navElement = iframeDoc.querySelector(&#39;nav.td-navbar&#39;);&#xA;      if (navElement) {&#xA;        navElement.style.display = &#39;none&#39;;&#xA;      }&#xA;&#xA;      // 隐藏 header 元素&#xA;      const headerElement = iframeDoc.querySelector(&#39;header&#39;);&#xA;      if (headerElement) {&#xA;        headerElement.style.display = &#39;none&#39;;&#xA;      }&#xA;&#xA;      // 隐藏页面底部元素&#xA;      const navFooterElement = iframeDoc.querySelector(&#39;footer.td-footer&#39;);&#xA;      if (navFooterElement) {&#xA;        navFooterElement.style.display = &#39;none&#39;;&#xA;      }&#xA;    }&#xA;  } catch (e) {&#xA;    console.log(&#39;无法访问 iframe 内容（可能是跨域限制）&#39;);&#xA;  }&#xA;}&#xA;&#xA;function setIframeHeight() {&#xA;  const iframe = document.getElementById(&#39;blog-iframe&#39;);&#xA;  if (!iframe) return;&#xA;&#xA;  try {&#xA;    const iframeDoc = iframe.contentDocument || iframe.contentWindow?.document;&#xA;    if (iframeDoc) {&#xA;      // 延迟执行以确保样式已应用&#xA;      setTimeout(() =&gt; {&#xA;        const height = Math.max(&#xA;          iframeDoc.body.scrollHeight,&#xA;          iframeDoc.documentElement.scrollHeight,&#xA;          iframeDoc.body.offsetHeight,&#xA;          iframeDoc.documentElement.offsetHeight,&#xA;          iframeDoc.body.clientHeight,&#xA;          iframeDoc.documentElement.clientHeight&#xA;        );&#xA;&#xA;        iframe.style.height = height + &#39;px&#39;;&#xA;      }, 100);&#xA;    }&#xA;  } catch (e) {&#xA;    // 跨域情况下的备选方案&#xA;    iframe.style.height = &#39;1200px&#39;;&#xA;  }&#xA;}&#xA;&#xA;function onIframeLoad() {&#xA;  hideInternalNav();&#xA;  setIframeHeight();&#xA;  &#xA;  // 隐藏加载状态&#xA;  const loadingState = document.getElementById(&#39;loading-state&#39;);&#xA;  if (loadingState) {&#xA;    loadingState.style.display = &#39;none&#39;;&#xA;  }&#xA;}&#xA;&#xA;function handleVisibilityChange() {&#xA;  const iframe = document.getElementById(&#39;blog-iframe&#39;);&#xA;  if (!document.hidden &amp;&amp; iframe) {&#xA;    // 页面变为可见时重新调整 iframe 高度&#xA;    setTimeout(() =&gt; {&#xA;      setIframeHeight();&#xA;    }, 300);&#xA;  }&#xA;}&#xA;&#xA;// 窗口大小改变时调整 iframe 高度&#xA;window.addEventListener(&#39;resize&#39;, function() {&#xA;  const iframe = document.getElementById(&#39;blog-iframe&#39;);&#xA;  if (iframe) {&#xA;    setIframeHeight();&#xA;  }&#xA;});&#xA;&lt;/script&gt;</description>
    </item>
    <item>
      <title>安装软件包</title>
      <link>https://cncfstack.com/p/vpn/docs/ops/ops-install2/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://cncfstack.com/p/vpn/docs/ops/ops-install2/</guid>
      <description>&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;openssl openssl-devel lzo lzo-devel pam pam-devel automake pkgconfig makecache&#xA;yum -y install epel-release&#xA;yum -y install openvpn easy-rsa &#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;https://www.163yun.com/help/documents/157215178789933056&#34;&gt;https://www.163yun.com/help/documents/157215178789933056&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://openvpn.net/community-resources/how-to/#quick&#34;&gt;https://openvpn.net/community-resources/how-to/#quick&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://qhh.me/2019/06/16/Cenos7-%E4%B8%8B%E6%90%AD%E5%BB%BA-OpenVPN-%E8%BF%87%E7%A8%8B%E8%AE%B0%E5%BD%95/&#34;&gt;https://qhh.me/2019/06/16/Cenos7-%E4%B8%8B%E6%90%AD%E5%BB%BA-OpenVPN-%E8%BF%87%E7%A8%8B%E8%AE%B0%E5%BD%95/&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;网易云部署方案&#xA;&lt;a href=&#34;https://www.163yun.com/help/documents/125681593272684544&#34;&gt;https://www.163yun.com/help/documents/125681593272684544&lt;/a&gt;&#xA;wget &lt;a href=&#34;https://nos-public.nos-eastchina1.126.net/vpn&#34;&gt;https://nos-public.nos-eastchina1.126.net/vpn&lt;/a&gt; -O openvpn-install.sh &amp;amp;&amp;amp; bash openvpn-install.sh&lt;/p&gt;&#xA;&lt;p&gt;官方安装脚本&#xA;&lt;a href=&#34;https://github.com/Angristan/OpenVPN-install&#34;&gt;https://github.com/Angristan/OpenVPN-install&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>搜索结果</title>
      <link>https://cncfstack.com/p/vpn/search/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://cncfstack.com/p/vpn/search/</guid>
      <description></description>
    </item>
    <item>
      <title>官方网站</title>
      <link>https://cncfstack.com/p/vpn/website/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://cncfstack.com/p/vpn/website/</guid>
      <description>&lt;section id=&#34;td-cover-block-0&#34; class=&#34;row td-cover-block td-cover-block--height-auto js-td-cover td-overlay td-overlay--dark -bg-primary&#34;&gt;&#xA;  &lt;div class=&#34;col-12&#34;&gt;&#xA;    &lt;div class=&#34;container td-overlay__inner&#34;&gt;&#xA;      &lt;div class=&#34;text-center&#34;&gt;&#xA;        &lt;h1 class=&#34;display-1 mt-0 mt-md-5 pb-4&#34;&gt;vpn 官方网站&lt;/h1&gt;&#xA;        &#xA;        &lt;div class=&#34;pt-3 lead&#34;&gt;&#xA;          &#xA;              软件项目第一手资料除了&lt;strong&gt;源码&lt;/strong&gt;，就是&lt;strong&gt;官网&lt;/strong&gt;&#xA;          &#xA;        &lt;/div&gt;&#xA;      &lt;/div&gt;&#xA;    &lt;/div&gt;&#xA;  &lt;/div&gt;&#xA;  &#xA;&lt;/section&gt;&#xA;&#xA;&lt;div&gt;&lt;a id=&#34;td-block-1&#34; class=&#34;td-offset-anchor&#34;&gt;&lt;/a&gt;&lt;/div&gt;&#xA;&lt;section class=&#34;row td-box td-box--dark td-box--height-auto&#34;&gt;&#xA;&lt;div class=&#34;col&#34;&gt;&#xA;&lt;div class=&#34;row&#34;&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&#34;col-lg-4 mb-5 mb-lg-0 text-center&#34;&gt;&#xA;&lt;div class=&#34;mb-4 h1&#34;&gt;&#xA;  &lt;i class=&#34;fas fa-lightbulb&#34;&gt;&lt;/i&gt;&#xA;&lt;/div&gt;&#xA;&lt;h4 class=&#34;h3&#34;&gt;官网&lt;/h4&gt;&#xA;&lt;div class=&#34;mb-0&#34;&gt;&#xA;&lt;p&gt;项目实际官网，由该项目的社区共同维护，是最新最权威的项目网站&lt;/p&gt;&#xA;&lt;div class=&#34;mx-auto&#34;&gt;&#xA;&#x9;&lt;a class=&#34;btn btn-lg btn-primary me-3 mb-4&#34; href=&#34;https://openvpn.net/&#34; target=&#34;_blank&#34;&gt;&#xA;&#x9;&#x9;访问网站 &lt;i class=&#34;fa-solid fa-circle-right ms-2&#34;&gt;&lt;/i&gt;&#xA;&#x9;&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;/section&gt;</description>
    </item>
    <item>
      <title>获取最新软件包</title>
      <link>https://cncfstack.com/p/vpn/docs/ops/vpn-install/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://cncfstack.com/p/vpn/docs/ops/vpn-install/</guid>
      <description>&lt;p&gt;openvpn提供了OpenVPN的核心&lt;/p&gt;&#xA;&lt;p&gt;easy-rsa包含了一些有用的密钥管理脚本&lt;/p&gt;&#xA;&lt;p&gt;dnsmasq是当我们的OpenVPN所在的主机将扮演客户端的路由器时会用到的域名服务器&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://github.com/OpenVPN/openvpn/archive/v2.4.6.tar.gz&#34;&gt;https://github.com/OpenVPN/openvpn/archive/v2.4.6.tar.gz&lt;/a&gt;&#xA;&lt;a href=&#34;https://github.com/OpenVPN/easy-rsa/releases/download/v3.0.4/EasyRSA-3.0.4.tgz&#34;&gt;https://github.com/OpenVPN/easy-rsa/releases/download/v3.0.4/EasyRSA-3.0.4.tgz&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h1 id=&#34;openvpn安装&#34;&gt;OpenVPN安装&lt;a class=&#34;td-heading-self-link&#34; href=&#34;#openvpn%e5%ae%89%e8%a3%85&#34; aria-label=&#34;Heading self-link&#34;&gt;&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;p&gt;安装依赖&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;yum install lzo-devel autoconf pam-devel  automake libtool&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;编译安装&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;autoreconf -i -v -f&#xA;./configure --prefix=/usr/local/openvpn/&#xA;make&#xA;make install&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&#34;easyrsa-配置&#34;&gt;EasyRsa 配置&lt;a class=&#34;td-heading-self-link&#34; href=&#34;#easyrsa-%e9%85%8d%e7%bd%ae&#34; aria-label=&#34;Heading self-link&#34;&gt;&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;h3 id=&#34;软件目录配置&#34;&gt;软件目录配置&lt;a class=&#34;td-heading-self-link&#34; href=&#34;#%e8%bd%af%e4%bb%b6%e7%9b%ae%e5%bd%95%e9%85%8d%e7%bd%ae&#34; aria-label=&#34;Heading self-link&#34;&gt;&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;tar xf EasyRSA-3.0.4.tgz&#xA;mv EasyRSA-3.0.4.tgz /usr/local/openvpn/EasyRSA&#xA;cd /usr/local/openvpn/EasyRSA&#xA;cp vars.example vars&#xA;vim vars&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;编辑vars文件&#34;&gt;编辑vars文件&lt;a class=&#34;td-heading-self-link&#34; href=&#34;#%e7%bc%96%e8%be%91vars%e6%96%87%e4%bb%b6&#34; aria-label=&#34;Heading self-link&#34;&gt;&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;set_var EASYRSA_REQ_COUNTRY &amp;#34;CN&amp;#34;&#xA;set_var EASYRSA_REQ_PROVINCE &amp;#34;ZheJiang&amp;#34;&#xA;set_var EASYRSA_REQ_CITY &amp;#34;HangZhou&amp;#34;&#xA;set_var EASYRSA_REQ_ORG &amp;#34;test ORG&amp;#34;&#xA;set_var EASYRSA_REQ_EMAIL &amp;#34;admin@zhaowenyu.com&amp;#34;&#xA;set_var EASYRSA_REQ_OU &amp;#34;vinnyzhao&amp;#34;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&#34;证书制作&#34;&gt;证书制作&lt;a class=&#34;td-heading-self-link&#34; href=&#34;#%e8%af%81%e4%b9%a6%e5%88%b6%e4%bd%9c&#34; aria-label=&#34;Heading self-link&#34;&gt;&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;h3 id=&#34;自建ca认证体系&#34;&gt;自建CA认证体系&lt;a class=&#34;td-heading-self-link&#34; href=&#34;#%e8%87%aa%e5%bb%baca%e8%ae%a4%e8%af%81%e4%bd%93%e7%b3%bb&#34; aria-label=&#34;Heading self-link&#34;&gt;&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;p&gt;1、初始化证书管理目录&#xA;进入/etc/openvpn/easy-rsa/easyrsa3/目录初始化：&lt;/p&gt;</description>
    </item>
    <item>
      <title>论坛</title>
      <link>https://cncfstack.com/p/vpn/discourse/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://cncfstack.com/p/vpn/discourse/</guid>
      <description>&lt;section id=&#34;td-cover-block-0&#34; class=&#34;row td-cover-block td-cover-block--height-full js-td-cover td-overlay td-overlay--dark -bg-primary&#34;&gt;&#xA;  &lt;div class=&#34;col-12&#34;&gt;&#xA;    &lt;div class=&#34;container td-overlay__inner&#34;&gt;&#xA;      &lt;div class=&#34;text-center&#34;&gt;&#xA;        &lt;h1 class=&#34;display-1 mt-0 mt-md-5 pb-4&#34;&gt;论坛&lt;/h1&gt;&#xA;        &#xA;        &lt;div class=&#34;pt-3 lead&#34;&gt;&#xA;          &#xA;              &lt;p&gt;开放的社区论坛，自由的技术交流&lt;/p&gt;&#xA;&lt;div class=&#34;mx-auto&#34;&gt;&#xA;&#x9;&lt;a class=&#34;btn btn-lg btn-primary me-3 mb-4&#34; href=&#34;https://discourse.cncfstack.com/tag/vpn/&#34; target=&#34;_blank&#34;&gt;&#xA;&#x9;&#x9;访问论坛 &lt;i class=&#34;fa-solid fa-circle-right ms-2&#34;&gt;&lt;/i&gt;&#xA;&#x9;&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;          &#xA;        &lt;/div&gt;&#xA;      &lt;/div&gt;&#xA;    &lt;/div&gt;&#xA;  &lt;/div&gt;&#xA;  &#xA;&lt;/section&gt;</description>
    </item>
  </channel>
</rss>
