Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions source/connection-string/tests/invalid-uris.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions source/connection-string/tests/invalid-uris.yml
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,14 @@ tests:
hosts: ~
auth: ~
options: ~
-
description: "srvAllowedHostsSuffix with non-SRV URI"
uri: "mongodb://localhost:27017/?srvAllowedHostsSuffix=.mongodb.net"
valid: false
warning: ~
hosts: ~
auth: ~
options: ~
-
description: "Username with password containing an unescaped percent sign"
uri: "mongodb://alice%foo:bar@127.0.0.1"
Expand Down

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.

There's no security-considerations text for this new option. The Design Rationale section is a single pre-existing sentence and doesn't mention srvAllowedHostsSuffix. I think we could benefit from a subsection covering: why the option exists; that the PSL bounds the blast radius but doesn't make a configuration safe; guidance to use the narrowest suffix that work. PyMongo already has good material for most of this in its MongoClient docstring warning. Also helps future spec readers.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

added in d2731ce
honestly i feel a bit off about my wording though? so lmk if you have any suggestions on the wording here!

Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ mongodb+srv://{hostname}/{options}
`{options}` refers to the optional elements from the [Connection String](../connection-string/connection-string-spec.md)
specification following the `Host Information`. This includes the `Auth database` and `Connection Options`.

For the purposes of this document, `{hostname}` will be divided using the following terminology. If an SRV `{hostname}`
For the purposes of this document, `{hostname}` will be divided using the following terminology. If
`srvAllowedHostsSuffix` has been configured, then that will act as the `{domainname}`. Otherwise, if an SRV `{hostname}`
Comment on lines +40 to +41

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.

Question for the rationale rather than a change request: nothing requires {hostname} itself to end with the suffix, so mongodb+srv://cluster.example.org/?srvAllowedHostsSuffix=bank.com is legal.

I initially thought that should be forbidden, but I don't think it should — it would break the vanity-CNAME shape, where the SRV name is a corporate alias and the hosts live in a provider domain, which seems like one of the cases this option might be used for. So I'd just like the rationale to say that decoupling is deliberate and why, since it's the point where the trust anchor stops being tied to the name the user typed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

that is correct, I've added this to the rationale in d2731ce

has:

1. Three or more `.` separated parts, then the left-most part is the `{subdomain}` and the remaining portion is the
Expand Down Expand Up @@ -65,6 +66,27 @@ Only `{domainname}` is used during SRV record verification and `{subdomain}` is

### MongoClient Configuration

#### srvAllowedHostsSuffix

This option is used to validate hosts. If present, its value MUST be treated as the `{domainname}` for
[DNS validation](#querying-dns) and
[SRV polling](../polling-srv-records-for-mongos-discovery/polling-srv-records-for-mongos-discovery.md). For example,

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.

The option description now says it applies to SRV polling but that spec document isn't touched by this PR and still carries its own normative sentence: "A driver MUST verify that the host names returned through SRV records have the same parent {domainname}."

More subtly, the two documents now use {domainname} to mean different things — in the polling spec it's the {hostname}.{domainname} split; here it's the entire configured suffix when the option is set. WE should update SRV polling spec accordingly as well.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

oo thanks for pointing that out! fixed in d2731ce

`srvAllowedHostsSuffix=.mydomain.net`. Drivers MUST apply the following normalization and validation to the value, in
this order:

1. Any trailing `.` MUST be stripped. For example, `srvAllowedHostsSuffix=.mydomain.net.` is treated as `.mydomain.net`.
2. The value MUST be converted to its A-label (Punycode) form, so that it is comparable against the A-label hostnames
returned by DNS.
3. The value MUST be normalized to lowercase using ASCII case folding.
4. The resulting value, with any leading `.` removed, MUST NOT be a public suffix, per the algorithm in
[Public Suffix List](../public-suffix-list/public-suffix-list.md).
5. If the value does not begin with a `.`, a `.` MUST be prepended. For example, `srvAllowedHostsSuffix=mydomain.net` is
treated as `.mydomain.net`.

If this option is not present, the `{domainname}` MUST be inferred from the `{hostname}` (as described in
[Connection String Format](#connection-string-format)). This option MUST only be configurable at the level of a
`MongoClient`.
Comment thread
rozza marked this conversation as resolved.

#### srvMaxHosts

This option is used to limit the number of mongos connections that may be created for sharded topologies. This option
Expand All @@ -84,9 +106,9 @@ requires a string value and defaults to "mongodb". This option MUST only be conf

#### URI Validation

The driver MUST report an error if either the `srvServiceName` or `srvMaxHosts` URI options are specified with a non-SRV
URI (i.e. scheme other than `mongodb+srv`). The driver MUST allow specifying the `srvServiceName` and `srvMaxHosts` URI
options with an SRV URI (i.e. `mongodb+srv` scheme).
The driver MUST report an error if any of `srvServiceName`, `srvMaxHosts`, or `srvAllowedHostsSuffix` URI options are
specified with a non-SRV URI (i.e. scheme other than `mongodb+srv`). The driver MUST allow specifying the
`srvServiceName`, `srvMaxHosts`, and `srvAllowedHostsSuffix` URI options with an SRV URI (i.e. `mongodb+srv` scheme).

If `srvMaxHosts` is a positive integer, the driver MUST throw an error in the following cases:

Expand Down Expand Up @@ -124,10 +146,16 @@ If the DNS result returns no SRV records, or no records at all, or a DNS error h
indicating that the URI could not be used to find hostnames. The error SHALL include the reason why they could not be
found.

Before validating returned hostnames, drivers MUST normalize them as follows:

- Trailing dots MUST be stripped (e.g. `host.example.com.` becomes `host.example.com`).
- Hostnames MUST be normalized to lowercase using ASCII case folding.

A driver MUST verify that the host names returned through SRV records share the original SRV's `{domainname}`. In
addition, SRV records with fewer than three `.` separated parts, the returned hostname MUST have at least one more
domain level than the SRV record hostname. Drivers MUST raise an error and MUST NOT initiate a connection to any
returned hostname which does not fulfill these requirements.
addition, when `srvAllowedHostsSuffix` is not configured and the SRV record hostname has fewer than three `.` separated
parts, the returned hostname MUST have at least one more domain level than the SRV record hostname. Drivers MUST raise
an error and MUST NOT initiate a connection to any returned hostname which does not fulfill these requirements. This
additional requirement does not apply when `srvAllowedHostsSuffix` is configured.

The driver MUST NOT attempt to connect to any hosts until the DNS query has returned its results.

Expand Down Expand Up @@ -247,6 +275,39 @@ Several of our users have asked for this through tickets:
The design specifically calls for a pre-processing stage of the processing of connection URLs to minimize the impact on
existing functionality.

### Rationale for `srvAllowedHostsSuffix`

By default, the parent domain that returned host names are checked against is inferred from the seed by treating its
leftmost label as the `{subdomain}`. A seed of `mongodb.mydomain.net` results in `mongodb1.us-east-1.mydomain.net` being
rejected, even when both are owned by the same user. `srvAllowedHostsSuffix` allows users to work around this limitation
by letting the user state the parent domain directly instead, admitting such nested names and hosts in a different
domain altogether.

Note that nothing requires the `{hostname}` itself to end with the configured suffix. For example,
`mongodb+srv://cluster.example.org/?srvAllowedHostsSuffix=.hosts.example.net` is valid, even though
`cluster.example.org` does not end with `.hosts.example.net`. This is intentional: the option exists so the user can
state the allowed parent domain explicitly, rather than having it inferred from the connection string. Requiring the two
to match would restore that coupling, and would rule out arrangements such as a vanity alias in one organization's
domain resolving to hosts in a provider's domain.

Because it replaces that default requirement, this option relaxes a DNS spoofing safeguard. An attacker able to forge
SRV responses is otherwise confined to host names under the seed's `{domainname}`; with the option set, they are
confined to host names under the configured suffix instead. The broader that suffix, the more hosts a forged response
can direct a driver to.

Requiring that the value not be a public suffix bounds how broad it can get. It rules out values such as `.com` or
`.co.uk`, which would place no meaningful limit on a forged response. It does not, however, make a given configuration
safe: `.example.com` is not a public suffix, but it still admits every host in a large organization's domain.

Users should be encouraged to configure the narrowest suffix that covers their deployment, and drivers should say so
wherever the option is documented. For a seed hostname of `cluster.test.internal.example.com`, prefer the second of
these:

```text
mongodb+srv://cluster.test.internal.example.com/?srvAllowedHostsSuffix=.example.com
mongodb+srv://cluster.test.internal.example.com/?srvAllowedHostsSuffix=.internal.example.com
```

## Justifications

### Why Are Multiple Key-Value Pairs Allowed in One TXT Record?
Expand Down Expand Up @@ -283,6 +344,8 @@ In the future we could consider using the priority and weight fields of the SRV

## ChangeLog

- 2026-08-24: Add `srvAllowedHostsSuffix` MongoClient option.

- 2024-09-24: Removed requirement for URI to have three '.' separated parts; these SRVs have stricter parent domain
matching requirements for security. Create terminology section. Remove usage of term `{TLD}`. The `{hostname}` now
refers to the entire hostname, not just the `{subdomain}`.
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Tests that srvAllowedHostsSuffix comparison is case-insensitive (ASCII case folding per RFC 4343).
# The suffix .BUILD.10GEN.CC is uppercase but should match the returned host localhost.build.10gen.cc.
uri: "mongodb+srv://test12.test.build.10gen.cc/?srvAllowedHostsSuffix=.BUILD.10GEN.CC"
seeds:
- localhost.build.10gen.cc:27017
options:
srvAllowedHostsSuffix: .BUILD.10GEN.CC
ssl: true
ping: false

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# DNS record for test12.test.build.10gen.cc returns localhost.build.10gen.cc which would not match test.build.10gen.cc
uri: "mongodb+srv://test12.test.build.10gen.cc/?srvAllowedHostsSuffix=test.build.10gen.cc"
seeds: []
hosts: []
error: true

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# srvAllowedHostsSuffix MUST NOT be a public suffix. "cc" is an ordinary rule in the
# Public Suffix List, so ".cc" is a public suffix and MUST raise an error.
# Note: .cc is used (rather than an unrelated public suffix) because the DNS records
# return hosts ending in .cc, so the host suffix check passes and the public suffix
# check is the only thing that can fail here.
uri: "mongodb+srv://test1.test.build.10gen.cc/?srvAllowedHostsSuffix=.cc"
seeds: []
hosts: []
error: true

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# A trailing dot in srvAllowedHostsSuffix must be stripped before comparison,
# so ".build.10gen.cc." is equivalent to ".build.10gen.cc".
uri: "mongodb+srv://test12.test.build.10gen.cc/?srvAllowedHostsSuffix=.build.10gen.cc."
seeds:
- localhost.build.10gen.cc:27017
options:
srvAllowedHostsSuffix: .build.10gen.cc.
ssl: true
ping: false

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Also tests trailing dot normalization: DNS returns targets with a trailing dot
# (localhost.build.10gen.cc.) which must be stripped before suffix comparison.
uri: "mongodb+srv://test12.test.build.10gen.cc/?srvAllowedHostsSuffix=.build.10gen.cc"
seeds:
- localhost.build.10gen.cc:27017
options:
srvAllowedHostsSuffix: .build.10gen.cc
ssl: true
ping: false

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# dot should be prepended to `srvAllowedHostsSuffix` causing the host to be .uild.10gen.cc which does not match any available DNS records
uri: "mongodb+srv://test12.test.build.10gen.cc/?srvAllowedHostsSuffix=uild.10gen.cc"
seeds: []
hosts: []
error: true

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
uri: mongodb+srv://test12.test.build.10gen.cc/?srvAllowedHostsSuffix=build.10gen.cc
seeds:
- localhost.build.10gen.cc:27017
options:
srvAllowedHostsSuffix: build.10gen.cc
ssl: true
ping: false
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,14 @@ initial seedlist discovery. Periodic scan MUST follow these rules:
and protocol. The SRV service name is provided in the
[srvServiceName](../initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md#srvservicename) URI option and
defaults to `mongodb`. The protocol is always `tcp`. After prefixing, the URI should look like:
`_{srvServiceName}._tcp.{hostname}.{domainname}`.
- A driver MUST verify that the host names returned through SRV records have the same parent `{domainname}`. When this
verification fails, a driver:
`_{srvServiceName}._tcp.{hostname}.{domainname}`. Here `{hostname}.{domainname}` is the host from the connection
string, and is not affected by
[srvAllowedHostsSuffix](../initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md#srvallowedhostssuffix):
that option changes which parent domain returned host names are verified against, not which records are queried.
- A driver MUST verify that the host names returned through SRV records have the same parent `{domainname}`. When
[srvAllowedHostsSuffix](../initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md#srvallowedhostssuffix)
is configured, its value is the parent domain used for this verification instead of the `{domainname}` derived from
the connection string. When this verification fails, a driver:
- MUST NOT add such a non-compliant host name to the topology
- MUST NOT raise an error
- SHOULD log the non-compliance, including the host name
Expand Down Expand Up @@ -165,6 +170,8 @@ No future work is expected.

## Changelog

- 2026-08-24: Account for the `srvAllowedHostsSuffix` MongoClient option when verifying returned host names.

- 2024-08-22: Migrated from reStructuredText to Markdown.

- 2022-10-05: Revise spec front matter and reformat changelog.
Expand Down
Loading
Loading