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

tlsconfig trace implementation #150

Merged
merged 1 commit into from
Sep 17, 2020
Merged

Conversation

joewilliams
Copy link

@joewilliams joewilliams commented Jul 28, 2020

This is an implementation of TLSConfig hooks scoped just to getTLSCertificate.

cc #149

Copy link
Member

@azdagron azdagron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this PoC, @joewilliams. I wonder, if instead of requiring a parameter at the end of the tlsconfig.*Config() methods how this would look if we adopted the Options pattern used in other packages? Thoughts?

v2/spiffetls/tlsconfig/config.go Outdated Show resolved Hide resolved
v2/spiffetls/tlsconfig/hooks.go Outdated Show resolved Hide resolved
@joewilliams
Copy link
Author

I wonder, if instead of requiring a parameter at the end of the tlsconfig.*Config() methods how this would look if we adopted the Options pattern used in other packages? Thoughts?

I assume that would work something like WithHooks() which would return a function containing the Hooks struct? I could see it being cumbersome needing to WithHooks any invocation of a package but maybe no more cumbersome than the struct in this PR. For something like workloadapi it might work well but for something like tlsconfig maybe less so?

That said, I don't have a strong preference here. @aybabtme what do you think?

@joewilliams
Copy link
Author

joewilliams commented Aug 4, 2020

I have integrated @aybabtme's suggestions, the important part is in

func getTLSCertificate(svid x509svid.Source, trace Trace) (*tls.Certificate, error) {
and
type GetTLSCertificateStart struct {

@joewilliams
Copy link
Author

@azdagron @aybabtme graciously implemented the WithOptions code for this PR. I think it's in a place for further review before we implement more calls and etc. For TLSConfig I think we'd want to add tracing for GetCertificate, VerifyPeerCertificate and AdaptMatcher to start. I don't want to start that work until there is agreement on how the API works. I think a future tlsconfig.HookMTLSServerConfig call with full tracing, when this PR is completed fully, would look something like:

// authorizerTraceConfig = stuff
// getCertTraceConfig = stuff
// verifyPeerTraceConfig = stuff

authorizerWithTracing := tlsconfig.AuthorizeMemberOf(
    tlsconfig.WithAuthorizerOption(
			tlsconfig.WithAuthorizerTrace(authorizerTraceConfig),
		)
)

tlsconfig.HookMTLSServerConfig(config, svid, bundle, authorizerWithTracing,
		tlsconfig.WithMTLSServerGetCertificateOption(
			tlsconfig.WithGetCertificateTrace(getCertTraceConfig),
		),
		tlsconfig.WithMTLSServerVerifyPeerCertificate(
			tlsconfig.WithMTLSServerVerifyPeerCertificate(verifyPeerTraceConfig),
		),
	)

@azdagron
Copy link
Member

azdagron commented Aug 4, 2020

Awesome. I'll take a detailed look as soon as I can.

@joewilliams joewilliams changed the title naive tlsconfig hooks implementation tlsconfig trace implementation Aug 4, 2020
@azdagron
Copy link
Member

azdagron commented Aug 7, 2020

My opinion is that the above approach is too granular? I don't think we need separate options for Authorizer,GetCertificate,VerifyPeerCertificate. I also don't anticipate that we'll need per-method options. I think an option type for the whole package is probably sufficient?

How does the following look?

trace := tlsconfig.Trace{
    // populate the functions for the trace points you are interested in, leave the rest zero initialized
}

tlsconfig.HookMTLSServerConfig(config, svid, bundle, tlsconfig.WithTrace(trace))

@aybabtme
Copy link

aybabtme commented Aug 7, 2020 via email

@aybabtme
Copy link

aybabtme commented Aug 7, 2020

@azdagron PTAL

@joewilliams
Copy link
Author

👍

Copy link
Member

@azdagron azdagron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, folks! I appreciate your patience with the back-and-forth on this.

As I reviewed the Start/End structs, I wondered if we could get away with not doing the measurement of elapsed time itself in the library by having the *Start callbacks return an interface{} that is given to the *End callback. Maybe something like:

type Trace struct {
    GetCertificate() interface{}
    GotCertificate(interface{}, GotCertificateInfo)

An implementation could be:

trace.GetCertificate = func() interface{} {
    // ... log or do whatever with your metrics to start recording elapsed time ...
    return time.Now()
}
trace.GotCertificate = func(value interface{}, info GotCertificateInfo) {
   elapsed := time.Since(value.(*time.Time))
   // ... do something with `elapsed` and `info` ...
}

I'm not even sure how I feel about the above to be honest, but it came to mind.
Pros:

  • it keeps all assumptions about how the trace is going to be used out of the library, providing only the barebones, but allowing for trace implementors to provide context between start/stop events when needed.

Cons:

  • type assertions, woof!
  • increases the boilerplate on trace implementations

Thoughts??

@@ -26,63 +27,86 @@ func HookTLSClientConfig(config *tls.Config, bundle x509bundle.Source, authorize
config.VerifyPeerCertificate = WrapVerifyPeerCertificate(config.VerifyPeerCertificate, bundle, authorizer)
}

// A Option changes the defaults used to by mTLS ClientConfig functions.
type Option func(*options)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably use the same pattern we have for other options:

Suggested change
type Option func(*options)
type Option interface {
apply(*options)
}
type option func(*options)
func (fn option) apply(o *option) {
fn(o)
}

Then the options can be implemented in terms of option we get the flexibility later if we do end up needing to have per-method options mixed in with common options.

func WithSomeOption() Option {
    return option(func(o *options) {
        // modify `o`
    })
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, I'd never seen what the purpose of these interface-based options were, so always used the simpler func-based pattern. I see now that this makes it possible to reuse options and still have scoped down option types for narrower use-cases, which obviates the need for the N option types I had included in this PR initially!

Nice! The more you know!

Comment on lines 10 to 12
End time.Time
Cert *tls.Certificate
Err error
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These fields aren't used.

Copy link

@aybabtme aybabtme Aug 7, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oups! Bad copy-pasta!

v2/go.sum Outdated
@@ -17,6 +17,7 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/spiffe/go-spiffe v1.0.0 h1:zP+DNibBYpALBw597lwikzhriUhMqSUU3zCjBAD+BFw=
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, where did this come from? What is pulling in go-spiffe v1?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's a
image

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure where this came from but it's cleaned up now

@aybabtme
Copy link

aybabtme commented Aug 7, 2020

I like your idea, I was thinking of something similar initially using context.Context but there no instance of it anywhere in scope here. The advantage of context.Context is we'd be able to use a type-safe key argument, which would allow nested Trace implementations to be invoked without as much risk of mangling the type that the receiving GotCertificate sees, sort of how http.Handler middleware get stacked up. Seems unlikely to be needed here though, so interface{} lgtm.

@aybabtme
Copy link

aybabtme commented Aug 7, 2020

@azdagron PTAL

Copy link
Member

@amartinezfayo amartinezfayo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a great improvement. Thank you for this contribution!
I have some minor suggestions around the testing. I think that it would also be good to update some of our example code to show how this capability can be leveraged (not necessarily as part of this PR).

v2/spiffetls/tlsconfig/config_test.go Outdated Show resolved Hide resolved
v2/spiffetls/tlsconfig/trace.go Show resolved Hide resolved
v2/spiffetls/tlsconfig/config_test.go Outdated Show resolved Hide resolved
v2/spiffetls/tlsconfig/config_test.go Outdated Show resolved Hide resolved
Copy link
Member

@azdagron azdagron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few small changes so the interface is future proof (i.e. some functions in tlsconfig don't take options). I think we're good to go after that.

v2/spiffetls/tlsconfig/config.go Show resolved Hide resolved
v2/spiffetls/dial.go Show resolved Hide resolved
v2/spiffetls/listen.go Show resolved Hide resolved
v2/spiffetls/option.go Outdated Show resolved Hide resolved
v2/spiffetls/option.go Outdated Show resolved Hide resolved
@aybabtme
Copy link

@azdagron thanks for the quick review! I addressed your comments, PTAL.

Copy link
Member

@azdagron azdagron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

\o/! Would you mind squashing this? (to preserve DCO). I'm happy to merge after that.

Signed-off-by: Joe Williams <[email protected]>

add time to function call

typo

forgotten imports

aybabtme's suggestions

function name change clean up

use two structs

Signed-off-by: Antoine Grondin <[email protected]>

introduce func. opts. pattern and convert GetCertificate tracing to it

Signed-off-by: Antoine Grondin <[email protected]>

adjust trickle down occurences of tlsconfig.Trace

Signed-off-by: Antoine Grondin <[email protected]>

collapse all option types into 1 for whole tlsconfig package

Signed-off-by: Antoine Grondin <[email protected]>

rework option type and trace mechanism

Signed-off-by: Antoine Grondin <[email protected]>

remove weird go-sum addition

Signed-off-by: Antoine Grondin <[email protected]>

test clean up

Signed-off-by: Antoine Grondin <[email protected]>

Update v2 to beta

Signed-off-by: Andrew Harding <[email protected]>
Signed-off-by: Antoine Grondin <[email protected]>

Print out the expected peer and domains when encountering mismatches

Signed-off-by: Kyle Anderson <[email protected]>
Signed-off-by: Antoine Grondin <[email protected]>

assert that GetCertificate gets called as expected

Signed-off-by: Antoine Grondin <[email protected]>

pass config.Option to every func

Signed-off-by: Antoine Grondin <[email protected]>

pluralize options funcs

Signed-off-by: Antoine Grondin <[email protected]>
@joewilliams
Copy link
Author

@azdagron things are green, should be good to go.

@azdagron azdagron merged commit 5bab594 into spiffe:master Sep 17, 2020
@azdagron
Copy link
Member

\o/ thanks folks! I'll have a follow-up PR with some additional events.

@aybabtme aybabtme deleted the metrics branch February 15, 2021 08:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants