Repository: getmoto/moto

Issue:
environment variable S3_IGNORE_SUBDOMAIN_BUCKETNAME is ignored for stand alone s3 server
I am running a standalone s3 server on a fixed domain. I want the bucket name to be in the url, not in the domain as those domains will not be resolved. However creating a bucket will fail with `The specified bucket does not exist`. Setting the S3_IGNORE_SUBDOMAIN_BUCKETNAME environment variable to '1' does not solve the issue.

To reproduce:
* Prepare /etc/hosts so I can address a server running on localhost by a domainname 
/etc/hosts
  ````
  127.0.0.1       localhost a.test.com
  ````
* Run the s3 server
  ````bash
  export S3_IGNORE_SUBDOMAIN_BUCKETNAME=1
  moto_server s3 -H 0.0.0.0
  ````
* Test the server using boto3
  ````python
  >>> import boto3
  >>> client = boto3.client('s3', endpoint_url="http://a.test.com:5000", aws_access_key_id="myid", aws_secret_access_key='secret')
  >>> client.create_bucket(Bucket="test")
  ````
  This will generate an error:
  ````
  Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/onno/tmp/moto/venv/lib/python3.8/site-packages/botocore/client.py", line 357, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/home/onno/tmp/moto/venv/lib/python3.8/site-packages/botocore/client.py", line 676, in _make_api_call
    raise error_class(parsed_response, operation_name)
  botocore.errorfactory.NoSuchBucket: An error occurred (NoSuchBucket) when calling the CreateBucket operation: The specified bucket does not exist
  ````
  
What I expected was the same beheavure as when addressing the server as localhost
````python
>>> import boto3
>>> client = boto3.client('s3', endpoint_url="http://localhost:5000", aws_access_key_id="myid", aws_secret_access_key='secret')
>>> client.create_bucket(Bucket="test")
{'ResponseMetadata': {'HTTPStatusCode': 200, 'HTTPHeaders': {'content-type': 'text/html; charset=utf-8', 'content-length': '158', 'access-control-allow-origin': '*', 'server': 'Werkzeug/1.0.1 Python/3.8.5', 'date': 'Wed, 24 Mar 2021 12:42:05 GMT'}, 'RetryAttempts': 0}}
````

I believe this issue is caused by the environment variable S3_IGNORE_SUBDOMAIN_BUCKETNAME being checked in utils.py `bucket_name_from_url` but not in responses.py `subdomain_based_buckets`. 



Discussion:

Resolving patch:
diff --git a/moto/s3/responses.py b/moto/s3/responses.py
--- a/moto/s3/responses.py
+++ b/moto/s3/responses.py
@@ -27,6 +27,7 @@
 from moto.core.responses import _TemplateEnvironmentMixin, ActionAuthenticatorMixin
 from moto.core.utils import path_url
 from moto.core import ACCOUNT_ID
+from moto.settings import S3_IGNORE_SUBDOMAIN_BUCKETNAME
 
 from moto.s3bucket_path.utils import (
     bucket_name_from_url as bucketpath_bucket_name_from_url,
@@ -186,6 +187,8 @@ def all_buckets(self):
         return template.render(buckets=all_buckets)
 
     def subdomain_based_buckets(self, request):
+        if S3_IGNORE_SUBDOMAIN_BUCKETNAME:
+            return False
         host = request.headers.get("host", request.headers.get("Host"))
         if not host:
             host = urlparse(request.url).netloc
diff --git a/moto/s3/utils.py b/moto/s3/utils.py
--- a/moto/s3/utils.py
+++ b/moto/s3/utils.py
@@ -1,12 +1,12 @@
 from __future__ import unicode_literals
 import logging
-import os
 
 import re
 import six
 from six.moves.urllib.parse import urlparse, unquote, quote
 from requests.structures import CaseInsensitiveDict
 import sys
+from moto.settings import S3_IGNORE_SUBDOMAIN_BUCKETNAME
 
 
 log = logging.getLogger(__name__)
@@ -26,7 +26,7 @@
 
 
 def bucket_name_from_url(url):
-    if os.environ.get("S3_IGNORE_SUBDOMAIN_BUCKETNAME", "") in ["1", "true"]:
+    if S3_IGNORE_SUBDOMAIN_BUCKETNAME:
         return None
     domain = urlparse(url).netloc
 
diff --git a/moto/settings.py b/moto/settings.py
--- a/moto/settings.py
+++ b/moto/settings.py
@@ -6,6 +6,10 @@
 )
 DEFAULT_CONTAINER_REGISTRY = os.environ.get("DEFAULT_CONTAINER_REGISTRY", "docker.io")
 
+S3_IGNORE_SUBDOMAIN_BUCKETNAME = os.environ.get(
+    "S3_IGNORE_SUBDOMAIN_BUCKETNAME", ""
+) in ["1", "true"]
+
 
 def get_sf_execution_history_type():
     """


Test patch:
diff --git a/tests/compat.py b/tests/compat.py
new file mode 100644
--- /dev/null
+++ b/tests/compat.py
@@ -0,0 +1,5 @@
+try:
+    from unittest.mock import patch
+except ImportError:
+    # for python 2.7
+    from mock import patch
diff --git a/tests/test_s3/test_s3_utils.py b/tests/test_s3/test_s3_utils.py
--- a/tests/test_s3/test_s3_utils.py
+++ b/tests/test_s3/test_s3_utils.py
@@ -1,5 +1,4 @@
 from __future__ import unicode_literals
-import os
 import pytest
 from sure import expect
 from moto.s3.utils import (
@@ -9,6 +8,7 @@
     clean_key_name,
     undo_clean_key_name,
 )
+from tests.compat import patch
 
 
 def test_base_url():
@@ -25,12 +25,11 @@ def test_localhost_without_bucket():
     expect(bucket_name_from_url("https://www.localhost:5000/def")).should.equal(None)
 
 
-def test_force_ignore_subdomain_for_bucketnames():
-    os.environ["S3_IGNORE_SUBDOMAIN_BUCKETNAME"] = "1"
-    expect(
-        bucket_name_from_url("https://subdomain.localhost:5000/abc/resource")
-    ).should.equal(None)
-    del os.environ["S3_IGNORE_SUBDOMAIN_BUCKETNAME"]
+def test_force_ignore_subdomain_for_bucketnames(monkeypatch):
+    with patch("moto.s3.utils.S3_IGNORE_SUBDOMAIN_BUCKETNAME", True):
+        expect(
+            bucket_name_from_url("https://subdomain.localhost:5000/abc/resource")
+        ).should.equal(None)
 
 
 def test_versioned_key_store():
diff --git a/tests/test_s3/test_server.py b/tests/test_s3/test_server.py
--- a/tests/test_s3/test_server.py
+++ b/tests/test_s3/test_server.py
@@ -7,6 +7,7 @@
 
 from flask.testing import FlaskClient
 import moto.server as server
+from tests.compat import patch
 
 """
 Test the different server responses
@@ -56,6 +57,15 @@ def test_s3_server_bucket_create():
     res.data.should.equal(b"test value")
 
 
+def test_s3_server_ignore_subdomain_for_bucketnames():
+    with patch("moto.s3.responses.S3_IGNORE_SUBDOMAIN_BUCKETNAME", True):
+        test_client = authenticated_client()
+
+        res = test_client.put("/mybucket", "http://foobaz.localhost:5000/")
+        res.status_code.should.equal(200)
+        res.data.should.contain(b"mybucket")
+
+
 def test_s3_server_bucket_versioning():
     test_client = authenticated_client()