2 Commits

11 changed files with 157 additions and 3 deletions

View File

@@ -7,6 +7,9 @@
* В настройках analyzer.toml добавил параметры su_enable и su_notify.
* su_enable = Включает отслеживание авторизаций через su. По умолчанию включён.
* su_notify = Включает уведомления об авторизациях через su. По умолчанию включён.
* В настройках analyzer.toml добавил параметры sudo_enable и sudo_notify.
* sudo_enable = Включает отслеживание авторизаций через sudo. По умолчанию выключен.
* sudo_notify = Включает уведомления об авторизациях через sudo. По умолчанию включён.
***
#### English
* Added local_enable and local_notify parameters to analyzer.toml settings.
@@ -15,6 +18,9 @@
* Added su_enable and su_notify parameters to analyzer.toml settings.
* su_enable = Enables tracking of logins via su. Enabled by default.
* su_notify = Enables notifications about logins via su. Enabled by default.
* Added sudo_enable and sudo_notify parameters to analyzer.toml settings.
* sudo_enable = Enables tracking of logins via sudo. Off by default.
* sudo_notify = Enables notifications about logins via sudo. Enabled by default.
***
## 0.4.0 (11.1.2026)
***

View File

@@ -81,3 +81,30 @@ su_enable = true
# Default: true
###
su_notify = true
###
# Включает отслеживание, если кто-либо использует команду `sudo` для доступа к другой учетной записи.
#
# ПРИМЕЧАНИЕ: Эта опция может стать обременительной, если команда sudo широко используется
# для получения root-доступа администраторами или панелями управления.
#
# По умолчанию: false
# ***
# Enables tracking if someone uses the `sudo` command to access another account.
#
# NOTE: This option could become onerous if sudo is used extensively for root
# access by administrators or control panels.
#
# Default: false
###
sudo_enable = false
###
# Включает уведомления, если кто-либо использует команду `sudo` для доступа к другой учетной записи.
# По умолчанию: true
# ***
# Enables notifications if someone uses the `sudo` command to access another account.
# Default: true
###
sudo_notify = true

View File

@@ -41,6 +41,10 @@ func New(config config2.Config, logger log.Logger, notify notifications.Notifica
if config.Login.Su.Enabled {
units = append(units, "SYSLOG_IDENTIFIER=su")
}
if config.Login.Sudo.Enabled {
units = append(units, "SYSLOG_IDENTIFIER=sudo")
}
}
systemdService := analyzerLog.NewSystemd(config.BinPath.Journalctl, units, logger)
@@ -84,6 +88,10 @@ func (a *analyzer) processLogs(ctx context.Context) {
if err := a.analysis.Locale(&entry); err != nil {
a.logger.Error(fmt.Sprintf("Failed to analyze locale logs: %s", err))
}
case entry.SyslogIdentifier == "sudo":
if err := a.analysis.Sudo(&entry); err != nil {
a.logger.Error(fmt.Sprintf("Failed to analyze sudo logs: %s", err))
}
case entry.SyslogIdentifier == "su":
if err := a.analysis.Su(&entry); err != nil {
a.logger.Error(fmt.Sprintf("Failed to analyze su logs: %s", err))

View File

@@ -6,6 +6,7 @@ type Login struct {
SSH LoginSSH
Local LoginLocal
Su LoginSu
Sudo LoginSudo
}
type LoginSSH struct {
@@ -22,3 +23,8 @@ type LoginSu struct {
Enabled bool
Notify bool
}
type LoginSudo struct {
Enabled bool
Notify bool
}

View File

@@ -11,12 +11,14 @@ type Analysis interface {
SSH(entry *analysisServices.Entry) error
Locale(entry *analysisServices.Entry) error
Su(entry *analysisServices.Entry) error
Sudo(entry *analysisServices.Entry) error
}
type analysis struct {
sshService analysisServices.Analysis
localeService analysisServices.Analysis
suService analysisServices.Analysis
sudoService analysisServices.Analysis
logger log.Logger
notify notifications.Notifications
@@ -27,6 +29,7 @@ func NewAnalysis(config *config.Config, logger log.Logger, notify notifications.
sshService: analysisServices.NewSSH(config, logger, notify),
localeService: analysisServices.NewLocale(config, logger, notify),
suService: analysisServices.NewSu(config, logger, notify),
sudoService: analysisServices.NewSudo(config, logger, notify),
logger: logger,
notify: notify,
}
@@ -43,3 +46,7 @@ func (a *analysis) Locale(entry *analysisServices.Entry) error {
func (a *analysis) Su(entry *analysisServices.Entry) error {
return a.suService.Process(entry)
}
func (a *analysis) Sudo(entry *analysisServices.Entry) error {
return a.sudoService.Process(entry)
}

View File

@@ -0,0 +1,81 @@
package analysis
import (
"fmt"
"regexp"
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/analyzer/config"
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/notifications"
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/i18n"
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
)
type sudo struct {
login sudoLogin
logger log.Logger
notify notifications.Notifications
}
type sudoLogin struct {
enabled bool
notify bool
}
func NewSudo(config *config.Config, logger log.Logger, notify notifications.Notifications) Analysis {
if !config.Login.Enabled || !config.Login.Su.Enabled {
return &EmptyAnalysis{}
}
return &sudo{
login: sudoLogin{
enabled: config.Login.Enabled && config.Login.Sudo.Enabled,
notify: config.Login.Notify && config.Login.Sudo.Notify,
},
logger: logger,
notify: notify,
}
}
func (s *sudo) Process(entry *Entry) error {
if s.login.enabled {
result, err := s.login.process(entry)
if err != nil {
s.logger.Error(fmt.Sprintf("Failed to process Sudo login: %s", err))
} else if result.found {
if s.login.notify {
s.notify.SendAsync(notifications.Message{Subject: result.subject, Body: result.body})
}
s.logger.Info(fmt.Sprintf("Sudo login detected: %s", entry.Message))
}
}
return nil
}
func (s *sudoLogin) process(entry *Entry) (processReturn, error) {
re := regexp.MustCompile(`^pam_unix\(sudo:session\): session opened for user (\S+)\(\S+\) by (\S+)\(\S+\)`)
matches := re.FindStringSubmatch(entry.Message)
if matches != nil {
user := matches[1]
byUser := matches[2]
return processReturn{
found: true,
subject: i18n.Lang.T("alert.login.sudo.subject", map[string]any{
"User": user,
"ByUser": byUser,
}),
body: i18n.Lang.T("alert.login.sudo.body", map[string]any{
"User": user,
"ByUser": byUser,
"Log": entry.Message,
"Time": entry.Time,
}),
}, nil
}
return processReturn{found: false}, nil
}

View File

@@ -34,5 +34,8 @@
"alert.login.locale.body": "Logged into the OS via TTY:\n Time: {{.Time}}\n User: {{.User}}\n Log: {{.Log}}",
"alert.login.su.subject": "User {{.ByUser}} has accessed user {{.User}} via su",
"alert.login.su.body": "User {{.ByUser}} accessed user {{.User}} via su.\nTime: {{.Time}}\nLog: {{.Log}}"
"alert.login.su.body": "User {{.ByUser}} accessed user {{.User}} via su.\nTime: {{.Time}}\nLog: {{.Log}}",
"alert.login.sudo.subject": "User {{.ByUser}} has accessed user {{.User}} via sudo",
"alert.login.sudo.body": "User {{.ByUser}} accessed user {{.User}} via sudo.\nTime: {{.Time}}\nLog: {{.Log}}"
}

View File

@@ -34,5 +34,8 @@
"alert.login.locale.body": "ОЖ-ға TTY арқылы кірдіңіз:\n Уақыт: {{.Time}}\n Пайдаланушы: {{.User}}\n Лог: {{.Log}}",
"alert.login.su.subject": "{{.ByUser}} пайдаланушысы {{.User}} пайдаланушысына su арқылы кіру мүмкіндігін алды",
"alert.login.su.body": "{{.ByUser}} пайдаланушысы {{.User}} пайдаланушысына su арқылы кірді.\nУақыты: {{.Time}}\nЛог: {{.Log}}"
"alert.login.su.body": "{{.ByUser}} пайдаланушысы {{.User}} пайдаланушысына su арқылы кірді.\nУақыты: {{.Time}}\nЛог: {{.Log}}",
"alert.login.sudo.subject": "{{.ByUser}} пайдаланушысы {{.User}} пайдаланушысына sudo арқылы кіру мүмкіндігін алды",
"alert.login.sudo.body": "{{.ByUser}} пайдаланушысы {{.User}} пайдаланушысына sudo арқылы кірді.\nУақыты: {{.Time}}\nЛог: {{.Log}}"
}

View File

@@ -34,5 +34,8 @@
"alert.login.locale.body": "Вошли в ОС через TTY:\n Время: {{.Time}}\n Пользователь: {{.User}}\n Лог: {{.Log}}",
"alert.login.su.subject": "Пользователь {{.ByUser}} получил доступ к пользователю {{.User}} через su",
"alert.login.su.body": "Пользователь {{.ByUser}} получил доступ к пользователю {{.User}} через su.\nВремя: {{.Time}}\nЛог: {{.Log}}"
"alert.login.su.body": "Пользователь {{.ByUser}} получил доступ к пользователю {{.User}} через su.\nВремя: {{.Time}}\nЛог: {{.Log}}",
"alert.login.sudo.subject": "Пользователь {{.ByUser}} получил доступ к пользователю {{.User}} через sudo",
"alert.login.sudo.body": "Пользователь {{.ByUser}} получил доступ к пользователю {{.User}} через sudo.\nВремя: {{.Time}}\nЛог: {{.Log}}"
}

View File

@@ -12,6 +12,9 @@ type Login struct {
SuEnable bool `mapstructure:"su_enable"`
SuNotify bool `mapstructure:"su_notify"`
SudoEnable bool `mapstructure:"sudo_enable"`
SudoNotify bool `mapstructure:"sudo_notify"`
}
func defaultLogin() Login {
@@ -27,6 +30,9 @@ func defaultLogin() Login {
SuEnable: true,
SuNotify: true,
SudoEnable: false,
SudoNotify: true,
}
}

View File

@@ -165,6 +165,10 @@ func (o *otherSettingsPath) ToAnalyzerConfig(binaryLocations *binaryLocations) (
Enabled: setting.Login.SuEnable,
Notify: setting.Login.SuNotify,
},
Sudo: config.LoginSudo{
Enabled: setting.Login.SudoEnable,
Notify: setting.Login.SudoNotify,
},
}
return config.Config{