mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-13 22:26:49 +00:00
trusted_proxies held the host's public addresses, but OpenResty reaches Nextcloud through the published port, which Docker forwards from the gateway of one of the container's networks (192.168.201.1, the central MariaDB network, on production). Nextcloud therefore ignored X-Forwarded-For, attributed every request to that gateway and brute-force throttled it. The TRUSTED_PROXIES env var was joined with commas while the image's reverse-proxy.config.php splits it on spaces, which produced the single malformed entry the admin overview rejects, and the occ value reached config:system:set as a Python repr instead of JSON, so config.php got null. Add the proxy_hops lookup next to resolve_upstream in utils/networks/proxy.py: under compose it returns the gateway of every network the application joins, under swarm the subnets of the overlays it shares with svc-prx-openresty, both derived from the service registry and role_subnets. NEXTCLOUD_TRUSTED_PROXIES now uses it, env.j2 joins the list with spaces and system.yml passes it through to_json. Rendered with the real roles and group_vars for the galaxyserver layout: compose trusts 192.168.41.1, 192.168.201.1 and 192.168.202.1, swarm 192.168.107.0/24; unit and integration tests cover both modes, and two mutations of proxy_hops fail them. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
62 lines
2.0 KiB
Python
62 lines
2.0 KiB
Python
"""Lookup ``proxy_hops``: the addresses the reverse proxy's requests reach an
|
|
application from, for that application's trusted-proxy list.
|
|
|
|
Usage:
|
|
|
|
{{ lookup('proxy_hops', application_id) }} # compose: ['192.168.41.1', '192.168.201.1']
|
|
# swarm: ['192.168.107.0/24']
|
|
|
|
Returns what :func:`utils.networks.proxy.proxy_hops` derives from the networks
|
|
the service_registry attaches.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from ansible.errors import AnsibleError
|
|
from ansible.plugins.loader import lookup_loader
|
|
from ansible.plugins.lookup import LookupBase
|
|
|
|
from utils.networks.lookup_context import build_context, resolve_var
|
|
from utils.networks.proxy import proxy_hops
|
|
from utils.networks.render import compute_external_network_roles
|
|
|
|
|
|
class LookupModule(LookupBase):
|
|
def run(
|
|
self,
|
|
terms: list[Any] | None,
|
|
variables: dict[str, Any] | None = None,
|
|
**kwargs: Any,
|
|
) -> list[Any]:
|
|
if not terms or len(terms) != 1:
|
|
raise AnsibleError(
|
|
"proxy_hops lookup requires exactly one term: application_id"
|
|
)
|
|
|
|
ctx = build_context(self, variables)
|
|
application_id = str(resolve_var(ctx.templar, terms[0]))
|
|
role_subnets = lookup_loader.get(
|
|
"role_subnets", loader=self._loader, templar=ctx.templar
|
|
)
|
|
|
|
def network_roles(role: str) -> list[str]:
|
|
return compute_external_network_roles(
|
|
application_id=role,
|
|
deployment_mode=ctx.deployment_mode,
|
|
registry=ctx.registry,
|
|
lookup_config=ctx.lookup_config,
|
|
lookup_database=ctx.lookup_database,
|
|
)
|
|
|
|
def subnets(roles: list[str]) -> list[str]:
|
|
return role_subnets.run([roles], variables=ctx.vars)[0]
|
|
|
|
try:
|
|
return [
|
|
proxy_hops(application_id, ctx.deployment_mode, network_roles, subnets)
|
|
]
|
|
except ValueError as exc:
|
|
raise AnsibleError(f"proxy_hops: {exc}") from exc
|