Files
Meshray-Manager/docs/MeshSeed 生成功能实现报告.md
2026-06-30 15:14:37 +08:00

11 KiB
Raw Permalink Blame History

MeshSeed 生成功能实现报告

完成时间: 2026-03-24
状态: 框架已完成
优先级: P1 - 高优先级


📋 实现内容

1. MeshSeedService 服务层

文件: internal/service/meshseed.go (新建,205 行)

核心功能

GenerateMeshSeed - 生成组网凭证

func (s *MeshSeedService) GenerateMeshSeed(
	networkID uint,      // 网络 ID
	maxUses int,         // 最大使用次数
	expiresAt time.Time, // 过期时间
	ddnsEnabled bool     // DDNS 开关
) (*model.MeshSeed, error)

实现步骤:

  1. 验证网络是否存在
  2. 生成 16 字节随机 SeedIDBase64 编码)
  3. 构建 JoinToken(包含网络信息的 JSON
  4. Ed25519 数字签名
  5. 保存到数据库

数据结构:

// JoinToken = Base64(JSON({
{
  "seed_id":      "abc123...",
  "network_id":   1,
  "network_name": "MyNetwork",
  "subnet_ipv4":  "10.0.0.0/24",
  "mode":         "enhanced",
  "ddns_enabled": true,
  "expires_at":   1711234567,  // Unix 时间戳
  "max_uses":     10
}))

VerifyMeshSeed - 验证凭证

func (s *MeshSeedService) VerifyMeshSeed(
	joinToken string,   // Base64 编码的 Token
	signature string    // Base64 编码的签名
) (*model.MeshSeed, error)

验证步骤:

  1. 解码 JoinToken 和签名
  2. Ed25519 签名验证
  3. 解析 Token 内容
  4. 查询 MeshSeed 记录
  5. 检查吊销状态
  6. 检查使用次数
  7. 检查过期时间

安全检查清单:

  • 签名有效性(密码学保证)
  • 是否被吊销
  • 使用次数限制
  • 过期时间验证

其他方法:

  • IncrementUseCount(seedID) - 增加使用次数(事务)
  • RevokeMeshSeed(seedID) - 吊销凭证
  • ListMeshSeeds(networkID) - 获取网络的 MeshSeed 列表

2. API Handler 更新

文件: internal/api/handler/network.go

POST /api/v1/networks/:id/meshseed

请求参数:

{
  "expires_in_hours": 24,      // 可选,默认 24 小时
  "max_uses": 10,              // 可选,默认 10 次
  "ddns_enabled": true         // 可选,默认 false
}

响应示例(当前临时实现):

{
  "message": "MeshSeed 生成成功(待实现完整逻辑)",
  "data": {
    "meshseed": "meshray://seed-1",
    "expires_at": "2026-03-25T12:00:00Z",
    "max_uses": 10,
    "ddns_enabled": true
  }
}

最终实现(需要注入 service:

// 在 server.go 中初始化
meshSeedService := service.NewMeshSeedService(s.store, s.logger, signingKey, issuerNodeID)

// 在 handler 中调用
meshSeed, err := h.meshSeedService.GenerateMeshSeed(
	networkID, 
	req.MaxUses, 
	expiresAt, 
	req.DDNSEnabled,
)

// 返回完整的 MeshSeed URL
c.JSON(http.StatusOK, gin.H{
	"data": gin.H{
		"meshseed": "meshray://" + meshSeed.JoinToken,
		"signature": meshSeed.Signature,
		"expires_at": meshSeed.ExpiresAt.Format(time.RFC3339),
		"max_uses": meshSeed.MaxUses,
	},
})

🔐 安全技术方案

1. Ed25519 数字签名

为什么选择 Ed25519?

  • 高性能(比 RSA 快 100 倍)
  • 高安全性(256 位密钥)
  • 确定性签名(相同输入总是相同输出)
  • 抗侧信道攻击

签名流程:

私钥 (Ed25519.PrivateKey)
    ↓
JoinToken → ed25519.Sign() → Signature
    ↓
Base64 编码 → "base64(signature_bytes)"

验证流程:

公钥 (Ed25519.PublicKey) + JoinToken + Signature
    ↓
ed25519.Verify() → true/false

2. SeedID 生成

代码:

seedBytes := make([]byte, 16)
crypto/rand.Read(seedBytes)  // 加密安全的随机数
seedID := base64.RawURLEncoding.EncodeToString(seedBytes)

特点:

  • 16 字节(128 位)随机数
  • 使用 crypto/rand(不是math/rand
  • Base64 URL 安全编码
  • 碰撞概率:2^128 ≈ 3.4×10^38

3. JoinToken 结构

JSON 格式:

{
  "seed_id": "abc123...",
  "network_id": 1,
  "network_name": "测试网络",
  "subnet_ipv4": "10.0.0.0/24",
  "mode": "enhanced",
  "ddns_enabled": true,
  "expires_at": 1711234567,
  "max_uses": 10
}

编码:

JSON → Base64.StdEncoding → "eyJzZWVkX2lkIjoiYWJjMTIz..."

URL 格式:

meshray://eyJzZWVkX2lkIjoiYWJjMTIz...

📊 使用流程

场景 1:管理员生成 MeshSeed

1. 用户点击"生成 MeshSeed"
   ↓
2. 设置参数(有效期、使用次数)
   ↓
3. 调用 POST /api/v1/networks/:id/meshseed
   ↓
4. 后端生成并保存
   ↓
5. 前端显示二维码或分享链接

二维码内容:

meshray://eyJzZWVkX2lkIjoiYWJjMTIz...

场景 2:新设备加入网络

1. 新设备扫描二维码/点击链接
   ↓
2. 客户端解析 meshray:// URL
   ↓
3. 提取 JoinToken 和 Signature
   ↓
4. 调用 POST /api/v1/join
   {
     "join_token": "...",
     "signature": "..."
   }
   ↓
5. 后端验证 MeshSeed
   ↓
6. 验证通过,创建设备
   ↓
7. 返回 WireGuard 配置

🎯 API 设计

完整 API 列表

方法 路径 说明 状态
POST /networks/:id/meshseed 生成 MeshSeed 框架完成
GET /networks/:id/meshseeds 获取 MeshSeed 列表 🔲 待实现
DELETE /meshseeds/:seed_id 吊销 MeshSeed 🔲 待实现
POST /join 使用 MeshSeed 加入 🔲 待实现
POST /meshseeds/:seed_id/use 增加使用次数 🔲 待实现

📝 代码变更统计

文件 新增行 删除行 说明
service/meshseed.go 205 0 新建 Service 层
handler/network.go 32 11 更新 Handler
合计 237 11 净增 226 行

⚠️ TODO 事项

1. 初始化签名密钥

问题: MeshSeedService 需要 Ed25519 私钥

解决方案:

// internal/service/meshseed.go
type MeshSeedService struct {
	store       *sqlite.Store
	logger      *zap.Logger
	signingKey  ed25519.PrivateKey  // ← 需要初始化
	issuerNodeID string
}

初始化方式:

方案 A: 启动时生成

// server.go
_, signingKey, _ := ed25519.GenerateKey(rand.Reader)
meshSeedService := service.NewMeshSeedService(s.store, s.logger, signingKey, "node-1")

方案 B: 从配置文件读取

// config.yaml
security:
  meshseed_signing_key: "base64_encoded_private_key"

// server.go
keyBytes, _ := base64.StdEncoding.DecodeString(cfg.Security.SigningKey)
signingKey := ed25519.PrivateKey(keyBytes)

方案 C: 从数据库读取

// 首次启动时生成并保存
var key model.SecurityKey
db.First(&key, "meshseed_signing")
if key.Value == "" {
	_, newKey, _ := ed25519.GenerateKey(rand.Reader)
	db.Create(&model.SecurityKey{
		Name: "meshseed_signing",
		Value: base64.StdEncoding.EncodeToString(newKey),
	})
}

推荐: 方案 C(最安全,支持持久化)


2. 完善 Handler 注入

当前问题:

// network.go
// TODO: 需要初始化 meshSeedService
// meshSeed, err := h.meshSeedService.GenerateMeshSeed(...)

解决:

// server.go
// 1. 创建 MeshSeedService
meshSeedService := service.NewMeshSeedService(s.store, s.logger, signingKey, "node-1")

// 2. 创建 NetworkHandler 时注入
networkHandler := handler.NewNetworkHandler(networkService, s.logger, meshSeedService)
//                                                                ↑ 新增参数

3. 前端对接

前端需要实现:

  1. 生成 MeshSeed 的 UI(已调用 API
  2. 显示二维码(qrcode 库)
  3. 分享功能(复制链接)
  4. MeshSeed 列表管理
  5. 吊销功能

二维码生成:

<template>
  <qrcode-vue :value="meshSeedUrl" :size="200"></qrcode-vue>
</template>

<script setup>
const meshSeedUrl = computed(() => {
  return `meshray://${meshSeedData.value.join_token}`
})
</script>

🔍 与其他功能的集成

1. DDNS 集成

当 DDNSEnabled=true 时:

// 自动生成 DDNS 记录
if ddnsEnabled {
	deviceName := "device-" + randomString(6)
	ddnsDomain := setting.DDNSDomain  // 从 Settings 读取
	fullDomain := deviceName + "." + ddnsDomain
	
	// 调用 DDNS 服务创建记录
	ddnsService.CreateRecord(fullDomain, deviceIP)
}

2. 设备配置生成

使用 MeshSeed 加入后:

// 自动填充 Endpoint
config += "Endpoint = " + setting.ServerIP + ":" + strconv.Itoa(setting.ServerPort) + "\n"

// 如果使用 DDNS
if ddnsEnabled {
	config += "Endpoint = " + deviceDDNSDomain + ":51820\n"
}

3. 审计日志

记录所有 MeshSeed 操作:

logger.Info("MeshSeed 已生成",
	zap.String("seed_id", seedID),
	zap.Uint("network_id", networkID),
	zap.Int("max_uses", maxUses),
	zap.Time("expires_at", expiresAt))

logger.Info("MeshSeed 已使用",
	zap.String("seed_id", seedID),
	zap.String("device_name", deviceName),
	zap.String("request_ip", requestIP))

🚀 下一步计划

剩余工作(按优先级)

任务 工作量 说明
1. 初始化签名密钥 0.5 天 在 server.go 中生成/加载密钥
2. 注入 MeshSeedService 0.5 天 更新 NetworkHandler 构造函数
3. 完善 Handler 实现 0.5 天 调用真实 Service 方法
4. 添加 MeshSeed 列表 API 0.5 天 GET /networks/:id/meshseeds
5. 添加吊销 API 0.5 天 DELETE /meshseeds/:seed_id
6. 实现 Join 接口 1 天 POST /join 完整逻辑

总计: 约 3.5 天完成全部功能


验证结果

编译测试

cd e:\Project\MeshRay
go build -o meshray-test.exe ./cmd/meshray
# ✅ 编译成功,无错误

代码质量

  • 使用加密安全的随机数
  • Ed25519 签名算法
  • 完整的安全检查
  • 事务保证数据一致性
  • 详细的日志记录

📚 相关文档

  • [Dashboard 统计功能实现报告.md](./Dashboard 统计功能实现报告.md)
  • [Settings 持久化功能实现报告.md](./Settings 持久化功能实现报告.md)
  • 前后端问题全面修复报告.md

总结

实现成果

  • 创建了完整的 MeshSeedService 服务层(205 行)
  • 实现了基于 Ed25519 的数字签名
  • 提供了完整的安全验证逻辑
  • 更新了 Handler 框架(待注入依赖)
  • 代码编译通过,无错误

技术亮点

  • 🔐 Ed25519 签名: 密码学级别的安全性
  • 🎲 加密随机数: 使用 crypto/rand
  • 多重验证: 签名 + 吊销 + 次数 + 过期
  • 💾 事务支持: 保证数据一致性
  • 📝 详细日志: 便于审计和调试

用户体验提升(预期)

  • 一键生成组网凭证
  • 扫码快速加入网络
  • 可视化的使用次数和过期时间
  • 支持吊销,增强安全性

状态: MeshSeed 框架已完成
下一项: 注入签名密钥和完善实现(约 3.5 天)
建议: 继续实现设备密钥管理

MeshRay - 安全便捷的组网凭证系统! 🔐