From 3460d825b9172073148acc68ee6f5bd105bdadd7 Mon Sep 17 00:00:00 2001 From: pzzzy <119996856+pzzzy@users.noreply.github.com> Date: Tue, 21 Jul 2026 20:13:50 -0400 Subject: [PATCH] Harden macOS deployment and tunnel reporting --- README.md | 11 ++++- scripts/deploy-remote-macos.sh | 52 ++++++++++++++++++---- scripts/install-macos-codesign-identity.sh | 19 +++++++- scripts/upgrade-cloudflared.sh | 4 +- scripts/weekly-report.sh | 6 +-- 5 files changed, 76 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 98ba87a..7ad4725 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ MACFTPD_CODESIGN_KEYCHAIN_PASS_FILE='/opt/macftpd/var/macftpd-codesign.keychain. ./scripts/deploy-remote-macos.sh ``` -Run the installer on the remote Mac itself. It prints the three values needed by the deploy script. If no stable identity is configured, deploy falls back to ad-hoc signing and warns that macOS may ask for removable-volume access again after a binary replacement. +Run the installer on the remote Mac itself. It prints the three values needed by the deploy script. The installer registers its custom keychain in the user search list and grants `codesign` access to the private key. If macOS asks for an interactive trust approval, use the exact case-sensitive `trustRoot` and `codeSign` values printed by the installer. When any stable-signing option is configured, deployment signs and verifies the staged binary before replacing the active one and fails closed if the identity cannot be used. Ad-hoc signing is retained only for deploys with no stable identity configured and may cause macOS to ask for removable-volume access again after a binary replacement. Override `REMOTE_DIR` and `STORAGE_ROOT` for site-specific installs, for example a home-directory app folder with an external-volume FTP root: @@ -96,6 +96,8 @@ ADMIN_PASS='same-password' ./scripts/smoke-remote.sh ADMIN_PASS='same-password' HOST=192.0.2.10 ./scripts/protocol-lab.sh ``` +Prefer a numeric LAN IPv4 address for the protocol lab when `.local` resolution exposes both link-local IPv6 and IPv4 routes. The lab intentionally exercises a long-lived FTP control connection, so a lossy link-local route can fail even while loopback monitoring and the IPv4 service are healthy. + ## Cloudflare HTTP Front Door `https://ftp.example.com` is served through Cloudflare Tunnel and a Worker: @@ -113,6 +115,13 @@ TUNNEL_TOKEN_FILE=/path/to/token ./scripts/start-cloudflare-tunnel.sh The token is stored on the remote Mac at `/opt/macftpd/var/cloudflared.env.token` with mode `0600`, and the screen session is `macftpd-cloudflared`. +Upgrade the bundled connector with the release-pinned, checksum-verified helper. A remote upgrade keeps a timestamped previous binary and restarts only the connector LaunchAgent: + +```bash +REMOTE='macftpd@example-host.local' KEY='/path/to/ssh-key' \ +REMOTE_DIR='/opt/macftpd' ./scripts/upgrade-cloudflared.sh +``` + Deploy or repair the Worker route: ```bash diff --git a/scripts/deploy-remote-macos.sh b/scripts/deploy-remote-macos.sh index 14e1bd7..72d75d0 100755 --- a/scripts/deploy-remote-macos.sh +++ b/scripts/deploy-remote-macos.sh @@ -98,17 +98,45 @@ set -euo pipefail REMOTE_DIR="${REMOTE_DIR:-/opt/macftpd}" sign_macftpd() { local binary="$1" - if ! command -v codesign >/dev/null 2>&1; then - return 0 - fi local identity="${MACFTPD_CODESIGN_IDENTITY:-}" local keychain="${MACFTPD_CODESIGN_KEYCHAIN:-}" local pass_file="${MACFTPD_CODESIGN_KEYCHAIN_PASS_FILE:-}" + local stable_requested=false + if [[ -n "${identity}" || -n "${keychain}" || -n "${pass_file}" ]]; then + stable_requested=true + fi + if ! command -v codesign >/dev/null 2>&1; then + if [[ "${stable_requested}" == true ]]; then + echo "error: stable codesigning was requested, but codesign is unavailable" >&2 + return 1 + fi + return 0 + fi local keychain_args=() if [[ -n "${keychain}" ]]; then keychain_args=(--keychain "${keychain}") if [[ -n "${pass_file}" && -f "${pass_file}" ]]; then - security unlock-keychain -p "$(cat "${pass_file}")" "${keychain}" >/dev/null 2>&1 || true + local keychain_pass + keychain_pass="$(cat "${pass_file}")" + security unlock-keychain -p "${keychain_pass}" "${keychain}" + security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "${keychain_pass}" "${keychain}" >/dev/null + fi + + local search_output entry found=false + local -a search_list=() + search_output="$(security list-keychains -d user)" + while IFS= read -r entry; do + entry="${entry#"${entry%%[![:space:]]*}"}" + entry="${entry#\"}" + entry="${entry%\"}" + [[ -n "${entry}" ]] || continue + search_list+=("${entry}") + if [[ "${entry}" == "${keychain}" ]]; then + found=true + fi + done <<<"${search_output}" + if [[ "${found}" != true ]]; then + security list-keychains -d user -s "${search_list[@]}" "${keychain}" fi fi if [[ -z "${identity}" && -n "${keychain}" ]]; then @@ -116,20 +144,26 @@ sign_macftpd() { fi if [[ -n "${identity}" ]]; then if codesign --force --sign "${identity}" "${keychain_args[@]}" --identifier org.rememe.macftpd "${binary}"; then - return 0 + if codesign --verify --strict --verbose=2 "${binary}"; then + return 0 + fi fi - echo "warning: stable codesign identity failed; falling back to ad-hoc signing" >&2 - else - echo "warning: no stable codesign identity configured; ad-hoc signing may retrigger macOS removable-volume prompts after each upgrade" >&2 + echo "error: stable codesign identity failed; refusing to replace the deployed binary" >&2 + return 1 + fi + if [[ "${stable_requested}" == true ]]; then + echo "error: stable codesigning was requested, but no valid identity was found" >&2 + return 1 fi + echo "warning: no stable codesign identity configured; ad-hoc signing may retrigger macOS removable-volume prompts after each upgrade" >&2 codesign --force --sign - --identifier org.rememe.macftpd "${binary}" } chmod 755 "${REMOTE_DIR}/bin/macftpd.new" +sign_macftpd "${REMOTE_DIR}/bin/macftpd.new" if [[ -f "${REMOTE_DIR}/bin/macftpd" ]]; then cp "${REMOTE_DIR}/bin/macftpd" "${REMOTE_DIR}/bin/macftpd.prev.$(date -u +%Y%m%dT%H%M%SZ)" fi mv "${REMOTE_DIR}/bin/macftpd.new" "${REMOTE_DIR}/bin/macftpd" -sign_macftpd "${REMOTE_DIR}/bin/macftpd" if [[ ! -f "${REMOTE_DIR}/config.json" ]]; then mv "${REMOTE_DIR}/config.json.new" "${REMOTE_DIR}/config.json" else diff --git a/scripts/install-macos-codesign-identity.sh b/scripts/install-macos-codesign-identity.sh index 224f470..6611c7a 100755 --- a/scripts/install-macos-codesign-identity.sh +++ b/scripts/install-macos-codesign-identity.sh @@ -23,6 +23,23 @@ if [[ ! -f "${KEYCHAIN}" ]]; then fi security unlock-keychain -p "${PASS}" "${KEYCHAIN}" +search_output="$(security list-keychains -d user)" +search_list=() +found=false +while IFS= read -r entry; do + entry="${entry#"${entry%%[![:space:]]*}"}" + entry="${entry#\"}" + entry="${entry%\"}" + [[ -n "${entry}" ]] || continue + search_list+=("${entry}") + if [[ "${entry}" == "${KEYCHAIN}" ]]; then + found=true + fi +done <<<"${search_output}" +if [[ "${found}" != true ]]; then + security list-keychains -d user -s "${search_list[@]}" "${KEYCHAIN}" +fi + cat >"${OPENSSL_CONFIG}" </dev/null 2>&1 security import "${P12}" -k "${KEYCHAIN}" -P "${PASS}" -T /usr/bin/codesign >/dev/null - security set-key-partition-list -S apple-tool:,apple: -s -k "${PASS}" "${KEYCHAIN}" >/dev/null 2>&1 || true fi +security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "${PASS}" "${KEYCHAIN}" >/dev/null if ! security find-identity -v -p codesigning "${KEYCHAIN}" | grep -F "\"${SIGN_NAME}\"" >/dev/null 2>&1; then cat >&2 </dev/null || true)" fi rm -f /tmp/macftpd-weekly-health.$$ /tmp/macftpd-weekly-health-err.$$ - if command -v cloudflared >/dev/null 2>&1; then - printf -- '- system_cloudflared: `%s`\n' "$(cloudflared --version 2>/dev/null || true)" - elif [[ -x "${APP_DIR}/bin/cloudflared" ]]; then + if [[ -x "${APP_DIR}/bin/cloudflared" ]]; then printf -- '- bundled_cloudflared: `%s`\n' "$("${APP_DIR}/bin/cloudflared" --version 2>/dev/null || true)" + elif command -v cloudflared >/dev/null 2>&1; then + printf -- '- system_cloudflared: `%s`\n' "$(cloudflared --version 2>/dev/null || true)" fi if [[ -x "${APP_DIR}/bin/macftpd" ]]; then printf -- '- bundled_macftpd: present\n'