@@ -17,11 +17,26 @@ type Config struct {
1717
1818// GlobalConfig 全局配置
1919type GlobalConfig struct {
20- BufferSize int `toml:"buffer_size"`
21- LogLevel string `toml:"log_level"`
22- LogFile string `toml:"log_file"` // 日志文件路径,为空则只输出到控制台
23- GeoIP GeoIPConfig `toml:"geoip"`
24- TimeWindow TimeWindowConfig `toml:"time_window"`
20+ BufferSize int `toml:"buffer_size"`
21+ LogLevel string `toml:"log_level"`
22+ LogFile string `toml:"log_file"` // 日志文件路径,为空则只输出到控制台
23+ GeoIP GeoIPConfig `toml:"geoip"`
24+ TimeWindow TimeWindowConfig `toml:"time_window"`
25+ VShellDefense VShellDefenseConfig `toml:"vshell_defense"` // VShell 攻击防御配置
26+ }
27+
28+ // VShellDefenseConfig VShell 防御配置
29+ type VShellDefenseConfig struct {
30+ Enabled bool `toml:"enabled"` // 是否启用 VShell 防御
31+ BlockWebSocketUpgrade bool `toml:"block_websocket_upgrade"` // 拦截可疑的 WebSocket 升级请求
32+ BlockVersionHandshake bool `toml:"block_version_handshake"` // 拦截 VShell 版本握手
33+ BlockCommandPatterns bool `toml:"block_command_patterns"` // 拦截 VShell 命令模式
34+ BlockEncryptedPayloads bool `toml:"block_encrypted_payloads"` // 拦截可疑的加密载荷
35+ BlockVkeyPatterns bool `toml:"block_vkey_patterns"` // 拦截 Vkey 哈希模式
36+ BlockSuspiciousPaths bool `toml:"block_suspicious_paths"` // 拦截可疑路径
37+ CustomBlockPaths []string `toml:"custom_block_paths"` // 自定义拦截路径
38+ BlockedVkeys []string `toml:"blocked_vkeys"` // 已知恶意 Vkey 黑名单
39+ LogAttempts bool `toml:"log_attempts"` // 记录攻击尝试
2540}
2641
2742// GeoIPConfig GeoIP 配置
@@ -70,13 +85,13 @@ type TCPProcessorConfig struct {
7085
7186// Processor 处理器规则
7287type Processor struct {
73- Path interface {} `toml:"path"` // string 或 []string
74- MatchMode string `toml:"match_mode"` // prefix (前缀), exact (精确), regex (正则)
75- Action string `toml:"action"` // allow, drop, rewrite, file, proxy
76- Response string `toml:"response"` // 404, 403, 502, close (用于 drop)
77- RewriteTo string `toml:"rewrite_to"` // 路径重写目标 (用于 rewrite)
78- File string `toml:"file"` // 文件路径 (用于 file)
79- ProxyTo string `toml:"proxy_to"` // 代理目标 (用于 proxy)
88+ Path interface {} `toml:"path"` // string 或 []string
89+ MatchMode string `toml:"match_mode"` // prefix (前缀), exact (精确), regex (正则)
90+ Action string `toml:"action"` // allow, drop, rewrite, file, proxy
91+ Response string `toml:"response"` // 404, 403, 502, close (用于 drop)
92+ RewriteTo string `toml:"rewrite_to"` // 路径重写目标 (用于 rewrite)
93+ File string `toml:"file"` // 文件路径 (用于 file)
94+ ProxyTo string `toml:"proxy_to"` // 代理目标 (用于 proxy)
8095}
8196
8297// RouteRule 路由规则(兼容旧配置)
@@ -173,11 +188,11 @@ func (c *Config) Validate() error {
173188 return fmt .Errorf ("listener[%d]: backend_addr is required" , i )
174189 }
175190
176- // 检查协议类型
177- validProtocols := map [string ]bool {"tcp" : true }
178- if ! validProtocols [listener .Protocol ] {
179- return fmt .Errorf ("listener[%d]: protocol must be: tcp" , i )
180- } // 检查超时配置
191+ // 检查协议类型
192+ validProtocols := map [string ]bool {"tcp" : true }
193+ if ! validProtocols [listener .Protocol ] {
194+ return fmt .Errorf ("listener[%d]: protocol must be: tcp" , i )
195+ } // 检查超时配置
181196 if listener .Timeout .InitialRead < 0 {
182197 return fmt .Errorf ("listener[%d]: timeout.initial_read cannot be negative" , i )
183198 }
@@ -226,15 +241,15 @@ func (c *Config) Validate() error {
226241func validateProcessor (proc Processor , listenerIdx int , processorType string , procIdx int ) error {
227242 validActions := map [string ]bool {"allow" : true , "drop" : true , "rewrite" : true , "file" : true , "proxy" : true }
228243 if ! validActions [proc .Action ] {
229- return fmt .Errorf ("listener[%d].%s.processor[%d]: action must be one of: allow, drop, rewrite, file, proxy" ,
244+ return fmt .Errorf ("listener[%d].%s.processor[%d]: action must be one of: allow, drop, rewrite, file, proxy" ,
230245 listenerIdx , processorType , procIdx )
231246 }
232247
233248 // 验证匹配模式
234249 if proc .MatchMode != "" {
235250 validModes := map [string ]bool {"prefix" : true , "exact" : true , "regex" : true }
236251 if ! validModes [proc .MatchMode ] {
237- return fmt .Errorf ("listener[%d].%s.processor[%d]: match_mode must be one of: prefix, exact, regex" ,
252+ return fmt .Errorf ("listener[%d].%s.processor[%d]: match_mode must be one of: prefix, exact, regex" ,
238253 listenerIdx , processorType , procIdx )
239254 }
240255 }
@@ -244,22 +259,22 @@ func validateProcessor(proc Processor, listenerIdx int, processorType string, pr
244259 case "drop" :
245260 validResponses := map [string ]bool {"404" : true , "403" : true , "502" : true , "close" : true }
246261 if proc .Response != "" && ! validResponses [proc .Response ] {
247- return fmt .Errorf ("listener[%d].%s.processor[%d]: response must be one of: 404, 403, 502, close" ,
262+ return fmt .Errorf ("listener[%d].%s.processor[%d]: response must be one of: 404, 403, 502, close" ,
248263 listenerIdx , processorType , procIdx )
249264 }
250265 case "rewrite" :
251266 if proc .RewriteTo == "" {
252- return fmt .Errorf ("listener[%d].%s.processor[%d]: rewrite_to is required for rewrite action" ,
267+ return fmt .Errorf ("listener[%d].%s.processor[%d]: rewrite_to is required for rewrite action" ,
253268 listenerIdx , processorType , procIdx )
254269 }
255270 case "file" :
256271 if proc .File == "" {
257- return fmt .Errorf ("listener[%d].%s.processor[%d]: file is required for file action" ,
272+ return fmt .Errorf ("listener[%d].%s.processor[%d]: file is required for file action" ,
258273 listenerIdx , processorType , procIdx )
259274 }
260275 case "proxy" :
261276 if proc .ProxyTo == "" {
262- return fmt .Errorf ("listener[%d].%s.processor[%d]: proxy_to is required for proxy action" ,
277+ return fmt .Errorf ("listener[%d].%s.processor[%d]: proxy_to is required for proxy action" ,
263278 listenerIdx , processorType , procIdx )
264279 }
265280 }
@@ -355,27 +370,27 @@ func validateTimeWindowConfig(tw *TimeWindowConfig) error {
355370 if tw .Timezone == "" {
356371 return fmt .Errorf ("timezone is required" )
357372 }
358-
373+
359374 // 验证时区
360375 if _ , err := time .LoadLocation (tw .Timezone ); err != nil {
361376 return fmt .Errorf ("invalid timezone '%s': %w" , tw .Timezone , err )
362377 }
363-
378+
364379 if tw .StartTime == "" {
365380 return fmt .Errorf ("start_time is required" )
366381 }
367382 if tw .EndTime == "" {
368383 return fmt .Errorf ("end_time is required" )
369384 }
370-
385+
371386 // 验证时间格式
372387 if _ , err := time .Parse ("15:04" , tw .StartTime ); err != nil {
373388 return fmt .Errorf ("invalid start_time format '%s', expected HH:MM" , tw .StartTime )
374389 }
375390 if _ , err := time .Parse ("15:04" , tw .EndTime ); err != nil {
376391 return fmt .Errorf ("invalid end_time format '%s', expected HH:MM" , tw .EndTime )
377392 }
378-
393+
379394 return nil
380395}
381396
@@ -384,26 +399,26 @@ func (tw *TimeWindowConfig) IsInTimeWindow() (bool, error) {
384399 if ! tw .Enabled {
385400 return true , nil
386401 }
387-
402+
388403 // 加载时区
389404 loc , err := time .LoadLocation (tw .Timezone )
390405 if err != nil {
391406 return false , fmt .Errorf ("failed to load timezone: %w" , err )
392407 }
393-
408+
394409 // 获取当前时间(在指定时区)
395410 now := time .Now ().In (loc )
396411 currentHour := now .Hour ()
397412 currentMinute := now .Minute ()
398413 currentTimeInMinutes := currentHour * 60 + currentMinute
399-
414+
400415 // 解析开始和结束时间
401416 startTime , _ := time .Parse ("15:04" , tw .StartTime )
402417 endTime , _ := time .Parse ("15:04" , tw .EndTime )
403-
418+
404419 startMinutes := startTime .Hour ()* 60 + startTime .Minute ()
405420 endMinutes := endTime .Hour ()* 60 + endTime .Minute ()
406-
421+
407422 // 检查是否在时间窗口内
408423 if startMinutes <= endMinutes {
409424 // 正常情况:start_time < end_time (如 00:00 - 11:00)
0 commit comments