# MeshSeed 生成功能实现报告 **完成时间**: 2026-03-24 **状态**: ✅ **框架已完成** **优先级**: P1 - 高优先级 --- ## 📋 **实现内容** ### 1. MeshSeedService 服务层 **文件**: [`internal/service/meshseed.go`](file://e:\Project\MeshRay\internal\service\meshseed.go) (新建,205 行) #### **核心功能** **GenerateMeshSeed - 生成组网凭证** ```go func (s *MeshSeedService) GenerateMeshSeed( networkID uint, // 网络 ID maxUses int, // 最大使用次数 expiresAt time.Time, // 过期时间 ddnsEnabled bool // DDNS 开关 ) (*model.MeshSeed, error) ``` **实现步骤**: 1. ✅ 验证网络是否存在 2. ✅ 生成 16 字节随机 SeedID(Base64 编码) 3. ✅ 构建 JoinToken(包含网络信息的 JSON) 4. ✅ Ed25519 数字签名 5. ✅ 保存到数据库 **数据结构**: ```go // 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 - 验证凭证** ```go 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`](file://e:\Project\MeshRay\internal\api\handler\network.go#L315-L357) #### **POST /api/v1/networks/:id/meshseed** **请求参数**: ```json { "expires_in_hours": 24, // 可选,默认 24 小时 "max_uses": 10, // 可选,默认 10 次 "ddns_enabled": true // 可选,默认 false } ``` **响应示例**(当前临时实现): ```json { "message": "MeshSeed 生成成功(待实现完整逻辑)", "data": { "meshseed": "meshray://seed-1", "expires_at": "2026-03-25T12:00:00Z", "max_uses": 10, "ddns_enabled": true } } ``` **最终实现**(需要注入 service): ```go // 在 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 生成 **代码**: ```go 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 格式**: ```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 私钥 **解决方案**: ```go // internal/service/meshseed.go type MeshSeedService struct { store *sqlite.Store logger *zap.Logger signingKey ed25519.PrivateKey // ← 需要初始化 issuerNodeID string } ``` **初始化方式**: **方案 A: 启动时生成** ```go // server.go _, signingKey, _ := ed25519.GenerateKey(rand.Reader) meshSeedService := service.NewMeshSeedService(s.store, s.logger, signingKey, "node-1") ``` **方案 B: 从配置文件读取** ```go // config.yaml security: meshseed_signing_key: "base64_encoded_private_key" // server.go keyBytes, _ := base64.StdEncoding.DecodeString(cfg.Security.SigningKey) signingKey := ed25519.PrivateKey(keyBytes) ``` **方案 C: 从数据库读取** ```go // 首次启动时生成并保存 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 注入 **当前问题**: ```go // network.go // TODO: 需要初始化 meshSeedService // meshSeed, err := h.meshSeedService.GenerateMeshSeed(...) ``` **解决**: ```go // 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. ✅ 吊销功能 **二维码生成**: ```vue ``` --- ## 🔍 **与其他功能的集成** ### 1. DDNS 集成 **当 DDNSEnabled=true 时**: ```go // 自动生成 DDNS 记录 if ddnsEnabled { deviceName := "device-" + randomString(6) ddnsDomain := setting.DDNSDomain // 从 Settings 读取 fullDomain := deviceName + "." + ddnsDomain // 调用 DDNS 服务创建记录 ddnsService.CreateRecord(fullDomain, deviceIP) } ``` --- ### 2. 设备配置生成 **使用 MeshSeed 加入后**: ```go // 自动填充 Endpoint config += "Endpoint = " + setting.ServerIP + ":" + strconv.Itoa(setting.ServerPort) + "\n" // 如果使用 DDNS if ddnsEnabled { config += "Endpoint = " + deviceDDNSDomain + ":51820\n" } ``` --- ### 3. 审计日志 **记录所有 MeshSeed 操作**: ```go 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 天完成全部功能 --- ## ✅ **验证结果** ### 编译测试 ```bash cd e:\Project\MeshRay go build -o meshray-test.exe ./cmd/meshray # ✅ 编译成功,无错误 ``` ### 代码质量 - ✅ 使用加密安全的随机数 - ✅ Ed25519 签名算法 - ✅ 完整的安全检查 - ✅ 事务保证数据一致性 - ✅ 详细的日志记录 --- ## 📚 **相关文档** - [Dashboard 统计功能实现报告.md](./Dashboard 统计功能实现报告.md) - [Settings 持久化功能实现报告.md](./Settings 持久化功能实现报告.md) - [前后端问题全面修复报告.md](./前后端问题全面修复报告.md) --- ## ✅ **总结** ### 实现成果 - ✅ 创建了完整的 MeshSeedService 服务层(205 行) - ✅ 实现了基于 Ed25519 的数字签名 - ✅ 提供了完整的安全验证逻辑 - ✅ 更新了 Handler 框架(待注入依赖) - ✅ 代码编译通过,无错误 ### 技术亮点 - 🔐 **Ed25519 签名**: 密码学级别的安全性 - 🎲 **加密随机数**: 使用 crypto/rand - ✅ **多重验证**: 签名 + 吊销 + 次数 + 过期 - 💾 **事务支持**: 保证数据一致性 - 📝 **详细日志**: 便于审计和调试 ### 用户体验提升(预期) - ⭐⭐⭐⭐⭐ 一键生成组网凭证 - ⭐⭐⭐⭐⭐ 扫码快速加入网络 - ⭐⭐⭐⭐⭐ 可视化的使用次数和过期时间 - ⭐⭐⭐⭐⭐ 支持吊销,增强安全性 --- **状态**: ✅ **MeshSeed 框架已完成** **下一项**: 注入签名密钥和完善实现(约 3.5 天) **建议**: 继续实现设备密钥管理 *MeshRay - 安全便捷的组网凭证系统!* 🔐✨