Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Heartbeat] Add Additional ECS tls.* fields #17687

Merged
merged 30 commits into from
Apr 27, 2020
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f48c3a3
[Heartbeat] Add Additional ECS tls.* fields
andrewvc Apr 13, 2020
ba10d51
Improve TLS versions API + add tests
andrewvc Apr 13, 2020
bfc0974
Improve testing and code structure
andrewvc Apr 13, 2020
b11e5c4
Improve fmt and docs
andrewvc Apr 13, 2020
1187c26
Add changelog
andrewvc Apr 13, 2020
2c65b20
Add TLS1.3 Ciphers
andrewvc Apr 13, 2020
b063621
Update tests
andrewvc Apr 13, 2020
1e061ab
Move TLS metadata to separate package to break cycle
andrewvc Apr 13, 2020
69f7fd1
Use new x509 ECS fields
andrewvc Apr 15, 2020
7b2801a
Better testing
andrewvc Apr 15, 2020
ee3c547
DSA key len
andrewvc Apr 15, 2020
3347a9a
Add pk exponent
andrewvc Apr 15, 2020
bfae428
Enrich x509 data with invalid certs
andrewvc Apr 15, 2020
b37947b
Add TLS Cert data for expired HTTP endpoints
andrewvc Apr 20, 2020
002eb09
Fix sort of imports
andrewvc Apr 20, 2020
90578c4
Only handle HTTP TLS errors on error
andrewvc Apr 20, 2020
1eb461b
Record metadata properly for expired certs on TCP
andrewvc Apr 21, 2020
abc5f24
Merge remote-tracking branch 'origin/master' into extra-tls-fields
andrewvc Apr 21, 2020
577f203
start work on expired cert tests
andrewvc Apr 21, 2020
0bb2a09
Add tls.server.x509 fields
andrewvc Apr 21, 2020
deb7d6b
Add HTTP test for expired cert
andrewvc Apr 23, 2020
f64650c
Add TCP tests for expired certs
andrewvc Apr 23, 2020
2d748b3
Merge remote-tracking branch 'origin/master' into extra-tls-fields
andrewvc Apr 23, 2020
7a54cbc
FMT
andrewvc Apr 24, 2020
e9ac63d
Searchable experiment
andrewvc Apr 24, 2020
3979fbf
Merge remote-tracking branch 'origin/master' into extra-tls-fields
andrewvc Apr 24, 2020
83cd709
Merge branch 'searchable-certs' into extra-tls-fields
andrewvc Apr 27, 2020
63a02a9
Support new wildcard field
andrewvc Apr 27, 2020
96cde8e
Merge remote-tracking branch 'origin/master' into extra-tls-fields
andrewvc Apr 27, 2020
5b6c08e
Fix cert authorities on windows
andrewvc Apr 27, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.next.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d
*Heartbeat*

- Allow a list of status codes for HTTP checks. {pull}15587[15587]

- Add additional ECS compatible fields for TLS information. {pull}17687[17687]

*Journalbeat*

Expand Down
25 changes: 18 additions & 7 deletions heartbeat/hbtest/hbtestutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package hbtest

import (
"crypto/tls"
"crypto/x509"
"fmt"
"io"
Expand All @@ -29,6 +30,10 @@ import (
"strconv"
"strings"
"testing"
"time"

"github.com/elastic/beats/v7/heartbeat/monitors/active/dialchain/tlsmeta"
"github.com/elastic/beats/v7/libbeat/common"

"github.com/elastic/beats/v7/heartbeat/hbtestllext"

Expand Down Expand Up @@ -107,13 +112,19 @@ func ServerPort(server *httptest.Server) (uint16, error) {

// TLSChecks validates the given x509 cert at the given position.
func TLSChecks(chainIndex, certIndex int, certificate *x509.Certificate) validator.Validator {
return lookslike.MustCompile(map[string]interface{}{
"tls": map[string]interface{}{
"rtt.handshake.us": isdef.IsDuration,
"certificate_not_valid_before": certificate.NotBefore,
"certificate_not_valid_after": certificate.NotAfter,
},
})
expected := common.MapStr{}
// This function is well tested independently, so we just test that things match up here.
tlsmeta.AddTLSMetadata(expected, tls.ConnectionState{
Version: tls.VersionTLS13,
HandshakeComplete: true,
CipherSuite: tls.TLS_AES_128_GCM_SHA256,
ServerName: certificate.Subject.CommonName,
PeerCertificates: []*x509.Certificate{certificate},
}, time.Duration(1))

expected.Put("tls.rtt.handshake.us", isdef.IsDuration)

return lookslike.MustCompile(expected)
}

// BaseChecks creates a skima.Validator that represents the "monitor" field present
Expand Down
6 changes: 4 additions & 2 deletions heartbeat/monitors/active/dialchain/_meta/fields.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,12 @@
fields:
- name: certificate_not_valid_before
type: date
description: Earliest time at which the connection's certificates are valid.
deprecated: 7.8.0
description: Deprecated in favor of `tls.server.not_before`. Earliest time at which the connection's certificates are valid.
- name: certificate_not_valid_after
deprecated: 7.8.0
type: date
description: Latest time at which the connection's certificates are valid.
description: Deprecated in favor of `tls.server.not_after`. Latest time at which the connection's certificates are valid.
- name: rtt
type: group
description: >
Expand Down
62 changes: 5 additions & 57 deletions heartbeat/monitors/active/dialchain/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,19 @@ package dialchain

import (
cryptoTLS "crypto/tls"
"crypto/x509"
"fmt"
"net"
"time"

"github.com/elastic/beats/v7/heartbeat/look"
"github.com/elastic/beats/v7/heartbeat/monitors/active/dialchain/tlsmeta"
"github.com/elastic/beats/v7/libbeat/beat"
"github.com/elastic/beats/v7/libbeat/common"
"github.com/elastic/beats/v7/libbeat/common/transport"
"github.com/elastic/beats/v7/libbeat/common/transport/tlscommon"
)

// TLSLayer configures the TLS layer in a DialerChain.
//
// The layer will update the active event with:
//
// {
// "tls": {
// "rtt": { "handshake": { "us": ... }}
// }
// }
// The layer will update the active event with the TLS RTT and
// crypto/cert details.
func TLSLayer(cfg *tlscommon.TLSConfig, to time.Duration) Layer {
return func(event *beat.Event, next transport.Dialer) (transport.Dialer, error) {
var timer timer
Expand All @@ -58,56 +50,12 @@ func TLSLayer(cfg *tlscommon.TLSConfig, to time.Duration) Layer {
if !ok {
panic(fmt.Sprintf("TLS afterDial received a non-tls connection %t. This should never happen", conn))
}

// TODO: extract TLS connection parameters from connection object.
connState := tlsConn.ConnectionState()
timer.stop()
event.PutValue("tls.rtt.handshake", look.RTT(timer.duration()))

addCertMetdata(event.Fields, tlsConn.ConnectionState().PeerCertificates)
tlsmeta.AddTLSMetadata(event.Fields, connState, timer.duration())

return conn, nil
}), nil
}
}

func addCertMetdata(fields common.MapStr, certs []*x509.Certificate) {
// The behavior here might seem strange. We *always* set a notBefore, but only optionally set a notAfter.
// Why might we do this?
// The root cause is that the x509.Certificate type uses time.Time for these fields instead of *time.Time
// so we have no way to know if the user actually set these fields. The x509 RFC says that only one of the
// two fields must be set. Most tools (including openssl and go's certgen) always set both. BECAUSE WHY NOT
//
// In the wild, however, there are certs missing one of these two fields.
// So, what's the correct behavior here? We cannot know if a field was omitted due to the lack of nullability.
// So, in this case, we try to do what people will want 99.99999999999999999% of the time.
// People might set notBefore to go's zero date intentionally when creating certs. So, we always set that
// field, even if we find a zero value.
// However, it would be weird to set notAfter to the zero value. That could invalidate a cert that was intended
// to be valid forever. So, in that case, we treat the zero value as non-existent.
// This is why notBefore is a time.Time and notAfter is a *time.Time
var chainNotValidBefore time.Time
var chainNotValidAfter *time.Time

// We need the zero date later
var zeroTime time.Time

// Here we compute the minimal bounds during which this certificate chain is valid
// To do this correctly, we take the maximum NotBefore and the minimum NotAfter.
// This *should* always wind up being the terminal cert in the chain, but we should
// compute this correctly.
for _, cert := range certs {
if chainNotValidBefore.Before(cert.NotBefore) {
chainNotValidBefore = cert.NotBefore
}

if cert.NotAfter != zeroTime && (chainNotValidAfter == nil || chainNotValidAfter.After(cert.NotAfter)) {
chainNotValidAfter = &cert.NotAfter
}
}

fields.Put("tls.certificate_not_valid_before", chainNotValidBefore)

if chainNotValidAfter != nil {
fields.Put("tls.certificate_not_valid_after", *chainNotValidAfter)
}
}
144 changes: 0 additions & 144 deletions heartbeat/monitors/active/dialchain/tls_test.go

This file was deleted.

Loading