From 44d235374c80350093dfa468d90f02fae4111a5b Mon Sep 17 00:00:00 2001 From: Samgu Lee Date: Tue, 29 Sep 2026 22:09:49 +0900 Subject: [PATCH] feat: update documentation and tests to emphasize --env-file usage for credential management --- ARCHITECTURE.md | 4 ++-- FRAMEWORK.md | 18 +++--------------- PRODUCT_SPEC.md | 4 ++-- README.md | 14 +++++++++----- TASKS.md | 12 +++++------- examples/coolrestore-wrapper.sh | 17 ----------------- examples/coolrestore.env.example | 12 ------------ internal/source/source.go | 2 +- internal/source/source_test.go | 4 ++-- 9 files changed, 24 insertions(+), 63 deletions(-) delete mode 100755 examples/coolrestore-wrapper.sh delete mode 100644 examples/coolrestore.env.example diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 3497349..dcfdf80 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -224,7 +224,7 @@ boundaries. applied result, or a failure) into the reports and exit status defined in PRODUCT_SPEC.md. Responsible for never altering the substance of what it reports. For credential-loading failures, it may add the fixed, - non-sensitive wrapper hint defined by the product specification, but it + non-sensitive `--env-file` hint defined by the product specification, but it must never print credential values. --- @@ -257,7 +257,7 @@ boundaries. when target and staging are on the same filesystem. - **Result Reporter** owns communication of outcomes only. It has no authority to alter, retry, or reinterpret the outcome it is given. A - credential-loading failure may include the fixed wrapper hint, but no + credential-loading failure may include the fixed `--env-file` hint, but no credential value. - Authorization is a single boolean fact carried by the invocation from the Invocation Validator onward. Only the boundary between Restore diff --git a/FRAMEWORK.md b/FRAMEWORK.md index faf327a..83a72b7 100644 --- a/FRAMEWORK.md +++ b/FRAMEWORK.md @@ -372,21 +372,9 @@ credential provider chain. The tool must not accept credential values through CLI flags or positional arguments. Operators may use a protected external environment file selected -with `--env-file`, or a wrapper script to provide these variables for repeated -restores. Repository -examples live under: - -```text -examples/ - coolrestore.env.example - coolrestore-wrapper.sh -``` - -Examples must contain placeholders only and must never contain real -credentials. When the credential provider chain cannot load credentials, the -failure report must include a concise wrapper hint without displaying any -credential value. Release distribution must make the same wrapper pattern -available to operators who obtain only the binary. +with `--env-file` for repeated restores. When the credential provider chain +cannot load credentials, the failure report must include a concise +`--env-file` hint without displaying any credential value. --- diff --git a/PRODUCT_SPEC.md b/PRODUCT_SPEC.md index bc3ff5a..8c9bf9f 100644 --- a/PRODUCT_SPEC.md +++ b/PRODUCT_SPEC.md @@ -75,7 +75,7 @@ cannot be trusted to be well-formed. complete, describing which step failed and confirming whether the target directory was left unchanged or may contain merge-mode partial changes. If S3 credentials cannot be loaded, the report also gives a non-sensitive hint - about the supported environment wrapper mechanism without displaying any + about the supported `--env-file` mechanism without displaying any credential value. - A **process exit status** indicating success or failure, suitable for use in automated scripts. @@ -150,7 +150,7 @@ cannot be trusted to be well-formed. 17. When an S3 source is used, the product must load credentials only through the configured environment variables or the AWS SDK default credential provider chain. If no credentials are available, it must report the - supported wrapper mechanism without displaying credential values. + supported `--env-file` mechanism without displaying credential values. 18. When `--env-file` is supplied, the product must load that file before acquiring the target lock or archive. It must accept only the supported `KEY=VALUE` S3 environment entries, ignore blank lines and full-line diff --git a/README.md b/README.md index 50fac3f..88e035d 100644 --- a/README.md +++ b/README.md @@ -125,14 +125,18 @@ The file uses simple `KEY=VALUE` entries. Blank lines and full-line comments are allowed; shell commands and `export` statements are not evaluated. `./coolrestore.env` is never auto-discovered. -For repeated restores, use the wrapper examples in [`examples/`](./examples/) with a protected environment file instead of manually exporting credentials for each command: +For repeated restores, keep `coolrestore.env` protected and pass it explicitly: ```bash -install -m 600 examples/coolrestore.env.example /etc/coolrestore/env -export COOLRESTORE_ENV_FILE=/etc/coolrestore/env +install -m 600 ./coolrestore.env /etc/coolrestore/env +coolrestore \ + --env-file /etc/coolrestore/env \ + --source 's3://bucket/object-key.tar.gz' \ + --target /tmp/coolrestore-test ``` -Replace the placeholder values before use. The release also includes the examples as `coolrestore-examples.tar.gz` for operators who only download the binary. For a user-specific setup, use `~/.config/coolrestore/env` instead. +For a user-specific setup, use `~/.config/coolrestore/env` and pass that path +with `--env-file`. --- @@ -296,7 +300,7 @@ Verify: - `AWS_S3_FORCE_PATH_STYLE=true` when required by the endpoint - bucket and object-key permissions -If credentials cannot be loaded, coolrestore prints a short wrapper hint. It never prints credential values. The default system-wide environment file is `/etc/coolrestore/env`; `COOLRESTORE_ENV_FILE` can point to another protected file. +If credentials cannot be loaded, coolrestore prints a short `--env-file` hint. It never prints credential values. Use a protected file such as `/etc/coolrestore/env` and pass it explicitly with `--env-file`. ### Replace mode was rejected diff --git a/TASKS.md b/TASKS.md index 89d1e87..6356e73 100644 --- a/TASKS.md +++ b/TASKS.md @@ -114,17 +114,15 @@ are complete. - **Acceptance Criteria** - An operator can obtain a runnable copy of the tool for the server environments it is intended to run on, without needing a local development setup to build it themselves. - The tool runs correctly in that environment using only what is already present on a typical target server plus network access to the archive source. -- [x] **Task 7.2 — Provide safe credential wrapper guidance** +- [x] **Task 7.2 — Provide safe environment-file guidance** - **Acceptance Criteria** - Given an S3 restore and credentials supplied through a protected - environment wrapper, the tool can acquire the archive without CLI + environment file, the tool can acquire the archive without CLI credential arguments. - Given missing S3 credentials, the failure report includes a concise - wrapper hint without displaying credential values. - - Repository examples contain placeholders only and document the - environment-variable wrapper pattern for repeated S3-compatible restores. - - Release distribution makes the same wrapper guidance available to - operators who obtain the binary without cloning the repository. + `--env-file` hint without displaying credential values. + - Documentation describes the protected environment-file pattern for + repeated S3-compatible restores. - [x] **Task 7.3 — Select S3 credentials with an explicit environment file** - **Acceptance Criteria** diff --git a/examples/coolrestore-wrapper.sh b/examples/coolrestore-wrapper.sh deleted file mode 100755 index 39d570b..0000000 --- a/examples/coolrestore-wrapper.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -environment_file="${COOLRESTORE_ENV_FILE:-/etc/coolrestore/env}" -if [[ ! -r "$environment_file" ]]; then - echo "coolrestore credential environment file is not readable: $environment_file" >&2 - echo "copy examples/coolrestore.env.example to a protected environment file and set COOLRESTORE_ENV_FILE if needed" >&2 - exit 1 -fi - -set -a -# The environment file must be administrator-controlled and owner-readable. -# shellcheck disable=SC1090 -source "$environment_file" -set +a - -exec /usr/local/bin/coolrestore "$@" diff --git a/examples/coolrestore.env.example b/examples/coolrestore.env.example deleted file mode 100644 index 0347462..0000000 --- a/examples/coolrestore.env.example +++ /dev/null @@ -1,12 +0,0 @@ -# Copy this file to a protected system location such as: -# /etc/coolrestore/env -# -# For a per-user setup, use ~/.config/coolrestore/env instead. -# Set permissions on the copied file to owner-only access: -# chmod 600 /etc/coolrestore/env - -AWS_ACCESS_KEY_ID=replace-me -AWS_SECRET_ACCESS_KEY=replace-me -AWS_REGION=us-east-1 -AWS_ENDPOINT_URL=https://s3-compatible.example.com -AWS_S3_FORCE_PATH_STYLE=true diff --git a/internal/source/source.go b/internal/source/source.go index 71c5237..b3b19df 100644 --- a/internal/source/source.go +++ b/internal/source/source.go @@ -17,7 +17,7 @@ import ( "github.com/aws/aws-sdk-go-v2/service/s3" ) -const credentialWrapperHint = "hint: use --env-file with a protected environment file or a protected environment wrapper; see the release examples bundle" +const credentialWrapperHint = "hint: use --env-file with a protected environment file" // Artifact is an archive made available to later restore stages. Local // artifacts point at the supplied file; remote artifacts point at a temporary diff --git a/internal/source/source_test.go b/internal/source/source_test.go index c74521b..1cd9326 100644 --- a/internal/source/source_test.go +++ b/internal/source/source_test.go @@ -137,11 +137,11 @@ func TestVerifySizeRejectsTruncatedReader(t *testing.T) { } } -func TestCredentialWrapperHintDoesNotContainCredentialValues(t *testing.T) { +func TestCredentialHintDoesNotContainCredentialValues(t *testing.T) { if strings.Contains(credentialWrapperHint, "AWS_SECRET_ACCESS_KEY=") { t.Fatal("credential wrapper hint contains a credential assignment") } - if !strings.Contains(credentialWrapperHint, "protected environment wrapper") { + if !strings.Contains(credentialWrapperHint, "--env-file") { t.Fatalf("credential wrapper hint = %q", credentialWrapperHint) } }