Merge pull request #53 from beatz174-bit/codex/fix-dynu-api-authentication-issue
Auto-load secrets/dynu.env, harden Dynu credential handling, and update docs
This commit is contained in:
@@ -24,6 +24,18 @@ This repository includes a **read-only** Dynu DNS inventory workflow for `lan.dd
|
||||
- `DYNU_BASE_URL` (optional, defaults to `https://api.dynu.com`)
|
||||
- `DYNU_READ_ONLY` (**must** be `true`)
|
||||
|
||||
Recommended local secrets file (not committed): `secrets/dynu.env`
|
||||
|
||||
```bash
|
||||
DYNU_API_KEY=replace-with-real-api-key
|
||||
DYNU_READ_ONLY=true
|
||||
DYNU_BASE_URL=https://api.dynu.com
|
||||
```
|
||||
|
||||
Notes:
|
||||
- Keep values unquoted unless required by your shell.
|
||||
- `scripts/dynu/build_dns_inventory.sh` will auto-load `secrets/dynu.env` when present.
|
||||
|
||||
## Commands
|
||||
|
||||
Run directly:
|
||||
@@ -36,7 +48,7 @@ DYNU_READ_ONLY=true python3 scripts/dynu/correlate_dynu_with_traefik.py
|
||||
Or run the wrapper:
|
||||
|
||||
```bash
|
||||
DYNU_READ_ONLY=true DYNU_API_KEY=... scripts/dynu/build_dns_inventory.sh
|
||||
scripts/dynu/build_dns_inventory.sh
|
||||
```
|
||||
|
||||
## Artifacts
|
||||
|
||||
@@ -10,6 +10,7 @@ For machine-readable inventory metadata, use [`../secrets/inventory.json`](../se
|
||||
|
||||
- Canonical example template: [`../secrets/.env.secrets.example`](../secrets/.env.secrets.example)
|
||||
- Runtime-loaded secret env file (local, non-committed): `../secrets/stack-secrets.env`
|
||||
- Dynu DNS inventory env file (local, non-committed): `../secrets/dynu.env`
|
||||
- Docker secret files (local, non-committed): `../secrets/*.txt`
|
||||
|
||||
Treat the example template as the canonical shape for expected environment variables.
|
||||
@@ -20,9 +21,11 @@ Treat the example template as the canonical shape for expected environment varia
|
||||
- Document expected variable names and usage expectations.
|
||||
2. **Local runtime env file (`stack-secrets.env`)**
|
||||
- Holds local runtime secret values loaded during compose rendering.
|
||||
3. **Local Docker secret files (`*.txt`)**
|
||||
3. **Local Dynu env file (`dynu.env`)**
|
||||
- Holds `DYNU_*` values used by read-only Dynu DNS inventory scripts.
|
||||
4. **Local Docker secret files (`*.txt`)**
|
||||
- Hold password/token material consumed via `*_FILE` style configuration.
|
||||
4. **Externally managed secret inputs**
|
||||
5. **Externally managed secret inputs**
|
||||
- Some values are managed outside shared templates and provided through file mounts or environment substitution.
|
||||
|
||||
## Machine-readable inventory
|
||||
@@ -41,6 +44,7 @@ Before running compose operations, follow [`./deployment-prerequisites.md`](./de
|
||||
Never commit:
|
||||
|
||||
- `secrets/stack-secrets.env`
|
||||
- `secrets/dynu.env`
|
||||
- real `secrets/*.txt` secret files
|
||||
- real Terraform `.tfvars` files containing credentials
|
||||
- Terraform state files with sensitive runtime metadata
|
||||
|
||||
@@ -4,10 +4,23 @@ set -euo pipefail
|
||||
# This integration is intentionally read-only.
|
||||
# No Dynu mutations are permitted in this repo at this stage.
|
||||
|
||||
# Optional convenience: auto-load local Dynu env file when variables are unset.
|
||||
if [[ -f "secrets/dynu.env" ]]; then
|
||||
set -a
|
||||
# shellcheck source=/dev/null
|
||||
source "secrets/dynu.env"
|
||||
set +a
|
||||
fi
|
||||
|
||||
if [[ "${DYNU_READ_ONLY:-}" != "true" ]]; then
|
||||
echo "Refusing to run: DYNU_READ_ONLY must be exactly 'true'." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ -z "${DYNU_API_KEY:-}" ]]; then
|
||||
echo "Missing DYNU_API_KEY. Set it in env or secrets/dynu.env." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
python3 scripts/dynu/fetch_dynu_dns.py
|
||||
python3 scripts/dynu/correlate_dynu_with_traefik.py
|
||||
|
||||
@@ -54,7 +54,13 @@ def get_json(base_url: str, api_key: str, path: str, query: Optional[Dict[str, A
|
||||
body = resp.read().decode("utf-8")
|
||||
except HTTPError as exc:
|
||||
detail = exc.read().decode("utf-8", errors="replace")
|
||||
raise RuntimeError(f"Dynu API GET failed at {path}: HTTP {exc.code} {detail}") from exc
|
||||
hint = ""
|
||||
if exc.code == 401:
|
||||
hint = (
|
||||
" Check DYNU_API_KEY from secrets/dynu.env, verify it is a valid Dynu API key, "
|
||||
"and ensure DYNU_BASE_URL points to the Dynu API endpoint."
|
||||
)
|
||||
raise RuntimeError(f"Dynu API GET failed at {path}: HTTP {exc.code} {detail}.{hint}") from exc
|
||||
except URLError as exc:
|
||||
raise RuntimeError(f"Dynu API GET failed at {path}: {exc}") from exc
|
||||
|
||||
@@ -177,8 +183,12 @@ def main() -> int:
|
||||
if not api_key:
|
||||
print("Missing DYNU_API_KEY. Refusing to call Dynu API.", file=sys.stderr)
|
||||
return 2
|
||||
api_key = api_key.strip().strip("'").strip('"')
|
||||
if not api_key:
|
||||
print("DYNU_API_KEY is empty after trimming quotes/whitespace.", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
base_url = os.environ.get("DYNU_BASE_URL", DEFAULT_BASE_URL)
|
||||
base_url = os.environ.get("DYNU_BASE_URL", DEFAULT_BASE_URL).strip().strip("'").strip('"')
|
||||
|
||||
domains = list_domains(base_url, api_key)
|
||||
target = [d for d in domains if str(d.get("name", "")).strip(".").lower() == BASE_DOMAIN]
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"scope_and_authority": {
|
||||
"canonical_example_template": "secrets/.env.secrets.example",
|
||||
"runtime_loaded_secret_env_file": "secrets/stack-secrets.env",
|
||||
"dns_inventory_secret_env_file": "secrets/dynu.env",
|
||||
"docker_secret_files_pattern": "secrets/*.txt"
|
||||
},
|
||||
"env_template_variables": [
|
||||
@@ -140,6 +141,7 @@
|
||||
],
|
||||
"commit_safety_rules": [
|
||||
"Never commit secrets/stack-secrets.env.",
|
||||
"Never commit secrets/dynu.env.",
|
||||
"Never commit real secrets/*.txt files.",
|
||||
"Never commit real Terraform .tfvars containing credentials.",
|
||||
"Never commit Terraform state files with sensitive runtime metadata."
|
||||
|
||||
Reference in New Issue
Block a user