--- title: Nginx API Key Proxy emoji: 🔐 colorFrom: gray colorTo: blue sdk: docker app_port: 7860 pinned: false --- # Nginx API Key Proxy Builds nginx **from source** and runs it as a reverse proxy that injects an upstream **API key server-side**. Clients never need the key, and the key is **never written into `nginx.conf`**. ## What this Space demonstrates | Concern | Implementation | | --- | --- | | Build | `install-nginx.sh` compiles nginx 1.25.3 with `http_ssl`, `stream`, `realip`, `http_v2`, `threads`, `file_aio`, `stub_status` | | Key discovery | Vault → AWS Secrets Manager → local files → systemd units → placeholder | | Key at rest | Only `/etc/nginx-upstream.env` (`chmod 600`, root) | | Key in config | **No** — `env upstream_api_key;` + `map` exposes it at runtime | | Injection | `proxy_set_header Authorization "Bearer $api_key_value"` overrides any client header | ## Verifying the injection Requests to `/model-api/` are proxied to the demo upstream, which echoes the headers it received: ```bash curl -s https://.hf.space/model-api/ ``` The response shows `received_headers.Authorization` — a value nginx added server-side. Send your own `Authorization` header and note it is **overridden**, not passed through. ## Key sources (in precedence order) | # | Source | Configuration | | --- | --- | --- | | 1 | Explicit env var | `UPSTREAM_API_KEY=...` | | 2 | Cached secrets file | `/etc/nginx-secrets.env`, `/etc/nginx-upstream.env` | | 3 | HashiCorp Vault | `VAULT_ADDR`, `VAULT_SECRET_PATH`, `VAULT_SECRET_FIELD` | | 4 | Vault AppRole | `VAULT_ROLE_ID` + `VAULT_SECRET_ID_FILE` | | 5 | AWS Secrets Manager | `AWS_SECRET_ID`, `AWS_SECRET_FIELD`, `AWS_REGION` | | 6 | Local files | `/etc/myapp/api.key`, `/etc/myapp/secrets.env`, `/opt/app/.env`, `/etc/environment` | | 7 | Backend systemd units | `myapp.service`, `model-api.service` | | 8 | Placeholder | `__REPLACE_ME__` (visible, deliberately invalid) | Tune `LOCAL_KEY_SOURCES` and `BACKEND_UNITS` in `install-nginx.sh` for your real paths. ## Deploying on a systemd host `install-nginx.sh` is written for a VM/EC2-style host: ```bash sudo AWS_SECRET_ID=myapp/api-key AWS_REGION=us-east-1 ./install-nginx.sh ``` It installs the unit at `/etc/systemd/system/nginx.service` with `EnvironmentFile=/etc/nginx-upstream.env` and enables it. Vault via AppRole, avoiding a static token on disk: ```bash sudo install -m 600 /dev/stdin /etc/vault-approle-secret-id <<< "$SECRET_ID" sudo VAULT_ADDR=https://vault.example.com:8200 \ VAULT_SECRET_PATH=secret/data/myapp \ VAULT_ROLE_ID=... ./install-nginx.sh ``` ## Honest limitations - **The key is still on disk**, moved from `nginx.conf` to a root-only env file. Truly diskless requires a vault agent or custom module. - **Env vars are readable by root** via `/proc//environ`. Inherent to the `EnvironmentFile` mechanism. - **Scraping `.env` / systemd files is heuristic** — it assumes simple `KEY=value` lines and will miss values fetched from a vault at runtime. - **No systemd in this container.** The unit file is written for parity but the entrypoint runs nginx in the foreground. - **Build time is several minutes** — nginx is compiled from source on every image build rather than installed from a package.