[feature] add SSL certificate verification toggle for email server (#4327)

This commit is contained in:
NekoPunch
2026-08-26 11:26:32 +08:00
committed by GitHub
parent c884f11995
commit b0d8516822
11 changed files with 66 additions and 0 deletions
@@ -98,6 +98,7 @@ public class EmailAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl
Properties props = sender.getJavaMailProperties(); Properties props = sender.getJavaMailProperties();
props.put("mail.smtp.ssl.enable", emailNoticeSenderConfig.isEmailSsl()); props.put("mail.smtp.ssl.enable", emailNoticeSenderConfig.isEmailSsl());
props.put("mail.smtp.starttls.enable", emailNoticeSenderConfig.isEmailStarttls()); props.put("mail.smtp.starttls.enable", emailNoticeSenderConfig.isEmailStarttls());
applySslCertVerify(props, emailNoticeSenderConfig.isEmailSslCertVerify());
fromUsername = emailNoticeSenderConfig.getEmailUsername(); fromUsername = emailNoticeSenderConfig.getEmailUsername();
useDatabase = true; useDatabase = true;
} }
@@ -111,6 +112,7 @@ public class EmailAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl
Properties props = sender.getJavaMailProperties(); Properties props = sender.getJavaMailProperties();
props.put("mail.smtp.ssl.enable", sslEnable); props.put("mail.smtp.ssl.enable", sslEnable);
props.put("mail.smtp.starttls.enable", starttlsEnable); props.put("mail.smtp.starttls.enable", starttlsEnable);
applySslCertVerify(props, true);
} }
} catch (Exception e) { } catch (Exception e) {
log.error("Type not found {}", e.getMessage()); log.error("Type not found {}", e.getMessage());
@@ -133,6 +135,17 @@ public class EmailAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl
} }
} }
// the sender is a singleton, so both branches must set the props to avoid stale state
private void applySslCertVerify(Properties props, boolean verify) {
if (verify) {
props.remove("mail.smtp.ssl.trust");
props.remove("mail.smtp.ssl.checkserveridentity");
} else {
props.put("mail.smtp.ssl.trust", "*");
props.put("mail.smtp.ssl.checkserveridentity", "false");
}
}
@Override @Override
public byte type() { public byte type() {
return 1; return 1;
@@ -17,6 +17,8 @@
package org.apache.hertzbeat.alert.notice.impl; package org.apache.hertzbeat.alert.notice.impl;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.lenient;
@@ -124,6 +126,40 @@ class EmailAlertNotifyHandlerImplTest {
verify(mailSender).send(any(MimeMessage.class)); verify(mailSender).send(any(MimeMessage.class));
} }
@Test
public void testSkipSslCertVerifyTrustsAllHosts() throws Exception {
Properties props = stubMailConfig(false);
emailAlertNotifyHandler.send(receiver, template, groupAlert);
assertEquals("*", props.get("mail.smtp.ssl.trust"));
assertEquals("false", props.get("mail.smtp.ssl.checkserveridentity"));
}
@Test
public void testEnableSslCertVerifyClearsStaleTrustProps() throws Exception {
Properties props = stubMailConfig(true);
props.put("mail.smtp.ssl.trust", "*");
props.put("mail.smtp.ssl.checkserveridentity", "false");
emailAlertNotifyHandler.send(receiver, template, groupAlert);
assertNull(props.get("mail.smtp.ssl.trust"));
assertNull(props.get("mail.smtp.ssl.checkserveridentity"));
}
private Properties stubMailConfig(boolean sslCertVerify) {
MailServerConfig config = new MailServerConfig();
config.setEmailHost("smtp.example.com");
config.setEmailPort(465);
config.setEmailUsername("sender@example.com");
config.setEmailPassword("password");
config.setEnable(true);
config.setEmailSslCertVerify(sslCertVerify);
when(generalConfigDao.findByType(any()))
.thenReturn(GeneralConfig.builder().content(JsonUtil.toJson(config)).build());
Properties props = new Properties();
when(mailSender.getJavaMailProperties()).thenReturn(props);
when(mailSender.createMimeMessage()).thenReturn(mimeMessage);
return props;
}
@Test @Test
public void testNotifyAlertFailure() { public void testNotifyAlertFailure() {
when(mailSender.createMimeMessage()).thenThrow(new RuntimeException("Test Error")); when(mailSender.createMimeMessage()).thenThrow(new RuntimeException("Test Error"));
@@ -56,5 +56,7 @@ public class MailServerConfig {
private boolean emailStarttls = false; private boolean emailStarttls = false;
private boolean emailSslCertVerify = true;
private boolean enable = true; private boolean enable = true;
} }
@@ -25,6 +25,7 @@ export class EmailNoticeSender {
emailPassword!: string; emailPassword!: string;
emailSsl: boolean = true; emailSsl: boolean = true;
emailStarttls: boolean = false; emailStarttls: boolean = false;
emailSslCertVerify: boolean = true;
enable!: boolean; enable!: boolean;
creator!: string; creator!: string;
modifier!: string; modifier!: string;
@@ -134,6 +134,14 @@
<nz-switch [(ngModel)]="emailSender.emailStarttls" required name="emailStarttls" id="emailStarttls"></nz-switch> <nz-switch [(ngModel)]="emailSender.emailStarttls" required name="emailStarttls" id="emailStarttls"></nz-switch>
</nz-form-control> </nz-form-control>
</nz-form-item> </nz-form-item>
<nz-form-item *ngIf="emailSender.emailSsl || emailSender.emailStarttls">
<nz-form-label nzSpan="7" nzFor="emailSslCertVerify" nzRequired="true">{{
'alert.notice.sender.mail.ssl-cert-verify' | i18n
}}</nz-form-label>
<nz-form-control nzSpan="12">
<nz-switch [(ngModel)]="emailSender.emailSslCertVerify" required name="emailSslCertVerify" id="emailSslCertVerify"></nz-switch>
</nz-form-control>
</nz-form-item>
<nz-form-item> <nz-form-item>
<nz-form-label nzSpan="7" nzFor="emailEnable" nzRequired="true">{{ 'common.enable' | i18n }}</nz-form-label> <nz-form-label nzSpan="7" nzFor="emailEnable" nzRequired="true">{{ 'common.enable' | i18n }}</nz-form-label>
<nz-form-control nzSpan="12"> <nz-form-control nzSpan="12">
+1
View File
@@ -147,6 +147,7 @@
"alert.notice.sender.mail.port": "Email Port", "alert.notice.sender.mail.port": "Email Port",
"alert.notice.sender.mail.ssl": "Enable SSL", "alert.notice.sender.mail.ssl": "Enable SSL",
"alert.notice.sender.mail.starttls": "Enable STARTTLS", "alert.notice.sender.mail.starttls": "Enable STARTTLS",
"alert.notice.sender.mail.ssl-cert-verify": "Verify SSL Certificate",
"alert.notice.sender.mail.username": "Email Account", "alert.notice.sender.mail.username": "Email Account",
"alert.notice.sender.sms.tencent.appId": "Tencent Sms AppId", "alert.notice.sender.sms.tencent.appId": "Tencent Sms AppId",
"alert.notice.sender.sms.tencent.secretId": "Tencent Sms SecretId", "alert.notice.sender.sms.tencent.secretId": "Tencent Sms SecretId",
+1
View File
@@ -146,6 +146,7 @@
"alert.notice.sender.mail.port": "メールポート", "alert.notice.sender.mail.port": "メールポート",
"alert.notice.sender.mail.ssl": "SSLを有効化", "alert.notice.sender.mail.ssl": "SSLを有効化",
"alert.notice.sender.mail.starttls": "STARTTLSを有効化", "alert.notice.sender.mail.starttls": "STARTTLSを有効化",
"alert.notice.sender.mail.ssl-cert-verify": "SSL証明書を検証",
"alert.notice.sender.mail.username": "メールアカウント", "alert.notice.sender.mail.username": "メールアカウント",
"alert.notice.sender.sms.tencent.appId": "Tencent Sms AppId", "alert.notice.sender.sms.tencent.appId": "Tencent Sms AppId",
"alert.notice.sender.sms.tencent.secretId": "Tencent Sms SecretId", "alert.notice.sender.sms.tencent.secretId": "Tencent Sms SecretId",
+1
View File
@@ -147,6 +147,7 @@
"alert.notice.sender.mail.port": "이메일 포트", "alert.notice.sender.mail.port": "이메일 포트",
"alert.notice.sender.mail.ssl": "SSL 활성화", "alert.notice.sender.mail.ssl": "SSL 활성화",
"alert.notice.sender.mail.starttls": "STARTTLS 활성화", "alert.notice.sender.mail.starttls": "STARTTLS 활성화",
"alert.notice.sender.mail.ssl-cert-verify": "SSL 인증서 검증",
"alert.notice.sender.mail.username": "이메일 계정", "alert.notice.sender.mail.username": "이메일 계정",
"alert.notice.sender.sms.tencent.appId": "Tencent SMS AppId", "alert.notice.sender.sms.tencent.appId": "Tencent SMS AppId",
"alert.notice.sender.sms.tencent.secretId": "Tencent SMS SecretId", "alert.notice.sender.sms.tencent.secretId": "Tencent SMS SecretId",
+1
View File
@@ -233,6 +233,7 @@
"alert.notice.sender.mail.port": "Porta do Email", "alert.notice.sender.mail.port": "Porta do Email",
"alert.notice.sender.mail.ssl": "Habilitar SSL", "alert.notice.sender.mail.ssl": "Habilitar SSL",
"alert.notice.sender.mail.starttls": "Habilitar STARTTLS", "alert.notice.sender.mail.starttls": "Habilitar STARTTLS",
"alert.notice.sender.mail.ssl-cert-verify": "Verificar Certificado SSL",
"alert.notice.sender.mail.enable": "Habilitar Configuração de Email", "alert.notice.sender.mail.enable": "Habilitar Configuração de Email",
"alert.notice.sender.sms.type": "Tipo de SMS", "alert.notice.sender.sms.type": "Tipo de SMS",
"alert.notice.sender.sms.type.tencent": "SMS Tencent", "alert.notice.sender.sms.type.tencent": "SMS Tencent",
+1
View File
@@ -147,6 +147,7 @@
"alert.notice.sender.mail.port": "邮箱端口", "alert.notice.sender.mail.port": "邮箱端口",
"alert.notice.sender.mail.ssl": "是否启用SSL", "alert.notice.sender.mail.ssl": "是否启用SSL",
"alert.notice.sender.mail.starttls": "是否启用STARTTLS", "alert.notice.sender.mail.starttls": "是否启用STARTTLS",
"alert.notice.sender.mail.ssl-cert-verify": "是否校验SSL证书",
"alert.notice.sender.mail.username": "邮箱账号", "alert.notice.sender.mail.username": "邮箱账号",
"alert.notice.sender.sms.tencent.appId": "腾讯短信AppId", "alert.notice.sender.sms.tencent.appId": "腾讯短信AppId",
"alert.notice.sender.sms.tencent.secretId": "腾讯短信SecretId", "alert.notice.sender.sms.tencent.secretId": "腾讯短信SecretId",
+1
View File
@@ -146,6 +146,7 @@
"alert.notice.sender.mail.port": "郵件端口", "alert.notice.sender.mail.port": "郵件端口",
"alert.notice.sender.mail.ssl": "是否啟用SSL", "alert.notice.sender.mail.ssl": "是否啟用SSL",
"alert.notice.sender.mail.starttls": "是否啟用STARTTLS", "alert.notice.sender.mail.starttls": "是否啟用STARTTLS",
"alert.notice.sender.mail.ssl-cert-verify": "是否校驗SSL證書",
"alert.notice.sender.mail.username": "郵件帳號", "alert.notice.sender.mail.username": "郵件帳號",
"alert.notice.sender.sms.tencent.appId": "騰訊短訊AppId", "alert.notice.sender.sms.tencent.appId": "騰訊短訊AppId",
"alert.notice.sender.sms.tencent.secretId": "騰訊短訊SecretId", "alert.notice.sender.sms.tencent.secretId": "騰訊短訊SecretId",