mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-10-09 03:06:37 +08:00
去除代理头支持, 增加节点面板通信加密 增加导出sql
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// AESCrypto AES 加密器结构
|
||||
type AESCrypto struct {
|
||||
key []byte
|
||||
}
|
||||
|
||||
// NewAESCrypto 创建新的 AES 加密器
|
||||
// secret: 用于生成密钥的密码字符串
|
||||
func NewAESCrypto(secret string) (*AESCrypto, error) {
|
||||
if secret == "" {
|
||||
return nil, fmt.Errorf("密钥不能为空")
|
||||
}
|
||||
|
||||
// 使用 SHA256 将密码转换为 32 字节密钥
|
||||
hash := sha256.Sum256([]byte(secret))
|
||||
|
||||
return &AESCrypto{
|
||||
key: hash[:],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Encrypt 加密数据
|
||||
// data: 要加密的原始数据
|
||||
// 返回: base64编码的加密数据
|
||||
func (a *AESCrypto) Encrypt(data []byte) (string, error) {
|
||||
if len(data) == 0 {
|
||||
return "", fmt.Errorf("待加密数据不能为空")
|
||||
}
|
||||
|
||||
// 创建 AES cipher
|
||||
block, err := aes.NewCipher(a.key)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("创建 AES cipher 失败: %v", err)
|
||||
}
|
||||
|
||||
// 使用 GCM 模式
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("创建 GCM 失败: %v", err)
|
||||
}
|
||||
|
||||
// 生成随机 nonce
|
||||
nonce := make([]byte, gcm.NonceSize())
|
||||
if _, err := rand.Read(nonce); err != nil {
|
||||
return "", fmt.Errorf("生成 nonce 失败: %v", err)
|
||||
}
|
||||
|
||||
// 加密数据
|
||||
ciphertext := gcm.Seal(nil, nonce, data, nil)
|
||||
|
||||
// 组合 nonce + ciphertext
|
||||
encrypted := append(nonce, ciphertext...)
|
||||
|
||||
// 返回 base64 编码结果
|
||||
return base64.StdEncoding.EncodeToString(encrypted), nil
|
||||
}
|
||||
|
||||
// Decrypt 解密数据
|
||||
// encryptedData: base64编码的加密数据
|
||||
// 返回: 解密后的原始数据
|
||||
func (a *AESCrypto) Decrypt(encryptedData string) ([]byte, error) {
|
||||
if encryptedData == "" {
|
||||
return nil, fmt.Errorf("加密数据不能为空")
|
||||
}
|
||||
|
||||
// base64 解码
|
||||
encrypted, err := base64.StdEncoding.DecodeString(encryptedData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("base64 解码失败: %v", err)
|
||||
}
|
||||
|
||||
// 创建 AES cipher
|
||||
block, err := aes.NewCipher(a.key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建 AES cipher 失败: %v", err)
|
||||
}
|
||||
|
||||
// 使用 GCM 模式
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建 GCM 失败: %v", err)
|
||||
}
|
||||
|
||||
nonceSize := gcm.NonceSize()
|
||||
if len(encrypted) < nonceSize {
|
||||
return nil, fmt.Errorf("加密数据长度不足")
|
||||
}
|
||||
|
||||
// 分离 nonce 和 ciphertext
|
||||
nonce := encrypted[:nonceSize]
|
||||
ciphertext := encrypted[nonceSize:]
|
||||
|
||||
// 解密数据
|
||||
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("解密失败: %v", err)
|
||||
}
|
||||
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
// EncryptString 加密字符串并返回加密后的字符串
|
||||
func (a *AESCrypto) EncryptString(data string) (string, error) {
|
||||
return a.Encrypt([]byte(data))
|
||||
}
|
||||
|
||||
// DecryptString 解密字符串并返回解密后的字符串
|
||||
func (a *AESCrypto) DecryptString(encryptedData string) (string, error) {
|
||||
plaintext, err := a.Decrypt(encryptedData)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(plaintext), nil
|
||||
}
|
||||
@@ -11,11 +11,13 @@ import (
|
||||
|
||||
"github.com/go-gost/core/observer/stats"
|
||||
"github.com/go-gost/x/config"
|
||||
"github.com/go-gost/x/internal/util/crypto"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
var httpReportURL string
|
||||
var configReportURL string
|
||||
var httpAESCrypto *crypto.AESCrypto // 新增:HTTP上报加密器
|
||||
|
||||
// TrafficReportItem 流量报告项(压缩格式)
|
||||
type TrafficReportItem struct {
|
||||
@@ -28,6 +30,16 @@ type TrafficReportItem struct {
|
||||
func SetHTTPReportURL(addr string, secret string) {
|
||||
httpReportURL = "http://" + addr + "/flow/upload?secret=" + secret
|
||||
configReportURL = "http://" + addr + "/flow/config?secret=" + secret
|
||||
|
||||
// 创建 AES 加密器
|
||||
var err error
|
||||
httpAESCrypto, err = crypto.NewAESCrypto(secret)
|
||||
if err != nil {
|
||||
fmt.Printf("❌ 创建 HTTP AES 加密器失败: %v\n", err)
|
||||
httpAESCrypto = nil
|
||||
} else {
|
||||
fmt.Printf("🔐 HTTP AES 加密器创建成功\n")
|
||||
}
|
||||
}
|
||||
|
||||
// sendTrafficReport 发送流量报告到HTTP接口
|
||||
@@ -37,7 +49,32 @@ func sendTrafficReport(ctx context.Context, reportItems []TrafficReportItem) (bo
|
||||
return false, fmt.Errorf("序列化报告数据失败: %v", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", httpReportURL, bytes.NewBuffer(jsonData))
|
||||
var requestBody []byte
|
||||
|
||||
// 如果有加密器,则加密数据
|
||||
if httpAESCrypto != nil {
|
||||
encryptedData, err := httpAESCrypto.Encrypt(jsonData)
|
||||
if err != nil {
|
||||
fmt.Printf("⚠️ 加密流量报告失败,发送原始数据: %v\n", err)
|
||||
requestBody = jsonData
|
||||
} else {
|
||||
// 创建加密消息包装器
|
||||
encryptedMessage := map[string]interface{}{
|
||||
"encrypted": true,
|
||||
"data": encryptedData,
|
||||
"timestamp": time.Now().Unix(),
|
||||
}
|
||||
requestBody, err = json.Marshal(encryptedMessage)
|
||||
if err != nil {
|
||||
fmt.Printf("⚠️ 序列化加密流量报告失败,发送原始数据: %v\n", err)
|
||||
requestBody = jsonData
|
||||
}
|
||||
}
|
||||
} else {
|
||||
requestBody = jsonData
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", httpReportURL, bytes.NewBuffer(requestBody))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("创建HTTP请求失败: %v", err)
|
||||
}
|
||||
@@ -88,13 +125,38 @@ func sendConfigReport(ctx context.Context) (bool, error) {
|
||||
return false, fmt.Errorf("获取配置数据失败: %v", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", configReportURL, bytes.NewBuffer(configData))
|
||||
var requestBody []byte
|
||||
|
||||
// 如果有加密器,则加密数据
|
||||
if httpAESCrypto != nil {
|
||||
encryptedData, err := httpAESCrypto.Encrypt(configData)
|
||||
if err != nil {
|
||||
fmt.Printf("⚠️ 加密配置报告失败,发送原始数据: %v\n", err)
|
||||
requestBody = configData
|
||||
} else {
|
||||
// 创建加密消息包装器
|
||||
encryptedMessage := map[string]interface{}{
|
||||
"encrypted": true,
|
||||
"data": encryptedData,
|
||||
"timestamp": time.Now().Unix(),
|
||||
}
|
||||
requestBody, err = json.Marshal(encryptedMessage)
|
||||
if err != nil {
|
||||
fmt.Printf("⚠️ 序列化加密配置报告失败,发送原始数据: %v\n", err)
|
||||
requestBody = configData
|
||||
}
|
||||
}
|
||||
} else {
|
||||
requestBody = configData
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", configReportURL, bytes.NewBuffer(requestBody))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("创建HTTP请求失败: %v", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("User-Agent", "GOST-Config-Reporter/1.0")
|
||||
req.Header.Set("User-Agent", "Config-Reporter/1.0")
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 10 * time.Second, // 配置上报可以稍长一些
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/go-gost/x/config"
|
||||
"github.com/go-gost/x/internal/util/crypto"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/shirou/gopsutil/v3/cpu"
|
||||
"github.com/shirou/gopsutil/v3/host"
|
||||
@@ -90,13 +91,24 @@ type WebSocketReporter struct {
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
connected bool
|
||||
connecting bool // 新增:正在连接状态
|
||||
connMutex sync.Mutex // 新增:连接状态锁
|
||||
connecting bool // 新增:正在连接状态
|
||||
connMutex sync.Mutex // 新增:连接状态锁
|
||||
aesCrypto *crypto.AESCrypto // 新增:AES加密器
|
||||
}
|
||||
|
||||
// NewWebSocketReporter 创建一个新的WebSocket报告器
|
||||
func NewWebSocketReporter(serverURL string) *WebSocketReporter {
|
||||
func NewWebSocketReporter(serverURL string, secret string) *WebSocketReporter {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
// 创建 AES 加密器
|
||||
aesCrypto, err := crypto.NewAESCrypto(secret)
|
||||
if err != nil {
|
||||
fmt.Printf("❌ 创建 AES 加密器失败: %v\n", err)
|
||||
aesCrypto = nil
|
||||
} else {
|
||||
fmt.Printf("🔐 AES 加密器创建成功\n")
|
||||
}
|
||||
|
||||
return &WebSocketReporter{
|
||||
url: serverURL,
|
||||
reconnectTime: 5 * time.Second, // 重连间隔
|
||||
@@ -106,6 +118,7 @@ func NewWebSocketReporter(serverURL string) *WebSocketReporter {
|
||||
cancel: cancel,
|
||||
connected: false,
|
||||
connecting: false,
|
||||
aesCrypto: aesCrypto,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +147,7 @@ func (w *WebSocketReporter) run() {
|
||||
w.connMutex.Lock()
|
||||
needConnect := !w.connected && !w.connecting
|
||||
w.connMutex.Unlock()
|
||||
|
||||
|
||||
if needConnect {
|
||||
if err := w.connect(); err != nil {
|
||||
fmt.Printf("❌ WebSocket连接失败: %v,%v后重试\n", err, w.reconnectTime)
|
||||
@@ -167,12 +180,12 @@ func (w *WebSocketReporter) run() {
|
||||
func (w *WebSocketReporter) connect() error {
|
||||
w.connMutex.Lock()
|
||||
defer w.connMutex.Unlock()
|
||||
|
||||
|
||||
// 如果已经在连接中或已连接,直接返回
|
||||
if w.connecting || w.connected {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
// 设置连接中状态
|
||||
w.connecting = true
|
||||
defer func() {
|
||||
@@ -242,7 +255,7 @@ func (w *WebSocketReporter) handleConnection() {
|
||||
w.connMutex.Lock()
|
||||
isConnected := w.connected
|
||||
w.connMutex.Unlock()
|
||||
|
||||
|
||||
if !isConnected {
|
||||
return
|
||||
}
|
||||
@@ -276,7 +289,7 @@ func (w *WebSocketReporter) collectSystemInfo() SystemInfo {
|
||||
func (w *WebSocketReporter) sendSystemInfo(sysInfo SystemInfo) error {
|
||||
w.connMutex.Lock()
|
||||
defer w.connMutex.Unlock()
|
||||
|
||||
|
||||
if w.conn == nil || !w.connected {
|
||||
return fmt.Errorf("连接未建立")
|
||||
}
|
||||
@@ -287,10 +300,35 @@ func (w *WebSocketReporter) sendSystemInfo(sysInfo SystemInfo) error {
|
||||
return fmt.Errorf("序列化系统信息失败: %v", err)
|
||||
}
|
||||
|
||||
var messageData []byte
|
||||
|
||||
// 如果有加密器,则加密数据
|
||||
if w.aesCrypto != nil {
|
||||
encryptedData, err := w.aesCrypto.Encrypt(jsonData)
|
||||
if err != nil {
|
||||
fmt.Printf("⚠️ 加密失败,发送原始数据: %v\n", err)
|
||||
messageData = jsonData
|
||||
} else {
|
||||
// 创建加密消息包装器
|
||||
encryptedMessage := map[string]interface{}{
|
||||
"encrypted": true,
|
||||
"data": encryptedData,
|
||||
"timestamp": time.Now().Unix(),
|
||||
}
|
||||
messageData, err = json.Marshal(encryptedMessage)
|
||||
if err != nil {
|
||||
fmt.Printf("⚠️ 序列化加密消息失败,发送原始数据: %v\n", err)
|
||||
messageData = jsonData
|
||||
}
|
||||
}
|
||||
} else {
|
||||
messageData = jsonData
|
||||
}
|
||||
|
||||
// 设置写入超时
|
||||
w.conn.SetWriteDeadline(time.Now().Add(5 * time.Second))
|
||||
|
||||
if err := w.conn.WriteMessage(websocket.TextMessage, jsonData); err != nil {
|
||||
if err := w.conn.WriteMessage(websocket.TextMessage, messageData); err != nil {
|
||||
w.connected = false // 标记连接已断开
|
||||
return fmt.Errorf("写入消息失败: %v", err)
|
||||
}
|
||||
@@ -309,7 +347,7 @@ func (w *WebSocketReporter) receiveMessages() {
|
||||
conn := w.conn
|
||||
connected := w.connected
|
||||
w.connMutex.Unlock()
|
||||
|
||||
|
||||
if conn == nil || !connected {
|
||||
return
|
||||
}
|
||||
@@ -338,6 +376,30 @@ func (w *WebSocketReporter) receiveMessages() {
|
||||
func (w *WebSocketReporter) handleReceivedMessage(messageType int, message []byte) {
|
||||
switch messageType {
|
||||
case websocket.TextMessage:
|
||||
// 先检查是否是加密消息
|
||||
var encryptedWrapper struct {
|
||||
Encrypted bool `json:"encrypted"`
|
||||
Data string `json:"data"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
}
|
||||
|
||||
// 尝试解析为加密消息格式
|
||||
if err := json.Unmarshal(message, &encryptedWrapper); err == nil && encryptedWrapper.Encrypted {
|
||||
if w.aesCrypto != nil {
|
||||
// 解密数据
|
||||
decryptedData, err := w.aesCrypto.Decrypt(encryptedWrapper.Data)
|
||||
if err != nil {
|
||||
fmt.Printf("❌ 解密失败: %v\n", err)
|
||||
w.sendErrorResponse("DecryptError", fmt.Sprintf("解密失败: %v", err))
|
||||
return
|
||||
}
|
||||
message = decryptedData
|
||||
} else {
|
||||
fmt.Printf("❌ 收到加密消息但没有加密器\n")
|
||||
w.sendErrorResponse("NoDecryptor", "没有可用的解密器")
|
||||
return
|
||||
}
|
||||
}
|
||||
// 先尝试解析是否是压缩消息
|
||||
var compressedMsg struct {
|
||||
Type string `json:"type"`
|
||||
@@ -744,7 +806,7 @@ func (w *WebSocketReporter) handleCall(data interface{}) error {
|
||||
func (w *WebSocketReporter) sendResponse(response CommandResponse) {
|
||||
w.connMutex.Lock()
|
||||
defer w.connMutex.Unlock()
|
||||
|
||||
|
||||
if w.conn == nil || !w.connected {
|
||||
fmt.Printf("❌ 无法发送响应:连接未建立\n")
|
||||
return
|
||||
@@ -756,19 +818,44 @@ func (w *WebSocketReporter) sendResponse(response CommandResponse) {
|
||||
return
|
||||
}
|
||||
|
||||
var messageData []byte
|
||||
|
||||
// 如果有加密器,则加密数据
|
||||
if w.aesCrypto != nil {
|
||||
encryptedData, err := w.aesCrypto.Encrypt(jsonData)
|
||||
if err != nil {
|
||||
fmt.Printf("⚠️ 加密响应失败,发送原始数据: %v\n", err)
|
||||
messageData = jsonData
|
||||
} else {
|
||||
// 创建加密消息包装器
|
||||
encryptedMessage := map[string]interface{}{
|
||||
"encrypted": true,
|
||||
"data": encryptedData,
|
||||
"timestamp": time.Now().Unix(),
|
||||
}
|
||||
messageData, err = json.Marshal(encryptedMessage)
|
||||
if err != nil {
|
||||
fmt.Printf("⚠️ 序列化加密响应失败,发送原始数据: %v\n", err)
|
||||
messageData = jsonData
|
||||
}
|
||||
}
|
||||
} else {
|
||||
messageData = jsonData
|
||||
}
|
||||
|
||||
// 检查消息大小,如果超过10MB则记录警告
|
||||
if len(jsonData) > 10*1024*1024 {
|
||||
fmt.Printf("⚠️ 响应消息过大 (%.2f MB),可能会被拒绝\n", float64(len(jsonData))/(1024*1024))
|
||||
if len(messageData) > 10*1024*1024 {
|
||||
fmt.Printf("⚠️ 响应消息过大 (%.2f MB),可能会被拒绝\n", float64(len(messageData))/(1024*1024))
|
||||
}
|
||||
|
||||
// 设置较长的写入超时,以应对大消息
|
||||
timeout := 5 * time.Second
|
||||
if len(jsonData) > 1024*1024 {
|
||||
if len(messageData) > 1024*1024 {
|
||||
timeout = 30 * time.Second
|
||||
}
|
||||
|
||||
w.conn.SetWriteDeadline(time.Now().Add(timeout))
|
||||
if err := w.conn.WriteMessage(websocket.TextMessage, jsonData); err != nil {
|
||||
if err := w.conn.WriteMessage(websocket.TextMessage, messageData); err != nil {
|
||||
fmt.Printf("❌ 发送响应失败: %v\n", err)
|
||||
w.connected = false
|
||||
}
|
||||
@@ -852,7 +939,7 @@ func StartWebSocketReporterWithConfig(Addr string, Secret string, Version string
|
||||
|
||||
fmt.Printf("🔗 WebSocket连接URL: %s\n", fullURL)
|
||||
|
||||
reporter := NewWebSocketReporter(fullURL)
|
||||
reporter := NewWebSocketReporter(fullURL, Secret) // Pass Secret here
|
||||
reporter.Start()
|
||||
return reporter
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user