#!/usr/bin/env bash set -euo pipefail umask 077 SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" # shellcheck source=Scripts/_burrow-secrets.sh source "${REPO_ROOT}/Scripts/_burrow-secrets.sh" usage() { cat <<'EOF' Usage: Tools/forwardemail-custom-s3.sh \ --domain burrow.net \ --api-token-file secrets/forwardemail/api-token.age \ --s3-endpoint https:// \ --s3-region \ --s3-bucket \ --s3-access-key-file secrets/forwardemail/hetzner-s3-user.age \ --s3-secret-key-file secrets/forwardemail/hetzner-s3-secret.age Options: --domain Forward Email domain to update. --api-token-file File containing the Forward Email API token. --s3-endpoint S3-compatible endpoint URL. --s3-region S3 region string expected by Forward Email. --s3-bucket Bucket used for alias backup uploads. --s3-access-key-file File containing the S3 access key id. --s3-secret-key-file File containing the S3 secret access key. --test-only Skip the update call and only test the saved connection. --help Show this help text. Notes: - Secrets are passed to curl through a temporary config file to avoid putting them in the process list. - By default the script updates the domain settings and then calls /test-s3-connection. - For Hetzner Object Storage, use the regional S3 endpoint such as https://hel1.your-objectstorage.com, not an account alias endpoint. EOF } fail() { printf 'error: %s\n' "$*" >&2 exit 1 } require_file() { local path="$1" [[ -f "$path" ]] || fail "missing file: $path" } read_secret() { local path="$1" local value value="$(tr -d '\r\n' < "$path")" [[ -n "$value" ]] || fail "empty secret file: $path" printf '%s' "$value" } cleanup() { burrow_cleanup_secret_tmpfiles } trap cleanup EXIT domain="" api_token_file="${FORWARDEMAIL_API_TOKEN_FILE:-}" s3_endpoint="" s3_region="" s3_bucket="" s3_access_key_file="${FORWARDEMAIL_S3_ACCESS_KEY_FILE:-}" s3_secret_key_file="${FORWARDEMAIL_S3_SECRET_KEY_FILE:-}" test_only=false while [[ $# -gt 0 ]]; do case "$1" in --domain) domain="${2:-}" shift 2 ;; --api-token-file) api_token_file="${2:-}" shift 2 ;; --s3-endpoint) s3_endpoint="${2:-}" shift 2 ;; --s3-region) s3_region="${2:-}" shift 2 ;; --s3-bucket) s3_bucket="${2:-}" shift 2 ;; --s3-access-key-file) s3_access_key_file="${2:-}" shift 2 ;; --s3-secret-key-file) s3_secret_key_file="${2:-}" shift 2 ;; --test-only) test_only=true shift ;; --help|-h) usage exit 0 ;; *) fail "unknown argument: $1" ;; esac done [[ -n "$domain" ]] || fail "--domain is required" [[ -n "$s3_endpoint" || "$test_only" == true ]] || fail "--s3-endpoint is required unless --test-only is set" [[ -n "$s3_region" || "$test_only" == true ]] || fail "--s3-region is required unless --test-only is set" [[ -n "$s3_bucket" || "$test_only" == true ]] || fail "--s3-bucket is required unless --test-only is set" api_token_file="$( burrow_resolve_secret_file \ "${REPO_ROOT}" \ "${api_token_file}" \ "${REPO_ROOT}/intake/forwardemail_api_token.txt" \ "${REPO_ROOT}/secrets/forwardemail/api-token.age" )" || fail "unable to resolve Forward Email API token file" require_file "$api_token_file" api_token="$(read_secret "$api_token_file")" if [[ "$test_only" != true ]]; then s3_access_key_file="$( burrow_resolve_secret_file \ "${REPO_ROOT}" \ "${s3_access_key_file}" \ "${REPO_ROOT}/intake/hetzner-s3-user.txt" \ "${REPO_ROOT}/secrets/forwardemail/hetzner-s3-user.age" )" || fail "unable to resolve Hetzner S3 access key file" s3_secret_key_file="$( burrow_resolve_secret_file \ "${REPO_ROOT}" \ "${s3_secret_key_file}" \ "${REPO_ROOT}/intake/hetzner-s3-secret.txt" \ "${REPO_ROOT}/secrets/forwardemail/hetzner-s3-secret.age" )" || fail "unable to resolve Hetzner S3 secret key file" require_file "$s3_access_key_file" require_file "$s3_secret_key_file" fi if [[ "$test_only" == false ]]; then require_file "$s3_access_key_file" require_file "$s3_secret_key_file" s3_access_key_id="$(read_secret "$s3_access_key_file")" s3_secret_access_key="$(read_secret "$s3_secret_key_file")" case "$s3_endpoint" in http://*|https://*) ;; *) fail "--s3-endpoint must start with http:// or https://" ;; esac fi curl_config="$(mktemp)" trap 'rm -f "$curl_config"' EXIT if [[ "$test_only" == false ]]; then cat >"$curl_config" <&2 curl --config "$curl_config" printf '\n' >&2 fi cat >"$curl_config" <&2 curl --config "$curl_config" printf '\n' >&2