Per-client TLS config vs global insecureSkipTLSVerify for mixed-trust k6 targets
0 reputation · 10 Jan 2023, 20:52 UTC
0 reputation · 10 Jan 2023, 20:52 UTC
k6 scripts that target both public CA-signed endpoints and internal services with self-signed certificates face a configuration trade-off. The http client supports insecureSkipTLSVerify, which disables certificate validation for every request using that client instance, but k6 does not document a per-request override. Mixing trust models therefore requires either a dedicated client with validation disabled or accepting a global bypass across the script.
The unresolved concern is failure observability. Certificate validation errors, DNS resolution problems, and connection failures are aggregated into the same http_req_failed metric with only a generic error string from Go's TLS package. Without built-in separation, distinguishing an expired certificate from an untrusted CA or a DNS lookup failure requires parsing error messages manually.
When a script uses multiple http clients—some with certificate validation enabled and others with insecureSkipTLSVerify—does the default metric aggregation still obscure the boundary between TLS handshake failures and DNS errors, or does client-scoped configuration alter how those failures are reported? Is there a documented approach for preserving separate failure classes for connection setup versus certificate validation when mixed TLS configurations coexist in a single k6 run?
33275 reputation · 11 Jan 2023, 04:15 UTC
Using multiple HTTP clients or per-request configurations with insecureSkipTLSVerify does not alter how k6 aggregates failures. Regardless of whether a request is configured to skip TLS verification or enforce it, all connection-level failures—including DNS resolution errors, TCP timeouts, and TLS handshake failures—are aggregated into the same http_req_failed metric.
k6 does not provide separate built-in metrics for different classes of connection failures. The http_req_failed metric is a binary indicator; it does not distinguish between a target that is unreachable (DNS/Network) and a target that is unreachable due to a trust violation (TLS). Client-scoped configurations change the outcome of the request, but they do not change the reporting mechanism.
To preserve separate failure classes for connection setup versus certificate validation, you must manually categorize the error returned in the response object. Because k6 is built on Go, TLS errors are passed through from the Go tls package.
The following pattern allows you to create custom Trend or Counter metrics to track these failures separately:
import { check } from 'k6';
import { Counter } from 'k6/metrics';
import http from 'k6/http';
const tlsFailures = new Counter('tls_failures');
const dnsFailures = new Counter('dns_failures');
export default function () {
const res = http.get('https://internal-service.local', {
insecureSkipTLSVerify: false,
});
if (res.status === 0) {
const error = res.error || '';
if (error.includes('certificate signed by unknown authority') || error.includes('x509')) {
tlsFailures.add(1);
} else if (error.includes('no such host') || error.includes('dns')) {
dnsFailures.add(1);
}
}
}
res.error string parsing is fragile. Go's internal error messages can change between versions, which may break your categorization logic.To verify this in your environment, run a script targeting a non-existent domain and a domain with an expired/self-signed certificate. Confirm that both increment http_req_failed but produce different strings in the res.error field.
Missing Diagnostic: Are you utilizing a custom k6 extension for HTTP handling, or are you using the standard k6/http module?
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.