mirror of
https://github.com/inventree/InvenTree.git
synced 2025-10-23 09:27:39 +00:00
Fixes for SITE_URL validity checks (#10619)
* [docker] Allow HTTPS port to be specified for Caddy proxy * Fix naming collision for INVENTREE_WEB_PORT * Push InvenTree version first * Adjust Caddyfile - Change backup server * Fix docstring * Tweak for site URL check: - Ignore port if SITE_LAX_PROTOCOL_CHECK is set - Invert logic for readability * Additional checks for port mismatch * Adjust middleware checks - Allow for less strict checking of CSRF_TRUSTED_ORIGINS * Slight refactor
This commit is contained in:
@@ -30,9 +30,9 @@
|
||||
}
|
||||
|
||||
# The default server address is configured in the .env file
|
||||
# If not specified, the default address is used - http://inventree.localhost
|
||||
# If not specified, the proxy listens for all http/https traffic
|
||||
# If you need to listen on multiple addresses, or use a different port, you can modify this section directly
|
||||
{$INVENTREE_SITE_URL:http://inventree.localhost} {
|
||||
{$INVENTREE_SITE_URL:"http://, https://"} {
|
||||
import log_common inventree
|
||||
|
||||
encode gzip
|
||||
|
@@ -101,6 +101,7 @@ services:
|
||||
restart: unless-stopped
|
||||
|
||||
# caddy acts as reverse proxy and static file server
|
||||
# You can adjust the ports that the proxy listens on via the .env file
|
||||
# https://hub.docker.com/_/caddy
|
||||
inventree-proxy:
|
||||
container_name: inventree-proxy
|
||||
@@ -109,8 +110,8 @@ services:
|
||||
depends_on:
|
||||
- inventree-server
|
||||
ports:
|
||||
- ${INVENTREE_WEB_PORT:-80}:80
|
||||
- 443:443
|
||||
- ${INVENTREE_HTTP_PORT:-80}:80
|
||||
- ${INVENTREE_HTTPS_PORT:-443}:443
|
||||
env_file:
|
||||
- .env
|
||||
volumes:
|
||||
|
@@ -239,13 +239,29 @@ class InvenTreeHostSettingsMiddleware(MiddlewareMixin):
|
||||
accessed_scheme = request._current_scheme_host
|
||||
referer = urlsplit(accessed_scheme)
|
||||
|
||||
# Ensure that the settings are set correctly with the current request
|
||||
matches = (
|
||||
(accessed_scheme and not accessed_scheme.startswith(settings.SITE_URL))
|
||||
if not settings.SITE_LAX_PROTOCOL_CHECK
|
||||
else not is_same_domain(referer.netloc, urlsplit(settings.SITE_URL).netloc)
|
||||
site_url = urlsplit(settings.SITE_URL)
|
||||
|
||||
# Check if the accessed URL matches the SITE_URL setting
|
||||
site_url_match = (
|
||||
(
|
||||
# Exact match on domain
|
||||
is_same_domain(referer.netloc, site_url.netloc)
|
||||
and referer.scheme == site_url.scheme
|
||||
)
|
||||
or (
|
||||
# Lax protocol match, accessed URL starts with SITE_URL
|
||||
settings.SITE_LAX_PROTOCOL_CHECK
|
||||
and accessed_scheme.startswith(settings.SITE_URL)
|
||||
)
|
||||
or (
|
||||
# Lax protocol match, same domain
|
||||
settings.SITE_LAX_PROTOCOL_CHECK
|
||||
and referer.hostname == site_url.hostname
|
||||
)
|
||||
)
|
||||
if matches:
|
||||
|
||||
if not site_url_match:
|
||||
# The accessed URL does not match the SITE_URL setting
|
||||
if (
|
||||
isinstance(settings.CSRF_TRUSTED_ORIGINS, list)
|
||||
and len(settings.CSRF_TRUSTED_ORIGINS) > 1
|
||||
@@ -263,17 +279,31 @@ class InvenTreeHostSettingsMiddleware(MiddlewareMixin):
|
||||
request, 'config_error.html', {'error_message': msg}, status=500
|
||||
)
|
||||
|
||||
# Check trusted origins
|
||||
if not any(
|
||||
is_same_domain(referer.netloc, host)
|
||||
for host in [
|
||||
urlsplit(origin).netloc.lstrip('*')
|
||||
trusted_origins_match = (
|
||||
# Matching domain found in allowed origins
|
||||
any(
|
||||
is_same_domain(referer.netloc, host)
|
||||
for host in [
|
||||
urlsplit(origin).netloc.lstrip('*')
|
||||
for origin in settings.CSRF_TRUSTED_ORIGINS
|
||||
]
|
||||
)
|
||||
) or (
|
||||
# Lax protocol match allowed
|
||||
settings.SITE_LAX_PROTOCOL_CHECK
|
||||
and any(
|
||||
referer.hostname == urlsplit(origin).hostname
|
||||
for origin in settings.CSRF_TRUSTED_ORIGINS
|
||||
]
|
||||
):
|
||||
)
|
||||
)
|
||||
|
||||
# Check trusted origins
|
||||
if not trusted_origins_match:
|
||||
msg = f'INVE-E7: The used path `{accessed_scheme}` is not in the TRUSTED_ORIGINS'
|
||||
logger.error(msg)
|
||||
return render(
|
||||
request, 'config_error.html', {'error_message': msg}, status=500
|
||||
)
|
||||
|
||||
# All checks passed
|
||||
return None
|
||||
|
@@ -112,6 +112,15 @@ class MiddlewareTests(InvenTreeTestCase):
|
||||
|
||||
def test_site_lax_protocol(self):
|
||||
"""Test that the site URL check is correctly working with/without lax protocol check."""
|
||||
# Test that a completely different host fails
|
||||
with self.settings(
|
||||
SITE_URL='https://testserver', CSRF_TRUSTED_ORIGINS=['https://testserver']
|
||||
):
|
||||
response = self.client.get(
|
||||
reverse('web'), HTTP_HOST='otherhost.example.com'
|
||||
)
|
||||
self.assertContains(response, 'INVE-E7: The visited path', status_code=500)
|
||||
|
||||
# Simple setup with proxy
|
||||
with self.settings(
|
||||
SITE_URL='https://testserver', CSRF_TRUSTED_ORIGINS=['https://testserver']
|
||||
@@ -128,6 +137,24 @@ class MiddlewareTests(InvenTreeTestCase):
|
||||
response = self.client.get(reverse('web'))
|
||||
self.assertContains(response, 'INVE-E7: The visited path', status_code=500)
|
||||
|
||||
def test_site_url_port(self):
|
||||
"""URL checks with different ports."""
|
||||
with self.settings(
|
||||
SITE_URL='https://testserver:8000',
|
||||
CSRF_TRUSTED_ORIGINS=['https://testserver:8000'],
|
||||
):
|
||||
response = self.client.get(reverse('web'), HTTP_HOST='testserver:8008')
|
||||
self.do_positive_test(response)
|
||||
|
||||
# Try again with strict protocol check
|
||||
with self.settings(
|
||||
SITE_URL='https://testserver:8000',
|
||||
CSRF_TRUSTED_ORIGINS=['https://testserver:8000'],
|
||||
SITE_LAX_PROTOCOL_CHECK=False,
|
||||
):
|
||||
response = self.client.get(reverse('web'), HTTP_HOST='testserver:8008')
|
||||
self.assertContains(response, 'INVE-E7: The visited path', status_code=500)
|
||||
|
||||
def test_site_url_checks_multi(self):
|
||||
"""Test that the site URL check is correctly working in a multi-site setup."""
|
||||
# multi-site setup with trusted origins
|
||||
@@ -149,7 +176,7 @@ class MiddlewareTests(InvenTreeTestCase):
|
||||
)
|
||||
self.do_positive_test(response)
|
||||
|
||||
# A non-trsuted origin must still fail in multi - origin setup
|
||||
# A non-trusted origin must still fail in multi - origin setup
|
||||
response = self.client.get(
|
||||
'https://not-my-testserver.example.com/web/',
|
||||
SERVER_NAME='not-my-testserver.example.com',
|
||||
|
4
tasks.py
4
tasks.py
@@ -1556,10 +1556,10 @@ Static {get_static_dir(error=False) or NOT_SPECIFIED}
|
||||
Backup {get_backup_dir(error=False) or NOT_SPECIFIED}
|
||||
|
||||
Versions:
|
||||
Python {python_version()}
|
||||
Django {InvenTreeVersion.inventreeDjangoVersion()}
|
||||
InvenTree {InvenTreeVersion.inventreeVersion()}
|
||||
API {InvenTreeVersion.inventreeApiVersion()}
|
||||
Python {python_version()}
|
||||
Django {InvenTreeVersion.inventreeDjangoVersion()}
|
||||
Node {node if node else NA}
|
||||
Yarn {yarn if yarn else NA}
|
||||
|
||||
|
Reference in New Issue
Block a user