diff --git a/Makefile b/Makefile index b2531564f..de7e5950e 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ .PHONY: default all -default: bin/moby bin/linuxkit bin/rtf bin/linuxkit-push-manifest +default: bin/moby bin/linuxkit bin/rtf all: default VERSION="0.0" # dummy for now @@ -20,9 +20,6 @@ endif PREFIX?=/usr/local/ -bin/linuxkit-push-manifest: scripts/push-manifest.sh | bin - cp $< $@ - MOBY_REPO=https://github.com/moby/tool.git MOBY_COMMIT=177969dc4a0fb843f635e45ab8e87d2cd2c531cd MOBY_VERSION=0.0 diff --git a/src/cmd/linuxkit/pkglib/docker.go b/src/cmd/linuxkit/pkglib/docker.go index b05da8c98..0efe290d7 100644 --- a/src/cmd/linuxkit/pkglib/docker.go +++ b/src/cmd/linuxkit/pkglib/docker.go @@ -2,6 +2,8 @@ package pkglib // Thin wrappers around Docker CLI invocations +//go:generate ./gen + import ( "fmt" "os" @@ -66,7 +68,7 @@ func (dr dockerRunner) pushWithManifest(img, suffix string) error { dctArg = "1" } - cmd := exec.Command("linuxkit-push-manifest", img, dctArg) + cmd := exec.Command("/bin/sh", "-c", manifestPushScript, "manifest-push-script", img, dctArg) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if debugDockerCommands { diff --git a/src/cmd/linuxkit/pkglib/manifest_push_script.go b/src/cmd/linuxkit/pkglib/manifest_push_script.go new file mode 100644 index 000000000..9b6d81b7b --- /dev/null +++ b/src/cmd/linuxkit/pkglib/manifest_push_script.go @@ -0,0 +1,112 @@ +package pkglib + +const manifestPushScript = ` +#! /bin/sh + +set -e + +# This script pushes a multiarch manifest for packages and signs it. +# +# The TARGET must be of the form /: and this is what +# the manifest is pushed to. It assumes that there is are images of +# the form /:- already on hub. +# +# If TRUST is not set, the manifest will not be signed. +# +# For signing, DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE must be set. + +# This should all be replaced with 'docker manifest' once it lands. + +TARGET=$1 +TRUST=$2 + +REPO=$(echo "$TARGET" | cut -d':' -f1) +TAG=$(echo "$TARGET" | cut -d':' -f2) + +# Work out credentials. On macOS they are needed for manifest-tool and +# we need them for notary on all platforms. +case $(uname -s) in + Darwin) + CRED=$(echo "https://index.docker.io/v1/" | /Applications/Docker.app/Contents/Resources/bin/docker-credential-osxkeychain.bin get) + USER=$(echo "$CRED" | jq -r '.Username') + PASS=$(echo "$CRED" | jq -r '.Secret') + MT_ARGS="--username $USER --password $PASS" + ;; + Linux) + CRED=$(cat ~/.docker/config.json | jq -r '.auths."https://index.docker.io/v1/".auth' | base64 -d -) + USER=$(echo $CRED | cut -d ':' -f 1) + PASS=$(echo $CRED | cut -d ':' -f 2-) + # manifest-tool can use docker credentials directly + MT_ARGS= + ;; + *) + echo "Unsupported platform" + exit 1 + ;; +esac + +# Push manifest list +OUT=$(manifest-tool $MT_ARGS push from-args \ + --ignore-missing \ + --platforms linux/amd64,linux/arm64 \ + --template "$TARGET"-ARCH \ + --target "$TARGET") + +echo "$OUT" +if [ -z "$TRUST" ]; then + echo "Not signing $TARGET" + exit 0 +fi + +# Extract sha256 and length from the manifest-tool output +SHA256=$(echo "$OUT" | cut -d' ' -f2 | cut -d':' -f2) +LEN=$(echo "$OUT" | cut -d' ' -f3) + +# Notary requires a PTY for username/password so use expect for that. +export NOTARY_DELEGATION_PASSPHRASE="$DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE" +NOTARY_CMD="notary -s https://notary.docker.io -d $HOME/.docker/trust addhash \ + -p docker.io/$REPO $TAG $LEN --sha256 $SHA256 \ + -r targets/releases" + +echo ' +spawn '"$NOTARY_CMD"' +set pid [exp_pid] +set timeout 60 +expect { + timeout { + puts "Expected username prompt" + exec kill -9 $pid + exit 1 + } + "username: " { + send "'"$USER"'\n" + } +} +expect { + timeout { + puts "Expected password prompt" + exec kill -9 $pid + exit 1 + } + "password: " { + send "'"$PASS"'\n" + } +} +expect { + timeout { + puts "Expected password prompt" + exec kill -9 $pid + exit 1 + } + eof { + } +} +set waitval [wait -i $spawn_id] +set exval [lindex $waitval 3] +exit $exval +' | expect -f - + +echo +echo "New signed multi-arch image: $REPO:$TAG" +echo +`