Skip to content

Instantly share code, notes, and snippets.

@hrodrig
Created March 31, 2026 18:18
Show Gist options
  • Select an option

  • Save hrodrig/4e1d56560714d722afc05ec4ddbfc123 to your computer and use it in GitHub Desktop.

Select an option

Save hrodrig/4e1d56560714d722afc05ec4ddbfc123 to your computer and use it in GitHub Desktop.
Resolve bitnamisecure/postgresql digest-style tags → platforms + versions (docker, jq, crane)
#!/bin/sh
# SPDX-License-Identifier: ISC
#
# Copyright 2026 Hermes Rodriguez
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
#
# Personal / educational helper: resolves digest-style tags on OCI registries,
# including Bitnami Secure PostgreSQL patterns (Notary subject chain, TAC,
# Docker manifest lists, SLSA DSSE). Requires: docker, jq, crane.
set -eu
usage() {
cat <<'EOF'
Resolve Bitnami Secure PostgreSQL tag details (version + per-arch digest).
Usage:
resolve-bitnami-postgresql-tag.sh <tag-without-prefix>
resolve-bitnami-postgresql-tag.sh <sha256-...>
resolve-bitnami-postgresql-tag.sh --repo <repo> <tag-without-prefix>
Examples:
resolve-bitnami-postgresql-tag.sh 3b18e4b8fd00bd17db692ab9a54d3c11191a9e300ffaaf7dcc5d2ef707f1e26f
resolve-bitnami-postgresql-tag.sh sha256-3b18e4b8fd00bd17db692ab9a54d3c11191a9e300ffaaf7dcc5d2ef707f1e26f
resolve-bitnami-postgresql-tag.sh --repo docker.io/bitnami/postgresql 621359194c6a0f0fd502c1996f40f539c3e5f2da43401b6d52ee5614722b7cba
EOF
}
require_cmd() {
if ! command -v "$1" >/dev/null 2>&1; then
echo "❌ missing dependency: $1" >&2
exit 1
fi
}
repo="docker.io/bitnamisecure/postgresql"
if [ "${1:-}" = "--repo" ]; then
if [ $# -lt 3 ]; then
usage
exit 1
fi
repo="$2"
shift 2
fi
if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then
usage
exit 0
fi
if [ $# -ne 1 ]; then
usage
exit 1
fi
require_cmd docker
require_cmd jq
require_cmd crane
raw_tag="$1"
case "$raw_tag" in
sha256-*) tag="$raw_tag" ;;
*) tag="sha256-$raw_tag" ;;
esac
ref="${repo}:${tag}"
echo "🔍 Resolving ${ref}"
manifest_json="$(docker manifest inspect "$ref")"
resolve_arch_digest() {
arch="$1"
_mj="$2"
printf "%s" "$_mj" | jq -r --arg arch "$arch" '.manifests[]? | select(.platform.os=="linux" and .platform.architecture==$arch) | .digest' | head -n 1
}
# Ensure digest ref includes sha256: for crane/docker references.
with_sha256_prefix() {
_dig="$1"
[ -z "$_dig" ] && return
case "$_dig" in
sha256:*) printf "%s" "$_dig" ;;
*) printf "sha256:%s" "$_dig" ;;
esac
}
# Print linux/amd64 and linux/arm64 blocks with versions from image config labels.
# Args: repo, amd64_digest (hex or sha256:... or empty), arm64_digest, optional note.
emit_runnable_report() {
_repo="$1"
_amd_plain="$2"
_arm_plain="$3"
case "$_amd_plain" in sha256:*) _amd_plain="${_amd_plain#sha256:}" ;; esac
case "$_arm_plain" in sha256:*) _arm_plain="${_arm_plain#sha256:}" ;; esac
if [ -n "${4:-}" ]; then
printf "%s\n" "$4"
fi
_amd_ref="$(with_sha256_prefix "$_amd_plain")"
_arm_ref="$(with_sha256_prefix "$_arm_plain")"
_ver_amd="n/a"
_ver_arm="n/a"
if [ -n "$_amd_plain" ]; then
_ver_amd="$(crane config "${_repo}@${_amd_ref}" 2>/dev/null | jq -r '.config.Labels["org.opencontainers.image.version"] // "unknown"')"
fi
if [ -n "$_arm_plain" ]; then
_ver_arm="$(crane config "${_repo}@${_arm_ref}" 2>/dev/null | jq -r '.config.Labels["org.opencontainers.image.version"] // "unknown"')"
fi
echo ""
echo "Tag: ${tag}"
echo "Repo: ${_repo}"
echo ""
echo "linux/amd64"
echo " digest: ${_amd_plain:-n/a}"
echo " version: ${_ver_amd}"
if [ -n "$_amd_plain" ]; then
echo " image: ${_repo}@${_amd_ref}"
else
echo " image: n/a"
fi
echo ""
echo "linux/arm64"
echo " digest: ${_arm_plain:-n/a}"
echo " version: ${_ver_arm}"
if [ -n "$_arm_plain" ]; then
echo " image: ${_repo}@${_arm_ref}"
else
echo " image: n/a"
fi
}
digests_from_manifest_list_json() {
_mj="$1"
_amd="$(printf "%s" "$_mj" | jq -r '.manifests[]? | select(.platform.os=="linux" and .platform.architecture=="amd64") | .digest' | head -n 1)"
_arm="$(printf "%s" "$_mj" | jq -r '.manifests[]? | select(.platform.os=="linux" and .platform.architecture=="arm64") | .digest' | head -n 1)"
printf "%s\n%s\n" "$_amd" "$_arm"
}
try_resolve_slsa_dsse() {
_repo="$1"
_json="$2"
_dsse_d="$(printf "%s" "$_json" | jq -r '.layers[]? | select(.mediaType == "application/vnd.dsse.envelope.v1+json") | .digest' | head -n 1)"
[ -z "$_dsse_d" ] && return 1
_blob="$(crane blob "${_repo}@${_dsse_d}" 2>/dev/null)" || return 1
_stmt="$(printf "%s" "$_blob" | jq -r ".payload | @base64d" 2>/dev/null)" || return 1
[ -z "$_stmt" ] && return 1
_amd_hex="$(printf "%s" "$_stmt" | jq -r '.subject[]? | select(.name | test("linux/amd64")) | .digest.sha256' | head -n 1)"
_arm_hex="$(printf "%s" "$_stmt" | jq -r '.subject[]? | select(.name | test("linux/arm64")) | .digest.sha256' | head -n 1)"
if [ -z "$_amd_hex" ] && [ -z "$_arm_hex" ]; then
return 1
fi
emit_runnable_report "${_repo}" "${_amd_hex}" "${_arm_hex}" "ℹ️ Per-arch digests taken from SLSA provenance (in-toto statement inside DSSE)."
return 0
}
# Tags copied from some UIs point at an OCI index whose only entry is a Notary
# signature. subject may be: a VMware TAC bundle, a Docker/OCI multi-arch list,
# or an SLSA provenance manifest (DSSE) listing per-arch image digests.
resolve_attestation_chain() {
_repo="$1"
_d="$2"
_depth=0
while [ "$_depth" -lt 8 ]; do
_m="$(crane manifest "${_repo}@${_d}" 2>/dev/null)" || return 1
_subj="$(printf "%s" "$_m" | jq -r ".subject.digest // empty")"
if [ -n "$_subj" ]; then
_d="$_subj"
_depth=$((_depth + 1))
continue
fi
_pmd="$(printf "%s" "$_m" | jq -r '.layers[]? | select(.mediaType == "application/vnd.vmware.tac.product-metadata.layer.v1.json") | .digest' | head -n 1)"
if [ -n "$_pmd" ]; then
_meta="$(crane blob "${_repo}@${_pmd}" 2>/dev/null)" || return 1
_ver="$(printf "%s" "$_meta" | jq -r '.version // "unknown"')"
_name="$(printf "%s" "$_meta" | jq -r '.name // "unknown"')"
echo ""
echo "⚠️ This tag resolves to supply-chain metadata (e.g. Notary + VMware TAC), not a runnable image manifest list."
echo " Product version from TAC product-metadata: ${_name} ${_ver}"
echo ""
echo "Tag: ${tag}"
echo "Repo: ${_repo}"
echo ""
echo "linux/amd64"
echo " digest: n/a (pin a manifest-list tag like :latest, then use the per-arch digest from that list)"
echo " version: n/a"
echo " image: n/a"
echo ""
echo "linux/arm64"
echo " digest: n/a"
echo " version: n/a"
echo " image: n/a"
return 0
fi
_pair="$(digests_from_manifest_list_json "$_m")"
_amd_d="$(printf "%s" "$_pair" | sed -n "1p")"
_arm_d="$(printf "%s" "$_pair" | sed -n "2p")"
if [ -n "$_amd_d" ] || [ -n "$_arm_d" ]; then
emit_runnable_report "${_repo}" "${_amd_d}" "${_arm_d}" "ℹ️ Runnable images resolved from a manifest list after following the attestation chain."
return 0
fi
if try_resolve_slsa_dsse "${_repo}" "$_m"; then
return 0
fi
_cfgmt="$(printf "%s" "$_m" | jq -r ".config.mediaType // empty")"
case "$_cfgmt" in
*docker.container.image.v1*|*oci.image.config.v1*)
_dref="$(with_sha256_prefix "$(printf "%s" "$_d" | sed 's/^sha256://')")"
_lbl="$(crane config "${_repo}@${_dref}" 2>/dev/null | jq -r '.config.Labels["org.opencontainers.image.version"] // "unknown"')"
echo ""
echo "ℹ️ Single-platform image manifest (no amd64/arm64 in parent index); config digest ${_d}"
echo ""
echo "Tag: ${tag}"
echo "Repo: ${_repo}"
echo ""
echo "linux/amd64"
echo " digest: n/a"
echo " version: n/a"
echo " image: n/a"
echo ""
echo "linux/arm64"
echo " digest: n/a"
echo " version: n/a"
echo " image: n/a"
echo ""
echo "Resolved OCI version label for this manifest: ${_lbl}"
return 0
;;
esac
return 1
done
return 1
}
amd64_digest="$(resolve_arch_digest amd64 "$manifest_json")"
arm64_digest="$(resolve_arch_digest arm64 "$manifest_json")"
if [ -z "$amd64_digest" ] && [ -z "$arm64_digest" ]; then
resolved=0
while IFS= read -r cand; do
[ -z "$cand" ] && continue
if resolve_attestation_chain "$repo" "$cand"; then
resolved=1
break
fi
done <<EOF
$(printf "%s" "$manifest_json" | jq -r ".manifests[]? | .digest")
EOF
if [ "$resolved" -eq 0 ]; then
echo "❌ no linux/amd64 or linux/arm64 digest found in manifest list (and no TAC/single-image fallback matched)" >&2
exit 1
fi
exit 0
fi
resolve_version() {
digest="$1"
if [ -z "$digest" ]; then
printf "n/a"
return
fi
crane config "${repo}@${digest}" 2>/dev/null | jq -r '.config.Labels["org.opencontainers.image.version"] // "unknown"'
}
amd64_version="$(resolve_version "$amd64_digest")"
arm64_version="$(resolve_version "$arm64_digest")"
echo ""
echo "Tag: ${tag}"
echo "Repo: ${repo}"
echo ""
echo "linux/amd64"
echo " digest: ${amd64_digest:-n/a}"
echo " version: ${amd64_version}"
echo " image: ${repo}@${amd64_digest:-n/a}"
echo ""
echo "linux/arm64"
echo " digest: ${arm64_digest:-n/a}"
echo " version: ${arm64_version}"
echo " image: ${repo}@${arm64_digest:-n/a}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment