nginx_log.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. package nginx_log
  2. import (
  3. "fmt"
  4. "os"
  5. "path/filepath"
  6. "regexp"
  7. "github.com/0xJacky/Nginx-UI/internal/cache"
  8. "github.com/0xJacky/Nginx-UI/internal/helper"
  9. "github.com/0xJacky/Nginx-UI/internal/nginx"
  10. "github.com/0xJacky/Nginx-UI/settings"
  11. "github.com/uozi-tech/cosy/logger"
  12. )
  13. // Regular expression for log directives - matches access_log or error_log
  14. var logDirectiveRegex = regexp.MustCompile(`(?m)(access_log|error_log)\s+([^\s;]+)(?:\s+[^;]+)?;`)
  15. // Use init function to automatically register callback
  16. func init() {
  17. // Register the callback directly with the global registry
  18. cache.RegisterCallback(scanForLogDirectives)
  19. }
  20. // scanForLogDirectives scans and parses configuration files for log directives
  21. func scanForLogDirectives(configPath string, content []byte) error {
  22. // Clear previous scan results when scanning the main config
  23. if configPath == nginx.GetConfPath("", "nginx.conf") {
  24. ClearLogCache()
  25. }
  26. // Find log directives using regex
  27. matches := logDirectiveRegex.FindAllSubmatch(content, -1)
  28. // Parse log paths
  29. for _, match := range matches {
  30. if len(match) >= 3 {
  31. directiveType := string(match[1]) // "access_log" or "error_log"
  32. logPath := string(match[2]) // Path to log file
  33. // Validate log path
  34. if IsLogPathUnderWhiteList(logPath) && isValidLogPath(logPath) {
  35. logType := "access"
  36. if directiveType == "error_log" {
  37. logType = "error"
  38. }
  39. // Add to cache
  40. AddLogPath(logPath, logType, filepath.Base(logPath))
  41. }
  42. }
  43. }
  44. return nil
  45. }
  46. // GetAllLogs returns all log paths
  47. func GetAllLogs(filters ...func(*NginxLogCache) bool) []*NginxLogCache {
  48. return GetAllLogPaths(filters...)
  49. }
  50. // isValidLogPath checks if a log path is valid:
  51. // 1. It must be a regular file or a symlink to a regular file
  52. // 2. It must not point to a console or special device
  53. // 3. It must be under the whitelist directories
  54. func isValidLogPath(logPath string) bool {
  55. // First check if the path is in the whitelist
  56. if !IsLogPathUnderWhiteList(logPath) {
  57. logger.Warn("Log path is not under whitelist:", logPath)
  58. return false
  59. }
  60. // Check if the path exists
  61. fileInfo, err := os.Lstat(logPath)
  62. if err != nil {
  63. // If the file doesn't exist, it might be created later
  64. // We'll assume it's valid for now
  65. return true
  66. }
  67. // If it's a symlink, follow it
  68. if fileInfo.Mode()&os.ModeSymlink != 0 {
  69. linkTarget, err := os.Readlink(logPath)
  70. if err != nil {
  71. return false
  72. }
  73. // Make the link target path absolute if it's relative
  74. if !filepath.IsAbs(linkTarget) {
  75. linkTarget = filepath.Join(filepath.Dir(logPath), linkTarget)
  76. }
  77. // Check the target file
  78. targetInfo, err := os.Stat(linkTarget)
  79. if err != nil {
  80. return false
  81. }
  82. // Only accept regular files as targets
  83. return targetInfo.Mode().IsRegular()
  84. }
  85. // For non-symlinks, just check if it's a regular file
  86. return fileInfo.Mode().IsRegular()
  87. }
  88. // IsLogPathUnderWhiteList checks if a log path is under one of the paths in LogDirWhiteList
  89. func IsLogPathUnderWhiteList(path string) bool {
  90. cacheKey := fmt.Sprintf("isLogPathUnderWhiteList:%s", path)
  91. res, ok := cache.Get(cacheKey)
  92. // Deep copy the whitelist
  93. logDirWhiteList := append([]string{}, settings.NginxSettings.LogDirWhiteList...)
  94. accessLogPath := nginx.GetAccessLogPath()
  95. errorLogPath := nginx.GetErrorLogPath()
  96. if accessLogPath != "" {
  97. logDirWhiteList = append(logDirWhiteList, filepath.Dir(accessLogPath))
  98. }
  99. if errorLogPath != "" {
  100. logDirWhiteList = append(logDirWhiteList, filepath.Dir(errorLogPath))
  101. }
  102. // No cache, check it
  103. if !ok {
  104. for _, whitePath := range logDirWhiteList {
  105. if helper.IsUnderDirectory(path, whitePath) {
  106. cache.Set(cacheKey, true, 0)
  107. return true
  108. }
  109. }
  110. return false
  111. }
  112. return res.(bool)
  113. }