feat: honor HTTP proxies across install and runtime (#3239) - #3242
Conversation
Installing and running Anthias behind an HTTP-proxy-only host previously required manually patching every network layer separately, because none of them share environment or config: the ansible play, apt, the Docker daemon, the anthias-host-agent unit, and the app containers each have an isolated environment. Capture the proxy once (a new install.sh prompt, prefilled from any HTTP_PROXY/HTTPS_PROXY already in the environment) and propagate it to every layer from a single on-disk source of truth (/etc/anthias/proxy.env, written by the ansible system role): - install.sh: prompt early (before prerequisites/require_network); export proxy env for its own curl/git/uv; write an apt proxy conf (its apt runs via bare sudo and would not see the env); pass the proxy and the install user to ansible as explicit --extra-vars. - ansible: play-level environment: block so get_url/apt traverse the proxy; proxy.env source of truth; apt proxy conf; Docker daemon systemd drop-in (EnvironmentFile) so ghcr.io pulls work; host-agent unit EnvironmentFile. - containers: HTTP_PROXY/HTTPS_PROXY/NO_PROXY rendered into the server/viewer/celery services via envsubst (upgrade_containers.sh sources proxy.env). All app HTTP clients use requests (trust_env) so they honor it for free; yt-dlp and QtWebEngine inherit the container env. NO_PROXY is computed, not hand-typed: it always includes the compose service names (anthias-server, redis) plus loopback, so container-to- container traffic never gets force-proxied — the failure that blanked the screen in the report. requests matches NO_PROXY on the URL hostname, so the service names are what matter. Everything is idempotent in both directions: with no proxy configured the managed files are removed and the rendered env values are empty (which every client treats as "no proxy"). Also fixes the robustness issues the report surfaced: - get_url for the Docker apt key gets a stat+when guard (network-free when the key is already present). - boot.yml (writes /boot/firmware/config.txt) gets a device_type when-guard so a direct ansible-playbook run can't hit it on non-Pi hardware. - install.sh warns before `git reset --hard` discards local tracked edits. - anthias_user is passed explicitly and falls back to ansible_user_id, not just env USER (which resolves to root under sudo, breaking host-agent paths). Balena images use a different (supervisor/NetworkManager) proxy mechanism and are out of scope here. Co-Authored-By: Claude Opus 4.8 <[email protected]> Claude-Session: https://claude.ai/code/session_01HJ3ucEkn62cbgPoisAZ5LQ
- Annotate the two 0755 dir modes (/etc/anthias, docker.service.d) with NOSONAR: world-traversable dirs are expected here, same as the existing /etc/apt/keyrings task (S2612 is a false positive for a traversable dir). - Use [[ ]] instead of [ ] for the new proxy conditionals in install.sh and upgrade_containers.sh (S7688). - Reuse a local for the repeated --extra-vars literal (S1192). Co-Authored-By: Claude Opus 4.8 <[email protected]> Claude-Session: https://claude.ai/code/session_01HJ3ucEkn62cbgPoisAZ5LQ
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #3242 +/- ##
=========================================
Coverage ? 90.85%
=========================================
Files ? 76
Lines ? 8361
Branches ? 885
=========================================
Hits ? 7596
Misses ? 548
Partials ? 217 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR adds end-to-end HTTP(S) proxy propagation for non-balena installs so a single proxy configuration can be applied consistently across install-time tooling (Ansible/apt), the Docker daemon, the host-agent systemd unit, and the runtime containers.
Changes:
- Adds an interactive proxy prompt in
install.sh, persists proxy settings via a new/etc/anthias/proxy.env, and injects proxy variables into Ansible, apt, and container rendering. - Configures proxy support for dockerd (systemd drop-in) and the
anthias-host-agentservice (EnvironmentFile). - Ensures proxied installs avoid breaking internal container-to-container traffic by computing
NO_PROXYto includeanthias-serverandredis.
Reviewed changes
Copilot reviewed 10 out of 12 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
docker-compose.yml.tmpl |
Injects HTTP(S) proxy env vars into server/viewer/celery containers. |
bin/upgrade_containers.sh |
Sources /etc/anthias/proxy.env before envsubst so rendered compose gets proxy values. |
bin/install.sh |
Prompts for proxy settings, exports them for installer tooling, writes apt proxy config, and passes proxy/user vars into Ansible. |
ansible/site.yml |
Adds play-level proxy vars + computed NO_PROXY, and sets play-wide environment: for proxied tasks. |
ansible/roles/system/templates/proxy.env.j2 |
Introduces the persisted proxy “source of truth” env file format consumed by systemd and scripts. |
ansible/roles/system/tasks/proxy.yml |
Manages /etc/anthias/proxy.env and apt proxy config idempotently (create/remove). |
ansible/roles/system/tasks/main.yml |
Includes proxy task setup and adds a non-Pi guard for boot partition tasks. |
ansible/roles/system/tasks/docker.yml |
Avoids unnecessary network fetch for Docker apt key and adds dockerd proxy drop-in management. |
ansible/roles/system/handlers/main.yml |
Adds a handler to restart docker after proxy drop-in changes. |
ansible/roles/anthias/templates/anthias-host-agent.service |
Adds EnvironmentFile=-/etc/anthias/proxy.env so host-agent requests honors proxy. |
ansible/roles/anthias/tasks/main.yml |
Restarts Anthias systemd units when unit templates change so new EnvironmentFile is applied. |
ansible/roles/anthias/handlers/main.yml |
Adds handler to restart Anthias systemd units on template change. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
A proxy URL can embed credentials (http://user:pass@proxy), so a world-readable /etc/anthias/proxy.env could leak them to other local users. Make it 0640, group-owned by the Anthias user so upgrade_containers.sh (the only non-root reader) can still source it; the Docker daemon and host-agent EnvironmentFiles are read by systemd as root. Also removes the NOSONAR — 0640 has no world bits to flag. Co-Authored-By: Claude Opus 4.8 <[email protected]> Claude-Session: https://claude.ai/code/session_01HJ3ucEkn62cbgPoisAZ5LQ
|



Closes #3239.
Installing and running Anthias on a host whose only internet path is an HTTP proxy previously meant patching every network layer by hand, because none of them share environment or config: the Ansible play, apt, the Docker daemon, the
anthias-host-agentunit, and the app containers each have an isolated environment. Each was verified against the code before this change (the reporter's nine points all held up).Design — one input, one source of truth, four layers
The proxy is captured once (a new
install.shprompt, prefilled from anyHTTP_PROXY/HTTPS_PROXYalready in the environment) and propagated from a single on-disk file,/etc/anthias/proxy.env, written by the Ansible system role:environment:soget_url/apttraverse the proxy;install.shalso exports it for its own curl/git/uv and writes an apt proxy conf (its apt runs via baresudo, so it never sees the shell env).docker.service.d/http-proxy.confsystemd drop-in (EnvironmentFile) soghcr.iopulls work.anthias-host-agent) gets anEnvironmentFile; itsrequestscalls then honor it viatrust_env.HTTP_PROXY/HTTPS_PROXY/NO_PROXYrendered into the server/viewer/celery services viaenvsubst(upgrade_containers.shsourcesproxy.env). All app HTTP clients userequestsso they honor it for free; yt-dlp and QtWebEngine inherit the container env.NO_PROXYis computed, not hand-typed. It always includes the Compose service names (anthias-server,redis) plus loopback, so container-to-container traffic is never force-proxied — the failure that blanked the screen in the report (requestsmatchesNO_PROXYon the URL hostname, so the service names are what matter). Everything is idempotent both ways: with no proxy configured the managed files are removed and rendered values are empty (treated as "no proxy").Also fixed (robustness points from the report)
get_urlfor the Docker apt key gets astat+whenguard (network-free when the key is already present).boot.yml(writes/boot/firmware/config.txt) gets adevice_typewhen:guard so a directansible-playbookrun can't hit it on non-Pi hardware.install.shwarns beforegit reset --harddiscards local tracked edits.anthias_useris passed explicitly and falls back toansible_user_id, not just envUSER(which resolves to root under sudo, breaking host-agent paths).Balena images use a different (supervisor/NetworkManager) proxy mechanism and are out of scope here.
Testing — validated end-to-end against a real logging proxy
Stood up a logging forward proxy and introspected exactly what routed through it vs bypassed it. Hosts seen through the proxy:
Internal service names (
anthias-server,redis) in the proxy log: 0 — proven bypass by absence.Per layer:
apt.conf→ all repos incl.download.docker.comrouted via proxy.requests,trust_env) →https://example.comvia proxy (200);http://anthias-server:8080andhttp://redis:6379bypassed (no proxy log entry).example.comvia proxy (200);anthias-serverbypassed (resolved directly, no proxy entry).proxy.env(set -a) +envsubstrenders the proxy into all three services.Static checks all green:
bash -n,ansible-lint(production profile),ansible-playbook --syntax-check, and the computedNO_PROXY/environmenttemplating evaluated across proxy / no-proxy / https-only scenarios.The Docker-daemon drop-in is the standard systemd
EnvironmentFilemechanism; the apt run already provesdownload.docker.comreaches through the proxy, which is the same env the daemon reads for pulls. A full behind-proxy install on the x86 hardware testbed is a sensible pre-merge acceptance check and can follow.🤖 Generated with Claude Code
https://claude.ai/code/session_01HJ3ucEkn62cbgPoisAZ5LQ