Skip to content

Commit bb1762d

Browse files
committed
ci: verify Artifactory serves bytes rather than redirecting
The download counters cannot see a redirect. `go mod download -x` traces the URL Go requested, and http.Client.Do follows redirects inside that call, logging the final status against the original URL. So a 3xx handing the transfer to another origin would increment `served` and leave `other` at zero while the bytes came from somewhere else. Raised in review of #2376. Two changes. The GOPROXY assertion is now an exact match on the full expected URL rather than a host prefix. The `-cdn` repository variants are the one documented path where Artifactory hands a client to another origin (Direct Download), so pinning the repo key closes it. Upstream F-009 records that Direct Download never engages for Go — no module archive approaches its 100 MB threshold — but the assertion costs nothing and does not depend on that remaining true. And a canary: fetch one module archive with curl and assert zero redirects and an Artifactory final URL. Redirect behavior is a repository-level setting rather than per-module, so one request is representative; if the repo were configured to redirect, it would redirect every zip rather than one. The credential is passed through `curl --config` on stdin so the token never reaches argv, where a process listing on the runner would expose it. That mirrors why the action writes a GOAUTH helper instead of an environment variable. Not adopting the suggested outbound-host allowlist: enforcing egress at the network layer on a GitHub-hosted runner is disproportionate for a workflow that verifies a property and ships nothing. Signed-off-by: Mark Chmarny <mark@chmarny.com>
1 parent 8fa2d10 commit bb1762d

1 file changed

Lines changed: 42 additions & 4 deletions

File tree

.github/workflows/dgxc-goproxy-probe.yaml

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,15 @@ jobs:
128128
case "${proxy}" in
129129
*"|"*) echo "GOPROXY uses '|', which falls through on 401/403. Use ','."; exit 1 ;;
130130
esac
131-
case "${proxy%%,*}" in
132-
https://edge.urm.nvidia.com/*) ;;
133-
*) echo "first GOPROXY entry is '${proxy%%,*}', not Artifactory"; exit 1 ;;
134-
esac
131+
# Exact match, not a host prefix. The `-cdn` repository variants are
132+
# the one documented path on which Artifactory hands a client off to
133+
# another origin (Direct Download), so pinning the repo key and not
134+
# just the host closes it. Upstream F-009 records that Direct
135+
# Download never engages for Go anyway — no module archive
136+
# approaches its 100 MB threshold — but the assertion costs nothing.
137+
expected="https://edge.urm.nvidia.com/artifactory/api/go/dgxc-go-virtual"
138+
[ "${proxy}" = "${expected}" ] \
139+
|| { echo "GOPROXY is '${proxy}', expected exactly '${expected}'"; exit 1; }
135140
[ "$(go env GOSUMDB)" != off ] || { echo "GOSUMDB=off"; exit 1; }
136141
# Enforced means no fallback at all. Reject any comma rather than
137142
# just `,direct`: Go consults later entries on 404/410 whatever they
@@ -142,6 +147,39 @@ jobs:
142147
esac
143148
echo "OK: Artifactory is the only GOPROXY entry and GOSUMDB is intact"
144149
150+
# `go mod download -x` traces the URL Go *requested*, and http.Client.Do
151+
# follows redirects inside that call, logging the final status against
152+
# the original URL. So the download counters below cannot see a 3xx that
153+
# hands the transfer to another origin: `served` would increment and
154+
# `other` stay zero while the bytes came from elsewhere.
155+
#
156+
# Redirect behavior is a repository-level setting rather than per-module,
157+
# so one request is a representative canary. The credential comes from
158+
# the GOAUTH helper via curl --config so the token never reaches argv,
159+
# where a process listing would expose it.
160+
- name: Assert Artifactory serves bytes rather than redirecting
161+
shell: bash
162+
run: |
163+
url="https://edge.urm.nvidia.com/artifactory/api/go/dgxc-go-virtual/gopkg.in/yaml.v2/@v/v2.4.0.zip"
164+
auth="$("${GOAUTH}" | grep -i '^Authorization:')"
165+
[ -n "${auth}" ] || { echo "::error::GOAUTH helper produced no Authorization header"; exit 1; }
166+
167+
read -r redirects effective < <(
168+
printf 'header = "%s"\nurl = "%s"\n' "${auth}" "${url}" \
169+
| curl --config - -sSL --max-time 60 -o /dev/null \
170+
-w '%{num_redirects} %{url_effective}'
171+
)
172+
echo "redirects: ${redirects}"
173+
echo "final URL host: $(printf '%s' "${effective}" | sed -E 's#^https?://([^/]+)/.*#\1#')"
174+
175+
[ "${redirects}" -eq 0 ] \
176+
|| { echo "::error::Artifactory redirected ${redirects} time(s) to ${effective}"; exit 1; }
177+
case "${effective}" in
178+
https://edge.urm.nvidia.com/*) ;;
179+
*) echo "::error::bytes came from ${effective}, not Artifactory"; exit 1 ;;
180+
esac
181+
echo "OK: Artifactory served the archive directly, no redirect to another origin"
182+
145183
# Guards the guard. Both assertions above are satisfied by a job that
146184
# never fetches anything, which is how the first enforced run passed on a
147185
# restored module cache. Coverage is only demonstrated if modules were

0 commit comments

Comments
 (0)