Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions .github/scripts/build-extension.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#!/usr/bin/env bash
#
# build-extension.sh — one dispatcher for RHEL and Ubuntu FIPS builds.
#
# Matrix inputs (from workflow env):
# MATRIX_FAMILY rhel-core | rhel-fips | ubuntu-fips
# MATRIX_VERSION 8/9 (rhel) or 20.04/22.04 (ubuntu-fips)
# MATRIX_TAG full registry tag to push
# MATRIX_DOCKERFILE path to the Dockerfile
# MATRIX_CONTEXT build context dir
# CREDS_DIR where materialize_credentials.sh wrote secret files
#
# All families use BuildKit --secret so credentials never enter image layers.

set -euo pipefail

: "${MATRIX_FAMILY:?}"
: "${MATRIX_TAG:?}"
: "${MATRIX_DOCKERFILE:?}"
: "${MATRIX_CONTEXT:?}"
: "${CREDS_DIR:?}"

export DOCKER_BUILDKIT=1

# Sanity: the Dockerfile must use BuildKit --mount=type=secret. If we ever
# regressed the RHEL Dockerfile port, refuse to build rather than bake
# credentials into image layers via --build-arg.
if [[ "$MATRIX_FAMILY" == rhel-* ]]; then
if ! grep -q 'mount=type=secret,id=rhsm' "$MATRIX_DOCKERFILE"; then
echo "::error::$MATRIX_DOCKERFILE does not use --mount=type=secret,id=rhsm_username/rhsm_password"
exit 1
fi
fi

echo "→ Building $MATRIX_TAG from $MATRIX_DOCKERFILE (context: $MATRIX_CONTEXT)"

case "$MATRIX_FAMILY" in
rhel-core|rhel-fips)
docker build \
--secret id=rhsm_username,src="$CREDS_DIR/rhsm_username" \
--secret id=rhsm_password,src="$CREDS_DIR/rhsm_password" \
--label "canvos.build=${MATRIX_FAMILY}-${MATRIX_VERSION}" \
-t "$MATRIX_TAG" \
-f "$MATRIX_DOCKERFILE" \
"$MATRIX_CONTEXT"
;;

ubuntu-fips)
docker build \
--secret id=pro-attach-config,src="$CREDS_DIR/pro-attach-config.yaml" \
--label "canvos.build=${MATRIX_FAMILY}-${MATRIX_VERSION}" \
-t "$MATRIX_TAG" \
-f "$MATRIX_DOCKERFILE" \
"$MATRIX_CONTEXT"
;;

*)
echo "::error::Unknown MATRIX_FAMILY: $MATRIX_FAMILY"
exit 1
;;
esac

echo "→ Pushing $MATRIX_TAG"
docker push "$MATRIX_TAG"
99 changes: 99 additions & 0 deletions .github/scripts/decrypt-creds.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#!/usr/bin/env bash
# decrypt-creds.sh — runs inside the extensions job of base-images.yaml.
#
# Reads three ciphertext env vars (safe to have in `env:` because ciphertext
# is useless without the private key) and the private key (from a repo secret,
# so GitHub Actions has already added it to the mask set). Decrypts each,
# calls ::add-mask:: on the plaintext BEFORE any subsequent step can log it,
# and exports the plaintext to $GITHUB_ENV.
#
# Invariants — do not break:
# - Never `set -x`
# - Never `echo` a plaintext value
# - Never write plaintext to a file other than $GITHUB_ENV (post-mask)
# - Wipe the private key file on exit
#
# Inputs (env vars, set by the workflow step):
# RHEL_USER_CIPHER base64(age(username)) may be empty
# RHEL_PASS_CIPHER base64(age(password)) may be empty
# PRO_TOKEN_CIPHER base64(age(ubuntu pro)) may be empty
# DECRYPT_KEY age private key (masked) required if any cipher is non-empty
#
# Outputs (via $GITHUB_ENV):
# RHSM_USER (if RHEL_USER_CIPHER provided)
# RHSM_PASS (if RHEL_PASS_CIPHER provided)
# UBUNTU_PRO_TOKEN (if PRO_TOKEN_CIPHER provided)

set -euo pipefail

: "${GITHUB_ENV:?}"

any_cipher="${RHEL_USER_CIPHER:-}${RHEL_PASS_CIPHER:-}${PRO_TOKEN_CIPHER:-}"
if [ -z "$any_cipher" ]; then
echo "No ciphertext inputs supplied — nothing to decrypt."
exit 0
fi

if [ -z "${DECRYPT_KEY:-}" ]; then
echo "::error::DECRYPT_KEY is empty — set the WORKFLOW_DECRYPT_KEY repo secret to the age private key."
exit 1
fi

if ! command -v age >/dev/null 2>&1; then
echo "::error::age is not installed on this runner. Add a setup step before Decrypt credentials."
exit 1
fi

# Write the private key to a per-process file with restricted permissions.
# Trap wipes it on exit (success or failure).
key_file="$(mktemp)"
chmod 600 "$key_file"
trap 'shred -u "$key_file" 2>/dev/null || rm -f "$key_file"' EXIT
printf '%s' "$DECRYPT_KEY" > "$key_file"

decrypt_one() {
# Args: $1 = base64(age(plaintext))
# Reads plaintext to stdout. Never echoes the input.
printf '%s' "$1" | base64 -d 2>/dev/null | age --decrypt -i "$key_file"
}

mask_and_export() {
# Args: $1 = env var name, $2 = plaintext value
# Order matters: ::add-mask:: must run BEFORE the value ever reaches
# $GITHUB_ENV, because GitHub reads $GITHUB_ENV into the process env
# for subsequent steps and any step that lists this var in its `env:`
# block would otherwise get the plaintext dumped.
local name="$1" value="$2"
if [ -z "$value" ]; then
# Empty decrypt result is treated as "nothing to export" — do NOT
# write an empty env line (that would blank out any inherited value).
echo "::warning::$name decrypted to empty string — skipping export"
return 0
fi
# ::add-mask:: line is intercepted by the runner; the value is not
# written to the log.
printf '::add-mask::%s\n' "$value"
# Single-line assignment (no here-doc). RHSM usernames/passwords and
# Ubuntu Pro tokens are single-line values by nature.
printf '%s=%s\n' "$name" "$value" >> "$GITHUB_ENV"
}

if [ -n "${RHEL_USER_CIPHER:-}" ]; then
plain="$(decrypt_one "$RHEL_USER_CIPHER")"
mask_and_export "RHSM_USER" "$plain"
unset plain
fi

if [ -n "${RHEL_PASS_CIPHER:-}" ]; then
plain="$(decrypt_one "$RHEL_PASS_CIPHER")"
mask_and_export "RHSM_PASS" "$plain"
unset plain
fi

if [ -n "${PRO_TOKEN_CIPHER:-}" ]; then
plain="$(decrypt_one "$PRO_TOKEN_CIPHER")"
mask_and_export "UBUNTU_PRO_TOKEN" "$plain"
unset plain
fi

echo "Credentials decrypted, masked, and exported to \$GITHUB_ENV (values not shown)."
50 changes: 50 additions & 0 deletions .github/scripts/materialize_credentials.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/env bash
#
# materialize_credentials.sh — write credentials for THIS extensions-matrix
# row to $RUNNER_TEMP/creds with umask 077, then export CREDS_DIR to
# $GITHUB_ENV so build-extension.sh can pass them via BuildKit --secret.
#
# Never echo the values. Never assign values to $GITHUB_ENV. No `set -x`.

set -euo pipefail

: "${RUNNER_TEMP:?RUNNER_TEMP not set — this must run inside GitHub Actions}"
: "${MATRIX_FAMILY:?MATRIX_FAMILY not set}"

umask 077
creds_dir="$RUNNER_TEMP/creds"
mkdir -p "$creds_dir"

case "$MATRIX_FAMILY" in
rhel-core|rhel-fips)
if [ -z "${RHSM_USER:-}" ] || [ -z "${RHSM_PASS:-}" ]; then
echo "::error::$MATRIX_FAMILY build requires RHSM username+password"
exit 1
fi
printf '%s' "$RHSM_USER" > "$creds_dir/rhsm_username"
printf '%s' "$RHSM_PASS" > "$creds_dir/rhsm_password"
;;

ubuntu-fips)
if [ -z "${UBUNTU_PRO_TOKEN:-}" ]; then
echo "::error::ubuntu-fips build requires ubuntu_pro_token"
exit 1
fi
# Render pro-attach-config.yaml on the fly. The committed template
# in ubuntu-fips/*/pro-attach-config.yaml has "REPLACE_WITH_TOKEN"
# and must not be used.
cat > "$creds_dir/pro-attach-config.yaml" <<EOF
token: "$UBUNTU_PRO_TOKEN"
enable_services:
- fips-updates
EOF
;;

*)
echo "::error::Unknown MATRIX_FAMILY: $MATRIX_FAMILY"
exit 1
;;
esac

echo "CREDS_DIR=$creds_dir" >> "$GITHUB_ENV"
echo "Credentials materialized for family=$MATRIX_FAMILY (values not shown)."
Loading
Loading