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

Tls rootca option #337

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 10 additions & 0 deletions transport/tls/stream_dialer.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ type ClientConfig struct {
NextProtos []string
// The cache for sessin resumption.
SessionCache tls.ClientSessionCache
// The root certificate authorities for client to use for certificate verification.
Copy link
Contributor

Choose a reason for hiding this comment

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

We need to update the validation function, otherwise I think this will have no effect.

It would be helpful to have a test to exercise this. I wonder if LLM can help 😅

Copy link
Author

Choose a reason for hiding this comment

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

LLM?

Copy link
Contributor

Choose a reason for hiding this comment

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

I meant to generate the test that shows it working/not working, because it's a bit complicated to create the test. But we can probably piggy back on existing tests somewhere

Copy link
Contributor

Choose a reason for hiding this comment

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

Here is an example test: https://pkg.go.dev/crypto/tls#example-Dial

We need to redesign the certificate validation. I don't want to repeat the same mistake many TLS libraries do that spread a bunch of options that tweak validation, making it hard to figure out what options are relevant for what and how they interact.

We should get rid of all validation options (CertificateName, RootCA), and replace with a VerifyConnection function instead. Unlike the standard function, I'd make it take a slice of PeerCertificates, not tls.ConnectionState, so we don't tie it to the standard TLS implementation.

Then we can provide a helper struct type with CertificateName and RootCAs that implements the verify function. It needs to set x509.VerifyOptions.Roots and DNSName. That function can be passed to our tls.ClientConfig.

If someone wants the "InsecureSkipVerify" behavior, they can simply set a function that returns nil.

Copy link
Author

Choose a reason for hiding this comment

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

Oooohh, OK. I will do that!

RootCAs *x509.CertPool
}

// toStdConfig creates a [tls.Config] based on the configured parameters.
Expand All @@ -102,6 +104,7 @@ func (cfg *ClientConfig) toStdConfig() *tls.Config {
ServerName: cfg.ServerName,
NextProtos: cfg.NextProtos,
ClientSessionCache: cfg.SessionCache,
RootCAs: cfg.RootCAs,
// Set InsecureSkipVerify to skip the default validation we are
// replacing. This will not disable VerifyConnection.
InsecureSkipVerify: true,
Expand Down Expand Up @@ -188,3 +191,10 @@ func WithCertificateName(hostname string) ClientOption {
config.CertificateName = hostname
}
}

// WithRootCAs sets the root certificate authorities for the client to use for certificate verification.
func WithRootCAs(rootCAs *x509.CertPool) ClientOption {
return func(_ string, config *ClientConfig) {
config.RootCAs = rootCAs
}
}
11 changes: 10 additions & 1 deletion transport/tls/stream_dialer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ import (
"crypto/x509"
"testing"

"github.com/Jigsaw-Code/outline-sdk/transport"
"github.com/stretchr/testify/require"

"github.com/Jigsaw-Code/outline-sdk/transport"
)

func TestDomain(t *testing.T) {
Expand Down Expand Up @@ -140,6 +141,14 @@ func TestWithALPN(t *testing.T) {
require.Equal(t, []string{"h2", "http/1.1"}, cfg.NextProtos)
}

func TestWithRootCAs(t *testing.T) {
var cfg ClientConfig
rootCAs := x509.NewCertPool()
rootCAs.AddCert(&x509.Certificate{})
WithRootCAs(rootCAs)("", &cfg)
require.Equal(t, rootCAs, cfg.RootCAs)
}

// Make sure there are no connection leakage in DialStream
func TestDialStreamCloseInnerConnOnError(t *testing.T) {
inner := &connCounterDialer{base: &transport.TCPDialer{}}
Expand Down
Loading