Repository: getmoto/moto

Issue:
Add trusted hosts / networks to Server mode
Currently (Java) AWS API can only work with Moto from the local network (if it starts with "192.168.X.X") or if the request is coming from the same host as the server, that's very limiting because i want to run Moto on a container in an external network.

Any chance to add trusted networks or hosts via config file or CLI args?

I'd appreciate any info if this had been addressed or i'm using it wrong


Discussion:
SOLVED using 
request.putCustomRequestHeader("HOST", "motoserver")

on AWS api


I'm reopening this To request that server mode could work with requests coming from different IP addresses (e.g. that do _NOT_ start 192.168.X.X)

I'm trying to work with boto3 to Moto container running in a different network
and since boto3 doesn't support putting custom headers on the request - it's impossible to use Moto

I'd greatly appreciate any guidance if you think i'm doing something wrong  
Maybe I'm misunderstanding, does something like `moto_server ec2 -H 0.0.0.0` not work?
AFAIK No, not while working from a different network (requires, custom headers to be added)
If we bind to 0.0.0.0, it should allow requests from anyone (this is just a light wrapper around flask).

What requires the custom headers?
when i tried reaching moto from outside a Docker - i couldn't because it'll only allow connections from hosts named: "localhost"/"motoserver" or from IP address starting with "192.168.X.X".

I fixed it using PR #2316 but it's your decision whether to approve :)
I checked out the PR and I am wondering what @trickstyler has done to get it to work because in my case it's giving the same result

Resolving patch:
diff --git a/moto/server.py b/moto/server.py
--- a/moto/server.py
+++ b/moto/server.py
@@ -58,9 +58,7 @@ def get_backend_for_host(self, host):
                 if re.match(url_base, 'http://%s' % host):
                     return backend_name
 
-        raise RuntimeError('Invalid host: "%s"' % host)
-
-    def infer_service_region(self, environ):
+    def infer_service_region_host(self, environ):
         auth = environ.get('HTTP_AUTHORIZATION')
         if auth:
             # Signed request
@@ -70,21 +68,35 @@ def infer_service_region(self, environ):
             try:
                 credential_scope = auth.split(",")[0].split()[1]
                 _, _, region, service, _ = credential_scope.split("/")
-                return service, region
             except ValueError:
                 # Signature format does not match, this is exceptional and we can't
                 # infer a service-region. A reduced set of services still use
                 # the deprecated SigV2, ergo prefer S3 as most likely default.
                 # https://docs.aws.amazon.com/general/latest/gr/signature-version-2.html
-                return DEFAULT_SERVICE_REGION
+                service, region = DEFAULT_SERVICE_REGION
         else:
             # Unsigned request
             target = environ.get('HTTP_X_AMZ_TARGET')
             if target:
                 service, _ = target.split('.', 1)
-                return UNSIGNED_REQUESTS.get(service, DEFAULT_SERVICE_REGION)
-            # S3 is the last resort when the target is also unknown
-            return DEFAULT_SERVICE_REGION
+                service, region = UNSIGNED_REQUESTS.get(service, DEFAULT_SERVICE_REGION)
+            else:
+                # S3 is the last resort when the target is also unknown
+                service, region = DEFAULT_SERVICE_REGION
+
+        if service == 'dynamodb':
+            if environ['HTTP_X_AMZ_TARGET'].startswith('DynamoDBStreams'):
+                host = 'dynamodbstreams'
+            else:
+                dynamo_api_version = environ['HTTP_X_AMZ_TARGET'].split("_")[1].split(".")[0]
+                # If Newer API version, use dynamodb2
+                if dynamo_api_version > "20111205":
+                    host = "dynamodb2"
+        else:
+            host = "{service}.{region}.amazonaws.com".format(
+                service=service, region=region)
+
+        return host
 
     def get_application(self, environ):
         path_info = environ.get('PATH_INFO', '')
@@ -101,22 +113,14 @@ def get_application(self, environ):
             host = "instance_metadata"
         else:
             host = environ['HTTP_HOST'].split(':')[0]
-        if host in {'localhost', 'motoserver'} or host.startswith("192.168."):
-            service, region = self.infer_service_region(environ)
-            if service == 'dynamodb':
-                if environ['HTTP_X_AMZ_TARGET'].startswith('DynamoDBStreams'):
-                    host = 'dynamodbstreams'
-                else:
-                    dynamo_api_version = environ['HTTP_X_AMZ_TARGET'].split("_")[1].split(".")[0]
-                    # If Newer API version, use dynamodb2
-                    if dynamo_api_version > "20111205":
-                        host = "dynamodb2"
-            else:
-                host = "{service}.{region}.amazonaws.com".format(
-                    service=service, region=region)
 
         with self.lock:
             backend = self.get_backend_for_host(host)
+            if not backend:
+                # No regular backend found; try parsing other headers
+                host = self.infer_service_region_host(environ)
+                backend = self.get_backend_for_host(host)
+
             app = self.app_instances.get(backend, None)
             if app is None:
                 app = self.create_app(backend)


Test patch:
diff --git a/tests/test_core/test_server.py b/tests/test_core/test_server.py
--- a/tests/test_core/test_server.py
+++ b/tests/test_core/test_server.py
@@ -38,12 +38,6 @@ def test_domain_dispatched():
     keys[0].should.equal('EmailResponse.dispatch')
 
 
-def test_domain_without_matches():
-    dispatcher = DomainDispatcherApplication(create_backend_app)
-    dispatcher.get_application.when.called_with(
-        {"HTTP_HOST": "not-matching-anything.com"}).should.throw(RuntimeError)
-
-
 def test_domain_dispatched_with_service():
     # If we pass a particular service, always return that.
     dispatcher = DomainDispatcherApplication(create_backend_app, service="s3")