Collector Outbound Proxy
When the customer requires all egress to pass through a forward proxy, the collector honours the standard proxy environment variables for every outbound call: registration, heartbeat, and ingest to ozone.techforcz.com. Device polling stays direct on the LAN and must be excluded from the proxy. The Orchestrator sits behind a Cloudflare tunnel, so egress lands at ozone.techforcz.com through Cloudflare rather than at a fixed VM IP.
1. Configure the proxy
Set the proxy variables in the collector config. NO_PROXY must list localhost and the device/internal ranges so polling and inter-container traffic never detour through the proxy.
# /opt/techforcz/config/.env (collector egress through a forward proxy)
HTTPS_PROXY=http://proxy.corp.example:3128
HTTP_PROXY=http://proxy.corp.example:3128
# Keep device polling, localhost, and the container network OFF the proxy:
NO_PROXY=localhost,127.0.0.1,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,<container-bridge-cidr>,.corp.example
# Apply: restart only the egress bridge, not the whole stack
cd /opt/techforcz/collector/deploy/docker-compose
docker compose restart saas-sync
docker compose logs -f saas-sync # watch for a successful heartbeat to ozone.techforcz.com
Include the collector's container bridge network in
NO_PROXY. If inter-service calls get sent to the proxy, services fail to reach NATS/Redis and the stack misbehaves in ways that look unrelated to networking.
2. Authenticated proxies
For a proxy that requires credentials, embed them in the URL and inject them as a secret, and never commit them to the image or a tracked file.
# Username/password (URL-encode reserved characters; avoid '@' in the password)
HTTPS_PROXY=http://<proxy-user>:<proxy-password>@proxy.corp.example:3128
# Inject the password at runtime from your secret manager rather than hard-coding it,
# for example by exporting it into the environment before the stack starts.
Password gotcha: a literal
@in the password breaks URL parsing (it is read as the host delimiter). URL-encode it (%40) or choose a password without it. This is the same parsing constraint that applies to service DB passwords (see Security & Compliance, rotation).
PAC files and proxy auto-discovery (WPAD) are not consulted by the appliance, so set the explicit proxy URL the PAC would resolve to for ozone.techforcz.com.
3. TLS-intercepting proxies
If the proxy terminates and re-issues TLS (deep inspection), the certificate the appliance sees for ozone.techforcz.com is signed by the proxy's CA, not a public CA. The handshake then fails with unable to get local issuer certificate unless the appliance trusts that CA.
Two options:
A. Trust the proxy CA on the appliance (preferred if interception is mandatory):
# Add the corporate root CA to the appliance trust store
sudo cp corp-root-ca.crt /usr/local/share/ca-certificates/corp-root-ca.crt
sudo update-ca-certificates
docker compose restart saas-sync
B. Exempt the Orchestrator from interception (cleaner): add ozone.techforcz.com to the proxy's TLS bypass / allowlist so the appliance negotiates TLS directly with the real endpoint. This keeps the end-to-end TLS chain intact and is the recommended posture.
Application-layer Fernet encryption (Enterprise) sits inside TLS, so an intercepting proxy still cannot read forwarded payloads, but it will still break the handshake unless A or B is applied.
4. Verify egress traverses the proxy
# 1. The proxy itself is reachable
nc -vz proxy.corp.example 3128
# 2. A proxied HTTPS round-trip to the Orchestrator succeeds
https_proxy=http://proxy.corp.example:3128 \
curl -sS -o /dev/null -w 'via-proxy: %{http_code}\n' https://ozone.techforcz.com/health
# 3. The collector's own egress is healthy (heartbeat landing)
docker compose logs --tail 50 saas-sync | grep -iE 'heartbeat|register|2[0-9][0-9]'
# 4. Inspect the negotiated cert issuer; confirms direct vs intercepted TLS
openssl s_client -connect ozone.techforcz.com:443 -servername ozone.techforcz.com \
-proxy proxy.corp.example:3128 </dev/null 2>/dev/null | openssl x509 -noout -issuer
If step 4 shows the corporate CA as issuer, interception is in effect, so apply §3. If the heartbeat still does not land with a clean proxy path, continue to Enrolment and Troubleshooting, TLS handshake failures.
5. Proxy troubleshooting quick table
| Symptom | Cause | Fix |
|---|---|---|
unable to get local issuer certificate | TLS interception, proxy CA not trusted | §3 option A or B |
| Heartbeat works, device polling broke | device subnets sent through proxy | add them to NO_PROXY |
| 407 Proxy Authentication Required | missing/incorrect proxy credentials | §2; verify the injected secret |
| Intermittent connects | proxy pool / partial allowlist | allowlist ozone.techforcz.com on every proxy node |
| Stack errors after enabling proxy | container network proxied | add the container bridge network to NO_PROXY |