mirror of
https://github.com/swissmakers/fail2ban-ui.git
synced 2026-04-17 05:53:15 +02:00
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:
@@ -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"}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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ä",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -2413,10 +2413,19 @@ func isLOTRModeActive(alertCountries []string) bool {
|
||||
func sendEmail(to, subject, body string, settings config.AppSettings) error {
|
||||
// Validate SMTP settings
|
||||
if settings.SMTP.Host == "" || settings.SMTP.Username == "" || settings.SMTP.Password == "" || settings.SMTP.From == "" {
|
||||
return errors.New("SMTP settings are incomplete. Please configure all required fields")
|
||||
err := errors.New("SMTP settings are incomplete. Please configure all required fields")
|
||||
log.Printf("❌ sendEmail validation failed: %v (Host: %q, Username: %q, From: %q)", err, settings.SMTP.Host, settings.SMTP.Username, settings.SMTP.From)
|
||||
return err
|
||||
}
|
||||
|
||||
// Format message with **correct HTML headers**
|
||||
// Validate port range
|
||||
if settings.SMTP.Port <= 0 || settings.SMTP.Port > 65535 {
|
||||
err := errors.New("SMTP port must be between 1 and 65535")
|
||||
log.Printf("❌ sendEmail validation failed: %v (Port: %d)", err, settings.SMTP.Port)
|
||||
return err
|
||||
}
|
||||
|
||||
// Format message with correct HTML headers
|
||||
message := fmt.Sprintf("From: %s\nTo: %s\nSubject: %s\n"+
|
||||
"MIME-Version: 1.0\nContent-Type: text/html; charset=\"UTF-8\"\n\n%s",
|
||||
settings.SMTP.From, to, subject, body)
|
||||
@@ -2425,60 +2434,91 @@ func sendEmail(to, subject, body string, settings config.AppSettings) error {
|
||||
// SMTP Connection Config
|
||||
smtpHost := settings.SMTP.Host
|
||||
smtpPort := settings.SMTP.Port
|
||||
auth := LoginAuth(settings.SMTP.Username, settings.SMTP.Password)
|
||||
smtpAddr := net.JoinHostPort(smtpHost, fmt.Sprintf("%d", smtpPort))
|
||||
|
||||
// **Choose Connection Type**
|
||||
switch smtpPort {
|
||||
case 465:
|
||||
// SMTPS (Implicit TLS) - Not supported at the moment.
|
||||
tlsConfig := &tls.Config{ServerName: smtpHost}
|
||||
// Determine TLS configuration
|
||||
tlsConfig := &tls.Config{
|
||||
ServerName: smtpHost,
|
||||
InsecureSkipVerify: settings.SMTP.InsecureSkipVerify,
|
||||
}
|
||||
|
||||
// Determine authentication method
|
||||
authMethod := settings.SMTP.AuthMethod
|
||||
if authMethod == "" {
|
||||
authMethod = "auto" // Default to auto if not set
|
||||
}
|
||||
auth, err := getSMTPAuth(settings.SMTP.Username, settings.SMTP.Password, authMethod, smtpHost)
|
||||
if err != nil {
|
||||
log.Printf("❌ sendEmail: failed to create SMTP auth (method: %q): %v", authMethod, err)
|
||||
return fmt.Errorf("failed to create SMTP auth: %w", err)
|
||||
}
|
||||
log.Printf("📧 sendEmail: Using SMTP auth method: %q, host: %s, port: %d, useTLS: %v, insecureSkipVerify: %v", authMethod, smtpHost, smtpPort, settings.SMTP.UseTLS, settings.SMTP.InsecureSkipVerify)
|
||||
|
||||
// Determine connection type based on port and UseTLS setting
|
||||
// Port 465 typically uses implicit TLS (SMTPS)
|
||||
// Port 587 typically uses STARTTLS
|
||||
// Other ports: use UseTLS setting to determine behavior
|
||||
useImplicitTLS := (smtpPort == 465) || (settings.SMTP.UseTLS && smtpPort != 587 && smtpPort != 25)
|
||||
useSTARTTLS := settings.SMTP.UseTLS && (smtpPort == 587 || (smtpPort != 465 && smtpPort != 25))
|
||||
|
||||
var client *smtp.Client
|
||||
|
||||
if useImplicitTLS {
|
||||
// SMTPS (Implicit TLS) - Connect directly with TLS
|
||||
conn, err := tls.Dial("tcp", smtpAddr, tlsConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect via TLS: %w", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
client, err := smtp.NewClient(conn, smtpHost)
|
||||
client, err = smtp.NewClient(conn, smtpHost)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create SMTP client: %w", err)
|
||||
}
|
||||
defer client.Quit()
|
||||
|
||||
if err := client.Auth(auth); err != nil {
|
||||
return fmt.Errorf("SMTP authentication failed: %w", err)
|
||||
}
|
||||
|
||||
return sendSMTPMessage(client, settings.SMTP.From, to, msg)
|
||||
|
||||
case 587:
|
||||
// STARTTLS (Explicit TLS)
|
||||
conn, err := net.Dial("tcp", smtpAddr)
|
||||
} else {
|
||||
// Plain connection (may upgrade to STARTTLS)
|
||||
conn, err := net.DialTimeout("tcp", smtpAddr, 30*time.Second)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect to SMTP server: %w", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
client, err := smtp.NewClient(conn, smtpHost)
|
||||
client, err = smtp.NewClient(conn, smtpHost)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create SMTP client: %w", err)
|
||||
}
|
||||
defer client.Quit()
|
||||
|
||||
// Start TLS Upgrade
|
||||
tlsConfig := &tls.Config{ServerName: smtpHost}
|
||||
if err := client.StartTLS(tlsConfig); err != nil {
|
||||
return fmt.Errorf("failed to start TLS: %w", err)
|
||||
// Upgrade to STARTTLS if requested
|
||||
if useSTARTTLS {
|
||||
if err := client.StartTLS(tlsConfig); err != nil {
|
||||
return fmt.Errorf("failed to start TLS: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := client.Auth(auth); err != nil {
|
||||
return fmt.Errorf("SMTP authentication failed: %w", err)
|
||||
}
|
||||
|
||||
return sendSMTPMessage(client, settings.SMTP.From, to, msg)
|
||||
}
|
||||
|
||||
return errors.New("unsupported SMTP port. Use 587 (STARTTLS) or 465 (SMTPS)")
|
||||
// Ensure client is closed
|
||||
defer func() {
|
||||
if client != nil {
|
||||
client.Quit()
|
||||
}
|
||||
}()
|
||||
|
||||
// Authenticate if credentials are provided
|
||||
if auth != nil {
|
||||
if err := client.Auth(auth); err != nil {
|
||||
log.Printf("❌ sendEmail: SMTP authentication failed: %v", err)
|
||||
return fmt.Errorf("SMTP authentication failed: %w", err)
|
||||
}
|
||||
log.Printf("📧 sendEmail: SMTP authentication successful")
|
||||
}
|
||||
|
||||
err = sendSMTPMessage(client, settings.SMTP.From, to, msg)
|
||||
if err != nil {
|
||||
log.Printf("❌ sendEmail: Failed to send message: %v", err)
|
||||
return err
|
||||
}
|
||||
log.Printf("📧 sendEmail: Successfully sent email to %s", to)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Helper Function to Send SMTP Message
|
||||
@@ -3104,8 +3144,37 @@ func TestEmailHandler(c *gin.Context) {
|
||||
}
|
||||
|
||||
// *******************************************************************
|
||||
// * Office365 LOGIN Authentication : *
|
||||
// * SMTP Authentication Methods : *
|
||||
// *******************************************************************
|
||||
|
||||
// getSMTPAuth returns the appropriate SMTP authentication mechanism
|
||||
// based on the authMethod parameter: "auto", "login", "plain", "cram-md5"
|
||||
func getSMTPAuth(username, password, authMethod, host string) (smtp.Auth, error) {
|
||||
if username == "" || password == "" {
|
||||
return nil, nil // No auth if credentials are empty
|
||||
}
|
||||
|
||||
// Normalize auth method
|
||||
authMethod = strings.ToLower(strings.TrimSpace(authMethod))
|
||||
if authMethod == "" || authMethod == "auto" {
|
||||
// Auto-detect: prefer LOGIN for Office365/Gmail, fallback to PLAIN
|
||||
authMethod = "login"
|
||||
}
|
||||
|
||||
switch authMethod {
|
||||
case "login":
|
||||
return LoginAuth(username, password), nil
|
||||
case "plain":
|
||||
return smtp.PlainAuth("", username, password, host), nil
|
||||
case "cram-md5":
|
||||
return smtp.CRAMMD5Auth(username, password), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported auth method: %s (supported: login, plain, cram-md5)", authMethod)
|
||||
}
|
||||
}
|
||||
|
||||
// LoginAuth implements the LOGIN authentication mechanism
|
||||
// Used by Office365, Gmail, and other providers that require LOGIN instead of PLAIN
|
||||
type loginAuth struct {
|
||||
username, password string
|
||||
}
|
||||
|
||||
@@ -27,7 +27,9 @@ function updateEmailFieldsState() {
|
||||
document.getElementById('smtpUsername'),
|
||||
document.getElementById('smtpPassword'),
|
||||
document.getElementById('smtpFrom'),
|
||||
document.getElementById('smtpAuthMethod'),
|
||||
document.getElementById('smtpUseTLS'),
|
||||
document.getElementById('smtpInsecureSkipVerify'),
|
||||
document.getElementById('sendTestEmailBtn')
|
||||
];
|
||||
|
||||
@@ -143,7 +145,9 @@ function loadSettings() {
|
||||
document.getElementById('smtpUsername').value = data.smtp.username || '';
|
||||
document.getElementById('smtpPassword').value = data.smtp.password || '';
|
||||
document.getElementById('smtpFrom').value = data.smtp.from || '';
|
||||
document.getElementById('smtpUseTLS').checked = data.smtp.useTLS || false;
|
||||
document.getElementById('smtpUseTLS').checked = data.smtp.useTLS !== undefined ? data.smtp.useTLS : true;
|
||||
document.getElementById('smtpInsecureSkipVerify').checked = data.smtp.insecureSkipVerify || false;
|
||||
document.getElementById('smtpAuthMethod').value = data.smtp.authMethod || 'auto';
|
||||
}
|
||||
|
||||
document.getElementById('bantimeIncrement').checked = data.bantimeIncrement || false;
|
||||
@@ -186,13 +190,22 @@ function saveSettings(event) {
|
||||
|
||||
showLoading(true);
|
||||
|
||||
const smtpPort = parseInt(document.getElementById('smtpPort').value, 10);
|
||||
if (isNaN(smtpPort) || smtpPort < 1 || smtpPort > 65535) {
|
||||
showToast('SMTP port must be between 1 and 65535', 'error');
|
||||
showLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const smtpSettings = {
|
||||
host: document.getElementById('smtpHost').value.trim(),
|
||||
port: parseInt(document.getElementById('smtpPort').value, 10) || 587,
|
||||
port: smtpPort,
|
||||
username: document.getElementById('smtpUsername').value.trim(),
|
||||
password: document.getElementById('smtpPassword').value.trim(),
|
||||
from: document.getElementById('smtpFrom').value.trim(),
|
||||
useTLS: document.getElementById('smtpUseTLS').checked,
|
||||
insecureSkipVerify: document.getElementById('smtpInsecureSkipVerify').checked,
|
||||
authMethod: document.getElementById('smtpAuthMethod').value || 'auto',
|
||||
};
|
||||
|
||||
const selectedCountries = Array.from(document.getElementById('alertCountries').selectedOptions).map(opt => opt.value);
|
||||
|
||||
@@ -757,10 +757,9 @@
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="smtpPort" class="block text-sm font-medium text-gray-700 mb-2" data-i18n="settings.smtp_port">SMTP Port</label>
|
||||
<select id="smtpPort" class="w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed">
|
||||
<option value="587" selected>587 (Recommended - STARTTLS)</option>
|
||||
<option value="465" disabled>465 (Not Supported)</option>
|
||||
</select>
|
||||
<input type="number" min="1" max="65535" class="w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed" id="smtpPort"
|
||||
data-i18n-placeholder="settings.smtp_port_placeholder" placeholder="587" value="587" required />
|
||||
<p class="mt-1 text-xs text-gray-500" data-i18n="settings.smtp_port_hint">Common ports: 25 (plain), 587 (STARTTLS), 465 (SMTPS), 2525 (alternative STARTTLS)</p>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="smtpUsername" class="block text-sm font-medium text-gray-700 mb-2" data-i18n="settings.smtp_username">SMTP Username</label>
|
||||
@@ -777,11 +776,29 @@
|
||||
<input type="email" class="w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed" id="smtpFrom"
|
||||
data-i18n-placeholder="settings.smtp_sender_placeholder" placeholder="noreply@swissmakers.ch" required />
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="smtpAuthMethod" class="block text-sm font-medium text-gray-700 mb-2" data-i18n="settings.smtp_auth_method">Authentication Method</label>
|
||||
<select id="smtpAuthMethod" class="w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed">
|
||||
<option value="auto" selected data-i18n="settings.smtp_auth_method_auto">Auto (LOGIN preferred)</option>
|
||||
<option value="login" data-i18n="settings.smtp_auth_method_login">LOGIN</option>
|
||||
<option value="plain" data-i18n="settings.smtp_auth_method_plain">PLAIN</option>
|
||||
<option value="cram-md5" data-i18n="settings.smtp_auth_method_cram_md5">CRAM-MD5</option>
|
||||
</select>
|
||||
<p class="mt-1 text-xs text-gray-500" data-i18n="settings.smtp_auth_method_hint">LOGIN is recommended for Office365/Gmail. PLAIN is standard SMTP auth. CRAM-MD5 is challenge-response based.</p>
|
||||
</div>
|
||||
<div class="flex items-center mb-4">
|
||||
<input type="checkbox" id="smtpUseTLS" class="h-4 w-7 text-blue-600 transition duration-150 ease-in-out disabled:opacity-50 disabled:cursor-not-allowed">
|
||||
<label for="smtpUseTLS" class="ml-2 block text-sm text-gray-700" data-i18n="settings.smtp_tls">Use TLS (Recommended)</label>
|
||||
</div>
|
||||
<button type="button" class="bg-gray-600 text-white px-4 py-2 rounded hover:bg-gray-700 transition-colors disabled:bg-gray-400 disabled:cursor-not-allowed" onclick="sendTestEmail()" id="sendTestEmailBtn" data-i18n="settings.send_test_email">Send Test Email</button>
|
||||
<div class="flex items-center mb-4">
|
||||
<input type="checkbox" id="smtpInsecureSkipVerify" class="h-4 w-7 text-blue-600 transition duration-150 ease-in-out disabled:opacity-50 disabled:cursor-not-allowed">
|
||||
<label for="smtpInsecureSkipVerify" class="ml-2 block text-sm text-gray-700" data-i18n="settings.smtp_insecure_skip_verify">Skip TLS Certificate Verification</label>
|
||||
<span class="ml-2 text-xs text-red-600" data-i18n="settings.smtp_insecure_skip_verify_warning">⚠️ Not recommended for production</span>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<button type="button" class="bg-gray-600 text-white px-4 py-2 rounded hover:bg-gray-700 transition-colors disabled:bg-gray-400 disabled:cursor-not-allowed" onclick="sendTestEmail()" id="sendTestEmailBtn" data-i18n="settings.send_test_email">Send Test Email</button>
|
||||
<p class="mt-2 text-xs text-amber-600" data-i18n="settings.send_test_email_hint">⚠️ Please save your SMTP settings first before sending a test email.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Fail2Ban Configuration Group -->
|
||||
|
||||
Reference in New Issue
Block a user