k6 environment‑variable credential handling without built‑in secret redaction
0 reputation · 01 Jul 2022, 21:45 UTC
0 reputation · 01 Jul 2022, 21:45 UTC
Goal: Execute k6 load‑test scripts that read credentials from environment variables so that no secret is stored in the script file, while running against a staging or mock integration that mirrors production behavior.
Concern: k6 injects variables into the JavaScript runtime but provides no automatic redaction; if a script unintentionally logs or outputs __ENV values, the credentials appear in test output, logs, or result files, exposing them to all virtual users.
Uncertainty: Teams must decide whether to rely solely on environment‑variable injection for credential handling in k6 or to supplement it with an external secrets manager (e.g., HashiCorp Vault, AWS Secrets Manager) and fetch secrets at runtime via an HTTP call, which k6 does not natively support.
34275 reputation · 02 Jul 2022, 08:47 UTC
The main risk is accidental disclosure. Any call to console.log, console.error, or a custom logger that includes __ENV (or a specific key) will emit the secret to every virtual user. The value then appears in k6’s terminal output, CSV/JSON result files, and any downstream monitoring system (InfluxDB, Prometheus, Grafana). Because k6 does not scrub __ENV automatically, a single debug line can leak credentials across the entire test run.
Apply a global wrapper in the test’s init context that intercepts all console output and strips __ENV keys before they are written.
// init.js (loaded via --include or via import in the main script)
const originalLog = console.log;
const originalError = console.error;
function safeLog(method, args) {
const clean = args.map(a => {
if (typeof a === 'object' && a !== null) {
const copy = { ...a };
delete copy.__ENV;
return copy;
}
return a;
});
method.call(console, ...clean);
}
console.log = (...args) => safeLog(originalLog, args);
console.error = (...args) => safeLog(originalError, args);
export { }; // make it an ES module so k6 loads it
Place this file (e.g., init.js) and run k6 with k6 run --include init.js script.js. All VUs inherit the overridden logger, so no per‑script changes are needed.
Yes. Perform an HTTP request to the secrets manager in the init phase (or a setup function), store the returned token in a module‑scoped variable, and reuse it in VU iterations.
// secrets.js
let token = '';
export function setup() {
const res = http.get('https://vault.example.com/v1/secret/data/k6-test', {
headers: { 'X-Vault-Token': __ENV.VAULT_TOKEN }
});
if (res.status === 200) {
token = res.json().data.data.token;
} else {
throw new Error('Failed to retrieve secret');
}
}
export default function () {
// use token here; no further HTTP call per iteration
http.get('https://api.example.com/protected', {
headers: { Authorization: `Bearer ${token}` }
});
}
This adds a single latency cost at test start; subsequent iterations incur no extra secret‑fetch overhead. Ensure the load‑generator nodes can reach the secrets manager endpoint.
__ENV and run it with a dummy env var (K6_TEST_TOKEN=foo). Confirm the secret appears in the output.init.js logger override and re‑run; verify the secret no longer appears while other log messages still do.secrets.js pattern with a mock endpoint that returns a JSON token. Run the test and check that http_req_duration shows only one call (the setup) and that subsequent iterations succeed without additional latency.Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.