This repository has been archived on 2026-03-28. You can view files and clone it, but cannot push or open issues or pull requests.
old-sysmonitord/internal/whitelist/manager.go
2026-03-22 12:16:48 +08:00

176 lines
4.1 KiB
Go

package whitelist
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"sync"
"github.com/wuko233/sysmonitord/internal/config"
)
type FileStatus string
const (
StatusIgnored FileStatus = "IGNORED"
StatusNonWhitelisted FileStatus = "NON_WHITELISTED"
StatusHashMismatch FileStatus = "HASH_MISMATCH"
StatusSafe FileStatus = "SAFE"
)
type Manager struct {
mu sync.RWMutex
official config.OfficialConfig
user config.UserConfig
mergedIgnore []string
}
func NewManager(o config.OfficialConfig, u config.UserConfig) *Manager {
m := &Manager{
official: o,
user: u,
}
m.mergedIgnore = append([]string{}, m.official.IgnoredPaths...)
m.mergedIgnore = append(m.mergedIgnore, m.user.IgnoredPaths...)
return m
}
func (m *Manager) UpdateConfig(official config.OfficialConfig, user config.UserConfig) {
m.mu.Lock()
defer m.mu.Unlock()
m.official = official
m.user = user
m.mergedIgnore = append([]string{}, m.official.IgnoredPaths...)
m.mergedIgnore = append(m.mergedIgnore, m.user.IgnoredPaths...)
}
func (m *Manager) IsPathIgnored(path string) bool {
m.mu.RLock()
defer m.mu.RUnlock()
return m.IsPathIgnoredUnsafe(path)
}
func (m *Manager) IsPathIgnoredUnsafe(path string) bool {
path = filepath.Clean(path)
for _, ignore := range m.mergedIgnore {
if strings.HasPrefix(path, filepath.Clean(ignore)) {
return true
}
}
return false
}
// CheckFileStatus 检查文件状态
// 返回: isWhitelisted(是否在白名单), isValid(Hash是否匹配), err
func (m *Manager) CheckFileStatus(path string) (bool, bool, error) {
m.mu.RLock()
defer m.mu.RUnlock()
// 1. 首先检查是否在忽略列表中
if m.IsPathIgnoredUnsafe(path) {
return true, true, nil
}
// 2. 合并官方和用户的白名单(并集策略)
var allowedHashes []string
// 添加官方白名单 Hash
if officialHashes, exists := m.official.WhitelistFiles[path]; exists {
allowedHashes = append(allowedHashes, officialHashes...)
}
// 添加用户补充白名单 Hash
if userHashes, exists := m.user.SupplementFiles[path]; exists {
allowedHashes = append(allowedHashes, userHashes...)
}
// 如果两个白名单都没有这个文件
if len(allowedHashes) == 0 {
return false, false, nil
}
// 3. 计算当前文件 Hash
fileHash, err := CalculateFileHash(path)
if err != nil {
return true, false, fmt.Errorf("计算文件哈希失败: %v", err)
}
// 4. 检查 Hash 是否在允许列表中
for _, h := range allowedHashes {
// 支持 sha256:xxx 格式或纯 hash 格式
normalizedHash := h
if strings.HasPrefix(h, "sha256:") {
normalizedHash = h[7:]
}
if strings.EqualFold(normalizedHash, fileHash) {
return true, true, nil
}
}
// 在白名单中但 Hash 不匹配
return true, false, nil
}
func CalculateFileHash(filePath string) (string, error) {
file, err := os.Open(filePath)
if err != nil {
return "", err
}
defer file.Close()
hash := sha256.New()
if _, err := io.Copy(hash, file); err != nil {
return "", err
}
return hex.EncodeToString(hash.Sum(nil)), nil
}
func (m *Manager) IsProcessAllowed(procName string, cmdLine string) bool {
m.mu.RLock()
defer m.mu.RUnlock()
// 检查官方白名单
for _, p := range m.official.WhitelistProcesses {
if p == procName {
return true
}
}
// 检查用户补充白名单(数组形式)
for _, p := range m.user.SupplementProcesses {
if p == procName {
return true
}
}
return false
}
func (m *Manager) GetAuditServerUrl() string {
m.mu.RLock()
defer m.mu.RUnlock()
return m.user.Connection.AuditServerURL
}
func (m *Manager) rebuildIgnoreList() {
totalLen := len(m.official.IgnoredPaths) + len(m.user.IgnoredPaths)
// 预分配容量以提高性能
m.mergedIgnore = make([]string, 0, totalLen)
m.mergedIgnore = append(m.mergedIgnore, m.official.IgnoredPaths...)
m.mergedIgnore = append(m.mergedIgnore, m.user.IgnoredPaths...)
}