Environment:
- Ubuntu 22.04.5 LTS
- Pritunl 1.34.4681.89-0ubuntu1~jammy
- Pritunl embedded Python 3.12
- OpenSSL 3.0.2
- SMTP server: smtp.gmail.com:587
- STARTTLS enabled
After updating the server, Pritunl profile emails began failing during
STARTTLS with:
ssl.SSLCertVerificationError:
[SSL: CERTIFICATE_VERIFY_FAILED]
certificate verify failed: unable to get local issuer certificate
The failure occurs in pritunl/utils/mail.py at:
smtp_conn.starttls(context=context)
The SMTP SSL context is currently created with:
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
but the default CA certificates are not loaded afterward.
I reproduced the issue using Pritunl’s embedded Python interpreter.
This fails with the same certificate error:
import ssl
import smtplib
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.minimum_version = ssl.TLSVersion.TLSv1_2
smtp = smtplib.SMTP(“smtp.gmail.com”, 587)
smtp.ehlo()
smtp.starttls(context=ctx)
This succeeds when one line is added:
ctx.load_default_certs()
Complete successful test:
import ssl
import smtplib
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.load_default_certs()
ctx.minimum_version = ssl.TLSVersion.TLSv1_2
smtp = smtplib.SMTP(“smtp.gmail.com”, 587)
smtp.ehlo()
smtp.starttls(context=ctx)
smtp.ehlo()
smtp.quit()
Additional verification:
- openssl s_client validates smtp.gmail.com successfully
- Verify return code is 0
- /usr/lib/ssl/certs correctly points to /etc/ssl/certs
- CA certificates and OpenSSL packages are current
- No SSL_CERT_FILE or SSL_CERT_DIR overrides are set
- No stale or deleted libraries are mapped into the running process
- Pritunl is the current package version in the Jammy repository
The following CA store behavior was also observed:
Before load_default_certs():
{‘x509’: 0, ‘crl’: 0, ‘x509_ca’: 0}
After load_default_certs(), before the handshake:
{‘x509’: 0, ‘crl’: 0, ‘x509_ca’: 0}
After a successful SMTP TLS handshake:
{‘x509’: 1, ‘crl’: 0, ‘x509_ca’: 1}
This appears to be normal lazy loading from the hashed CA directory, but it
confirms that calling load_default_certs() configures the trust source needed
for verification.
Temporary workaround:
pritunl set app.email_skip_verify true
That allows email delivery, but disables certificate and hostname
verification, so it is not suitable as a permanent fix.
Would it be appropriate to change mail.py to either:
context = ssl.create_default_context()
or:
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.load_default_certs()
before applying the TLS minimum-version setting?