Refactor sendEmail function and add support for multiple SMTP auth methods (LOGIN, PLAIN, CRAM-MD5) and TLS verification option, fix syntax error in sendSMTPMessage function

This commit is contained in:
2026-01-22 19:34:05 +01:00
parent 4e61fdf9f4
commit 90d4ff4e9a
11 changed files with 275 additions and 81 deletions

View File

@@ -40,12 +40,14 @@ import (
// SMTPSettings holds the SMTP server configuration for sending alert emails
type SMTPSettings struct {
Host string `json:"host"`
Port int `json:"port"`
Username string `json:"username"`
Password string `json:"password"`
From string `json:"from"`
UseTLS bool `json:"useTLS"`
Host string `json:"host"`
Port int `json:"port"`
Username string `json:"username"`
Password string `json:"password"`
From string `json:"from"`
UseTLS bool `json:"useTLS"`
InsecureSkipVerify bool `json:"insecureSkipVerify"`
AuthMethod string `json:"authMethod"`
}
// AppSettings holds the main UI settings and Fail2ban configuration
@@ -413,12 +415,14 @@ func applyAppSettingsRecordLocked(rec storage.AppSettingsRecord) {
currentSettings.Banaction = rec.Banaction
currentSettings.BanactionAllports = rec.BanactionAllports
currentSettings.SMTP = SMTPSettings{
Host: rec.SMTPHost,
Port: rec.SMTPPort,
Username: rec.SMTPUsername,
Password: rec.SMTPPassword,
From: rec.SMTPFrom,
UseTLS: rec.SMTPUseTLS,
Host: rec.SMTPHost,
Port: rec.SMTPPort,
Username: rec.SMTPUsername,
Password: rec.SMTPPassword,
From: rec.SMTPFrom,
UseTLS: rec.SMTPUseTLS,
InsecureSkipVerify: rec.SMTPInsecureSkipVerify,
AuthMethod: rec.SMTPAuthMethod,
}
if rec.AlertCountriesJSON != "" {
@@ -504,12 +508,14 @@ func toAppSettingsRecordLocked() (storage.AppSettingsRecord, error) {
EmailAlertsForBans: currentSettings.EmailAlertsForBans,
EmailAlertsForUnbans: currentSettings.EmailAlertsForUnbans,
// SMTP settings
SMTPHost: currentSettings.SMTP.Host,
SMTPPort: currentSettings.SMTP.Port,
SMTPUsername: currentSettings.SMTP.Username,
SMTPPassword: currentSettings.SMTP.Password,
SMTPFrom: currentSettings.SMTP.From,
SMTPUseTLS: currentSettings.SMTP.UseTLS,
SMTPHost: currentSettings.SMTP.Host,
SMTPPort: currentSettings.SMTP.Port,
SMTPUsername: currentSettings.SMTP.Username,
SMTPPassword: currentSettings.SMTP.Password,
SMTPFrom: currentSettings.SMTP.From,
SMTPUseTLS: currentSettings.SMTP.UseTLS,
SMTPInsecureSkipVerify: currentSettings.SMTP.InsecureSkipVerify,
SMTPAuthMethod: currentSettings.SMTP.AuthMethod,
// Fail2Ban DEFAULT settings
BantimeIncrement: currentSettings.BantimeIncrement,
DefaultJailEnable: currentSettings.DefaultJailEnable,
@@ -654,6 +660,9 @@ func setDefaultsLocked() {
if !currentSettings.SMTP.UseTLS {
currentSettings.SMTP.UseTLS = true
}
if currentSettings.SMTP.AuthMethod == "" {
currentSettings.SMTP.AuthMethod = "auto"
}
if len(currentSettings.IgnoreIPs) == 0 {
currentSettings.IgnoreIPs = []string{"127.0.0.1/8", "::1"}
}

View File

@@ -142,6 +142,16 @@
"settings.smtp_host": "SMTP-Host",
"settings.smtp_host_placeholder": "z.B. smtp.gmail.com",
"settings.smtp_port": "SMTP-Port",
"settings.smtp_port_placeholder": "587",
"settings.smtp_port_hint": "Häufige Ports: 25 (unverschlüsselt), 587 (STARTTLS), 465 (SMTPS), 2525 (alternatives STARTTLS)",
"settings.smtp_auth_method": "Authentifizierungsmethode",
"settings.smtp_auth_method_auto": "Auto (LOGIN bevorzugt)",
"settings.smtp_auth_method_login": "LOGIN",
"settings.smtp_auth_method_plain": "PLAIN",
"settings.smtp_auth_method_cram_md5": "CRAM-MD5",
"settings.smtp_auth_method_hint": "LOGIN wird für Office365/Gmail empfohlen. PLAIN ist die Standard-SMTP-Authentifizierung. CRAM-MD5 ist challenge-response-basiert.",
"settings.smtp_insecure_skip_verify": "TLS-Zertifikatsüberprüfung überspringen",
"settings.smtp_insecure_skip_verify_warning": "⚠️ Nicht für Produktion empfohlen",
"settings.smtp_username": "SMTP-Benutzername",
"settings.smtp_username_placeholder": "z.B. user@example.com",
"settings.smtp_password": "SMTP-Passwort",
@@ -150,6 +160,7 @@
"settings.smtp_sender_placeholder": "noreply@swissmakers.ch",
"settings.smtp_tls": "TLS verwenden (empfohlen)",
"settings.send_test_email": "Test-E-Mail senden",
"settings.send_test_email_hint": "⚠️ Bitte speichern Sie zuerst Ihre SMTP-Einstellungen, bevor Sie eine Test-E-Mail senden.",
"settings.fail2ban": "Globale Standard-Fail2Ban-Konfigurationen",
"settings.fail2ban.description": "Diese Einstellungen werden auf allen aktivierten Fail2Ban-Servern angewendet und in deren jail.local [DEFAULT]-Abschnitt gespeichert.",
"settings.enable_bantime_increment": "Bantime-Inkrement aktivieren",

View File

@@ -142,6 +142,16 @@
"settings.smtp_host": "SMTP-Host",
"settings.smtp_host_placeholder": "z.B. smtp.gmail.com",
"settings.smtp_port": "SMTP-Port",
"settings.smtp_port_placeholder": "587",
"settings.smtp_port_hint": "Hüfigi Ports: 25 (unverschlüsselt), 587 (STARTTLS), 465 (SMTPS), 2525 (alternativs STARTTLS)",
"settings.smtp_auth_method": "Authentifizierungsmethode",
"settings.smtp_auth_method_auto": "Auto (LOGIN bevorzugt)",
"settings.smtp_auth_method_login": "LOGIN",
"settings.smtp_auth_method_plain": "PLAIN",
"settings.smtp_auth_method_cram_md5": "CRAM-MD5",
"settings.smtp_auth_method_hint": "LOGIN wird für Office365/Gmail empfohle. PLAIN isch d Standard-SMTP-Authentifizierung. CRAM-MD5 isch challenge-response-basiert.",
"settings.smtp_insecure_skip_verify": "TLS-Zertifikatsüberprüfig überspringe",
"settings.smtp_insecure_skip_verify_warning": "⚠️ Nid fürd Produktion empfohle",
"settings.smtp_username": "SMTP-Benutzername",
"settings.smtp_username_placeholder": "z.B. user@example.com",
"settings.smtp_password": "SMTP-Passwort",
@@ -150,6 +160,7 @@
"settings.smtp_sender_placeholder": "noreply@swissmakers.ch",
"settings.smtp_tls": "TLS bruuche (empfohlen)",
"settings.send_test_email": "Test-Email schicke",
"settings.send_test_email_hint": "⚠️ Bitte speichere zersch dini SMTP-Iistellige, bevor du e Test-Email schicksch.",
"settings.fail2ban": "Globale Standard-Fail2Ban-Konfiguratione",
"settings.fail2ban.description": "Die Einstellige werde uf alli aktivierte Fail2Ban-Server aagwändet und i däre jail.local [DEFAULT]-Abschnitt gspeicheret.",
"settings.enable_bantime_increment": "Bantime-Inkrement aktivierä",

View File

@@ -142,6 +142,16 @@
"settings.smtp_host": "SMTP Host",
"settings.smtp_host_placeholder": "e.g., smtp.gmail.com",
"settings.smtp_port": "SMTP Port",
"settings.smtp_port_placeholder": "587",
"settings.smtp_port_hint": "Common ports: 25 (plain), 587 (STARTTLS), 465 (SMTPS), 2525 (alternative STARTTLS)",
"settings.smtp_auth_method": "Authentication Method",
"settings.smtp_auth_method_auto": "Auto (LOGIN preferred)",
"settings.smtp_auth_method_login": "LOGIN",
"settings.smtp_auth_method_plain": "PLAIN",
"settings.smtp_auth_method_cram_md5": "CRAM-MD5",
"settings.smtp_auth_method_hint": "LOGIN is recommended for Office365/Gmail. PLAIN is standard SMTP auth. CRAM-MD5 is challenge-response based.",
"settings.smtp_insecure_skip_verify": "Skip TLS Certificate Verification",
"settings.smtp_insecure_skip_verify_warning": "⚠️ Not recommended for production",
"settings.smtp_username": "SMTP Username",
"settings.smtp_username_placeholder": "e.g., user@example.com",
"settings.smtp_password": "SMTP Password",
@@ -150,6 +160,7 @@
"settings.smtp_sender_placeholder": "noreply@swissmakers.ch",
"settings.smtp_tls": "Use TLS (Recommended)",
"settings.send_test_email": "Send Test Email",
"settings.send_test_email_hint": "⚠️ Please save your SMTP settings first before sending a test email.",
"settings.fail2ban": "Global Default Fail2Ban Configurations",
"settings.fail2ban.description": "These settings will be applied to all enabled Fail2Ban servers and stored in their jail.local [DEFAULT] section.",
"settings.enable_bantime_increment": "Enable Bantime Increment",

View File

@@ -142,6 +142,16 @@
"settings.smtp_host": "Host SMTP",
"settings.smtp_host_placeholder": "p.ej., smtp.gmail.com",
"settings.smtp_port": "Puerto SMTP",
"settings.smtp_port_placeholder": "587",
"settings.smtp_port_hint": "Puertos comunes: 25 (sin cifrar), 587 (STARTTLS), 465 (SMTPS), 2525 (STARTTLS alternativo)",
"settings.smtp_auth_method": "Método de Autenticación",
"settings.smtp_auth_method_auto": "Auto (LOGIN preferido)",
"settings.smtp_auth_method_login": "LOGIN",
"settings.smtp_auth_method_plain": "PLAIN",
"settings.smtp_auth_method_cram_md5": "CRAM-MD5",
"settings.smtp_auth_method_hint": "LOGIN se recomienda para Office365/Gmail. PLAIN es la autenticación SMTP estándar. CRAM-MD5 está basado en challenge-response.",
"settings.smtp_insecure_skip_verify": "Omitir Verificación de Certificado TLS",
"settings.smtp_insecure_skip_verify_warning": "⚠️ No recomendado para producción",
"settings.smtp_username": "Nombre de usuario SMTP",
"settings.smtp_username_placeholder": "p.ej., usuario@example.com",
"settings.smtp_password": "Contraseña SMTP",
@@ -150,6 +160,7 @@
"settings.smtp_sender_placeholder": "noreply@swissmakers.ch",
"settings.smtp_tls": "Usar TLS (recomendado)",
"settings.send_test_email": "Enviar correo de prueba",
"settings.send_test_email_hint": "⚠️ Por favor, guarde primero su configuración SMTP antes de enviar un correo de prueba.",
"settings.fail2ban": "Configuraciones Globales Predeterminadas de Fail2Ban",
"settings.fail2ban.description": "Estas configuraciones se aplicarán a todos los servidores Fail2Ban habilitados y se almacenarán en su sección [DEFAULT] de jail.local.",
"settings.enable_bantime_increment": "Habilitar incremento de Bantime",

View File

@@ -142,6 +142,16 @@
"settings.smtp_host": "Hôte SMTP",
"settings.smtp_host_placeholder": "par exemple, smtp.gmail.com",
"settings.smtp_port": "Port SMTP",
"settings.smtp_port_placeholder": "587",
"settings.smtp_port_hint": "Ports communs: 25 (non chiffré), 587 (STARTTLS), 465 (SMTPS), 2525 (STARTTLS alternatif)",
"settings.smtp_auth_method": "Méthode d'Authentification",
"settings.smtp_auth_method_auto": "Auto (LOGIN préféré)",
"settings.smtp_auth_method_login": "LOGIN",
"settings.smtp_auth_method_plain": "PLAIN",
"settings.smtp_auth_method_cram_md5": "CRAM-MD5",
"settings.smtp_auth_method_hint": "LOGIN est recommandé pour Office365/Gmail. PLAIN est l'authentification SMTP standard. CRAM-MD5 est basé sur challenge-response.",
"settings.smtp_insecure_skip_verify": "Ignorer la Vérification du Certificat TLS",
"settings.smtp_insecure_skip_verify_warning": "⚠️ Non recommandé pour la production",
"settings.smtp_username": "Nom d'utilisateur SMTP",
"settings.smtp_username_placeholder": "par exemple, utilisateur@example.com",
"settings.smtp_password": "Mot de passe SMTP",
@@ -150,6 +160,7 @@
"settings.smtp_sender_placeholder": "noreply@swissmakers.ch",
"settings.smtp_tls": "Utiliser TLS (recommandé)",
"settings.send_test_email": "Envoyer un email de test",
"settings.send_test_email_hint": "⚠️ Veuillez d'abord enregistrer vos paramètres SMTP avant d'envoyer un email de test.",
"settings.fail2ban": "Configurations Globales par Défaut de Fail2Ban",
"settings.fail2ban.description": "Ces paramètres seront appliqués à tous les serveurs Fail2Ban activés et stockés dans leur section [DEFAULT] de jail.local.",
"settings.enable_bantime_increment": "Activer l'incrémentation du Bantime",

View File

@@ -142,6 +142,16 @@
"settings.smtp_host": "Host SMTP",
"settings.smtp_host_placeholder": "es. smtp.gmail.com",
"settings.smtp_port": "Porta SMTP",
"settings.smtp_port_placeholder": "587",
"settings.smtp_port_hint": "Porte comuni: 25 (non crittografato), 587 (STARTTLS), 465 (SMTPS), 2525 (STARTTLS alternativo)",
"settings.smtp_auth_method": "Metodo di Autenticazione",
"settings.smtp_auth_method_auto": "Auto (LOGIN preferito)",
"settings.smtp_auth_method_login": "LOGIN",
"settings.smtp_auth_method_plain": "PLAIN",
"settings.smtp_auth_method_cram_md5": "CRAM-MD5",
"settings.smtp_auth_method_hint": "LOGIN è raccomandato per Office365/Gmail. PLAIN è l'autenticazione SMTP standard. CRAM-MD5 è basato su challenge-response.",
"settings.smtp_insecure_skip_verify": "Ignora Verifica Certificato TLS",
"settings.smtp_insecure_skip_verify_warning": "⚠️ Non raccomandato per la produzione",
"settings.smtp_username": "Nome utente SMTP",
"settings.smtp_username_placeholder": "es. utente@example.com",
"settings.smtp_password": "Password SMTP",
@@ -150,6 +160,7 @@
"settings.smtp_sender_placeholder": "noreply@swissmakers.ch",
"settings.smtp_tls": "Usa TLS (raccomandato)",
"settings.send_test_email": "Invia email di test",
"settings.send_test_email_hint": "⚠️ Si prega di salvare prima le impostazioni SMTP prima di inviare un'email di test.",
"settings.fail2ban": "Configurazioni Globali Predefinite di Fail2Ban",
"settings.fail2ban.description": "Queste impostazioni verranno applicate a tutti i server Fail2Ban abilitati e memorizzate nella loro sezione [DEFAULT] di jail.local.",
"settings.enable_bantime_increment": "Abilita incremento del Bantime",

View File

@@ -63,12 +63,14 @@ type AppSettingsRecord struct {
// Console output settings
ConsoleOutput bool
// SMTP settings
SMTPHost string
SMTPPort int
SMTPUsername string
SMTPPassword string
SMTPFrom string
SMTPUseTLS bool
SMTPHost string
SMTPPort int
SMTPUsername string
SMTPPassword string
SMTPFrom string
SMTPUseTLS bool
SMTPInsecureSkipVerify bool
SMTPAuthMethod string
// Fail2Ban DEFAULT settings
BantimeIncrement bool
DefaultJailEnable bool
@@ -192,17 +194,17 @@ func GetAppSettings(ctx context.Context) (AppSettingsRecord, bool, error) {
}
row := db.QueryRowContext(ctx, `
SELECT language, port, debug, restart_needed, callback_url, callback_secret, alert_countries, email_alerts_for_bans, email_alerts_for_unbans, smtp_host, smtp_port, smtp_username, smtp_password, smtp_from, smtp_use_tls, bantime_increment, default_jail_enable, ignore_ip, bantime, findtime, maxretry, destemail, banaction, banaction_allports, advanced_actions, geoip_provider, geoip_database_path, max_log_lines, console_output
SELECT language, port, debug, restart_needed, callback_url, callback_secret, alert_countries, email_alerts_for_bans, email_alerts_for_unbans, smtp_host, smtp_port, smtp_username, smtp_password, smtp_from, smtp_use_tls, bantime_increment, default_jail_enable, ignore_ip, bantime, findtime, maxretry, destemail, banaction, banaction_allports, advanced_actions, geoip_provider, geoip_database_path, max_log_lines, console_output, smtp_insecure_skip_verify, smtp_auth_method
FROM app_settings
WHERE id = 1`)
var (
lang, callback, callbackSecret, alerts, smtpHost, smtpUser, smtpPass, smtpFrom, ignoreIP, bantime, findtime, destemail, banaction, banactionAllports, advancedActions, geoipProvider, geoipDatabasePath sql.NullString
port, smtpPort, maxretry, maxLogLines sql.NullInt64
debug, restartNeeded, smtpTLS, bantimeInc, defaultJailEn, emailAlertsForBans, emailAlertsForUnbans, consoleOutput sql.NullInt64
lang, callback, callbackSecret, alerts, smtpHost, smtpUser, smtpPass, smtpFrom, ignoreIP, bantime, findtime, destemail, banaction, banactionAllports, advancedActions, geoipProvider, geoipDatabasePath, smtpAuthMethod sql.NullString
port, smtpPort, maxretry, maxLogLines sql.NullInt64
debug, restartNeeded, smtpTLS, bantimeInc, defaultJailEn, emailAlertsForBans, emailAlertsForUnbans, consoleOutput, smtpInsecureSkipVerify sql.NullInt64
)
err := row.Scan(&lang, &port, &debug, &restartNeeded, &callback, &callbackSecret, &alerts, &emailAlertsForBans, &emailAlertsForUnbans, &smtpHost, &smtpPort, &smtpUser, &smtpPass, &smtpFrom, &smtpTLS, &bantimeInc, &defaultJailEn, &ignoreIP, &bantime, &findtime, &maxretry, &destemail, &banaction, &banactionAllports, &advancedActions, &geoipProvider, &geoipDatabasePath, &maxLogLines, &consoleOutput)
err := row.Scan(&lang, &port, &debug, &restartNeeded, &callback, &callbackSecret, &alerts, &emailAlertsForBans, &emailAlertsForUnbans, &smtpHost, &smtpPort, &smtpUser, &smtpPass, &smtpFrom, &smtpTLS, &bantimeInc, &defaultJailEn, &ignoreIP, &bantime, &findtime, &maxretry, &destemail, &banaction, &banactionAllports, &advancedActions, &geoipProvider, &geoipDatabasePath, &maxLogLines, &consoleOutput, &smtpInsecureSkipVerify, &smtpAuthMethod)
if errors.Is(err, sql.ErrNoRows) {
return AppSettingsRecord{}, false, nil
}
@@ -224,12 +226,14 @@ WHERE id = 1`)
EmailAlertsForBans: intToBool(intFromNull(emailAlertsForBans)),
EmailAlertsForUnbans: intToBool(intFromNull(emailAlertsForUnbans)),
// SMTP settings
SMTPHost: stringFromNull(smtpHost),
SMTPPort: intFromNull(smtpPort),
SMTPUsername: stringFromNull(smtpUser),
SMTPPassword: stringFromNull(smtpPass),
SMTPFrom: stringFromNull(smtpFrom),
SMTPUseTLS: intToBool(intFromNull(smtpTLS)),
SMTPHost: stringFromNull(smtpHost),
SMTPPort: intFromNull(smtpPort),
SMTPUsername: stringFromNull(smtpUser),
SMTPPassword: stringFromNull(smtpPass),
SMTPFrom: stringFromNull(smtpFrom),
SMTPUseTLS: intToBool(intFromNull(smtpTLS)),
SMTPInsecureSkipVerify: intToBool(intFromNull(smtpInsecureSkipVerify)),
SMTPAuthMethod: stringFromNull(smtpAuthMethod),
// Fail2Ban DEFAULT settings
BantimeIncrement: intToBool(intFromNull(bantimeInc)),
DefaultJailEnable: intToBool(intFromNull(defaultJailEn)),
@@ -258,9 +262,9 @@ func SaveAppSettings(ctx context.Context, rec AppSettingsRecord) error {
}
_, err := db.ExecContext(ctx, `
INSERT INTO app_settings (
id, language, port, debug, restart_needed, callback_url, callback_secret, alert_countries, email_alerts_for_bans, email_alerts_for_unbans, smtp_host, smtp_port, smtp_username, smtp_password, smtp_from, smtp_use_tls, bantime_increment, default_jail_enable, ignore_ip, bantime, findtime, maxretry, destemail, banaction, banaction_allports, advanced_actions, geoip_provider, geoip_database_path, max_log_lines, console_output
id, language, port, debug, restart_needed, callback_url, callback_secret, alert_countries, email_alerts_for_bans, email_alerts_for_unbans, smtp_host, smtp_port, smtp_username, smtp_password, smtp_from, smtp_use_tls, bantime_increment, default_jail_enable, ignore_ip, bantime, findtime, maxretry, destemail, banaction, banaction_allports, advanced_actions, geoip_provider, geoip_database_path, max_log_lines, console_output, smtp_insecure_skip_verify, smtp_auth_method
) VALUES (
1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
) ON CONFLICT(id) DO UPDATE SET
language = excluded.language,
port = excluded.port,
@@ -290,7 +294,9 @@ INSERT INTO app_settings (
geoip_provider = excluded.geoip_provider,
geoip_database_path = excluded.geoip_database_path,
max_log_lines = excluded.max_log_lines,
console_output = excluded.console_output
console_output = excluded.console_output,
smtp_insecure_skip_verify = excluded.smtp_insecure_skip_verify,
smtp_auth_method = excluded.smtp_auth_method
`, rec.Language,
rec.Port,
boolToInt(rec.Debug),
@@ -319,7 +325,9 @@ INSERT INTO app_settings (
rec.GeoIPProvider,
rec.GeoIPDatabasePath,
rec.MaxLogLines,
boolToInt(rec.ConsoleOutput))
boolToInt(rec.ConsoleOutput),
boolToInt(rec.SMTPInsecureSkipVerify),
rec.SMTPAuthMethod)
return err
}
@@ -940,13 +948,25 @@ CREATE INDEX IF NOT EXISTS idx_perm_blocks_status ON permanent_blocks(status);
// return err
// }
// }
// Migration: Add console_output column if it doesn't exist
if _, err := db.ExecContext(ctx, `ALTER TABLE app_settings ADD COLUMN console_output INTEGER DEFAULT 0`); err != nil {
if err != nil && !strings.Contains(strings.ToLower(err.Error()), "duplicate column name") {
return err
}
}
// Migration: Add new SMTP columns if they don't exist
if _, err := db.ExecContext(ctx, `ALTER TABLE app_settings ADD COLUMN smtp_insecure_skip_verify INTEGER DEFAULT 0`); err != nil {
if err != nil && !strings.Contains(strings.ToLower(err.Error()), "duplicate column name") {
return err
}
}
if _, err := db.ExecContext(ctx, `ALTER TABLE app_settings ADD COLUMN smtp_auth_method TEXT DEFAULT 'auto'`); err != nil {
if err != nil && !strings.Contains(strings.ToLower(err.Error()), "duplicate column name") {
return err
}
}
_ = strings.Contains // Keep strings import for migration example above
return nil