Skip to content

HaRP in docker, behind HAproxy #86

@stefangweichinger

Description

@stefangweichinger

I am starting fresh with HaRP, so maybe I have multiple mistakes in my approach.

docker-image nextcloud:32.0.3 in docker-compose stack, behind a HAproxy setup on a pfSense-Plus-25.11

The docker-host runs on 192.168.220.222
It's a debian-13.3 host

# docker info
Client:
 Version:    26.1.5+dfsg1
 Context:    default
 Debug Mode: false
 Plugins:
  compose: Docker Compose (Docker Inc.)
    Version:  2.26.1-4
    Path:     /usr/libexec/docker/cli-plugins/docker-compose

 Server Version: 26.1.5+dfsg1

The PROXY-IP in that subnet is 192.168.220.254

I have set up HAproxy to forward "https://nctest.my.tld" to 192.168.220.222:8780

The docker-compose.yml for harp:

# cat docker-compose.yml 
services:
  appapi-harp:
    image: ghcr.io/nextcloud/nextcloud-appapi-harp:release
    restart: unless-stopped
    environment:
      - HP_SHARED_KEY=xxxx
      - NC_INSTANCE_URL="https://nctest.my.tld"
      #- HP_EXAPPS_ADDRESS="192.168.220.222:8780"
      - HP_TRUSTED_PROXY_IPS="192.168.220.254"
      - HP_LOG_LEVEL=info
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./harp-certs:/certs
    ports:
      - "8780:8780"
      - "8782:8782"

I am not sure if I get the picture right, if external URL matches the internal config, etc

I also see an issue #69 mentioning debian-13, not sure if I simply should wait for fixes coming ;-)

nextcloud

I can add that proxy in nextcloud, the connection test works.
Somehow the password isn't stored consistently (that might be a nc-issue, sure).

See my logs:

appapi-harp-1  | INFO: Creating /haproxy.cfg from haproxy.cfg.template...
appapi-harp-1  | INFO: No /certs/cert.pem found, disabling HTTPS frontends...
appapi-harp-1  | INFO: Final /haproxy.cfg:
appapi-harp-1  | # SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
appapi-harp-1  | # SPDX-License-Identifier: AGPL-3.0-or-later
appapi-harp-1  | 
appapi-harp-1  | ###############################################################################
appapi-harp-1  | # haproxy.cfg.template
appapi-harp-1  | #
appapi-harp-1  | # This template is processed by envsubst in start.sh to replace variables:
appapi-harp-1  | #   HP_EXAPPS_ADDRESS,
appapi-harp-1  | #   HP_EXAPPS_HTTPS_ADDRESS,
appapi-harp-1  | #   HP_SPOA_ADDRESS,
appapi-harp-1  | #   HP_TIMEOUT_CONNECT,
appapi-harp-1  | #   HP_TIMEOUT_CLIENT,
appapi-harp-1  | #   HP_TIMEOUT_SERVER,
appapi-harp-1  | #
appapi-harp-1  | ## If /certs/cert.pem is not found, lines containing "_HTTPS_FRONTEND_" are
appapi-harp-1  | # commented out automatically in start.sh.
appapi-harp-1  | ###############################################################################
appapi-harp-1  | 
appapi-harp-1  | global
appapi-harp-1  |     log stdout local0 info
appapi-harp-1  |     maxconn 8192
appapi-harp-1  |     ca-base /etc/ssl/certs
appapi-harp-1  | 
appapi-harp-1  | defaults
appapi-harp-1  |     log global
appapi-harp-1  |     option httplog
appapi-harp-1  |     option dontlognull
appapi-harp-1  |     timeout connect 30s
appapi-harp-1  |     timeout client 30s
appapi-harp-1  |     timeout server 1800s
appapi-harp-1  | 
appapi-harp-1  | 
appapi-harp-1  | ###############################################################################
appapi-harp-1  | # FRONTEND: ex_apps (HTTP)
appapi-harp-1  | ###############################################################################
appapi-harp-1  | frontend ex_apps
appapi-harp-1  |     mode http
appapi-harp-1  |     bind 0.0.0.0:8780
appapi-harp-1  | 
appapi-harp-1  |     filter spoe engine exapps-spoe config /etc/haproxy/spoe-agent.conf
appapi-harp-1  |     http-request silent-drop if { var(txn.exapps.bad_request) -m int eq 1 }
appapi-harp-1  |     http-request return status 401 content-type text/plain string "401 Unauthorized" if { var(txn.exapps.unauthorized) -m int eq 1 }
appapi-harp-1  |     http-request return status 403 content-type text/plain string "403 Forbidden" if { var(txn.exapps.forbidden) -m int eq 1 }
appapi-harp-1  |     http-request return status 404 content-type text/plain string "404 Not Found" if { var(txn.exapps.not_found) -m int eq 1 }
appapi-harp-1  |     use_backend %[var(txn.exapps.backend)]
appapi-harp-1  | 
appapi-harp-1  | ###############################################################################
appapi-harp-1  | # FRONTEND: ex_apps_https (only enabled if /certs/cert.pem exists)
appapi-harp-1  | ###############################################################################
appapi-harp-1  | #_HTTPS_FRONTEND_ frontend ex_apps_https
appapi-harp-1  | #_HTTPS_FRONTEND_     mode http
appapi-harp-1  | #_HTTPS_FRONTEND_     bind 0.0.0.0:8781 ssl crt /certs/cert.pem
appapi-harp-1  | 
appapi-harp-1  | #_HTTPS_FRONTEND_     filter spoe engine exapps-spoe config /etc/haproxy/spoe-agent.conf
appapi-harp-1  | #_HTTPS_FRONTEND_     http-request silent-drop if { var(txn.exapps.bad_request) -m int eq 1 }
appapi-harp-1  | #_HTTPS_FRONTEND_     http-request return status 401 content-type text/plain string "401 Unauthorized" if { var(txn.exapps.unauthorized) -m int eq 1 }
appapi-harp-1  | #_HTTPS_FRONTEND_     http-request return status 403 content-type text/plain string "403 Forbidden" if { var(txn.exapps.forbidden) -m int eq 1 }
appapi-harp-1  | #_HTTPS_FRONTEND_     http-request return status 404 content-type text/plain string "404 Not Found" if { var(txn.exapps.not_found) -m int eq 1 }
appapi-harp-1  | #_HTTPS_FRONTEND_     use_backend %[var(txn.exapps.backend)]
appapi-harp-1  | 
appapi-harp-1  | ###############################################################################
appapi-harp-1  | # BACKENDS: ex_apps & ex_apps_backend_w_bruteforce
appapi-harp-1  | ###############################################################################
appapi-harp-1  | backend ex_apps_backend
appapi-harp-1  |     mode http
appapi-harp-1  |     server frp_server 0.0.0.0
appapi-harp-1  |     http-request set-path %[var(txn.exapps.target_path)]
appapi-harp-1  |     http-request set-dst var(txn.exapps.target_ip)
appapi-harp-1  |     http-request set-dst-port var(txn.exapps.target_port)
appapi-harp-1  |     http-request set-header EX-APP-ID %[var(txn.exapps.exapp_id)]
appapi-harp-1  |     http-request set-header EX-APP-VERSION %[var(txn.exapps.exapp_version)]
appapi-harp-1  |     http-request set-header AUTHORIZATION-APP-API %[var(txn.exapps.exapp_token)]
appapi-harp-1  |     http-request set-header AA-VERSION "32"  # TO-DO: temporary, remove it after we update all ExApps.
appapi-harp-1  | 
appapi-harp-1  | backend ex_apps_backend_w_bruteforce
appapi-harp-1  |     mode http
appapi-harp-1  |     server frp_server 0.0.0.0
appapi-harp-1  |     http-request set-path %[var(txn.exapps.target_path)]
appapi-harp-1  |     http-request set-dst var(txn.exapps.target_ip)
appapi-harp-1  |     http-request set-dst-port var(txn.exapps.target_port)
appapi-harp-1  |     http-request set-header EX-APP-ID %[var(txn.exapps.exapp_id)]
appapi-harp-1  |     http-request set-header EX-APP-VERSION %[var(txn.exapps.exapp_version)]
appapi-harp-1  |     http-request set-header AUTHORIZATION-APP-API %[var(txn.exapps.exapp_token)]
appapi-harp-1  |     http-request set-header AA-VERSION "32"  # TO-DO: temporary, remove it after we update all ExApps.
appapi-harp-1  |     filter spoe engine exapps-bruteforce-protection-spoe config /etc/haproxy/spoe-agent.conf
appapi-harp-1  | 
appapi-harp-1  | ###############################################################################
appapi-harp-1  | # BACKEND: nextcloud_control (HTTP)
appapi-harp-1  | ###############################################################################
appapi-harp-1  | backend nextcloud_control_backend
appapi-harp-1  |     mode http
appapi-harp-1  |     server nextcloud_control 127.0.0.1:8200
appapi-harp-1  |     http-request set-path %[var(txn.exapps.target_path)]
appapi-harp-1  | 
appapi-harp-1  | ###############################################################################
appapi-harp-1  | # BACKEND: docker_engine (HTTP)
appapi-harp-1  | ###############################################################################
appapi-harp-1  | backend docker_engine_backend
appapi-harp-1  |     mode http
appapi-harp-1  |     server frp_server 127.0.0.1
appapi-harp-1  |     http-request set-dst-port var(txn.exapps.target_port)
appapi-harp-1  |     http-request set-path %[var(txn.exapps.target_path)]
appapi-harp-1  | 
appapi-harp-1  |     # docker system _ping
appapi-harp-1  |     http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/_ping$ } METH_GET
appapi-harp-1  |     # docker inspect image
appapi-harp-1  |     http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/images/.*/json } METH_GET
appapi-harp-1  |     # container inspect: GET containers/%s/json
appapi-harp-1  |     http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/containers/nc_app_[a-zA-Z0-9_.-]+/json } METH_GET
appapi-harp-1  |     # container inspect: GET containers/%s/logs
appapi-harp-1  |     http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/containers/nc_app_[a-zA-Z0-9_.-]+/logs } METH_GET
appapi-harp-1  | 
appapi-harp-1  |     # image pull: POST images/create?fromImage=%s
appapi-harp-1  |     http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/images/create } METH_POST
appapi-harp-1  |     http-request deny
appapi-harp-1  | 
appapi-harp-1  | 
appapi-harp-1  | backend agents
appapi-harp-1  |     mode tcp
appapi-harp-1  |     timeout connect 5s
appapi-harp-1  |     timeout server  3m
appapi-harp-1  |     option spop-check
appapi-harp-1  |     server agent1 127.0.0.1:9600 check
appapi-harp-1  | INFO: FRP server configuration generated at /frps.toml.
appapi-harp-1  | INFO: Detected /var/run/docker.sock, generating /frpc-docker.toml configuration file...
appapi-harp-1  | INFO: Starting Python HaProxy Agent on 127.0.0.1:8200 and 127.0.0.1:9600...
appapi-harp-1  | INFO: Waiting for HaRP Agent HTTP (GET http://127.0.0.1:8200/info) to be ready...
appapi-harp-1  | [2026-01-14T12:13:08+0000] [ERROR] Invalid value for HP_TRUSTED_PROXY_IPS: '"192.168.220.254"' does not appear to be an IPv4 or IPv6 network. Client IP detection from headers is disabled. The X-Forwarded-For and X-Real-IP headers will not be respected. This can lead to the outer proxy's IP being blocked during a bruteforce attempt instead of the actual client's IP.
appapi-harp-1  | [2026-01-14T12:13:08+0000] [INFO] Starting both servers: SPOA on 127.0.0.1:9600, HTTP on 127.0.0.1:8200
appapi-harp-1  | [2026-01-14T12:13:08+0000] [INFO] HTTP server listening at 127.0.0.1:8200
appapi-harp-1  | [2026-01-14T12:13:08+0000] [INFO] HAProxy SPO Agent listening at 127.0.0.1:9600
appapi-harp-1  | [2026-01-14T12:13:08+0000] [INFO] 127.0.0.1 [14/Jan/2026:12:13:08 +0000] "GET /info HTTP/1.1" 200 175 "-" "curl/8.14.1"
appapi-harp-1  | INFO: Waiting for SPOA port 127.0.0.1:9600...
appapi-harp-1  | INFO: Starting FRP server on 0.0.0.0:8782...
appapi-harp-1  | INFO: Waiting for FRP server port 127.0.0.1:8782...
appapi-harp-1  | INFO: Starting FRP client for Docker Engine...
appapi-harp-1  | INFO: Starting HAProxy...
appapi-harp-1  | 2026-01-14 12:13:09.495 [I] [sub/root.go:142] start frpc service for config file [/frpc-docker.toml]
appapi-harp-1  | 2026-01-14 12:13:09.495 [I] [client/service.go:295] try to connect to server...
appapi-harp-1  | [NOTICE]   (1) : Initializing new worker (52)
appapi-harp-1  | [2026-01-14T12:13:09+0000] [INFO] 127.0.0.1 [14/Jan/2026:12:13:09 +0000] "POST /frp_handler?op=Login&version=0.1.0 HTTP/1.1" 200 194 "-" "Go-http-client/1.1"
appapi-harp-1  | 2026-01-14 12:13:09.501 [I] [client/service.go:287] [a3c00f7d24f46f36] login to server success, get run id [a3c00f7d24f46f36]
appapi-harp-1  | 2026-01-14 12:13:09.501 [I] [proxy/proxy_manager.go:173] [a3c00f7d24f46f36] proxy added: [bundled-deploy-daemon]
appapi-harp-1  | 2026-01-14 12:13:09.501 [I] [client/control.go:168] [a3c00f7d24f46f36] [bundled-deploy-daemon] start proxy success
appapi-harp-1  | [NOTICE]   (1) : Loading success.
appapi-harp-1  | [2026-01-14T12:13:09+0000] [INFO] [a8d550f8] Received request on key 'exapps_msg'
appapi-harp-1  | [2026-01-14T12:13:09+0000] [INFO] [a8d550f8] Found 1 matching handlers, awaiting response...
appapi-harp-1  | [2026-01-14T12:13:09+0000] [ERROR] Invalid request path, cannot find AppID: /index.php/login
appapi-harp-1  | [2026-01-14T12:13:09+0000] [WARNING] Recorded failure for IP 192.168.220.254. Failures in window: 1
appapi-harp-1  | [2026-01-14T12:13:09+0000] [INFO] [a8d550f8] Responding with combined payload of 50 bytes
appapi-harp-1  | <134>Jan 14 12:13:09 haproxy[52]: 192.168.220.254:17620 [14/Jan/2026:12:13:09.677] ex_apps ex_apps/<NOSRV> 1/-1/-1/-1/1 404 92 - - LR-- 1/1/0/0/0 0/0 "GET /index.php/login HTTP/1.1"
appapi-harp-1  | [2026-01-14T12:13:10+0000] [INFO] [a8d550f8] Received request on key 'exapps_msg'
appapi-harp-1  | [2026-01-14T12:13:10+0000] [INFO] [a8d550f8] Found 1 matching handlers, awaiting response...
appapi-harp-1  | [2026-01-14T12:13:10+0000] [ERROR] Invalid request path, cannot find AppID: /index.php/login
appapi-harp-1  | [2026-01-14T12:13:10+0000] [WARNING] Recorded failure for IP 192.168.220.254. Failures in window: 2
appapi-harp-1  | [2026-01-14T12:13:10+0000] [INFO] [a8d550f8] Responding with combined payload of 50 bytes
appapi-harp-1  | <134>Jan 14 12:13:10 haproxy[52]: 192.168.220.254:18736 [14/Jan/2026:12:13:10.680] ex_apps ex_apps/<NOSRV> 1/-1/-1/-1/1 404 92 - - LR-- 1/1/0/0/0 0/0 "GET /index.php/login HTTP/1.1"
appapi-harp-1  | [2026-01-14T12:13:11+0000] [INFO] [a8d550f8] Received request on key 'exapps_msg'
appapi-harp-1  | [2026-01-14T12:13:11+0000] [INFO] [a8d550f8] Found 1 matching handlers, awaiting response...

I see this:

[ERROR] Invalid value for HP_TRUSTED_PROXY_IPS: '"192.168.220.254"' does not appear to be an IPv4 or IPv6 network.

Seems as if I have to allow a full subnet here? Although that's a bit generous, right?

I will try to fix that one issue.

Maybe someone can tell me where I have a mistake or if there's something upstream.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions