Compare commits
2 Commits
74dce294bf
...
v0.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 69157c90cb | |||
| e76d2ae398 |
13
CHANGELOG.md
13
CHANGELOG.md
@@ -1,16 +1,3 @@
|
||||
## 0.3.0 (soon)
|
||||
***
|
||||
#### Русский
|
||||
* Служба systemd
|
||||
* Изменено WantedBy с multi-user.target на multi-user.target
|
||||
* Убрано ExecStop. По факту это не работало. Чтобы остановить сервис с очисткой правил nftables выпоните команду: kor-elf-shield stop
|
||||
* Добавлено Restart=on-failure. Нужно для того, чтобы программа перезапустилась после критической ошибки.
|
||||
***
|
||||
#### English
|
||||
* Systemd service
|
||||
* Changed WantedBy from multi-user.target to multi-user.target
|
||||
* Removed ExecStop. It didn't actually work. To stop the service and clear the nftables rules, run the command: kor-elf-shield stop
|
||||
* Added Restart=on-failure. This is necessary to ensure the program restarts after a critical error.
|
||||
## 0.2.0 (29.11.2025)
|
||||
***
|
||||
#### Русский
|
||||
|
||||
@@ -210,14 +210,3 @@ nftables = "/usr/sbin/nft"
|
||||
# Default: /etc/kor-elf-shield/firewall.toml
|
||||
###
|
||||
firewall = "/etc/kor-elf-shield/firewall.toml"
|
||||
|
||||
###
|
||||
# Укажите путь к настройкам уведомлений.
|
||||
# Файл должен иметь расширение .toml.
|
||||
# По умолчанию: /etc/kor-elf-shield/notifications.toml
|
||||
# ***
|
||||
# Specify the path to notification settings.
|
||||
# The file must have the .toml extension.
|
||||
# Default: /etc/kor-elf-shield/notifications.toml
|
||||
###
|
||||
notifications = "/etc/kor-elf-shield/notifications.toml"
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
###############################################################################
|
||||
# РАЗДЕЛ:Базовые настройки
|
||||
# ***
|
||||
# SECTION:Basic settings
|
||||
###############################################################################
|
||||
|
||||
###
|
||||
# Включает или выключает уведомления.
|
||||
# !!! Не забудьте перед включением настроить email !!!
|
||||
# false = Выключает.
|
||||
# true = Включает.
|
||||
#
|
||||
# По умолчанию: false
|
||||
# ***
|
||||
# Turns notifications on or off.
|
||||
# !!! Don't forget to set up your email before turning it on !!!
|
||||
# false = Disables.
|
||||
# true = Enables.
|
||||
#
|
||||
# Default: false
|
||||
###
|
||||
enabled = false
|
||||
|
||||
###
|
||||
# Название сервера в уведомлениях
|
||||
# По умолчанию: server
|
||||
# ***
|
||||
# Server name in notifications
|
||||
# Default: server
|
||||
###
|
||||
server_name = "server"
|
||||
|
||||
###############################################################################
|
||||
# РАЗДЕЛ:email
|
||||
# ***
|
||||
# SECTION:email
|
||||
###############################################################################
|
||||
[email]
|
||||
|
||||
###
|
||||
# Сервер, через который будет отправляться почта.
|
||||
# Например: smtp.gmail.com
|
||||
# По умолчанию:
|
||||
# ***
|
||||
# The server through which mail will be sent.
|
||||
# For example: smtp.gmail.com
|
||||
# Default:
|
||||
###
|
||||
host = ""
|
||||
|
||||
###
|
||||
# Указать порт сервера, через который будет отправляться почта.
|
||||
# Например: 587
|
||||
# По умолчанию:
|
||||
# ***
|
||||
# Specify the server port through which mail will be sent.
|
||||
# For example: 587
|
||||
# Default:
|
||||
###
|
||||
port = ""
|
||||
|
||||
###
|
||||
# Логин к серверу, через который будет отправляться почта.
|
||||
# По умолчанию:
|
||||
# ***
|
||||
# Login to the server through which mail will be sent.
|
||||
# Default:
|
||||
###
|
||||
username = ""
|
||||
|
||||
###
|
||||
# Пароль к серверу, через который будет отправляться почта.
|
||||
# По умолчанию:
|
||||
# ***
|
||||
# Password for the server through which mail will be sent.
|
||||
# Default:
|
||||
###
|
||||
password = ""
|
||||
|
||||
###
|
||||
# Тип авторизации.
|
||||
# Варианты: "PLAIN", "LOGIN", "CRAM-MD5", "NONE"
|
||||
# Обычно используется "PLAIN". Если у вас внутренний релей без пароля - используйте "NONE".
|
||||
# По умолчанию: "PLAIN"
|
||||
# ***
|
||||
# Authorization type.
|
||||
# Options: "PLAIN", "LOGIN", "CRAM-MD5", "NONE"
|
||||
# Usually "PLAIN" is used. If you have an internal relay without a password - use "NONE".
|
||||
# Default: "PLAIN"
|
||||
###
|
||||
auth_type = "PLAIN"
|
||||
|
||||
###
|
||||
# Защищённое соединение.
|
||||
# Варианты: "NONE", "STARTTLS", "IMPLICIT"
|
||||
#
|
||||
# "NONE" — без TLS
|
||||
# "STARTTLS" — обычный SMTP на 587 (или 25) + upgrade через STARTTLS
|
||||
# "IMPLICIT" — SMTPS (TLS сразу), обычно 465
|
||||
#
|
||||
# По умолчанию: "STARTTLS"
|
||||
# ***
|
||||
# Secure connection.
|
||||
# Options: "NONE", "STARTTLS", "IMPLICIT"
|
||||
#
|
||||
# "NONE" — without TLS
|
||||
# "STARTTLS" — regular SMTP on 587 (or 25) + upgrade via STARTTLS
|
||||
# "IMPLICIT" — SMTPS (TLS Immediately), typically 465
|
||||
#
|
||||
# Default: "STARTTLS"
|
||||
###
|
||||
tls_mode = "STARTTLS"
|
||||
|
||||
###
|
||||
# Только если тип защищённого соединения в режиме starttls.
|
||||
# Варианты: "MANDATORY", "OPPORTUNISTIC"
|
||||
#
|
||||
# "MANDATORY" — если STARTTLS недоступен/не удался будет вызвана ошибка
|
||||
# "OPPORTUNISTIC" — попытаться STARTTLS, но если нельзя, то попытается отправить без TLS
|
||||
#
|
||||
# По умолчанию: "MANDATORY"
|
||||
# ***
|
||||
# Only if the secure connection type is in starttls mode.
|
||||
# Options: "MANDATORY", "OPPORTUNISTIC"
|
||||
#
|
||||
# "MANDATORY" — if STARTTLS is unavailable/failed, an error will be raised
|
||||
# "OPPORTUNISTIC" — try STARTTLS, but if that fails, it will try to send without TLS
|
||||
#
|
||||
# Default: "MANDATORY"
|
||||
###
|
||||
tls_policy = "MANDATORY"
|
||||
|
||||
###
|
||||
# Проверять ли сертификат защищённого соединения.
|
||||
#
|
||||
# false = Выключает.
|
||||
# true = Включает.
|
||||
#
|
||||
# По умолчанию: true
|
||||
# ***
|
||||
# Whether to check the secure connection certificate.
|
||||
#
|
||||
# false = Disables.
|
||||
# true = Enables.
|
||||
#
|
||||
# Default: true
|
||||
###
|
||||
tls_verify = true
|
||||
|
||||
###
|
||||
# Email, который будет указываться при отправки почты.
|
||||
# Например: test@localhost
|
||||
# По умолчанию:
|
||||
# ***
|
||||
# Email that will be specified when sending mail.
|
||||
# For example: test@localhost
|
||||
# Default:
|
||||
###
|
||||
from = ""
|
||||
|
||||
###
|
||||
# Адрес электронной почты, на который будет отправлено письмо.
|
||||
# Например: root@localhost
|
||||
# По умолчанию:
|
||||
# ***
|
||||
# Email to whom the mail will be sent.
|
||||
# For example: root@localhost
|
||||
# Default:
|
||||
###
|
||||
to = ""
|
||||
@@ -3,10 +3,8 @@ Description=kor-elf-shield
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/sbin/kor-elf-shield start
|
||||
Restart=on-failure
|
||||
RestartSec=10s
|
||||
ExecStop=/usr/sbin/kor-elf-shield stop
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
WantedBy=sysinit.target
|
||||
1
go.mod
1
go.mod
@@ -21,7 +21,6 @@ require (
|
||||
github.com/spf13/cast v1.10.0 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/wneessen/go-mail v0.7.2 // indirect
|
||||
go.uber.org/multierr v1.10.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||
|
||||
2
go.sum
2
go.sum
@@ -40,8 +40,6 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/urfave/cli/v3 v3.4.1 h1:1M9UOCy5bLmGnuu1yn3t3CB4rG79Rtoxuv1sPhnm6qM=
|
||||
github.com/urfave/cli/v3 v3.4.1/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo=
|
||||
github.com/wneessen/go-mail v0.7.2 h1:xxPnhZ6IZLSgxShebmZ6DPKh1b6OJcoHfzy7UjOkzS8=
|
||||
github.com/wneessen/go-mail v0.7.2/go.mod h1:+TkW6QP3EVkgTEqHtVmnAE/1MRhmzb8Y9/W3pweuS+k=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon"
|
||||
"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"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/setting"
|
||||
@@ -35,17 +34,6 @@ func runDaemon(ctx context.Context, _ *cli.Command) error {
|
||||
_ = logger.Sync()
|
||||
}()
|
||||
|
||||
notificationsConfig, err := setting.Config.OtherSettingsPath.ToNotificationsConfig()
|
||||
if err != nil {
|
||||
logger.Fatal(err.Error())
|
||||
|
||||
// Fatal should call os.Exit(1), but there's a chance that might not happen,
|
||||
// so we return err just in case.
|
||||
return err
|
||||
}
|
||||
|
||||
notificationsClient := notifications.New(notificationsConfig, logger)
|
||||
|
||||
config, err := setting.Config.ToDaemonOptions()
|
||||
if err != nil {
|
||||
logger.Fatal(err.Error())
|
||||
@@ -55,7 +43,7 @@ func runDaemon(ctx context.Context, _ *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
d, err := daemon.NewDaemon(config, logger, notificationsClient)
|
||||
d, err := daemon.NewDaemon(config, logger)
|
||||
if err != nil {
|
||||
logger.Fatal(err.Error())
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"time"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/notifications"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/pidfile"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/socket"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
@@ -18,11 +17,10 @@ type Daemon interface {
|
||||
}
|
||||
|
||||
type daemon struct {
|
||||
pidFile pidfile.PidFile
|
||||
socket socket.Socket
|
||||
logger log.Logger
|
||||
firewall firewall.API
|
||||
notifications notifications.Notifications
|
||||
pidFile pidfile.PidFile
|
||||
socket socket.Socket
|
||||
logger log.Logger
|
||||
firewall firewall.API
|
||||
|
||||
stopCh chan struct{}
|
||||
}
|
||||
@@ -54,11 +52,6 @@ func (d *daemon) Run(ctx context.Context, isTesting bool, testingInterval uint16
|
||||
_ = d.socket.Close()
|
||||
}()
|
||||
|
||||
d.notifications.Run()
|
||||
defer func() {
|
||||
_ = d.notifications.Close()
|
||||
}()
|
||||
|
||||
go d.socket.Run(ctx, d.socketCommand)
|
||||
d.runWorker(ctx, isTesting, testingInterval)
|
||||
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
package notifications
|
||||
|
||||
import (
|
||||
"github.com/wneessen/go-mail"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Enabled bool
|
||||
ServerName string
|
||||
Email Email
|
||||
}
|
||||
|
||||
type Email struct {
|
||||
Host string
|
||||
Port uint
|
||||
Username string
|
||||
Password string
|
||||
AuthType mail.SMTPAuthType
|
||||
TLS TLS
|
||||
From string
|
||||
To string
|
||||
}
|
||||
|
||||
type TLS struct {
|
||||
Mode TLSMode
|
||||
Policy TLSPolicy
|
||||
Verify bool
|
||||
}
|
||||
|
||||
type TLSMode string
|
||||
|
||||
const (
|
||||
TLSModeNone TLSMode = "NONE"
|
||||
TLSModeStartTLS TLSMode = "STARTTLS"
|
||||
TLSModeImplicit TLSMode = "IMPLICIT"
|
||||
)
|
||||
|
||||
type TLSPolicy string
|
||||
|
||||
const (
|
||||
TLSPolicyMandatory TLSPolicy = "MANDATORY"
|
||||
TLSPolicyOpportunistic TLSPolicy = "OPPORTUNISTIC"
|
||||
)
|
||||
@@ -1,151 +0,0 @@
|
||||
package notifications
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
"github.com/wneessen/go-mail"
|
||||
)
|
||||
|
||||
type Message struct {
|
||||
Subject string
|
||||
Body string
|
||||
}
|
||||
|
||||
type Notifications interface {
|
||||
Run()
|
||||
SendAsync(message Message)
|
||||
Close() error
|
||||
}
|
||||
|
||||
type notifications struct {
|
||||
config Config
|
||||
logger log.Logger
|
||||
msgQueue chan Message
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
func New(config Config, logger log.Logger) Notifications {
|
||||
return ¬ifications{
|
||||
config: config,
|
||||
logger: logger,
|
||||
msgQueue: make(chan Message, 100),
|
||||
}
|
||||
}
|
||||
|
||||
func (n *notifications) Run() {
|
||||
if n.config.Enabled == false {
|
||||
n.logger.Info("Notifications are disabled")
|
||||
}
|
||||
|
||||
n.wg.Add(1)
|
||||
go func() {
|
||||
defer n.wg.Done()
|
||||
for msg := range n.msgQueue {
|
||||
err := n.sendEmail(msg)
|
||||
if err != nil {
|
||||
n.logger.Error(fmt.Sprintf("failed to send email: %v", err))
|
||||
} else if n.config.Enabled {
|
||||
n.logger.Debug(fmt.Sprintf("email sent: Subject %s, Body %s", msg.Subject, msg.Body))
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (n *notifications) SendAsync(message Message) {
|
||||
select {
|
||||
case n.msgQueue <- message:
|
||||
if n.config.Enabled == false {
|
||||
n.logger.Debug(fmt.Sprintf("email sending is disabled, message was added to the queue: Subject %s, Body %s", message.Subject, message.Body))
|
||||
} else {
|
||||
n.logger.Debug(fmt.Sprintf("added to the mail sending queue: Subject %s, Body %s", message.Subject, message.Body))
|
||||
}
|
||||
default:
|
||||
n.logger.Error(fmt.Sprintf("failed to send email: queue is full"))
|
||||
}
|
||||
}
|
||||
|
||||
func (n *notifications) Close() error {
|
||||
close(n.msgQueue)
|
||||
n.logger.Debug("We are waiting for all notifications to be sent")
|
||||
n.wg.Wait()
|
||||
n.logger.Debug("Notifications queue processed and closed")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *notifications) sendEmail(message Message) error {
|
||||
if n.config.Enabled == false {
|
||||
return nil
|
||||
}
|
||||
|
||||
m := mail.NewMsg()
|
||||
if err := m.From(n.config.Email.From); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := m.To(n.config.Email.To); err != nil {
|
||||
return err
|
||||
}
|
||||
m.Subject(message.Subject + " (" + n.config.ServerName + ")")
|
||||
m.SetBodyString(mail.TypeTextPlain, "Server: "+n.config.ServerName+"\n"+message.Body)
|
||||
|
||||
client, err := newClient(n.config.Email)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() { _ = client.Close() }()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
return client.DialAndSendWithContext(ctx, m)
|
||||
}
|
||||
|
||||
func newClient(config Email) (*mail.Client, error) {
|
||||
options := []mail.Option{
|
||||
mail.WithPort(int(config.Port)),
|
||||
mail.WithSMTPAuth(config.AuthType),
|
||||
}
|
||||
|
||||
if config.AuthType != mail.SMTPAuthNoAuth {
|
||||
options = append(options, mail.WithUsername(config.Username), mail.WithPassword(config.Password))
|
||||
}
|
||||
|
||||
switch config.TLS.Mode {
|
||||
case TLSModeImplicit:
|
||||
options = append(options, mail.WithSSL())
|
||||
break
|
||||
case TLSModeStartTLS:
|
||||
|
||||
switch config.TLS.Policy {
|
||||
case TLSPolicyMandatory:
|
||||
options = append(options, mail.WithTLSPolicy(mail.TLSMandatory))
|
||||
break
|
||||
case TLSPolicyOpportunistic:
|
||||
options = append(options, mail.WithTLSPolicy(mail.TLSOpportunistic))
|
||||
break
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown tls policy: %s", config.TLS.Policy)
|
||||
}
|
||||
|
||||
if !config.TLS.Verify {
|
||||
tlsCfg := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
options = append(options, mail.WithTLSConfig(tlsCfg))
|
||||
}
|
||||
break
|
||||
case TLSModeNone:
|
||||
|
||||
break
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown tls mode: %s", config.TLS.Mode)
|
||||
}
|
||||
|
||||
options = append(options, mail.WithSSL())
|
||||
|
||||
return mail.NewClient(config.Host, options...)
|
||||
}
|
||||
@@ -4,13 +4,12 @@ import (
|
||||
"errors"
|
||||
|
||||
firewall2 "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/notifications"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/pidfile"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/socket"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
)
|
||||
|
||||
func NewDaemon(opts DaemonOptions, logger log.Logger, notifications notifications.Notifications) (Daemon, error) {
|
||||
func NewDaemon(opts DaemonOptions, logger log.Logger) (Daemon, error) {
|
||||
if logger == nil {
|
||||
return nil, errors.New("logger is nil")
|
||||
}
|
||||
@@ -28,10 +27,9 @@ func NewDaemon(opts DaemonOptions, logger log.Logger, notifications notification
|
||||
firewall, err := firewall2.New(opts.PathNftables, logger, opts.ConfigFirewall)
|
||||
|
||||
return &daemon{
|
||||
pidFile: pidFile,
|
||||
socket: sock,
|
||||
logger: logger,
|
||||
firewall: firewall,
|
||||
notifications: notifications,
|
||||
pidFile: pidFile,
|
||||
socket: sock,
|
||||
logger: logger,
|
||||
firewall: firewall,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
package notifications
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/notifications"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/setting/validate"
|
||||
"github.com/wneessen/go-mail"
|
||||
)
|
||||
|
||||
type Email struct {
|
||||
Host string `mapstructure:"host"`
|
||||
Port int `mapstructure:"port"`
|
||||
Username string `mapstructure:"username"`
|
||||
Password string `mapstructure:"password"`
|
||||
AuthType string `mapstructure:"auth_type"`
|
||||
|
||||
TLSMode string `mapstructure:"tls_mode"`
|
||||
TLSPolicy string `mapstructure:"tls_policy"`
|
||||
TLSVerify bool `mapstructure:"tls_verify"`
|
||||
|
||||
From string `mapstructure:"from"`
|
||||
To string `mapstructure:"to"`
|
||||
}
|
||||
|
||||
func defaultEmail() Email {
|
||||
return Email{
|
||||
Host: "",
|
||||
Port: 0,
|
||||
Username: "",
|
||||
Password: "",
|
||||
AuthType: "PLAIN",
|
||||
TLSMode: "STARTTLS",
|
||||
TLSPolicy: "MANDATORY",
|
||||
TLSVerify: true,
|
||||
From: "",
|
||||
To: "",
|
||||
}
|
||||
}
|
||||
|
||||
func (e Email) Validate() error {
|
||||
if e.Host == "" {
|
||||
return errors.New("host is not specified")
|
||||
}
|
||||
|
||||
if e.Port == 0 {
|
||||
return errors.New("port is not specified")
|
||||
}
|
||||
if err := validate.Port(e.Port, "port"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if e.From == "" {
|
||||
return errors.New("from is not specified")
|
||||
}
|
||||
|
||||
if e.To == "" {
|
||||
return errors.New("to is not specified")
|
||||
}
|
||||
|
||||
if e.Username == "" {
|
||||
return errors.New("username is not specified")
|
||||
}
|
||||
|
||||
if e.Password == "" {
|
||||
return errors.New("password is not specified")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e Email) ToTLSConfig() (notifications.TLS, error) {
|
||||
mode, err := parseTLSMode(e.TLSMode)
|
||||
if err != nil {
|
||||
return notifications.TLS{}, err
|
||||
}
|
||||
|
||||
policy, err := parseTLSPolicy(e.TLSPolicy)
|
||||
if err != nil {
|
||||
return notifications.TLS{}, err
|
||||
}
|
||||
|
||||
return notifications.TLS{
|
||||
Mode: mode,
|
||||
Policy: policy,
|
||||
Verify: e.TLSVerify,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ParseAuthType(authType string) (mail.SMTPAuthType, error) {
|
||||
switch strings.ToUpper(authType) {
|
||||
case "PLAIN":
|
||||
return mail.SMTPAuthPlain, nil
|
||||
case "LOGIN":
|
||||
return mail.SMTPAuthLogin, nil
|
||||
case "CRAM-MD5":
|
||||
return mail.SMTPAuthCramMD5, nil
|
||||
case "NONE":
|
||||
return mail.SMTPAuthNoAuth, nil
|
||||
}
|
||||
|
||||
return mail.SMTPAuthNoAuth, fmt.Errorf("unknown auth type: %s", authType)
|
||||
}
|
||||
|
||||
func parseTLSMode(mode string) (notifications.TLSMode, error) {
|
||||
switch strings.ToUpper(mode) {
|
||||
case "STARTTLS":
|
||||
return notifications.TLSModeStartTLS, nil
|
||||
case "IMPLICIT":
|
||||
return notifications.TLSModeImplicit, nil
|
||||
case "NONE":
|
||||
return notifications.TLSModeNone, nil
|
||||
}
|
||||
|
||||
return notifications.TLSModeNone, fmt.Errorf("unknown tls mode: %s", mode)
|
||||
}
|
||||
|
||||
func parseTLSPolicy(policy string) (notifications.TLSPolicy, error) {
|
||||
switch strings.ToUpper(policy) {
|
||||
case "MANDATORY":
|
||||
return notifications.TLSPolicyMandatory, nil
|
||||
case "OPPORTUNISTIC":
|
||||
return notifications.TLSPolicyOpportunistic, nil
|
||||
}
|
||||
|
||||
return notifications.TLSPolicyMandatory, fmt.Errorf("unknown tls policy: %s", policy)
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package notifications
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/setting/validate"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type Setting struct {
|
||||
Enabled bool `mapstructure:"enabled"`
|
||||
ServerName string `mapstructure:"server_name"`
|
||||
Email Email
|
||||
}
|
||||
|
||||
func InitSetting(path string) (Setting, error) {
|
||||
if err := validate.IsTomlFile(path, "otherSettingsPath.notifications"); err != nil {
|
||||
return Setting{}, err
|
||||
}
|
||||
|
||||
setting := settingDefault()
|
||||
|
||||
v := viper.New()
|
||||
v.SetConfigType("toml")
|
||||
v.SetConfigFile(path)
|
||||
|
||||
if err := v.ReadInConfig(); err != nil {
|
||||
return Setting{}, err
|
||||
}
|
||||
if err := v.Unmarshal(&setting); err != nil {
|
||||
return Setting{}, err
|
||||
}
|
||||
|
||||
if !setting.Enabled {
|
||||
return setting, nil
|
||||
}
|
||||
|
||||
if err := setting.Validate(); err != nil {
|
||||
return Setting{}, err
|
||||
}
|
||||
|
||||
return setting, nil
|
||||
}
|
||||
|
||||
func settingDefault() Setting {
|
||||
return Setting{
|
||||
Enabled: false,
|
||||
ServerName: "server",
|
||||
Email: defaultEmail(),
|
||||
}
|
||||
}
|
||||
|
||||
func (s Setting) Validate() error {
|
||||
if s.ServerName == "" {
|
||||
return errors.New("server_name is not specified")
|
||||
}
|
||||
|
||||
if err := validate.NameWithDot(s.ServerName, "server_name"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.Email.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -2,21 +2,16 @@ package setting
|
||||
|
||||
import (
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/notifications"
|
||||
firewallSetting "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/setting/firewall"
|
||||
notificationsSetting "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/setting/notifications"
|
||||
"github.com/wneessen/go-mail"
|
||||
)
|
||||
|
||||
type otherSettingsPath struct {
|
||||
Firewall string `mapstructure:"firewall"`
|
||||
Notifications string `mapstructure:"notifications"`
|
||||
Firewall string `mapstructure:"firewall"`
|
||||
}
|
||||
|
||||
func otherSettingsPathDefault() *otherSettingsPath {
|
||||
return &otherSettingsPath{
|
||||
Firewall: "/etc/kor-elf-shield/firewall.toml",
|
||||
Notifications: "/etc/kor-elf-shield/notifications.toml",
|
||||
Firewall: "/etc/kor-elf-shield/firewall.toml",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,39 +76,3 @@ func (o *otherSettingsPath) ToFirewallConfig() (firewall.Config, error) {
|
||||
Policy: configPolicy,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (o *otherSettingsPath) ToNotificationsConfig() (notifications.Config, error) {
|
||||
setting, err := notificationsSetting.InitSetting(o.Notifications)
|
||||
if err != nil {
|
||||
return notifications.Config{}, err
|
||||
}
|
||||
|
||||
authType := mail.SMTPAuthPlain
|
||||
tls := notifications.TLS{}
|
||||
if setting.Enabled {
|
||||
authType, err = notificationsSetting.ParseAuthType(setting.Email.AuthType)
|
||||
if err != nil {
|
||||
return notifications.Config{}, err
|
||||
}
|
||||
|
||||
tls, err = setting.Email.ToTLSConfig()
|
||||
if err != nil {
|
||||
return notifications.Config{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return notifications.Config{
|
||||
Enabled: setting.Enabled,
|
||||
ServerName: setting.ServerName,
|
||||
Email: notifications.Email{
|
||||
Host: setting.Email.Host,
|
||||
Port: uint(setting.Email.Port),
|
||||
Username: setting.Email.Username,
|
||||
Password: setting.Email.Password,
|
||||
AuthType: authType,
|
||||
TLS: tls,
|
||||
From: setting.Email.From,
|
||||
To: setting.Email.To,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -54,17 +54,6 @@ func Name(name string, parameterName string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NameWithDot(name string, parameterName string) error {
|
||||
if name == "" {
|
||||
return fmt.Errorf("%s is empty", parameterName)
|
||||
}
|
||||
re := regexp.MustCompile(`^[a-zA-Z0-9_\-\.]{1,64}$`)
|
||||
if !re.MatchString(name) {
|
||||
return fmt.Errorf("%s must not contain special characters", parameterName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Port(port int, parameterName string) error {
|
||||
if port < 0 || port > 65535 {
|
||||
return fmt.Errorf("%s must be in range 0-65535", parameterName)
|
||||
|
||||
Reference in New Issue
Block a user