Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
8c7bc69
add srvAllowedHostsSuffix
sleepyStick Jun 11, 2026
790505e
fix yml test
sleepyStick Jun 15, 2026
914c830
tests should error
sleepyStick Jun 15, 2026
529ad3b
Update source/initial-dns-seedlist-discovery/initial-dns-seedlist-dis…
sleepyStick Jun 22, 2026
6556c30
Update source/initial-dns-seedlist-discovery/initial-dns-seedlist-dis…
sleepyStick Jun 22, 2026
871c812
address MD feedback
sleepyStick Jun 22, 2026
c3f590c
edit comment on yml test
sleepyStick Jun 22, 2026
2dd89df
minor edit to comment again haha
sleepyStick Jun 22, 2026
86d1633
AC feedback
sleepyStick Jun 24, 2026
013594b
add tests
sleepyStick Jun 29, 2026
8122297
add tests + better param parsing specifications
sleepyStick Jul 1, 2026
78ff515
reference psl from drivers-3568 and update date in changelog
sleepyStick Aug 24, 2026
403ec19
Merge branch 'master' into DRIVERS-3329
sleepyStick Aug 24, 2026
fde06d6
Update source/initial-dns-seedlist-discovery/initial-dns-seedlist-dis…
sleepyStick Aug 25, 2026
d2731ce
add rationale and update language in other specs
sleepyStick Aug 25, 2026
a84bd3b
Update source/initial-dns-seedlist-discovery/initial-dns-seedlist-dis…
sleepyStick Aug 25, 2026
fd2ba7a
update changelog
sleepyStick Aug 26, 2026
2d909b6
Merge remote-tracking branch 'origin/master' into DRIVERS-3329
sleepyStick Aug 26, 2026
0521b1b
add test for period as suffix
sleepyStick Aug 27, 2026
a269f3a
add test
sleepyStick Aug 28, 2026
3d70d36
more feedback + update changelog dates
sleepyStick Sep 1, 2026
6f4dea1
compromise 1
sleepyStick Sep 2, 2026
bf61386
Update source/initial-dns-seedlist-discovery/initial-dns-seedlist-dis…
sleepyStick Sep 2, 2026
7f5ef49
i think rule 1-3 were accidentally removed? and run pre-commit
sleepyStick Sep 2, 2026
774e15a
AC feedback
sleepyStick Sep 2, 2026
4be82e2
AC feedback pt 2
sleepyStick Sep 2, 2026
5ab10b2
AC feedback pt 3
sleepyStick Sep 2, 2026
e3393a8
Update source/initial-dns-seedlist-discovery/initial-dns-seedlist-dis…
sleepyStick Sep 2, 2026
7711c55
AC feedback pt 4
sleepyStick Sep 2, 2026
c7c7726
fix changelog dates and AC feedback pt 5
sleepyStick Sep 3, 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

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!

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 think the section reads well! I wouldn't change anything. One thing I would suggest adding though is info around the alternative @damieng raised - the driver provided hook. Something like

Why not a validation callback?
An alternative considered was a driver-provided hook....bla.....bla.... bla

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,29 @@ 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 leading or trailing `.` MUST be stripped. For example, `srvAllowedHostsSuffix=.mydomain.net.` is treated as
`mydomain.net`. If the resulting stripped value is empty, an error MUST be raised.
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 MUST contain at least two `.` separated labels. For example, `srvAllowedHostsSuffix=net` MUST
raise an error.
5. Drivers SHOULD raise an error if the resulting value is a public suffix, per the algorithm in
[Public Suffix List](../public-suffix-list/public-suffix-list.md).
6. 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 +108,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 +148,22 @@ 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, in this order:

1. Any trailing `.` MUST be stripped. For example, `host.mydomain.net.` becomes `host.mydomain.net`.
2. The hostname MUST be converted to its A-label (Punycode) form.
3. The hostname MUST be normalized to lowercase using ASCII case folding.

The `{domainname}` that returned hostnames are validated against MUST have the same three normalizations applied, so
that neither trailing dots, case, nor Unicode/Punycode encoding can affect the comparison. When `{domainname}` comes
from `srvAllowedHostsSuffix`, steps 1-3 of [srvAllowedHostsSuffix](#srvallowedhostssuffix) already apply them. The
leading `.` that step 6 prepends makes the value a label-aligned suffix and is not part of this normalization.

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.

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.

Just noting that If we add the two-label MUST, this carve-out stays correct and nothing here needs to change. If we don't, this sentence has to go, because the property it relies on no longer holds.


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

Expand Down Expand Up @@ -247,6 +283,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 +352,11 @@ In the future we could consider using the priority and weight fields of the SRV

## ChangeLog

- 2026-09-03: Specify that host names returned through SRV records, and the `{domainname}` they are validated against,
are both normalized -- trailing dot stripped, converted to A-label form, ASCII lowercased -- before validation.

- 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,8 @@
# 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:
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,7 @@
# srvAllowedHostsSuffix MUST name at least one label. A value of "." is empty once the
# leading and trailing "." are stripped, so it names no domain at all and MUST raise an
# error rather than being treated as a suffix that every host matches.
uri: "mongodb+srv://test1.test.build.10gen.cc/?srvAllowedHostsSuffix=."
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
Expand Up @@ -11,5 +11,4 @@ hosts:
- localhost:27019
options:
ssl: true
srvAllowedHostsSuffix: "10gen.cc"
ping: true
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
# "cc" is an ordinary rule in the Public Suffix List, so it is itself a public suffix
# and must be rejected. The SRV hosts do end with "cc", so the host suffix check
# passes and the public suffix check is the only thing that can fail here.
# "cc" is an ordinary rule in the Public Suffix List and is only one label and therefore MUST be rejected.
uri: "mongodb+srv://test1.test.build.10gen.cc/?srvAllowedHostsSuffix=cc"
seeds: []
hosts: []
Expand Down

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

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.

We should add a test similar to this one, but using only a period as an allowed hosts suffix. The test should expect an error.

For example:

uri: "mongodb+srv://test1.test.build.10gen.cc/?srvAllowedHostsSuffix=."
seeds: []
hosts: []
error: true

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.

good idea! done in 0521b1b

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# srvAllowedHostsSuffix SHOULD NOT be a public suffix. "cc" is an ordinary rule in the
# Public Suffix List, and isn't two or more `.` separated labels so `.cc` MUST raise an error.
uri: "mongodb+srv://test1.test.build.10gen.cc/?srvAllowedHostsSuffix=.cc"
seeds: []
hosts: []
error: true
Comment thread
aclark4life marked this conversation as resolved.

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,8 @@
# 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:
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,8 @@
# 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:
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,6 @@
uri: "mongodb+srv://test12.test.build.10gen.cc/?srvAllowedHostsSuffix=build.10gen.cc"
seeds:
- localhost.build.10gen.cc:27017
options:
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-09-03: 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
7 changes: 7 additions & 0 deletions source/public-suffix-list/public-suffix-list.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ vendored file MUST therefore convert one side before comparing, so that a Punyco
`xn--` labels) and the Unicode rules it is compared against are in the same form. Comparing the two forms directly will
fail to match rules that should match.

Note: The leading `*` and `!` markers in the PSL are not part of a label and MUST NOT be included in that conversion. A
driver MUST strip them before converting a rule and reapply them after, so that a rule such as `!公司.cn` converts to
`!xn--55qx5d.cn` rather than being mangled.

### Determining the public suffix

To determine the public suffix of a domain, a driver using the vendored file MUST follow the
Expand Down Expand Up @@ -187,4 +191,7 @@ notice does not apply to the rest of this repository.

## Changelog

- 2026-09-03: Clarify that markers shouldn't be converted to Punycode and add a prose test covering internationalized
(Punycode vs. Unicode) rules.

- 2026-08-11: Vendor the Public Suffix List and add a script to sync it.
9 changes: 7 additions & 2 deletions source/public-suffix-list/tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,18 @@ This test utilizes the rule `!www.ck` in the PSL, which overrides the `*.ck` rul
When no rule matches, the prevailing rule is `*` and the rightmost label alone is the public suffix. Assert that
`is_public_suffix("nosuchtld") -> true` and `is_public_suffix("foo.nosuchtld") -> false`.

### 6. An internationalized rule

This test utilizes the rules `рф` and `公司.cn` in the PSL, which are stored as Unicode while the hostnames being compared
are Punycode-encoded. Assert that `is_public_suffix("xn--p1ai") -> true`,
`is_public_suffix("example.xn--p1ai") -> false`, and `is_public_suffix("xn--55qx5d.cn") -> true`.

## Connection String Tests

The `srvAllowedHostsSuffix-psl-*` tests in the
[Initial DNS Seedlist Discovery tests](https://github.com/mongodb/specifications/tree/master/source/initial-dns-seedlist-discovery/tests/replica-set)
cover the two cases that are observable through a connection string: a suffix that is a public suffix (`cc`) is
rejected, and one that is not (`10gen.cc`) is accepted. Both use a suffix the test SRV hosts end with, so the host
suffix check passes and the public suffix check is the only thing that can change the outcome.
rejected, and one that is not (`10gen.cc`) is accepted.

They live with the seedlist discovery tests because `srvAllowedHostsSuffix` is a connection string option, so a driver
should implement the parsing of the PSL and the uri option together.
9 changes: 9 additions & 0 deletions source/uri-options/tests/srv-options.json

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

7 changes: 7 additions & 0 deletions source/uri-options/tests/srv-options.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,10 @@ tests:
options:
loadBalanced: true
srvMaxHosts: 0
- description: "Non-SRV URI with srvAllowedHostsSuffix"
uri: "mongodb://example.com/?srvAllowedHostsSuffix=.mongodb.net"
valid: false
warning: false
hosts: ~
auth: ~
options: ~
Loading
Loading