@@ -450,6 +450,30 @@ def __init__(
450450 connect to. More specifically, when a "mongodb+srv://" connection string
451451 resolves to more than srvMaxHosts number of hosts, the client will randomly
452452 choose an srvMaxHosts sized subset of hosts.
453+ - `srvAllowedHostsSuffix`: (string) Overrides the default requirement that
454+ hosts returned by SRV DNS records share the same parent domain as the seed
455+ hostname. When set, the driver accepts any returned host whose name ends
456+ with this suffix (e.g. ``".atlas.mongodb.com"``). The value must contain
457+ at least two labels and must not be a public suffix (per the Public Suffix
458+ List). Only valid with ``mongodb+srv://`` URIs.
459+
460+ .. warning::
461+
462+ This option relaxes a built-in DNS spoofing safeguard. Use the most
463+ specific suffix possible for your deployment rather than a broad
464+ company-wide domain. For example, instead of::
465+
466+ AsyncMongoClient(
467+ "mongodb+srv://cluster.test.internal.example.com/",
468+ srvAllowedHostsSuffix=".example.com",
469+ )
470+
471+ which would accept any host across the entire domain, scope it further like so::
472+
473+ AsyncMongoClient(
474+ "mongodb+srv://cluster.test.internal.example.com/",
475+ srvAllowedHostsSuffix=".internal.example.com",
476+ )
453477
454478
455479 | **Write Concern options:**
@@ -803,6 +827,7 @@ def __init__(
803827 fqdn = None
804828 srv_service_name = keyword_opts .get ("srvservicename" )
805829 srv_max_hosts = keyword_opts .get ("srvmaxhosts" )
830+ srv_allowed_hosts_suffix = keyword_opts .get ("srvallowedhostssuffix" )
806831 if len ([h for h in self ._host if "/" in h ]) > 1 :
807832 raise ConfigurationError ("host must not contain multiple MongoDB URIs" )
808833 for entity in self ._host :
@@ -853,6 +878,8 @@ def __init__(
853878 srv_service_name = opts .get ("srvServiceName" , common .SRV_SERVICE_NAME )
854879
855880 srv_max_hosts = srv_max_hosts or opts .get ("srvmaxhosts" )
881+ if srv_allowed_hosts_suffix is None :
882+ srv_allowed_hosts_suffix = opts .get ("srvallowedhostssuffix" )
856883 opts = self ._normalize_and_validate_options (opts , self ._seeds )
857884
858885 # Username and password passed as kwargs override user info in URI.
@@ -890,7 +917,9 @@ def __init__(
890917
891918 self ._retry_policy = _RetryPolicy (attempts = self ._options .max_adaptive_retries )
892919
893- self ._init_based_on_options (self ._seeds , srv_max_hosts , srv_service_name )
920+ self ._init_based_on_options (
921+ self ._seeds , srv_max_hosts , srv_service_name , srv_allowed_hosts_suffix
922+ )
894923
895924 self ._opened = False
896925 self ._closed = False
@@ -908,6 +937,7 @@ async def _resolve_srv(self) -> None:
908937 opts = common ._CaseInsensitiveDictionary ()
909938 srv_service_name = keyword_opts .get ("srvservicename" )
910939 srv_max_hosts = keyword_opts .get ("srvmaxhosts" )
940+ srv_allowed_hosts_suffix = keyword_opts .get ("srvallowedhostssuffix" )
911941 for entity in self ._host :
912942 # A hostname can only include a-z, 0-9, '-' and '.'. If we find a '/'
913943 # it must be a URI,
@@ -928,6 +958,7 @@ async def _resolve_srv(self) -> None:
928958 connect_timeout = timeout ,
929959 srv_service_name = srv_service_name ,
930960 srv_max_hosts = srv_max_hosts ,
961+ srv_allowed_hosts_suffix = srv_allowed_hosts_suffix ,
931962 )
932963 seeds .update (res ["nodelist" ])
933964 opts = res ["options" ]
@@ -960,6 +991,8 @@ async def _resolve_srv(self) -> None:
960991 srv_service_name = opts .get ("srvServiceName" , common .SRV_SERVICE_NAME )
961992
962993 srv_max_hosts = srv_max_hosts or opts .get ("srvmaxhosts" )
994+ if srv_allowed_hosts_suffix is None :
995+ srv_allowed_hosts_suffix = opts .get ("srvAllowedHostsSuffix" )
963996 opts = self ._normalize_and_validate_options (opts , seeds )
964997
965998 # Username and password passed as kwargs override user info in URI.
@@ -969,10 +1002,16 @@ async def _resolve_srv(self) -> None:
9691002 username , password , self ._resolve_srv_info ["dbase" ], opts , _IS_SYNC
9701003 )
9711004
972- self ._init_based_on_options (seeds , srv_max_hosts , srv_service_name )
1005+ self ._init_based_on_options (
1006+ seeds , srv_max_hosts , srv_service_name , srv_allowed_hosts_suffix
1007+ )
9731008
9741009 def _init_based_on_options (
975- self , seeds : Collection [tuple [str , int ]], srv_max_hosts : Any , srv_service_name : Any
1010+ self ,
1011+ seeds : Collection [tuple [str , int ]],
1012+ srv_max_hosts : Any ,
1013+ srv_service_name : Any ,
1014+ srv_allowed_hosts_suffix : Any ,
9761015 ) -> None :
9771016 self ._event_listeners = self ._options .pool_options ._event_listeners
9781017 self ._topology_settings = TopologySettings (
@@ -991,6 +1030,7 @@ def _init_based_on_options(
9911030 load_balanced = self ._options .load_balanced ,
9921031 srv_service_name = srv_service_name ,
9931032 srv_max_hosts = srv_max_hosts ,
1033+ srv_allowed_hosts_suffix = srv_allowed_hosts_suffix ,
9941034 server_monitoring_mode = self ._options .server_monitoring_mode ,
9951035 topology_id = self ._topology_settings ._topology_id if self ._topology_settings else None ,
9961036 )
0 commit comments