无公网IP情况下家庭wireguard服务器的搭建

2024-10-30 lqy openwrt

### 方案概述

1. **家庭网络环境**:
   - 家庭宽带使用内网IP(通常是100.64.0.0/10)。
   - 运营商分配的公网IPv6地址。
   - 使用NAT1进行UDP打洞,获取公网IPv4地址和端口。

2. **服务器配置**:
   - 在公网VPS上搭建一个键值对服务器(例如使用PHP编写)。
   - 在家庭网络中运行WireGuard服务器,监听特定端口。

3. **客户端配置**:
   - 客户端在有IPv6的情况下直接使用IPv6连接WireGuard服务器。
   - 客户端在没有IPv6的情况下,通过键值对服务器查询WireGuard服务器的公网IPv4和端口。
   - 定义计划任务,定期更新WireGuard服务器的endpoint。

### 详细步骤

#### 1. 公网VPS上的键值对服务器

- **编写键值对服务器**:
  - 使用PHP或其他语言编写一个简单的键值对服务器,用于存储和查询WireGuard服务器的公网IPv4和端口信息。
  - 服务器可以提供API接口,如 `GET ?id=xxx` 和 `POST ?id=xxx&value=xxx`。

- **键值对服务器示例代码**(PHP):
<?php
// 设置数据库文件路径
$dbFile = __DIR__ . '/data.db';

// 创建一个新的PDO实例连接到SQLite数据库
try {
    $pdo = new PDO("sqlite:$dbFile");
    // 设置错误模式为异常
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    // 创建表(如果不存在)
    $pdo->exec("
        CREATE TABLE IF NOT EXISTS key_value_store (
            id TEXT PRIMARY KEY,
            value TEXT
        )
    ");
} catch (PDOException $e) {
    die("数据库连接失败: " . $e->getMessage());
}

// 检查请求方法
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // 处理POST请求,保存键值对
    $id = isset($_POST['id']) ? $_POST['id'] : null;
    $value = isset($_POST['value']) ? $_POST['value'] : null;

    if ($id && $value) {
        try {
            // 插入或更新数据
            $stmt = $pdo->prepare('INSERT OR REPLACE INTO key_value_store (id, value) VALUES (:id, :value)');
            $stmt->execute([':id' => $id, ':value' => $value]);
            echo "键值对 {$id}={$value} 已保存";
        } catch (PDOException $e) {
            echo "数据库操作失败: " . $e->getMessage();
        }
    } else {
        echo "缺少必要的参数:id 和 value";
    }
} elseif ($_SERVER['REQUEST_METHOD'] === 'GET') {
    // 处理GET请求,查询键值对
    $id = isset($_GET['id']) ? $_GET['id'] : null;

    if ($id) {
        try {
            $stmt = $pdo->prepare('SELECT value FROM key_value_store WHERE id = :id');
            $stmt->execute([':id' => $id]);
            $result = $stmt->fetch(PDO::FETCH_ASSOC);

            if ($result) {
                echo $result['value'];
            } else {
                echo "未找到ID {$id}";
            }
        } catch (PDOException $e) {
            echo "数据库操作失败: " . $e->getMessage();
        }
    } else {
        echo "缺少必要的参数:id";
    }
} else {
    // 不支持的方法
    http_response_code(405);
    echo "不支持的请求方法";
}
?>

#### 2. 家庭网络中的WireGuard服务器

- **安装和配置WireGuard**:
  - 在家庭网络中安装并配置WireGuard服务器。
  - 配置WireGuard服务器监听特定端口(例如51820)。

- **使用NAT映射工具**:
  - 使用NAT映射工具natpmp来获取公网IPv4地址和端口。
  - 将公网IPv4地址和端口更新到键值对服务器。

- **更新natmap获取的ip与端口示例脚本**(Shell):
#!/bin/sh

curl -X POST https://服务器域名/键值对文件名.php -d "id=自定义&value=${1}:${2}"


#### 3. 客户端配置

- **配置WireGuard客户端**:
  - 配置WireGuard客户端,使其优先使用IPv6连接。
  - 如果客户端没有IPv6,则从键值对服务器获取WireGuard服务器的公网IPv4和端口。

- **示例客户端配置文件**:
  ```ini
  [Interface]
  PrivateKey = <Your-PrivateKey>
  Address = 10.0.0.2/24
  DNS = 1.1.1.1, 8.8.8.8

  [Peer]
  PublicKey = <Server-Public-Key>
  AllowedIPs = 0.0.0.0/0, ::/0
  Endpoint = <Server-IPv6-Address>:51820
  PersistentKeepalive = 25
  ```

- **动态更新Endpoint**:
  - 编写一个脚本,当网络连通时或每半小时定时更新WireGuard服务器的endpoint。
  - 使用计划任务来运行这个脚本。

- **示例脚本**(VBS):
Set WshShell = CreateObject("WScript.Shell") 
WshShell.Run "powershell -WindowStyle Hidden -Command ""& { wg set wg接口名 peer 服务器公钥 endpoint (Invoke-WebRequest -Uri https://服务器域名/键值对文件名.php?id=自定义).Content }""", 0, True
Set WshShell = Nothing


- **创建计划任务**:
  - 打开“任务计划程序”。
  - 创建一个新的基本任务或触发器。
  - 在“常规”选项卡中,输入任务名称和描述。

  - 在“触发器”部分,设置任务的触发条件为发生事件时,选择自定义,输入如下 xml。

<QueryList>
  <Query Id="0" Path="Microsoft-Windows-NCSI/Operational">
    <Select Path="Microsoft-Windows-NCSI/Operational">*[System[(EventID=4042)]] and *[EventData[Data[@Name='Capability']='2']]</Select>
  </Query>
</QueryList>

  - 在“触发器”部分,设置任务的触发条件每半小时。

  - 在“操作”部分,选择“启动程序”。
  - 在“程序/脚本”字段中,输入 `你的vbs脚本全路径`。
  - 确保在“常规”选项卡中选择了“使用最高权限运行”。


通过上述步骤,您可以实现一个灵活的家庭WireGuard服务器配置,支持IPv4和IPv6连接,并能够自动更新服务器的endpoint。这样,无论客户端是否有IPv6,都能可靠地连接到WireGuard服务器。

标签: wireguard

发表评论:

Powered by emlog