feat: 完善日志审计功能

- 实现文件系统日志(FilesystemLog)记录文件管理器操作
- 实现操作日志(OperationLog)记录用户操作行为
- 实现数据库SQL日志(DatabaseSQLLog)模型和API
- 实现SSH会话命令记录(SessionCommand)含命令输出和风险等级
- 添加IP提取服务支持X-Real-IP和X-Forwarded-For
- 添加日志自动清理功能
- 修复ProFormSwitch required验证问题
- 修复设置页面默认值问题
- 修复文件上传错误检测逻辑
- 修复资产树key前缀问题
- 添加VNC/RDP设置默认值
- 修复文件管理标题翻译
This commit is contained in:
2026-04-19 06:57:42 +08:00
parent a2a1613384
commit 1f7c491048
42 changed files with 1214 additions and 130 deletions
+47
View File
@@ -0,0 +1,47 @@
package service
import (
"context"
"next-terminal/server/common"
"next-terminal/server/model"
"next-terminal/server/repository"
"next-terminal/server/utils"
)
var DatabaseSQLLogService = new(databaseSQLLogService)
type databaseSQLLogService struct {
baseService
}
type DatabaseSQLLogParams struct {
AssetId string
Database string
UserId string
ClientIP string
SQL string
DurationMs int
RowsAffected int
Status string
ErrorMessage string
Source string
}
func (s databaseSQLLogService) Record(ctx context.Context, params DatabaseSQLLogParams) error {
log := &model.DatabaseSQLLog{
ID: utils.UUID(),
AssetId: params.AssetId,
Database: params.Database,
UserId: params.UserId,
ClientIP: params.ClientIP,
SQL: params.SQL,
DurationMs: params.DurationMs,
RowsAffected: params.RowsAffected,
Status: params.Status,
ErrorMessage: params.ErrorMessage,
Source: params.Source,
Created: common.NowJsonTime(),
}
return repository.DatabaseSQLLogRepository.Create(ctx, log)
}
+47
View File
@@ -0,0 +1,47 @@
package service
import (
"context"
"next-terminal/server/common"
"next-terminal/server/model"
"next-terminal/server/repository"
"next-terminal/server/utils"
)
var OperationLogService = new(operationLogService)
type operationLogService struct {
baseService
}
type OperationLogParams struct {
AccountId string
AccountName string
Action string
Content string
IP string
Region string
UserAgent string
Status string
ErrorMessage string
Remark string
}
func (s operationLogService) Record(ctx context.Context, params OperationLogParams) error {
log := &model.OperationLog{
ID: utils.UUID(),
AccountId: params.AccountId,
AccountName: params.AccountName,
Action: params.Action,
Content: params.Content,
IP: params.IP,
Region: params.Region,
UserAgent: params.UserAgent,
Status: params.Status,
ErrorMessage: params.ErrorMessage,
Remark: params.Remark,
Created: common.NowJsonTime(),
}
return repository.OperationLogRepository.Create(ctx, log)
}
+88
View File
@@ -4,12 +4,15 @@ import (
"context"
"errors"
"fmt"
"net"
"strings"
"next-terminal/server/common/guacamole"
"next-terminal/server/env"
"next-terminal/server/model"
"next-terminal/server/repository"
"github.com/labstack/echo/v4"
"gorm.io/gorm"
)
@@ -40,10 +43,19 @@ var defaultProperties = map[string]string{
guacamole.EnableMenuAnimations: "true",
guacamole.DisableBitmapCaching: "false",
guacamole.DisableOffscreenCaching: "false",
guacamole.ColorDepth: "",
guacamole.Cursor: "",
guacamole.SwapRedBlue: "false",
"cron-log-saved-limit": "360",
"login-log-saved-limit": "360",
"session-saved-limit": "360",
"access-log-saved-limit": "30",
"filesystem-log-saved-limit": "30",
"operation-log-saved-limit": "30",
"database-sql-log-saved-limit": "30",
"user-default-storage-size": "5120",
"ip-extractor": "direct",
"ip-trust-list": "",
}
func (service propertyService) InitProperties() error {
@@ -60,6 +72,9 @@ func (service propertyService) InitProperties() error {
func (service propertyService) CreateIfAbsent(propertyMap map[string]string, name, value string) error {
if len(propertyMap[name]) == 0 {
if value == "" {
value = "-"
}
property := model.Property{
Name: name,
Value: value,
@@ -117,3 +132,76 @@ func (service propertyService) Update(item map[string]interface{}) error {
})
}
func (service propertyService) GetClientIP(c echo.Context) string {
propertyMap := repository.PropertyRepository.FindAllMap(context.TODO())
extractor := propertyMap["ip-extractor"]
trustList := propertyMap["ip-trust-list"]
directIP := c.RealIP()
if extractor == "" || extractor == "direct" {
return directIP
}
if !service.isTrustedIP(directIP, trustList) {
return directIP
}
switch extractor {
case "x-real-ip":
xRealIP := c.Request().Header.Get("X-Real-IP")
if xRealIP != "" {
return xRealIP
}
case "x-forwarded-for":
xForwardedFor := c.Request().Header.Get("X-Forwarded-For")
if xForwardedFor != "" {
ips := strings.Split(xForwardedFor, ",")
if len(ips) > 0 {
ip := strings.TrimSpace(ips[0])
if ip != "" {
return ip
}
}
}
}
return directIP
}
func (service propertyService) isTrustedIP(clientIP string, trustList string) bool {
if trustList == "" {
return false
}
trustIPs := strings.Split(trustList, ",")
clientIPAddr := net.ParseIP(clientIP)
if clientIPAddr == nil {
return false
}
for _, trustIP := range trustIPs {
trustIP = strings.TrimSpace(trustIP)
if trustIP == "" {
continue
}
if strings.Contains(trustIP, "/") {
_, ipNet, err := net.ParseCIDR(trustIP)
if err == nil && ipNet.Contains(clientIPAddr) {
return true
}
} else {
if trustIP == clientIP {
return true
}
trustIPAddr := net.ParseIP(trustIP)
if trustIPAddr != nil && trustIPAddr.Equal(clientIPAddr) {
return true
}
}
}
return false
}