Skip to content

Commit

Permalink
dynamic API_HOSTNAME & CONTENT_ORIGIN via dynaconf+django-crum (#2134)
Browse files Browse the repository at this point in the history
* Use crum to set the content origin and api hostname.

No-Issue

Signed-off-by: James Tanner <[email protected]>
  • Loading branch information
jctanner authored Jun 4, 2024
1 parent 22e49f3 commit 7e6b335
Show file tree
Hide file tree
Showing 12 changed files with 80 additions and 10 deletions.
43 changes: 41 additions & 2 deletions galaxy_ng/app/dynaconf_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from dynaconf import Dynaconf, Validator
from galaxy_ng.app.dynamic_settings import DYNAMIC_SETTINGS_SCHEMA
from django.apps import apps
from crum import get_current_request


logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -645,7 +646,10 @@ def configure_dynamic_settings(settings: Dynaconf) -> Dict[str, Any]:
change the value before it is returned allowing reading overrides from
database and cache.
"""
if settings.get("GALAXY_DYNAMIC_SETTINGS") is not True:
# we expect a list of function names here, which have to be in scope of
# locals() for this specific file
enabled_hooks = settings.get("DYNACONF_AFTER_GET_HOOKS")
if not enabled_hooks:
return {}

# Perform lazy imports here to avoid breaking when system runs with older
Expand Down Expand Up @@ -710,8 +714,43 @@ def read_settings_from_cache_or_db(

return temp_settings.get(key, value.value)

def alter_hostname_settings(
temp_settings: Settings,
value: HookValue,
key: str,
*args,
**kwargs
) -> Any:
"""Use the request headers to dynamically alter the content origin and api hostname.
This is useful in scenarios where the hub is accessible directly and through a
reverse proxy.
"""

# we only want to modify these settings base on request headers
ALLOWED_KEYS = ['CONTENT_ORIGIN', 'ANSIBLE_API_HOSTNAME']

# If app is starting up or key is not on allowed list bypass and just return the value
if not apps.ready or key.upper() not in ALLOWED_KEYS:
return value.value

# we have to assume the proxy or the edge device(s) set these headers correctly
req = get_current_request()
if req is not None:
headers = dict(req.headers)
proto = headers.get("X-Forwarded-Proto", "http")
host = headers.get("Host", "localhost:5001")
baseurl = proto + "://" + host
return baseurl

return value.value

# avoid scope errors by not using a list comprehension
hook_functions = []
for func_name in enabled_hooks:
hook_functions.append(Hook(locals()[func_name]))

return {
"_registered_hooks": {
Action.AFTER_GET: [Hook(read_settings_from_cache_or_db)]
Action.AFTER_GET: hook_functions
}
}
2 changes: 1 addition & 1 deletion galaxy_ng/app/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ class CustomBrowsableAPIRenderer(BrowsableAPIRenderer):

def show_form_for_method(self, view, method, request, obj):
"""Display forms only for superuser."""
if request.user.is_superuser:
if request.user and request.user.is_superuser:
return super().show_form_for_method(view, method, request, obj)
return False
2 changes: 2 additions & 0 deletions galaxy_ng/app/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@
# END: Pulp standard middleware
'django_prometheus.middleware.PrometheusAfterMiddleware',
]
MIDDLEWARE += ('crum.CurrentRequestUserMiddleware',)

INSTALLED_APPS = [
'rest_framework.authtoken',
'crum',
'dynaconf_merge',
]

Expand Down
2 changes: 1 addition & 1 deletion galaxy_ng/app/webserver_snippets/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ location /ui/ {

location /api/ {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
proxy_set_header Host $http_host;
# we don't want nginx trying to do something clever with
# redirects, we set the Host: header above already.
Expand Down
15 changes: 15 additions & 0 deletions galaxy_ng/tests/integration/dab/test_url_resolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,18 @@ def test_dab_collection_download_url_hostnames(settings, galaxy_client, publishe
# make sure the final redirect was through the gateway ...
expected_url = gc.galaxy_root.replace('/api/galaxy/', '')
assert dl_resp.url.startswith(expected_url)

# now check if we access it from localhost that the download url changes accordingly
if gc.galaxy_root == "http://jwtproxy:8080/api/galaxy/":
local_url = os.path.join(gc.galaxy_root, cv_url)
local_url = local_url.replace("http://jwtproxy:8080", "http://localhost:5001")
cv_info = gc.get(local_url, auth=("admin", "admin"))

download_url = cv_info["download_url"]
assert download_url.startswith("http://localhost:5001")

# try to GET the tarball ...
dl_resp = gc.get(download_url, parse_json=False, auth=("admin", "admin"))
assert dl_resp.status_code == 200
assert dl_resp.headers.get('Content-Type') == 'application/gzip'
assert dl_resp.url.startswith("http://localhost:5001")
9 changes: 8 additions & 1 deletion profiles/dab/pulp_config.env
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,12 @@ PULP_GALAXY_FEATURE_FLAGS__external_authentication=true

PULP_TOKEN_SERVER="https://localhost/token/"

# ease-of-use
ENABLE_SIGNING=1
PULP_GALAXY_AUTO_SIGN_COLLECTIONS=true
PULP_GALAXY_REQUIRE_CONTENT_APPROVAL=true
PULP_GALAXY_COLLECTION_SIGNING_SERVICE=ansible-default
PULP_GALAXY_CONTAINER_SIGNING_SERVICE=container-default

# dynamic download urls
PULP_GALAXY_DYNAMIC_SETTINGS=true
PULP_DYNACONF_AFTER_GET_HOOKS=["read_settings_from_cache_or_db", "alter_hostname_settings"]
2 changes: 1 addition & 1 deletion profiles/dab_jwt/proxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -679,7 +679,7 @@ func main() {
log.Printf("Request: %s %s", req.Method, req.URL.String())

// just assume this proxy is http ...
req.Header.Add("X-Forwarded-Proto", "https")
req.Header.Add("X-Forwarded-Proto", "http")

// https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#x-envoy-internal
req.Header.Add("X-Envoy-Internal", "true")
Expand Down
2 changes: 1 addition & 1 deletion profiles/dab_jwt/pulp_config.env
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ PULP_GALAXY_COLLECTION_SIGNING_SERVICE=ansible-default
PULP_GALAXY_CONTAINER_SIGNING_SERVICE=container-default

# dynamic download urls
PULP_GALAXY_DYNAMIC_SETTINGS=true
PULP_DYNACONF_AFTER_GET_HOOKS=["read_settings_from_cache_or_db", "alter_hostname_settings"]
4 changes: 3 additions & 1 deletion requirements/requirements.common.txt
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,9 @@ django-ansible-base[jwt_consumer] @ git+https://github.com/ansible/django-ansibl
django-auth-ldap==4.0.0
# via galaxy-ng (setup.py)
django-crum==0.7.9
# via django-ansible-base
# via
# django-ansible-base
# galaxy-ng (setup.py)
django-filter==23.5
# via pulpcore
django-guid==3.4.0
Expand Down
4 changes: 3 additions & 1 deletion requirements/requirements.insights.txt
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,9 @@ django-ansible-base[jwt_consumer] @ git+https://github.com/ansible/django-ansibl
django-auth-ldap==4.0.0
# via galaxy-ng (setup.py)
django-crum==0.7.9
# via django-ansible-base
# via
# django-ansible-base
# galaxy-ng (setup.py)
django-filter==23.5
# via pulpcore
django-guid==3.4.0
Expand Down
4 changes: 3 additions & 1 deletion requirements/requirements.standalone.txt
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,9 @@ django-ansible-base[jwt_consumer] @ git+https://github.com/ansible/django-ansibl
django-auth-ldap==4.0.0
# via galaxy-ng (setup.py)
django-crum==0.7.9
# via django-ansible-base
# via
# django-ansible-base
# galaxy-ng (setup.py)
django-filter==23.5
# via pulpcore
django-guid==3.4.0
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ def _format_pulp_requirement(plugin, specifier=None, ref=None, gh_namespace="pul
"boto3",
"distro",
"django-ansible-base[jwt_consumer] @ git+https://github.com/ansible/django-ansible-base@devel", # noqa 501
"django-crum==0.7.9",
# From vendored automated_logging
"marshmallow<4.0.0,>=3.6.1",
"django-picklefield<4.0.0,>=3.0.1",
Expand Down

0 comments on commit 7e6b335

Please sign in to comment.