Skip to content

MongoDB connection URIs with embedded credentials are logged in cleartext #1345

Description

@ademidoff

Describe the bug

Several log statements write MongoDB connection URIs verbatim. Because the exporter injects the monitoring user's credentials into the URI before these statements run, the emitted log line contains user:password in cleartext.

This came up while reviewing #1336, which redacts one of these call sites as a side effect of its feature work. The remaining sites are unrelated to that PR and should be fixed separately — hence this issue.

Affected call sites (all on main @ 9585218)

# Location Level Reachable at default log level
1 main.go:143log.Debug("Connection URI", "uri", uri) Debug No (needs --log.level=debug)
2 exporter/seedlist.go:31log.Fatalf("Failed to parse URI %s: %v", uri, err) Fatal Yes
3 exporter/seedlist.go:36logger.Error("Failed to lookup SRV records", "uri", uri, ...) Error Yes
4 exporter/seedlist.go:41logger.Error("No SRV records found", "uri", uri) Error Yes
5 main.go:259log.Fatalf("Failed to parse URI %s: %v", hosturl, err) (the --split-cluster path) Fatal Yes
6 exporter/server.go:181log.Error("Unable to parse provided address as url", "address", e.opts.URI, ...) Error Yes

Why the URI carries credentials

buildURI (main.go:300) injects them explicitly:

if parsedURI.User == nil && user != "" && password != "" {
    parsedURI.User = url.UserPassword(user, password)
}

and the fallback path buildURIManually (main.go:291) does the same by string concatenation. A URI supplied directly via --mongodb.uri / MONGODB_URI may of course already embed credentials, which covers the seedlist and buildServerMap cases where buildURI isn't involved.

Severity notes

  • Site 1 only fires with debug logging enabled. That is not the default, but debug logs are exactly what users attach to support tickets and GitHub issues, so credentials leak into third-party hands routinely.
  • Sites 2–6 fire at the default log level, on error paths that are easy to hit in practice: a malformed URI, an unresolvable SRV record, or a DNS hiccup. Sites 3 and 4 in particular are plain operational failures, not misconfiguration — an SRV lookup failure on a mongodb+srv:// URI will print the credentials on every affected startup.
  • These are the more serious of the two groups, and none of them are covered by feat: add dynamic target support #1336.

Expected behavior

No log statement should emit a MongoDB URI with a readable password, at any level.

Suggested fix

(*url.URL).Redacted() handles the cases where the URI parses:

func redactMongoURI(rawURI string) string {
    uri, err := url.Parse(rawURI)
    if err != nil {
        return "<invalid MongoDB URI>"
    }
    return uri.Redacted()
}

Two things to watch when applying it:

  • Sites 2, 5, and 6 log because parsing failed, so Redacted() is not available on the parsed value — those need string-level scrubbing (or should log only the host portion / omit the URI entirely and rely on the error).
  • A shared helper is preferable to per-site fixes so new logging doesn't reintroduce the leak. feat: add dynamic target support #1336 adds an unexported redactMongoURI in package main; exporter/seedlist.go and exporter/server.go live in package exporter and cannot reuse it as written. Placing it somewhere both packages can import (e.g. internal/) would be worthwhile.

Related

Environment

Not environment-specific; applies to all builds from main.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions