Skip to content

Commit dec2158

Browse files
authored
Improve trusted_ca_fingerprint warnings and fix tests (#285)
* Fix issues with current tests * Improve warnings for ca_trusted_fingerprint misconfigurations * Small updates to language and auto-pr feedback
1 parent 60ea3e0 commit dec2158

File tree

3 files changed

+75
-12
lines changed

3 files changed

+75
-12
lines changed

transport/tlscommon/tls_config.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,17 +183,24 @@ func trustRootCA(cfg *TLSConfig, peerCerts []*x509.Certificate) error {
183183
return fmt.Errorf("decode 'ca_trusted_fingerprint': %w", err)
184184
}
185185

186+
foundCADigests := []string{}
187+
186188
for _, cert := range peerCerts {
189+
187190
// Compute digest for each certificate.
188191
digest := sha256.Sum256(cert.Raw)
189192

193+
if cert.IsCA {
194+
foundCADigests = append(foundCADigests, hex.EncodeToString(digest[:]))
195+
}
196+
190197
if !bytes.Equal(digest[0:], fingerprint) {
191198
continue
192199
}
193200

194201
// Make sure the fingerprint matches a CA certificate
195202
if !cert.IsCA {
196-
logger.Info("Certificate matching 'ca_trusted_fingerprint' found, but is not a CA certificate")
203+
logger.Warn("Certificate matching 'ca_trusted_fingerprint' found, but it is not a CA certificate. 'ca_trusted_fingerprint' can only be used to trust CA certificates.")
197204
continue
198205
}
199206

@@ -206,7 +213,13 @@ func trustRootCA(cfg *TLSConfig, peerCerts []*x509.Certificate) error {
206213
return nil
207214
}
208215

209-
logger.Warn("no CA certificate matching the fingerprint")
216+
// if we are here, we didn't find any CA certificate matching the fingerprint
217+
if len(foundCADigests) == 0 {
218+
logger.Warn("The remote server's certificate is presented without its certificate chain. Using 'ca_trusted_fingerprint' requires that the server presents a certificate chain that includes the certificate's issuing certificate authority.")
219+
} else {
220+
logger.Warnf("The provided 'ca_trusted_fingerprint': '%s' does not match the fingerprint of any Certificate Authority present in the server's certificate chain. Found the following CA fingerprints instead: %v", cfg.CATrustedFingerprint, foundCADigests)
221+
}
222+
210223
return nil
211224
}
212225

transport/tlscommon/tls_config_test.go

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@
1818
package tlscommon
1919

2020
import (
21+
"crypto/sha256"
2122
"crypto/tls"
2223
"crypto/x509"
24+
"encoding/hex"
2325
"errors"
2426
"net"
2527
"net/http"
@@ -29,6 +31,7 @@ import (
2931
"github.com/stretchr/testify/assert"
3032
"github.com/stretchr/testify/require"
3133

34+
"github.com/elastic/elastic-agent-libs/logp"
3235
"github.com/elastic/elastic-agent-libs/transport/tlscommontest"
3336
)
3437

@@ -191,37 +194,60 @@ func TestTrustRootCA(t *testing.T) {
191194
nonEmptyCertPool.AddCert(certs["wildcard"])
192195
nonEmptyCertPool.AddCert(certs["unknown_authority"])
193196

194-
fingerprint := tlscommontest.GetCertFingerprint(certs["ca"])
197+
certfingerprint := tlscommontest.GetCertFingerprint(certs["correct"])
198+
cafingerprint := tlscommontest.GetCertFingerprint(certs["ca"])
199+
200+
unknownAuthorityDigest := sha256.Sum256(certs["unknown_authority"].Raw)
201+
unknownAuthoritySha256 := hex.EncodeToString(unknownAuthorityDigest[:])
195202

196203
testCases := []struct {
197204
name string
198205
rootCAs *x509.CertPool
199206
caTrustedFingerprint string
200207
peerCerts []*x509.Certificate
208+
expectingWarnings []string
201209
expectingError bool
202210
expectedRootCAsLen int
203211
}{
204212
{
205213
name: "RootCA cert matches the fingerprint and is added to cfg.RootCAs",
206-
caTrustedFingerprint: fingerprint,
214+
caTrustedFingerprint: cafingerprint,
207215
peerCerts: []*x509.Certificate{certs["correct"], certs["ca"]},
208216
expectedRootCAsLen: 1,
209217
},
210218
{
211-
name: "RootCA cert doesn not matche the fingerprint and is not added to cfg.RootCAs",
212-
caTrustedFingerprint: fingerprint,
213-
peerCerts: []*x509.Certificate{certs["correct"], certs["ca"]},
219+
name: "RootCA cert doesn't match the fingerprint and is not added to cfg.RootCAs",
220+
caTrustedFingerprint: cafingerprint,
221+
peerCerts: []*x509.Certificate{certs["correct"], certs["unknown_authority"]},
222+
expectingWarnings: []string{"The provided 'ca_trusted_fingerprint': '" + cafingerprint + "' does not match the fingerprint of any Certificate Authority present in the server's certificate chain. Found the following CA fingerprints instead: [" + unknownAuthoritySha256 + "]"},
223+
expectedRootCAsLen: 0,
224+
},
225+
{
226+
name: "Peer cert does not include a CA Certificate and is not added to cfg.RootCAs",
227+
caTrustedFingerprint: cafingerprint,
228+
peerCerts: []*x509.Certificate{certs["correct"]},
229+
expectingWarnings: []string{"The remote server's certificate is presented without its certificate chain. Using 'ca_trusted_fingerprint' requires that the server presents a certificate chain that includes the certificate's issuing certificate authority."},
214230
expectedRootCAsLen: 0,
215231
},
232+
{
233+
name: "fingerprint matches peer cert instead of the CA Certificate and is not added to cfg.RootCAs",
234+
caTrustedFingerprint: certfingerprint,
235+
peerCerts: []*x509.Certificate{certs["correct"]},
236+
expectingWarnings: []string{
237+
"Certificate matching 'ca_trusted_fingerprint' found, but it is not a CA certificate. 'ca_trusted_fingerprint' can only be used to trust CA certificates.",
238+
"The remote server's certificate is presented without its certificate chain. Using 'ca_trusted_fingerprint' requires that the server presents a certificate chain that includes the certificate's issuing certificate authority.",
239+
},
240+
expectedRootCAsLen: 0,
241+
},
216242
{
217243
name: "non empty CertPool has the RootCA added",
218244
rootCAs: nonEmptyCertPool,
219-
caTrustedFingerprint: fingerprint,
245+
caTrustedFingerprint: cafingerprint,
220246
peerCerts: []*x509.Certificate{certs["correct"], certs["ca"]},
221247
expectedRootCAsLen: 3,
222248
},
223249
{
224-
name: "invalis HEX encoding",
250+
name: "invalid HEX encoding",
225251
caTrustedFingerprint: "INVALID ENCODING",
226252
expectedRootCAsLen: 0,
227253
expectingError: true,
@@ -234,6 +260,10 @@ func TestTrustRootCA(t *testing.T) {
234260
RootCAs: tc.rootCAs,
235261
CATrustedFingerprint: tc.caTrustedFingerprint,
236262
}
263+
264+
// Capture the logs
265+
_ = logp.DevelopmentSetup(logp.ToObserverOutput())
266+
237267
err := trustRootCA(&cfg, tc.peerCerts)
238268
if tc.expectingError && err == nil {
239269
t.Fatal("expecting an error when calling trustRootCA")
@@ -243,9 +273,29 @@ func TestTrustRootCA(t *testing.T) {
243273
t.Fatalf("did not expect an error calling trustRootCA: %v", err)
244274
}
245275

246-
if tc.expectedRootCAsLen != 0 {
276+
if len(tc.expectingWarnings) > 0 {
277+
warnings := logp.ObserverLogs().FilterLevelExact(logp.WarnLevel.ZapLevel()).TakeAll()
278+
if len(warnings) == 0 {
279+
t.Fatal("expecting a warning message")
280+
}
281+
if len(warnings) != len(tc.expectingWarnings) {
282+
t.Fatalf("expecting %d warning messages, got %d", len(tc.expectingWarnings), len(warnings))
283+
}
284+
285+
for i, expectedWarning := range tc.expectingWarnings {
286+
if got := warnings[i].Message; got != expectedWarning {
287+
t.Fatalf("expecting warning message to be '%s', got '%s'", expectedWarning, got)
288+
}
289+
}
290+
}
291+
292+
if tc.expectedRootCAsLen == 0 {
293+
if cfg.RootCAs != nil {
294+
t.Fatal("cfg.RootCAs should be nil")
295+
}
296+
} else {
247297
if cfg.RootCAs == nil {
248-
t.Fatal("cfg.RootCAs cannot be nil")
298+
t.Fatal("cfg.RootCAs should not be nil")
249299
}
250300

251301
// we want to know the number of certificates in the CertPool (RootCAs), as it is not

transport/tlscommontest/test_helper.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ func GenTestCerts(t *testing.T) map[string]*x509.Certificate {
8888
"unknown_authority": {
8989
ca: unknownCA,
9090
keyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
91-
isCA: false,
91+
isCA: true,
9292
dnsNames: []string{"localhost"},
9393
// IPV4 and IPV6
9494
ips: []net.IP{{127, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},

0 commit comments

Comments
 (0)