diff --git a/.beads/knowledge/gotchas.jsonl b/.beads/knowledge/gotchas.jsonl index 5ab5740..d4e6069 100644 --- a/.beads/knowledge/gotchas.jsonl +++ b/.beads/knowledge/gotchas.jsonl @@ -4,3 +4,4 @@ {"id":"gotcha-example-001","type":"gotcha","fact":"Example: Truthy check on numeric values fails for explicit zero - if (value) is false when value === 0.","recommendation":"Use !== undefined or !== null instead of truthy checks when the value could legitimately be 0 or empty string.","confidence":"high","provenance":[{"source":"coderabbit","reference":"PR review","date":"2026-01-09"}],"tags":["javascript","type-coercion","validation"],"affectedFiles":["**/*.ts"],"affectedServices":[],"createdAt":"2026-01-09T00:00:00Z","updatedAt":"2026-01-09T00:00:00Z","usageCount":0,"helpfulCount":0,"outdatedReports":0} {"id":"gotcha-pin-shellcheck-version","type":"gotcha","fact":"GitHub-hosted runners ship a preinstalled ShellCheck whose version drifts; different versions report different findings (SC2236 fired on the runner but not on local 0.11.0).","recommendation":"Pin ShellCheck in CI by downloading the official koalaman release; keep SHELLCHECK_VERSION in ci.yml in sync with local dev (currently v0.11.0).","confidence":"high","provenance":[{"source":"agent","reference":"discovered fixing CI in PR #11","date":"2026-05-18"}],"tags":["ci","shellcheck","lint"],"affectedFiles":[".github/workflows/ci.yml"],"affectedServices":[],"createdAt":"2026-05-18T00:00:00Z","updatedAt":"2026-05-18T00:00:00Z","usageCount":0,"helpfulCount":0,"outdatedReports":0} {"id":"gotcha-certbot-latest-flag-drift","type":"gotcha","fact":"The container is built FROM certbot/certbot:latest, so certbot's CLI drifts with each rebuild. Two flags broke: --manual-public-ip-logging-ok was removed (certbot errors 'unrecognized arguments'), and the bare --dns-ionos plugin selector became an ambiguous prefix of --dns-ionos-credentials/--dns-ionos-propagation-seconds ('ambiguous option: --dns-ionos').","recommendation":"Select third-party DNS plugins with '--authenticator dns-' (unambiguous), never the bare '--dns-' selector (that convenience flag only exists for certbot's official plugins). Avoid deprecated certbot flags. Consider pinning the certbot image to a known tag instead of :latest to stop silent CLI drift on rebuild.","confidence":"high","provenance":[{"source":"agent","reference":"fixing certbot failures on ethicserver","date":"2026-06-09"}],"tags":["certbot","docker","dns-ionos","flags","letsencrypt"],"affectedFiles":["commands/proxy.sh","Dockerfile"],"affectedServices":[],"createdAt":"2026-06-09T00:00:00Z","updatedAt":"2026-06-09T00:00:00Z","usageCount":0,"helpfulCount":0,"outdatedReports":0} +{"id":"gotcha-reinstall-breaks-bind-mount","type":"gotcha","fact":"easy proxy create bind-mounts ${EASY_DIR}/easyhome -> /usr/local/share/easy (EASY_DIR = dirname of the resolved 'easy' binary, i.e. the npm install dir). Running 'npm install -g' while the proxy container is up rewrites that directory, so the live container's bind mount points at the replaced inode and nginx -s reload fails: open() \"/usr/local/share/easy/nginx.conf\" failed (No such file or directory).","recommendation":"After upgrading the CLI with npm install -g, recreate the container so it re-binds to the fresh easyhome: 'easy proxy destroy && easy proxy create' (or 'easy proxy restart', which re-resolves the bind source). A plain reload is not enough. If backends live on Docker networks, set EASY_PROXY_NETWORK or run 'easy proxy recover' after recreating.","confidence":"high","provenance":[{"source":"agent","reference":"reload failure on ethicserver after upgrading to 2.3.1","date":"2026-06-09"}],"tags":["docker","bind-mount","npm","reload","nginx","upgrade"],"affectedFiles":["commands/proxy.sh","easy"],"affectedServices":[],"createdAt":"2026-06-09T00:00:00Z","updatedAt":"2026-06-09T00:00:00Z","usageCount":0,"helpfulCount":0,"outdatedReports":0} diff --git a/CHANGELOG.md b/CHANGELOG.md index 330941e..f098820 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,19 @@ All notable changes to this project are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/). +## [2.3.2] — 2026-06-09 + +### Fixed + +- `easy proxy certbot-ionos` — write `ionos.ini` in the format the current + `certbot-dns-ionos` plugin requires: `dns_ionos_prefix`, `dns_ionos_secret` + and `dns_ionos_endpoint` (https://api.hosting.ionos.com). The old + `dns_ionos_api_key` / `dns_ionos_api_secret` keys were dropped upstream and + certbot aborted with "Missing properties in credentials configuration file". + The stored credentials are unchanged — `ionos/api-key` is the public prefix, + `ionos/api-secret` is the secret. The endpoint can be overridden with + `IONOS_API_ENDPOINT`. + ## [2.3.1] — 2026-06-09 ### Fixed diff --git a/CLAUDE.md b/CLAUDE.md index b31cd1c..c921067 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,7 +1,7 @@ # easy-proxy — CLAUDE.md > Nginx reverse proxy CLI con Let's Encrypt SSL automation e multi-DNS provider. -> Stato: **ATTIVO** — v2.3.1 +> Stato: **ATTIVO** — v2.3.2 > Ultimo aggiornamento: 2026-06-09 --- diff --git a/commands/proxy.sh b/commands/proxy.sh index 10b07c7..4157cbf 100644 --- a/commands/proxy.sh +++ b/commands/proxy.sh @@ -141,10 +141,17 @@ function __easy_command_proxy { return 1 fi - # Create credentials file in container + # IONOS Remote API endpoint — standard value covers all accounts; overridable. + local ionos_endpoint="${IONOS_API_ENDPOINT:-https://api.hosting.ionos.com}" + + # Create credentials file in container. The certbot-dns-ionos plugin expects + # prefix/secret/endpoint keys (the IONOS API key is ".", so the + # stored api-key is the public prefix and api-secret is the secret). The older + # dns_ionos_api_key / dns_ionos_api_secret keys were dropped upstream. docker exec "${EASY_PROXY_NAME}" /bin/sh -c "cat > /etc/letsencrypt/ionos.ini <<'EOF' -dns_ionos_api_key = ${api_key} -dns_ionos_api_secret = ${api_secret} +dns_ionos_prefix = ${api_key} +dns_ionos_secret = ${api_secret} +dns_ionos_endpoint = ${ionos_endpoint} EOF chmod 600 /etc/letsencrypt/ionos.ini" diff --git a/package.json b/package.json index c62bd3e..39359cf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ethiclab/easy-cli", - "version": "2.3.1", + "version": "2.3.2", "description": "Nginx reverse proxy with Let's Encrypt SSL automation, multi-DNS provider support (IONOS, Route53, Cloudflare, DigitalOcean)", "files": [ "easy", diff --git a/test/dispatcher.bats b/test/dispatcher.bats index 995ba2f..661ddcf 100644 --- a/test/dispatcher.bats +++ b/test/dispatcher.bats @@ -8,14 +8,14 @@ setup() { easy_setup; } @test "easy --version prints the package.json version" { run easy --version [ "$status" -eq 0 ] - [ "$output" = "2.3.1" ] + [ "$output" = "2.3.2" ] } @test "easy --version works without the runtime env vars set" { unset EASY_LETSENCRYPT_DIR EASY_DOMAINS_DIR run easy --version [ "$status" -eq 0 ] - [ "$output" = "2.3.1" ] + [ "$output" = "2.3.2" ] } @test "easy with no command prints usage and fails" { diff --git a/test/proxy.bats b/test/proxy.bats index 60065e5..bce860e 100644 --- a/test/proxy.bats +++ b/test/proxy.bats @@ -105,6 +105,20 @@ setup() { easy_setup; } ! grep -Eq -- "--dns-ionos( |$)" "$DOCKER_LOG" } +@test "easy proxy certbot-ionos writes ionos.ini in the prefix/secret/endpoint format" { + export DOCKER_LOG="$BATS_TEST_TMPDIR/docker.log" + mock_docker_running_record + export EASY_LETSENCRYPT_EMAIL="test@example.com" + export IONOS_API_KEY="key" IONOS_API_SECRET="secret" + run easy proxy certbot-ionos example.com + [ "$status" -eq 0 ] + # Modern certbot-dns-ionos requires these keys; the legacy api_key/api_secret are gone. + grep -q -- "dns_ionos_prefix = key" "$DOCKER_LOG" + grep -q -- "dns_ionos_secret = secret" "$DOCKER_LOG" + grep -q -- "dns_ionos_endpoint = https://api.hosting.ionos.com" "$DOCKER_LOG" + ! grep -q -- "dns_ionos_api_key" "$DOCKER_LOG" +} + @test "easy proxy certbot does not pass the removed --manual-public-ip-logging-ok flag" { export DOCKER_LOG="$BATS_TEST_TMPDIR/docker.log" mock_docker_running_record