Skip to content

Commit

Permalink
fix: allow Domains is empty in TLSAuth
Browse files Browse the repository at this point in the history
  • Loading branch information
rainingmaster committed Mar 8, 2022
1 parent 46d1d98 commit 0b6d759
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 124 deletions.
31 changes: 24 additions & 7 deletions js/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
"runtime/debug"
"strconv"
"strings"
"sync"
"time"

"github.com/dop251/goja"
Expand All @@ -59,6 +60,12 @@ import (
// Ensure Runner implements the lib.Runner interface
var _ lib.Runner = &Runner{}

// TODO: https://github.com/grafana/k6/issues/2186
// An advanced TLS support should cover the rid of the warning
//
// nolint:gochecknoglobals
var nameToCertWarning sync.Once

type Runner struct {
Bundle *Bundle
Logger *logrus.Logger
Expand Down Expand Up @@ -169,13 +176,13 @@ func (r *Runner) newVU(idLocal, idGlobal uint64, samplesOut chan<- stats.SampleC
certs := make([]tls.Certificate, len(tlsAuth))
nameToCert := make(map[string]*tls.Certificate)
for i, auth := range tlsAuth {
cert, errC := auth.Certificate()
if errC != nil {
return nil, errC
}
certs[i] = *cert
for _, name := range auth.Domains {
cert, err := auth.Certificate()
if err != nil {
return nil, err
}
certs[i] = *cert
nameToCert[name] = &certs[i]
nameToCert[name] = cert
}
}

Expand All @@ -200,9 +207,19 @@ func (r *Runner) newVU(idLocal, idGlobal uint64, samplesOut chan<- stats.SampleC
MinVersion: uint16(tlsVersions.Min),
MaxVersion: uint16(tlsVersions.Max),
Certificates: certs,
NameToCertificate: nameToCert,
Renegotiation: tls.RenegotiateFreelyAsClient,
}
// Follow NameToCertificate in https://pkg.go.dev/crypto/tls@go1.17.6#Config, leave this field nil
// when it is empty
if len(nameToCert) > 0 {
nameToCertWarning.Do(func() {
r.Logger.Warn("tlsAuth.domains option could be removed in the next releases, it's recommended to leave it empty " +
"and let k6 automatically detect from the provided certificate. It follows the Go's NameToCertificate " +
"deprecation - https://pkg.go.dev/crypto/tls@go1.17#Config.")
})
// nolint:staticcheck // ignore SA1019 we can deprecate it but we have to continue to support the previous code.
tlsConfig.NameToCertificate = nameToCert
}
transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: tlsConfig,
Expand Down
239 changes: 122 additions & 117 deletions js/runner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1372,158 +1372,163 @@ func TestVUIntegrationVUID(t *testing.T) {
}
}

/*
CA key:
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIDEm8bxihqYfAsWP39o5DpkAksPBw+3rlDHNX+d69oYGoAoGCCqGSM49
AwEHoUQDQgAEeeuCFQsdraFJr8JaKbAKfjYpZ2U+p3r/OzcmAsjFO8EckmV9uFZs
Gq3JurKi9Z3dDKQcwinHQ1malicbwWhamQ==
-----END EC PRIVATE KEY-----
*/
func TestVUIntegrationClientCerts(t *testing.T) {
t.Parallel()
clientCAPool := x509.NewCertPool()
assert.True(t, clientCAPool.AppendCertsFromPEM(
[]byte("-----BEGIN CERTIFICATE-----\n"+
"MIIBYzCCAQqgAwIBAgIUMYw1pqZ1XhXdFG0S2ITXhfHBsWgwCgYIKoZIzj0EAwIw\n"+
"EDEOMAwGA1UEAxMFTXkgQ0EwHhcNMTcwODE1MTYxODAwWhcNMjIwODE0MTYxODAw\n"+
"WjAQMQ4wDAYDVQQDEwVNeSBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABFWO\n"+
"fg4dgL8cdvjoSWDQFLBJxlbQFlZfOSyUR277a4g91BD07KWX+9ny+Q8WuUODog06\n"+
"xH1g8fc6zuaejllfzM6jQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD\n"+
"AQH/MB0GA1UdDgQWBBTeoSFylGCmyqj1X4sWez1r6hkhjDAKBggqhkjOPQQDAgNH\n"+
"ADBEAiAfuKi6u/BVXenCkgnU2sfXsYjel6rACuXEcx01yaaWuQIgXAtjrDisdlf4\n"+
"0ZdoIoYjNhDAXUtnyRBt+V6+rIklv/8=\n"+
"MIIBWzCCAQGgAwIBAgIJAIQMBgLi+DV6MAoGCCqGSM49BAMCMBAxDjAMBgNVBAMM\n"+
"BU15IENBMCAXDTIyMDEyMTEyMjkzNloYDzMwMjEwNTI0MTIyOTM2WjAQMQ4wDAYD\n"+
"VQQDDAVNeSBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHnrghULHa2hSa/C\n"+
"WimwCn42KWdlPqd6/zs3JgLIxTvBHJJlfbhWbBqtybqyovWd3QykHMIpx0NZmpYn\n"+
"G8FoWpmjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud\n"+
"DgQWBBSkukBA8lgFvvBJAYKsoSUR+PX71jAKBggqhkjOPQQDAgNIADBFAiEAiFF7\n"+
"Y54CMNRSBSVMgd4mQgrzJInRH88KpLsQ7VeOAaQCIEa0vaLln9zxIDZQKocml4Db\n"+
"AEJr8tDzMKIds6sRTBT4\n"+
"-----END CERTIFICATE-----"),
))
serverCert, err := tls.X509KeyPair(
[]byte("-----BEGIN CERTIFICATE-----\n"+
"MIIBxjCCAW2gAwIBAgIUICcYHG1bI28NZm676wHlMPxL+CEwCgYIKoZIzj0EAwIw\n"+
"EDEOMAwGA1UEAxMFTXkgQ0EwHhcNMTcwODE3MTQwNjAwWhcNMTgwODE3MTQwNjAw\n"+
"WjAZMRcwFQYDVQQDEw4xMjcuMC4wLjE6Njk2OTBZMBMGByqGSM49AgEGCCqGSM49\n"+
"AwEHA0IABCdD1IqowucJ5oUjGYCZZnXvgi7EMD4jD1osbOkzOFFnHSLRvdm6fcJu\n"+
"vPUcl4g8zUs466sC0AVUNpk21XbA/QajgZswgZgwDgYDVR0PAQH/BAQDAgWgMB0G\n"+
"A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1Ud\n"+
"DgQWBBTeAc8HY3sgGIV+fu/lY0OKr2Ho0jAfBgNVHSMEGDAWgBTeoSFylGCmyqj1\n"+
"X4sWez1r6hkhjDAZBgNVHREEEjAQgg4xMjcuMC4wLjE6Njk2OTAKBggqhkjOPQQD\n"+
"AgNHADBEAiAt3gC5FGQfSJXQ5DloXAOeJDFnKIL7d6xhftgPS5O08QIgRuAyysB8\n"+
"5JXHvvze5DMN/clHYptos9idVFc+weUZAUQ=\n"+
"MIIBcTCCARigAwIBAgIJAIP0njRt16gbMAoGCCqGSM49BAMCMBAxDjAMBgNVBAMM\n"+
"BU15IENBMCAXDTIyMDEyMTE1MTA0OVoYDzMwMjEwNTI0MTUxMDQ5WjAZMRcwFQYD\n"+
"VQQDDA4xMjcuMC4wLjE6Njk2OTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABH8Y\n"+
"exy5LI9r+RNwVpf/5ZX86EigMYHp9YOyiUMmfUfvDig+BGhlwjm7Lh2941Gz4amO\n"+
"lpN2YAkcd0wnNLHkVOmjUDBOMA4GA1UdDwEB/wQEAwIBBjAMBgNVHRMBAf8EAjAA\n"+
"MB0GA1UdDgQWBBQ9cIYUwwzfzBXPyRGB5tNpAgHWujAPBgNVHREECDAGhwR/AAAB\n"+
"MAoGCCqGSM49BAMCA0cAMEQCIDjRZlg+jKgI9K99HOM2wS9+URr6R1/FYLZYBtMc\n"+
"pq3hAiB9NQxNqV459fgN0BpbiLrEvJjquRFoUr9BWsG+hHrHtQ==\n"+
"-----END CERTIFICATE-----\n"+
"-----BEGIN CERTIFICATE-----\n"+
"MIIBYzCCAQqgAwIBAgIUMYw1pqZ1XhXdFG0S2ITXhfHBsWgwCgYIKoZIzj0EAwIw\n"+
"EDEOMAwGA1UEAxMFTXkgQ0EwHhcNMTcwODE1MTYxODAwWhcNMjIwODE0MTYxODAw\n"+
"WjAQMQ4wDAYDVQQDEwVNeSBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABFWO\n"+
"fg4dgL8cdvjoSWDQFLBJxlbQFlZfOSyUR277a4g91BD07KWX+9ny+Q8WuUODog06\n"+
"xH1g8fc6zuaejllfzM6jQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD\n"+
"AQH/MB0GA1UdDgQWBBTeoSFylGCmyqj1X4sWez1r6hkhjDAKBggqhkjOPQQDAgNH\n"+
"ADBEAiAfuKi6u/BVXenCkgnU2sfXsYjel6rACuXEcx01yaaWuQIgXAtjrDisdlf4\n"+
"0ZdoIoYjNhDAXUtnyRBt+V6+rIklv/8=\n"+
"MIIBWzCCAQGgAwIBAgIJAIQMBgLi+DV6MAoGCCqGSM49BAMCMBAxDjAMBgNVBAMM\n"+
"BU15IENBMCAXDTIyMDEyMTEyMjkzNloYDzMwMjEwNTI0MTIyOTM2WjAQMQ4wDAYD\n"+
"VQQDDAVNeSBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHnrghULHa2hSa/C\n"+
"WimwCn42KWdlPqd6/zs3JgLIxTvBHJJlfbhWbBqtybqyovWd3QykHMIpx0NZmpYn\n"+
"G8FoWpmjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud\n"+
"DgQWBBSkukBA8lgFvvBJAYKsoSUR+PX71jAKBggqhkjOPQQDAgNIADBFAiEAiFF7\n"+
"Y54CMNRSBSVMgd4mQgrzJInRH88KpLsQ7VeOAaQCIEa0vaLln9zxIDZQKocml4Db\n"+
"AEJr8tDzMKIds6sRTBT4\n"+
"-----END CERTIFICATE-----"),
[]byte("-----BEGIN EC PRIVATE KEY-----\n"+
"MHcCAQEEIKYptA4VtQ8UOKL+d1wkhl+51aPpvO+ppY62nLF9Z1w5oAoGCCqGSM49\n"+
"AwEHoUQDQgAEJ0PUiqjC5wnmhSMZgJlmde+CLsQwPiMPWixs6TM4UWcdItG92bp9\n"+
"wm689RyXiDzNSzjrqwLQBVQ2mTbVdsD9Bg==\n"+
"MHcCAQEEIHNpjs0P9/ejoUYF5Agzf9clHR4PwBsVfZ+JgslfuBg1oAoGCCqGSM49\n"+
"AwEHoUQDQgAEfxh7HLksj2v5E3BWl//llfzoSKAxgen1g7KJQyZ9R+8OKD4EaGXC\n"+
"ObsuHb3jUbPhqY6Wk3ZgCRx3TCc0seRU6Q==\n"+
"-----END EC PRIVATE KEY-----"),
)
require.NoError(t, err)

testdata := map[string]struct {
withClientCert bool
withDomains bool
insecureSkipVerify bool
errMsg string
}{
"WithoutCert": {false, false, true, "remote error: tls: bad certificate"},
"WithCert": {true, true, true, ""},
"VerifyServerCert": {true, false, false, "certificate signed by unknown authority"},
"WithoutDomains": {true, false, true, ""},
}

listener, err := tls.Listen("tcp", "127.0.0.1:0", &tls.Config{
Certificates: []tls.Certificate{serverCert},
ClientAuth: tls.RequireAndVerifyClientCert,
ClientCAs: clientCAPool,
})
require.NoError(t, err)
t.Cleanup(func() { _ = listener.Close() })
srv := &http.Server{
Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
_, _ = fmt.Fprintf(w, "ok")
}),
ErrorLog: stdlog.New(ioutil.Discard, "", 0),
}
go func() { _ = srv.Serve(listener) }()
t.Cleanup(func() { _ = listener.Close() })
for name, data := range testdata {
data := data

registry := metrics.NewRegistry()
builtinMetrics := metrics.RegisterBuiltinMetrics(registry)
t.Run("Unauthenticated", func(t *testing.T) {
t.Parallel()

r1, err := getSimpleRunner(t, "/script.js", fmt.Sprintf(`
var http = require("k6/http");;
exports.default = function() { http.get("https://%s")}
`, listener.Addr().String()))
require.NoError(t, err)
require.NoError(t, r1.SetOptions(lib.Options{
Throw: null.BoolFrom(true),
InsecureSkipTLSVerify: null.BoolFrom(true),
}))
r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{}, builtinMetrics, registry)
require.NoError(t, err)
registry := metrics.NewRegistry()
builtinMetrics := metrics.RegisterBuiltinMetrics(registry)
t.Run(name, func(t *testing.T) {
t.Parallel()

runners := map[string]*Runner{"Source": r1, "Archive": r2}
for name, r := range runners {
r := r
t.Run(name, func(t *testing.T) {
t.Parallel()
r.Logger, _ = logtest.NewNullLogger()
initVU, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))
if assert.NoError(t, err) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})
err := vu.RunOnce()
require.Error(t, err)
assert.Contains(t, err.Error(), "remote error: tls: bad certificate")
}
})
}
})
r1, err := getSimpleRunner(t, "/script.js", fmt.Sprintf(`
var http = require("k6/http");
var k6 = require("k6");
var check = k6.check;
exports.default = function() {
const res = http.get("https://%s")
check(res, {
'is status 200': (r) => r.status === 200,
'verify resp': (r) => r.body.includes('ok'),
})
}`, listener.Addr().String()))
require.NoError(t, err)

t.Run("Authenticated", func(t *testing.T) {
t.Parallel()
r1, err := getSimpleRunner(t, "/script.js", fmt.Sprintf(`
var http = require("k6/http");;
exports.default = function() { http.get("https://%s")}
`, listener.Addr().String()))
require.NoError(t, err)
require.NoError(t, r1.SetOptions(lib.Options{
Throw: null.BoolFrom(true),
InsecureSkipTLSVerify: null.BoolFrom(true),
}))

require.NoError(t, r1.SetOptions(lib.Options{
TLSAuth: []*lib.TLSAuth{
{
TLSAuthFields: lib.TLSAuthFields{
Domains: []string{"127.0.0.1"},
Cert: "-----BEGIN CERTIFICATE-----\n" +
"MIIBoTCCAUigAwIBAgIUd6XedDxP+rGo+kq0APqHElGZzs4wCgYIKoZIzj0EAwIw\n" +
"EDEOMAwGA1UEAxMFTXkgQ0EwHhcNMTcwODE3MTUwNjAwWhcNMTgwODE3MTUwNjAw\n" +
"WjARMQ8wDQYDVQQDEwZjbGllbnQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATL\n" +
"mi/a1RVvk05FyrYmartbo/9cW+53DrQLW1twurII2q5ZfimdMX05A32uB3Ycoy/J\n" +
"x+w7Ifyd/YRw0zEc3NHQo38wfTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYI\n" +
"KwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFN2SR/TD\n" +
"yNW5DQWxZSkoXHQWsLY+MB8GA1UdIwQYMBaAFN6hIXKUYKbKqPVfixZ7PWvqGSGM\n" +
"MAoGCCqGSM49BAMCA0cAMEQCICtETmyOmupmg4w3tw59VYJyOBqRTxg6SK+rOQmq\n" +
"kE1VAiAUvsflDfmWBZ8EMPu46OhX6RX6MbvJ9NNvRco2G5ek1w==\n" +
"-----END CERTIFICATE-----",
Key: "-----BEGIN EC PRIVATE KEY-----\n" +
"MHcCAQEEIOrnhT05alCeQEX66HgnSHah/m5LazjJHLDawYRnhUtZoAoGCCqGSM49\n" +
"AwEHoUQDQgAEy5ov2tUVb5NORcq2Jmq7W6P/XFvudw60C1tbcLqyCNquWX4pnTF9\n" +
"OQN9rgd2HKMvycfsOyH8nf2EcNMxHNzR0A==\n" +
"-----END EC PRIVATE KEY-----",
opt := lib.Options{Throw: null.BoolFrom(true)}
if data.insecureSkipVerify {
opt.InsecureSkipTLSVerify = null.BoolFrom(true)
}
if data.withClientCert {
opt.TLSAuth = []*lib.TLSAuth{
{
TLSAuthFields: lib.TLSAuthFields{
Cert: "-----BEGIN CERTIFICATE-----\n" +
"MIIBVzCB/6ADAgECAgkAg/SeNG3XqB0wCgYIKoZIzj0EAwIwEDEOMAwGA1UEAwwF\n" +
"TXkgQ0EwIBcNMjIwMTIxMTUxMjM0WhgPMzAyMTA1MjQxNTEyMzRaMBExDzANBgNV\n" +
"BAMMBmNsaWVudDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABKM7OJQMYG4KLtDA\n" +
"gZ8zOg2PimHMmQnjD2HtI4cSwIUJJnvHWLowbFe9fk6XeP9b3dK1ImUI++/EZdVr\n" +
"ABAcngejPzA9MA4GA1UdDwEB/wQEAwIBBjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQW\n" +
"BBSttJe1mcPEnBOZ6wvKPG4zL0m1CzAKBggqhkjOPQQDAgNHADBEAiBPSLgKA/r9\n" +
"u/FW6W+oy6Odm1kdNMGCI472iTn545GwJgIgb3UQPOUTOj0IN4JLJYfmYyXviqsy\n" +
"zk9eWNHFXDA9U6U=\n" +
"-----END CERTIFICATE-----",
Key: "-----BEGIN EC PRIVATE KEY-----\n" +
"MHcCAQEEINDaMGkOT3thu1A0LfLJr3Jd011/aEG6OArmEQaujwgpoAoGCCqGSM49\n" +
"AwEHoUQDQgAEozs4lAxgbgou0MCBnzM6DY+KYcyZCeMPYe0jhxLAhQkme8dYujBs\n" +
"V71+Tpd4/1vd0rUiZQj778Rl1WsAEByeBw==\n" +
"-----END EC PRIVATE KEY-----",
},
},
},
},
}))
r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{}, builtinMetrics, registry)
require.NoError(t, err)

runners := map[string]*Runner{"Source": r1, "Archive": r2}
for name, r := range runners {
r := r
t.Run(name, func(t *testing.T) {
initVU, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))
if assert.NoError(t, err) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})
err := vu.RunOnce()
assert.NoError(t, err)
}
})
}
})
if data.withDomains {
opt.TLSAuth[0].TLSAuthFields.Domains = []string{"127.0.0.1"}
}
_, _ = opt.TLSAuth[0].Certificate()
}
require.NoError(t, r1.SetOptions(opt))
r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{}, builtinMetrics, registry)
require.NoError(t, err)

runners := map[string]*Runner{"Source": r1, "Archive": r2}
for name, r := range runners {
r := r
t.Run(name, func(t *testing.T) {
t.Parallel()
r.Logger, _ = logtest.NewNullLogger()
initVU, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))
if assert.NoError(t, err) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})
err := vu.RunOnce()
if len(data.errMsg) > 0 {
require.Error(t, err)
assert.Contains(t, err.Error(), data.errMsg)
} else {
assert.NoError(t, err)
}
}
})
}
})
}
}

func TestHTTPRequestInInitContext(t *testing.T) {
Expand Down

0 comments on commit 0b6d759

Please sign in to comment.