You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SonarQube通过SMTP+STARTTLS发送测试通知失败的解决方法

SonarQube通过SMTP+STARTTLS发送通知失败的解决方法

问题背景

在SonarQube中配置SMTP协议结合STARTTLS发送测试通知时失败,但不使用STARTTLS的SMTP测试可以成功发送。

SonarQube配置信息

SMTP主机: 1X.XXX.XX.X1
SMTP端口: 587
安全连接类型: starttls

已正确填写目标邮箱地址,SonarQube运行在Debian 11系统,SMTP服务器为MS Exchange,使用自签名证书且已安装到系统truststore。

失败日志片段(web.log)

2022.10.24 09:36:57 INFO  web[AYPp5oPhM9pKCPrzAA6Z][javax.mail] JavaMail version 1.6.2
2022.10.24 09:36:57 INFO  web[AYPp5oPhM9pKCPrzAA6Z][javax.mail] successfully loaded resource: /META-INF/javamail.default.address.map
2022.10.24 09:36:57 DEBUG web[AYPp5oPhM9pKCPrzAA6Z][javax.activation] MailcapCommandMap: createDataContentHandler for text/plain
2022.10.24 09:36:57 DEBUG web[AYPp5oPhM9pKCPrzAA6Z][javax.activation]   search DB #1
2022.10.24 09:36:57 DEBUG web[AYPp5oPhM9pKCPrzAA6Z][javax.activation]     got content-handler
2022.10.24 09:36:57 DEBUG web[AYPp5oPhM9pKCPrzAA6Z][javax.activation]       class com.sun.mail.handlers.text_plain
2022.10.24 09:36:57 DEBUG web[AYPp5oPhM9pKCPrzAA6Z][javax.mail] getProvider() returning javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle]
2022.10.24 09:36:57 DEBUG web[AYPp5oPhM9pKCPrzAA6Z][com.sun.mail.smtp] useEhlo true, useAuth false
2022.10.24 09:36:57 DEBUG web[AYPp5oPhM9pKCPrzAA6Z][com.sun.mail.smtp] trying to connect to host "1X.XXX.XX.X1", port 587, isSSL false
2022.10.24 09:36:57 DEBUG web[AYPp5oPhM9pKCPrzAA6Z][com.sun.mail.smtp] connected to host "1X.XXX.XX.X1", port: 587
2022.10.24 09:36:57 DEBUG web[AYPp5oPhM9pKCPrzAA6Z][com.sun.mail.smtp] Found extension "SIZE", arg "26214400"
2022.10.24 09:36:58 DEBUG web[AYPp5oPhM9pKCPrzAA6Z][com.sun.mail.smtp] Found extension "PIPELINING", arg ""
2022.10.24 09:36:58 DEBUG web[AYPp5oPhM9pKCPrzAA6Z][com.sun.mail.smtp] Found extension "DSN", arg ""
2022.10.24 09:36:58 DEBUG web[AYPp5oPhM9pKCPrzAA6Z][com.sun.mail.smtp] Found extension "ENHANCEDSTATUSCODES", arg ""
2022.10.24 09:36:58 DEBUG web[AYPp5oPhM9pKCPrzAA6Z][com.sun.mail.smtp] Found extension "STARTTLS", arg ""
2022.10.24 09:36:58 DEBUG web[AYPp5oPhM9pKCPrzAA6Z][com.sun.mail.smtp] Found extension "AUTH", arg "NTLM"
2022.10.24 09:36:58 DEBUG web[AYPp5oPhM9pKCPrzAA6Z][com.sun.mail.smtp] Found extension "8BITMIME", arg ""
2022.10.24 09:36:58 DEBUG web[AYPp5oPhM9pKCPrzAA6Z][com.sun.mail.smtp] Found extension "BINARYMIME", arg ""
2022.10.24 09:36:58 DEBUG web[AYPp5oPhM9pKCPrzAA6Z][com.sun.mail.smtp] Found extension "CHUNKING", arg ""
2022.10.24 09:36:58 DEBUG web[AYPp5oPhM9pKCPrzAA6Z][o.s.s.n.e.EmailNotificationChannel] Fail to send test email to xxxxxxx@xxxxx.xxx: {}
org.apache.commons.mail.EmailException: Sending the email to the following server failed : 1X.XXX.XX.X1:587
...
Caused by: javax.mail.MessagingException: Could not convert socket to TLS
...
Caused by: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
...
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
...
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

相关命令执行结果

  1. 直接连接SMTP端口:
$ echo | openssl s_client -connect 1X.XXX.XX.X1:587

返回:

CONNECTED(00000003)
140269928117568:error:1408F10B:SSL routines:ssl3_get_record:wrong version number:../ssl/record/ssl3_record.c:331:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 5 bytes and written 283 bytes
Verification: OK
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---
  1. 使用STARTTLS连接SMTP端口:
$ echo | openssl s_client -connect 1X.XXX.XX.X1:587 -starttls smtp

返回:

CONNECTED(00000003)
Can't use SSL_get_servername
depth=2 CN = CA-ROOT
verify return:1
depth=1 DC = local, DC = regulator, CN = CA-SUB
verify return:1
depth=0 C = PL, ST = Aaa, L = Bbb, O = Ccc, OU = Ddd, CN = [DOMAIN.NAME]
verify return:1
---
Certificate chain
 0 s:C = PL, ST = Aaa, L = Bbb, O = Ccc, OU = Ddd, CN = [DOMAIN.NAME]
   i:DC = local, DC = regulator, CN = CA-SUB
 1 s:DC = local, DC = regulator, CN = CA-SUB
   i:CN = CA-ROOT
---
Server certificate
-----BEGIN CERTIFICATE-----
[...CERTIFICATE...]
-----END CERTIFICATE-----
subject=C = PL, ST = Aaa, L = Bbb, O = Ccc, OU = Ddd, CN = [DOMAIN.NAME]

issuer=DC = local, DC = regulator, CN = CA-SUB

---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: RSA
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 3596 bytes and written 498 bytes
Verification: OK
---
New, TLSv1.2, Cipher is ECDHE-RSA-AES256-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-SHA384
    Session-ID: [...SESSION ID...]
    Session-ID-ctx:
    Master-Key: [...MASTER ID...]
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1666767236
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
    Extended master secret: yes
---
250 CHUNKING
DONE

解决步骤

从日志中的PKIX path building failed错误可以看出,问题出在JVM没有信任Exchange的自签名证书链。虽然证书已经安装到系统truststore,但SonarQube使用的是自身JVM的truststore,而非系统的,因此需要将完整的证书链导入到SonarQube所用JVM的信任库中。

具体操作:

  1. 导出完整证书链
    从openssl s_client -starttls smtp的输出中,复制完整的证书链(从-----BEGIN CERTIFICATE-----到-----END CERTIFICATE-----,包括所有层级的证书),保存为exchange-cert-chain.pem文件。

  2. 找到SonarQube使用的JVM路径
    执行以下命令查看SonarQube的JVM路径:

    sudo su - sonarqube -c "echo \$JAVA_HOME"
    

    如果没有设置JAVA_HOME,可以查看SonarQube启动脚本中的JVM路径,或者使用which java找到系统默认JVM。

  3. 导入证书到JVM truststore
    使用keytool命令将证书链导入到JVM的cacerts信任库中:

    sudo keytool -import -alias exchange-smtp -keystore $JAVA_HOME/lib/security/cacerts -file exchange-cert-chain.pem
    

    默认的cacerts密码是changeit,根据提示输入密码并确认信任该证书。

  4. 重启SonarQube服务

    sudo systemctl restart sonarqube
    
  5. 额外配置(可选,如果仍有问题)
    如果导入证书后还是失败,可以在SonarQube的配置文件sonar.properties中添加以下参数,强制指定使用的truststore:

    sonar.web.javaAdditionalOpts=-Djavax.net.ssl.trustStore=/path/to/your/cacerts -Djavax.net.ssl.trustStorePassword=changeit
    

    替换/path/to/your/cacerts为实际的JVM truststore路径,然后再次重启SonarQube。

另外,注意日志中显示useAuth false,如果Exchange服务器需要NTLM认证,可能还需要在SonarQube配置中添加SMTP用户名和密码,确保认证信息正确。


内容的提问来源于stack exchange,提问作者KChyl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 05:30:46