Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
723cd8e
feat: add opt-in AIA certificate chain fetching (tlsAIAFetch)
vtorosyan May 6, 2026
f513f46
chore: fix lint issues in AIA implementation
vtorosyan May 6, 2026
a2a415b
fix: include TLSAIAFetch in Options.Apply merge
vtorosyan May 6, 2026
9127c18
test: add TLS handshake benchmarks for AIA fetching overhead
vtorosyan May 8, 2026
0379b93
test: add benchmark for AIA enabled but not needed (complete chain)
vtorosyan May 8, 2026
3c67b9c
fix(grpc): honour user-supplied cacerts when tlsAIAFetch is enabled
vtorosyan Jul 14, 2026
78ff7d7
feat(aia): warn when AIA response is PKCS#7 (unsupported)
vtorosyan Jul 14, 2026
388b514
chore: trim verbose comments in gRPC AIA fix and PKCS#7 warn
vtorosyan Jul 14, 2026
b0de5cd
chore(grpc): fix lint issues in AIA cacerts test (noctx, gofmt)
vtorosyan Jul 14, 2026
fcea2bc
Merge branch 'master' into vt/tls_fix_v2
vtorosyan Jul 14, 2026
a21a19f
Merge remote-tracking branch 'origin/master' into vt/tls_fix_v2
vtorosyan Jul 27, 2026
043f518
refactor(aia): coalesce concurrent fetches, cache TTL, bound HTTP calls
vtorosyan Jul 27, 2026
9c0391b
fix(grpc): honour user InsecureSkipVerify intent when AIA is enabled
vtorosyan Jul 27, 2026
94f7432
test: extract shared TLS chain helpers to testutils/tlstest
vtorosyan Jul 27, 2026
bf4499b
chore: trim verbose comments across AIA + gRPC AIA tests
vtorosyan Jul 27, 2026
f94bfdb
chore(aia): simplify VerifyConnection guard to ServerName-only
vtorosyan Jul 28, 2026
b1e478e
chore(aia): drop unused prev-callback chaining machinery
vtorosyan Jul 28, 2026
f9e0f31
chore: restore doc comments on exported AIAHandler methods
vtorosyan Jul 28, 2026
81fbdba
test(grpc): user's insecureSkipTLSVerify must be honoured with or wit…
vtorosyan Jul 28, 2026
c4ea8d5
chore(grpc): drop unused nolint:gosec on test's InsecureSkipVerify
vtorosyan Jul 28, 2026
dba2279
Merge branch 'master' into vt/tls_fix_v2
vtorosyan Jul 29, 2026
df44a73
fix(aia): drain queue for cached items even after fetch budget is spent
vtorosyan Jul 30, 2026
f4f0552
fix(aia): evict cached intermediates past their NotAfter
vtorosyan Jul 30, 2026
5e07e2e
Merge branch 'master' into vt/tls_fix_v2
vtorosyan Jul 30, 2026
7d88345
refactor(aia): introduce per-Runner AIAFetcher bound to caller's dialer
vtorosyan Jul 31, 2026
386ba39
test(aia): cover per-fetcher cache isolation and dialer routing
vtorosyan Jul 31, 2026
7caddeb
chore(aia): address review — SetOptions init, keep-alive, proxy, gRPC…
vtorosyan Aug 6, 2026
9736b37
Merge branch 'master' into vt/tls_fix_v2
vtorosyan Aug 6, 2026
aeb6807
Merge branch 'master' into vt/tls_fix_v2
vtorosyan Aug 12, 2026
a6dfed7
fix(tls): fail closed on empty ServerName in AIA verify callback
vtorosyan Aug 27, 2026
fad3b24
Merge remote-tracking branch 'origin/master' into vt/tls_fix_v2
vtorosyan Aug 27, 2026
e5909cd
test(js): dial AIA integration test via localhost, not 127.0.0.1
vtorosyan Aug 27, 2026
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 internal/cmd/tests/cmd_run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,7 @@ func TestExecutionTestOptionsDefaultValues(t *testing.T) {
loglines := ts.LoggerHook.Drain()
require.Len(t, loglines, 1)

expected := `{"paused":null,"executionSegment":null,"executionSegmentSequence":null,"noSetup":null,"setupTimeout":null,"noTeardown":null,"teardownTimeout":null,"rps":null,"dns":{"ttl":null,"select":null,"policy":null},"maxRedirects":null,"userAgent":null,"batch":null,"batchPerHost":null,"httpDebug":null,"insecureSkipTLSVerify":null,"tlsCipherSuites":null,"tlsVersion":null,"tlsAuth":null,"throw":null,"thresholds":null,"blacklistIPs":null,"blockHostnames":null,"hosts":null,"noConnectionReuse":null,"noVUConnectionReuse":null,"minIterationDuration":null,"ext":null,"summaryTrendStats":["avg", "min", "med", "max", "p(90)", "p(95)"],"summaryTimeUnit":null,"systemTags":["check","error","error_code","expected_response","group","method","name","proto","scenario","service","status","subproto","tls_version","url"],"tags":null,"metricSamplesBufferSize":null,"noCookiesReset":null,"discardResponseBodies":null,"consoleOutput":null,"scenarios":{"default":{"vus":null,"iterations":1,"executor":"shared-iterations","maxDuration":null,"startTime":null,"env":null,"tags":null,"gracefulStop":null,"exec":null}},"localIPs":null,"handleSummaryTimeout":null,"features":null}`
expected := `{"paused":null,"executionSegment":null,"executionSegmentSequence":null,"noSetup":null,"setupTimeout":null,"noTeardown":null,"teardownTimeout":null,"rps":null,"dns":{"ttl":null,"select":null,"policy":null},"maxRedirects":null,"userAgent":null,"batch":null,"batchPerHost":null,"httpDebug":null,"insecureSkipTLSVerify":null,"tlsCipherSuites":null,"tlsVersion":null,"tlsAuth":null,"tlsAIAFetch":null,"throw":null,"thresholds":null,"blacklistIPs":null,"blockHostnames":null,"hosts":null,"noConnectionReuse":null,"noVUConnectionReuse":null,"minIterationDuration":null,"ext":null,"summaryTrendStats":["avg", "min", "med", "max", "p(90)", "p(95)"],"summaryTimeUnit":null,"systemTags":["check","error","error_code","expected_response","group","method","name","proto","scenario","service","status","subproto","tls_version","url"],"tags":null,"metricSamplesBufferSize":null,"noCookiesReset":null,"discardResponseBodies":null,"consoleOutput":null,"scenarios":{"default":{"vus":null,"iterations":1,"executor":"shared-iterations","maxDuration":null,"startTime":null,"env":null,"tags":null,"gracefulStop":null,"exec":null}},"localIPs":null,"handleSummaryTimeout":null,"features":null}`
assert.JSONEq(t, expected, loglines[0].Message)
}

Expand Down
1 change: 0 additions & 1 deletion internal/js/modules/k6/browser/common/kill_other.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//go:build !linux
// +build !linux

package common

Expand Down
2 changes: 1 addition & 1 deletion internal/js/modules/k6/execution/execution_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ func TestFailTest(t *testing.T) {
func TestOptionsTestFull(t *testing.T) {
t.Parallel()

expected := `{"paused":true,"scenarios":{"const-vus":{"executor":"constant-vus","options":{"browser":{"someOption":true}},"startTime":"10s","gracefulStop":"30s","env":{"FOO":"bar"},"exec":"default","tags":{"tagkey":"tagvalue"},"vus":50,"duration":"10m0s"}},"executionSegment":"0:1/4","executionSegmentSequence":"0,1/4,1/2,1","noSetup":true,"setupTimeout":"1m0s","noTeardown":true,"teardownTimeout":"5m0s","rps":100,"dns":{"ttl":"1m","select":"roundRobin","policy":"any"},"maxRedirects":3,"userAgent":"k6-user-agent","batch":15,"batchPerHost":5,"httpDebug":"full","insecureSkipTLSVerify":true,"tlsCipherSuites":["TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"],"tlsVersion":{"min":"tls1.2","max":"tls1.3"},"tlsAuth":[{"domains":["example.com"],"cert":"mycert.pem","key":"mycert-key.pem","password":"mypwd"}],"throw":true,"thresholds":{"http_req_duration":[{"threshold":"rate>0.01","abortOnFail":true,"delayAbortEval":"10s"}]},"blacklistIPs":["192.0.2.0/24"],"blockHostnames":["test.k6.io","*.example.com"],"hosts":{"test.k6.io":"1.2.3.4:8443"},"noConnectionReuse":true,"noVUConnectionReuse":true,"minIterationDuration":"10s","ext":{"ext-one":{"rawkey":"rawvalue"}},"handleSummaryTimeout":null,"summaryTrendStats":["avg","min","max"],"summaryTimeUnit":"ms","systemTags":["iter","vu"],"tags":null,"metricSamplesBufferSize":8,"noCookiesReset":true,"discardResponseBodies":true,"consoleOutput":"loadtest.log","tags":{"runtag-key":"runtag-value"},"localIPs":"192.168.20.12-192.168.20.15,192.168.10.0/27","features":null}`
expected := `{"paused":true,"scenarios":{"const-vus":{"executor":"constant-vus","options":{"browser":{"someOption":true}},"startTime":"10s","gracefulStop":"30s","env":{"FOO":"bar"},"exec":"default","tags":{"tagkey":"tagvalue"},"vus":50,"duration":"10m0s"}},"executionSegment":"0:1/4","executionSegmentSequence":"0,1/4,1/2,1","noSetup":true,"setupTimeout":"1m0s","noTeardown":true,"teardownTimeout":"5m0s","rps":100,"dns":{"ttl":"1m","select":"roundRobin","policy":"any"},"maxRedirects":3,"userAgent":"k6-user-agent","batch":15,"batchPerHost":5,"httpDebug":"full","insecureSkipTLSVerify":true,"tlsCipherSuites":["TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"],"tlsVersion":{"min":"tls1.2","max":"tls1.3"},"tlsAuth":[{"domains":["example.com"],"cert":"mycert.pem","key":"mycert-key.pem","password":"mypwd"}],"tlsAIAFetch":null,"throw":true,"thresholds":{"http_req_duration":[{"threshold":"rate>0.01","abortOnFail":true,"delayAbortEval":"10s"}]},"blacklistIPs":["192.0.2.0/24"],"blockHostnames":["test.k6.io","*.example.com"],"hosts":{"test.k6.io":"1.2.3.4:8443"},"noConnectionReuse":true,"noVUConnectionReuse":true,"minIterationDuration":"10s","ext":{"ext-one":{"rawkey":"rawvalue"}},"handleSummaryTimeout":null,"summaryTrendStats":["avg","min","max"],"summaryTimeUnit":"ms","systemTags":["iter","vu"],"tags":null,"metricSamplesBufferSize":8,"noCookiesReset":true,"discardResponseBodies":true,"consoleOutput":"loadtest.log","tags":{"runtag-key":"runtag-value"},"localIPs":"192.168.20.12-192.168.20.15,192.168.10.0/27","features":null}`

var (
rt = sobek.New()
Expand Down
41 changes: 36 additions & 5 deletions internal/js/modules/k6/grpc/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@ import (
"go.k6.io/k6/v2/internal/lib/netext/grpcext"
"go.k6.io/k6/v2/js/common"
"go.k6.io/k6/v2/js/modules"
"go.k6.io/k6/v2/lib"
"go.k6.io/k6/v2/lib/netext"

"github.com/bufbuild/protocompile"
"github.com/grafana/sobek"
"github.com/sirupsen/logrus"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
Expand Down Expand Up @@ -148,7 +151,10 @@ func decryptPrivateKey(key, password []byte) ([]byte, error) {
return key, nil
}

func buildTLSConfig(parentConfig *tls.Config, certificate, key []byte, caCertificates [][]byte) (*tls.Config, error) {
func buildTLSConfig(
parentConfig *tls.Config, certificate, key []byte, caCertificates [][]byte,
aiaFetcher *netext.AIAFetcher, insecureSkipVerify bool, logger logrus.FieldLogger,
) (*tls.Config, error) {
var cp *x509.CertPool
if len(caCertificates) > 0 {
cp, _ = x509.SystemCertPool()
Expand All @@ -162,9 +168,12 @@ func buildTLSConfig(parentConfig *tls.Config, certificate, key []byte, caCertifi
// Ignoring 'TLS MinVersion is too low' because this tls.Config will inherit MinValue and MaxValue
// from the vu state tls.Config

// Take insecureSkipVerify from the user's option, not parentConfig — the AIA wrapper
// forces parentConfig.InsecureSkipVerify to true internally, so inheriting silently
// disables verification.
tlsCfg := &tls.Config{
CipherSuites: parentConfig.CipherSuites,
InsecureSkipVerify: parentConfig.InsecureSkipVerify, //nolint:gosec
InsecureSkipVerify: insecureSkipVerify, //nolint:gosec
MinVersion: parentConfig.MinVersion,
MaxVersion: parentConfig.MaxVersion,
Renegotiation: parentConfig.Renegotiation,
Expand All @@ -177,10 +186,17 @@ func buildTLSConfig(parentConfig *tls.Config, certificate, key []byte, caCertifi
}
tlsCfg.Certificates = []tls.Certificate{cert}
}
// Re-wrap so the verification closure reads this config's RootCAs, not the VU's.
if aiaFetcher != nil {
tlsCfg = aiaFetcher.Wrap(tlsCfg, logger)
}
return tlsCfg, nil
}

func buildTLSConfigFromMap(parentConfig *tls.Config, tlsConfigMap map[string]any) (*tls.Config, error) {
func buildTLSConfigFromMap(
parentConfig *tls.Config, tlsConfigMap map[string]any,
aiaFetcher *netext.AIAFetcher, insecureSkipVerify bool, logger logrus.FieldLogger,
) (*tls.Config, error) {
var cert, key, pass []byte
var ca [][]byte
var err error
Expand Down Expand Up @@ -212,7 +228,21 @@ func buildTLSConfigFromMap(parentConfig *tls.Config, tlsConfigMap map[string]any
ca = [][]byte{[]byte(caCertStr)}
}
}
return buildTLSConfig(parentConfig, cert, key, ca)
return buildTLSConfig(parentConfig, cert, key, ca, aiaFetcher, insecureSkipVerify, logger)
}

// buildTLSConfigForConnect builds the per-connect TLS config from the state and TLS map.
//
// TODO: this creates a fresh AIAFetcher per grpc.connect() call, so its cache and
// singleflight group don't carry over between connect calls in the same VU. gRPC's
// AIA use is niche, so acceptable for now; revisit by threading the Runner-level
// fetcher through lib.State.
func buildTLSConfigForConnect(parent *tls.Config, tlsMap map[string]any, state *lib.State) (*tls.Config, error) {
var aiaFetcher *netext.AIAFetcher
if state.Options.TLSAIAFetch.Bool {
aiaFetcher = netext.NewAIAFetcher(state.Dialer.DialContext)
}
return buildTLSConfigFromMap(parent, tlsMap, aiaFetcher, state.Options.InsecureSkipTLSVerify.Bool, state.Logger)
Comment on lines +241 to +245

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This here does create an AIA fetcher per grpc Connect. Given it likely won't be used with ti tall that much maybe we can have it just as a TODO / issue for the future/

}

// Connect is a block dial to the gRPC server at the given address (host:port)
Expand All @@ -233,7 +263,8 @@ func (c *Client) Connect(addr string, params sobek.Value) (bool, error) {
if !p.IsPlaintext {
tlsCfg := state.TLSConfig.Clone()
if len(p.TLS) > 0 {
if tlsCfg, err = buildTLSConfigFromMap(tlsCfg, p.TLS); err != nil {
tlsCfg, err = buildTLSConfigForConnect(tlsCfg, p.TLS, state)
if err != nil {
return false, err
}
}
Expand Down
197 changes: 197 additions & 0 deletions internal/js/modules/k6/grpc/client_aia_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
package grpc

import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"net"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"

"github.com/sirupsen/logrus"
"github.com/stretchr/testify/require"

"go.k6.io/k6/v2/internal/lib/testutils/tlstest"
"go.k6.io/k6/v2/lib/netext"
)

// AIA verification must honour per-connect cacerts, not the VU config's RootCAs.
func TestBuildTLSConfig_AIAWithCustomCACerts(t *testing.T) {
t.Parallel()

chain := tlstest.NewChain(t)

aiaSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/pkix-cert")
_, _ = w.Write(chain.IntermediateDER)
}))
t.Cleanup(aiaSrv.Close)
chain.SetAIAURL(aiaSrv.URL)

tlsListener := newLeafOnlyTLSListener(t, chain)
t.Cleanup(func() { _ = tlsListener.Close() })

// VU config has no RootCAs — the user relies on per-connect cacerts.
vuCfg := &tls.Config{MinVersion: tls.VersionTLS12}
wrappedVU := netext.NewAIAFetcher(nil).Wrap(vuCfg, nullLogger())

rootPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: chain.RootDER})
tlsCfg, err := buildTLSConfig(wrappedVU, nil, nil, [][]byte{rootPEM}, netext.NewAIAFetcher(nil), false, nullLogger())
require.NoError(t, err)

tlsCfg.ServerName = "localhost"

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
dialer := &tls.Dialer{Config: tlsCfg}
conn, err := dialer.DialContext(ctx, "tcp", tlsListener.Addr().String())
require.NoError(t, err)
_ = conn.Close()
}

func TestBuildTLSConfig_InsecureSkipVerifyIsHonoured(t *testing.T) {
t.Parallel()

chain := tlstest.NewChain(t)
chain.SetAIAURL("http://127.0.0.1:1") // never reached; skip short-circuits

tlsListener := newLeafOnlyTLSListener(t, chain)
t.Cleanup(func() { _ = tlsListener.Close() })

// Unrelated CA — verification would fail if it ran.
otherChain := tlstest.NewChain(t)
otherRootPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: otherChain.RootDER})

for _, aiaEnabled := range []bool{false, true} {
name := "aia_off"
if aiaEnabled {
name = "aia_on"
}
t.Run(name, func(t *testing.T) {
t.Parallel()

vuCfg := &tls.Config{
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: true,
}
parent := vuCfg
if aiaEnabled {
parent = netext.NewAIAFetcher(nil).Wrap(vuCfg, nullLogger())
}

var aiaFetcher *netext.AIAFetcher
if aiaEnabled {
aiaFetcher = netext.NewAIAFetcher(nil)
}
tlsCfg, err := buildTLSConfig(parent, nil, nil, [][]byte{otherRootPEM}, aiaFetcher, true, nullLogger())
require.NoError(t, err)
require.True(t, tlsCfg.InsecureSkipVerify, "user asked to skip verification; must be honoured")
tlsCfg.ServerName = "localhost"

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
dialer := &tls.Dialer{Config: tlsCfg}
conn, err := dialer.DialContext(ctx, "tcp", tlsListener.Addr().String())
require.NoError(t, err, "handshake must succeed with insecureSkipTLSVerify even against a mismatched CA")
_ = conn.Close()
})
}
}

// Guard against silent insecurity: with AIA enabled and mismatched cacerts, the dial
// must fail — even though the AIA wrapper flips InsecureSkipVerify internally.
func TestBuildTLSConfig_AIARejectsMismatchedCACert(t *testing.T) {
t.Parallel()

chain := tlstest.NewChain(t)

aiaSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/pkix-cert")
_, _ = w.Write(chain.IntermediateDER)
}))
t.Cleanup(aiaSrv.Close)
chain.SetAIAURL(aiaSrv.URL)

tlsListener := newLeafOnlyTLSListener(t, chain)
t.Cleanup(func() { _ = tlsListener.Close() })

// Unrelated root — the caller supplies this as cacerts; the server presents a chain
// rooted in a different CA, so verification must fail.
otherChain := tlstest.NewChain(t)
otherRootPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: otherChain.RootDER})

vuCfg := &tls.Config{MinVersion: tls.VersionTLS12}
wrappedVU := netext.NewAIAFetcher(nil).Wrap(vuCfg, nullLogger())

tlsCfg, err := buildTLSConfig(wrappedVU, nil, nil, [][]byte{otherRootPEM}, netext.NewAIAFetcher(nil), false, nullLogger())
require.NoError(t, err)
tlsCfg.ServerName = "localhost"

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
dialer := &tls.Dialer{Config: tlsCfg}
conn, err := dialer.DialContext(ctx, "tcp", tlsListener.Addr().String())
if conn != nil {
_ = conn.Close()
}
require.Error(t, err, "AIA-enabled gRPC dial must fail when cacerts do not cover the server chain")
require.True(t,
strings.Contains(err.Error(), "unknown authority") ||
strings.Contains(err.Error(), "signed by"),
"unexpected error: %v", err,
)
}

// newLeafOnlyTLSListener starts a raw tls.Listener serving only the leaf certificate.
func newLeafOnlyTLSListener(t testing.TB, c *tlstest.Chain) net.Listener {
t.Helper()

leafCert, err := tls.X509KeyPair(
pemEncodeCert(c.LeafTLSCertificate(t).Certificate[0]),
pemEncodeKey(t, c),
)
require.NoError(t, err)

listener, err := tls.Listen("tcp", "127.0.0.1:0", &tls.Config{
Certificates: []tls.Certificate{leafCert},
MinVersion: tls.VersionTLS12,
})
require.NoError(t, err)

go func() {
for {
conn, acceptErr := listener.Accept()
if acceptErr != nil {
return
}
if tlsConn, ok := conn.(*tls.Conn); ok {
hctx, hcancel := context.WithTimeout(context.Background(), 5*time.Second)
_ = tlsConn.HandshakeContext(hctx)
hcancel()
}
_ = conn.Close()
}
}()
return listener
}

func pemEncodeCert(der []byte) []byte {
return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der})
}

func pemEncodeKey(t testing.TB, c *tlstest.Chain) []byte {
t.Helper()
keyDER, err := x509.MarshalECPrivateKey(c.LeafKey)
require.NoError(t, err)
return pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: keyDER})
}

func nullLogger() logrus.FieldLogger {
l := logrus.New()
l.SetLevel(logrus.PanicLevel)
return l
}
17 changes: 17 additions & 0 deletions internal/js/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ type Runner struct {
RPSLimit *rate.Limiter
RunTags *metrics.TagSet

// aiaFetcher owns the AIA cache + HTTP client when tlsAIAFetch is enabled.
aiaFetcher *netext.AIAFetcher

console *console
setupData []byte
BufferPool *lib.BufferPool
Expand Down Expand Up @@ -194,6 +197,9 @@ func (r *Runner) newVU(
})
tlsConfig.NameToCertificate = nameToCert //nolint:staticcheck
}
if r.Bundle.Options.TLSAIAFetch.Bool {
tlsConfig = r.aiaFetcher.Wrap(tlsConfig, r.preInitState.Logger) //nolint:contextcheck
}
transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: tlsConfig,
Expand Down Expand Up @@ -579,6 +585,17 @@ func (r *Runner) SetOptions(opts lib.Options) error {
return err
}

// Rebuild the AIA fetcher against the new options — Blacklist / BlockedHostnames /
// Hosts / Resolver may have changed and the fetcher's dialer captures them.
runnerDialer := &netext.Dialer{
Dialer: r.BaseDialer,
Resolver: r.Resolver,
Blacklist: opts.BlacklistIPs,
BlockedHostnames: opts.BlockedHostnames.Trie,
Hosts: opts.Hosts.Trie,
}
r.aiaFetcher = netext.NewAIAFetcher(runnerDialer.DialContext)
Comment on lines +588 to +597

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I guess it's not critical, but this allocation could be gated behind:

if tlsAIAFetch := opts.TLSAIAFetch; tlsAIAFetch.Valid && tlsAIAFetch.Bool {
  ...
}


// FIXME: add tests
r.RunTags = r.preInitState.Registry.RootTagSet().WithTagsFromMap(r.Bundle.Options.RunTags)

Expand Down
Loading
Loading