ntopng tls certchain incomplete

Started by dopey1620, May 15, 2025, 06:37:44 PM

Previous topic - Next topic
So not sure if I'm missing anything but I have a couple of lets encrypt certs managed by the acme plugin.  I have ntopng configured to use one of the certs and I just recently noticed (using curl) that the TLS cert chain is incomplete. The screenshot shows where I selected the acme generated cert (doctored it to show the web gui cert to not expose my internal info :))

The Let's Encrypt cert relies on an intermediate CA, and this configuration doesn't configure ntopng with the intermetdiate cert.  So tools that don't have lets encrypt's intermediate in their trust store fail to validate.  (easy to workaround, just add the intermediate cert to the trust store db of the client) but I'm wondering, am I missing something obvious?

Is there a way to configure the certificate to be full cert (including the cert chain) ?  I'm not seeing an option in the acme client to do this.

I use the exact same cert for the webgui ui (also lets encrypt generated).  This is set in System->Settings->Administration and that appears to be smart enough to know to pull the full cert chain.

Quote from: dopey1620 on May 15, 2025, 06:37:44 PM...
Is there a way to configure the certificate to be full cert (including the cert chain) ?  I'm not seeing an option in the acme client to do this.

I don't believe that's the way it works. The certificate is not expected to contain the chain (especially not the root which should be in the client store).
OTOH, during the TLS handshake, the server shall send the server certificate and should also send the intermediate's certificate.
When they don't do the latter, the client is provided with information (in the cert, in the form of AIA - Authority Information Access) to get the additional certificates.

You can test URLs here: https://www.ssllabs.com/ssltest/

It is supposed to work that way if you're dealing with intermediate certs.
Here's an example using https://letsencrypt.org/
$ openssl s_client -connect letsencrypt.org:443 --showcerts
Connecting to 2600:1f18:16e:df00::65
CONNECTED(00000003)
depth=2 C=US, O=Internet Security Research Group, CN=ISRG Root X1
verify return:1
depth=1 C=US, O=Let's Encrypt, CN=E5
verify return:1
depth=0 CN=letsencrypt.org
verify return:1
---
Certificate chain
 0 s:CN=letsencrypt.org
   i:C=US, O=Let's Encrypt, CN=E5
   a:PKEY: id-ecPublicKey, 256 (bit); sigalg: ecdsa-with-SHA384
   v:NotBefore: Apr  6 13:03:58 2025 GMT; NotAfter: Jul  5 13:03:57 2025 GMT
-----BEGIN CERTIFICATE-----
MIIEAzCCA4qgAwIBAgISBdBGoj4CNEX5P2CnVO1k1hlSMAoGCCqGSM49BAMDMDIx
CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQDEwJF
NTAeFw0yNTA0MDYxMzAzNThaFw0yNTA3MDUxMzAzNTdaMBoxGDAWBgNVBAMTD2xl
dHNlbmNyeXB0Lm9yZzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLCiTBYLHyAh
0QyHvAzrlMYlVSYFuSwL4/E53SW95JorcZNJ3kIYm1q/EzA3aSL18cerF0Jv+Xcu
xCLTbQjKErejggKWMIICkjAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0lBBYwFAYIKwYB
BQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFJDLArKzImH/
YBJENl+2XXKkBda9MB8GA1UdIwQYMBaAFJ8rX888IU+dBLftKyzExnCL0tcNMFUG
CCsGAQUFBwEBBEkwRzAhBggrBgEFBQcwAYYVaHR0cDovL2U1Lm8ubGVuY3Iub3Jn
MCIGCCsGAQUFBzAChhZodHRwOi8vZTUuaS5sZW5jci5vcmcvMG8GA1UdEQRoMGaC
CWxlbmNyLm9yZ4IPbGV0c2VuY3J5cHQuY29tgg9sZXRzZW5jcnlwdC5vcmeCDXd3
dy5sZW5jci5vcmeCE3d3dy5sZXRzZW5jcnlwdC5jb22CE3d3dy5sZXRzZW5jcnlw
dC5vcmcwEwYDVR0gBAwwCjAIBgZngQwBAgEwLQYDVR0fBCYwJDAioCCgHoYcaHR0
cDovL2U1LmMubGVuY3Iub3JnLzgzLmNybDCCAQUGCisGAQQB1nkCBAIEgfYEgfMA
8QB3AKLjCuRF772tm3447Udnd1PXgluElNcrXhssxLlQpEfnAAABlgto8QYAAAQD
AEgwRgIhALQT7pCiTqcuSgDcKOuI+n6HV4SVY4rOHHmARRQMu7MZAiEAv1K9yHLo
elIsYuYHEooLs4WAi89C7O3aS/eHpO/Sdh8AdgB9WR4S4XgqexxhZ3xe/fjQh1wU
oE6VnrkDL9kOjC55uAAAAZYLaPKhAAAEAwBHMEUCIQDns62KoMl/obfhXOBpWoxM
qCLQ+TIA6FGyQqLIFj4uIwIgRrgu9lgI9iEoPU8AC39IPxoAWLppPUjNMT0TCppm
5v0wCgYIKoZIzj0EAwMDZwAwZAIwLgRga5fycj6i7WqdSAw9P15cUjc6aJS8g1yN
GikhWD5EfbjfyuSQiwI7pm8M9RZLAjAc84d37tx1tlMwvcQPlraksxz6u+7dEhQU
yMAibftFFh5FLnrSbal2vCGr5BlIzII=
-----END CERTIFICATE-----
 1 s:C=US, O=Let's Encrypt, CN=E5
   i:C=US, O=Internet Security Research Group, CN=ISRG Root X1
   a:PKEY: id-ecPublicKey, 384 (bit); sigalg: RSA-SHA256
   v:NotBefore: Mar 13 00:00:00 2024 GMT; NotAfter: Mar 12 23:59:59 2027 GMT
-----BEGIN CERTIFICATE-----
MIIEVzCCAj+gAwIBAgIRAIOPbGPOsTmMYgZigxXJ/d4wDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjQwMzEzMDAwMDAw
WhcNMjcwMzEyMjM1OTU5WjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg
RW5jcnlwdDELMAkGA1UEAxMCRTUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNCzqK
a2GOtu/cX1jnxkJFVKtj9mZhSAouWXW0gQI3ULc/FnncmOyhKJdyIBwsz9V8UiBO
VHhbhBRrwJCuhezAUUE8Wod/Bk3U/mDR+mwt4X2VEIiiCFQPmRpM5uoKrNijgfgw
gfUwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD
ATASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBSfK1/PPCFPnQS37SssxMZw
i9LXDTAfBgNVHSMEGDAWgBR5tFnme7bl5AFzgAiIyBpY9umbbjAyBggrBgEFBQcB
AQQmMCQwIgYIKwYBBQUHMAKGFmh0dHA6Ly94MS5pLmxlbmNyLm9yZy8wEwYDVR0g
BAwwCjAIBgZngQwBAgEwJwYDVR0fBCAwHjAcoBqgGIYWaHR0cDovL3gxLmMubGVu
Y3Iub3JnLzANBgkqhkiG9w0BAQsFAAOCAgEAH3KdNEVCQdqk0LKyuNImTKdRJY1C
2uw2SJajuhqkyGPY8C+zzsufZ+mgnhnq1A2KVQOSykOEnUbx1cy637rBAihx97r+
bcwbZM6sTDIaEriR/PLk6LKs9Be0uoVxgOKDcpG9svD33J+G9Lcfv1K9luDmSTgG
6XNFIN5vfI5gs/lMPyojEMdIzK9blcl2/1vKxO8WGCcjvsQ1nJ/Pwt8LQZBfOFyV
XP8ubAp/au3dc4EKWG9MO5zcx1qT9+NXRGdVWxGvmBFRAajciMfXME1ZuGmk3/GO
koAM7ZkjZmleyokP1LGzmfJcUd9s7eeu1/9/eg5XlXd/55GtYjAM+C4DG5i7eaNq
cm2F+yxYIPt6cbbtYVNJCGfHWqHEQ4FYStUyFnv8sjyqU8ypgZaNJ9aVcWSICLOI
E1/Qv/7oKsnZCWJ926wU6RqG1OYPGOi1zuABhLw61cuPVDT28nQS/e6z95cJXq0e
K1BcaJ6fJZsmbjRgD5p3mvEf5vdQM7MCEvU0tHbsx2I5mHHJoABHb8KVBgWp/lcX
GWiWaeOyB7RP+OfDtvi2OsapxXiV7vNVs7fMlrRjY1joKaqmmycnBvAq14AEbtyL
sVfOS66B8apkeFX2NY4XPEYV4ZSCe8VHPrdrERk2wILG3T/EGmSIkCYVUMSnjmJd
VQD9F6Na/+zmXCc=
-----END CERTIFICATE-----

Note how their web site returns both the web server cert
 0 s:CN=letsencrypt.org
and the intermediate cert
 1 s:C=US, O=Let's Encrypt, CN=E5

This is what the ntopng port returns on my system
$ openssl s_client -connect <redacted>:3443 -showcerts
Connecting to <redacted>
CONNECTED(00000003)
Can't use SSL_get_servername
depth=0 CN=<redacted>
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN=<redacted>
verify error:num=21:unable to verify the first certificate
verify return:1
depth=0 CN=<redacted>
verify return:1
---
Certificate chain
 0 s:CN=<redacted>
   i:C=US, O=Let's Encrypt, CN=R11
   a:PKEY: rsaEncryption, 4096 (bit); sigalg: RSA-SHA256
   v:NotBefore: Apr  2 04:06:20 2025 GMT; NotAfter: Jul  1 04:06:19 2025 GMT

The intermediate chain is incomplete.

Quotethe certificate is not expected to contain the chain

you're right, the cert doesn't contain the chain.  However, the mechanism that the server loads the cert CAN include a file that contains both.  Looking at:
https://www.ntop.org/ntopng/securing-ntopng-with-ssl-and-lets-encrypt/
root@myntopng:/home/deri/ntopng # cat /etc/letsencrypt/live/myntopng.ntop.org/privkey.pem /etc/letsencrypt/live/myntopng.ntop.org/fullchain.pem > ./httpdocs/ssl/ntopng-cert.pem
Note that they recommend using fullchain.pem

# grep CERTIFICATE--- /usr/local/share/ntopng/httpdocs/ssl/ntopng-cert.pem
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----

The ntopng-cert.pem file that is created by opnsense only contains one cert.  It is missing the full chain which is necessary for ntopng to present a the cert fully.

Opened this issue
https://github.com/opnsense/plugins/issues/4707

Might be considered a feature request :)   We'll see.

In case I was not clear, it was never a certificate issuance problem on the let's encrypt side, as implied in the OP.
It is indeed a deficiency on the server side (the plug-in in this case).
FWIW, I wouldn't call the "full" chain because the root is not expected to be part of it.
Only the server cert and the intermediate CA are present in the example you provided...