Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ server {
server_name nextcloud.com;

location /exapps/ {
proxy_pass http://127.0.0.1:8780;
proxy_pass http://127.0.0.1:8780/exapps/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
Expand Down Expand Up @@ -515,13 +515,13 @@ docker run \
4. Create folder `dev` at the root of repository, extract there content of the desired archive with the [FRP](https://github.com/fatedier/frp/releases/latest) archive which is located at `exapps_dev` folder of this repo.
5. Edit the `data/nginx/vhost.d/nextcloud.local_location` file from the `nextcloud-docker-dev` to point `/exapps/` web route to the host:
```
proxy_pass http://172.17.0.1:8780;
proxy_pass http://172.17.0.1:8780/exapps/;
```

> **Note:** my original content from my dev machine of file `nextcloud.local_location`:
> ```nginx
> location /exapps/ {
> proxy_pass http://172.17.0.1:8780;
> proxy_pass http://172.17.0.1:8780/exapps/;
> }
> ```
6. Use `docker compose up -d --force-recreate proxy` command from Julius `nextcloud-docker-dev` to recreate the proxy container.
Expand Down
18 changes: 16 additions & 2 deletions haproxy_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,20 @@ async def record_ip_failure(ip_address: str | IPv4Address | IPv6Address) -> None
LOGGER.warning("Recorded failure for IP %s. Failures in window: %d", ip_str, len(attempts))


async def record_failure_unless_trusted(
ip_address: str | IPv4Address | IPv6Address, request_headers: dict[str, str]
) -> None:
"""Record a blacklist failure unless the request is authenticated with a valid harp-shared-key.

Requests from Nextcloud/AppAPI carry `harp-shared-key` matching `SHARED_KEY`; a misconfiguration
on that trusted path (e.g. reverse-proxy stripping `/exapps/`) would otherwise cause the caller
to self-DoS once the blacklist window fills up.
"""
if request_headers.get("harp-shared-key") == SHARED_KEY:
return
await record_ip_failure(ip_address)


async def is_ip_banned(ip_address: str | IPv4Address | IPv6Address) -> bool:
"""Return True if IP has exceeded the maximum allowed failures in the request window."""
ip_str = str(ip_address)
Expand Down Expand Up @@ -375,7 +389,7 @@ async def exapps_msg(
match = APPID_PATTERN.search(path)
if not match:
LOGGER.error("Invalid request path, cannot find AppID: %s", path)
await record_ip_failure(client_ip_str)
await record_failure_unless_trusted(client_ip_str, request_headers)
return reply.set_txn_var("not_found", 1)
exapp_id = match.group(1)
exapp_id_lower = exapp_id.lower()
Expand Down Expand Up @@ -424,7 +438,7 @@ async def exapps_msg(
exapp_record = await _get_or_fetch_exapp(exapp_id_lower)
if not exapp_record:
LOGGER.error("No such ExApp enabled: %s", exapp_id)
await record_ip_failure(client_ip_str)
await record_failure_unless_trusted(client_ip_str, request_headers)
return reply.set_txn_var("not_found", 1)
except ValidationError as e:
LOGGER.error("Invalid ExApp metadata from Nextcloud: %s", e)
Expand Down
Loading