Skip to content

Commit

Permalink
Resolve security warning for macOS users (#194)
Browse files Browse the repository at this point in the history
Signed-off-by: Dan Luhring <dan.luhring@anchore.com>
  • Loading branch information
luhring authored Nov 5, 2020
1 parent 50e9800 commit 5725f26
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 31 deletions.
31 changes: 31 additions & 0 deletions .github/scripts/mac-prepare-for-signing.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/usr/bin/env bash
set -eu

CI_HOME="/Users/runner"
if [[ "${HOME}" != "${CI_HOME}" ]]; then
printf "WARNING! It looks like this isn't the CI environment. This script modifies the macOS Keychain setup in ways you probably wouldn't want for your own machine. It also requires an Apple Developer ID Certificate that you shouldn't have outside of the CI environment.\n\nExiting early to make sure nothing bad happens.\n"
exit 1
fi

# Install gon (see https://github.com/mitchellh/gon for details).
brew tap mitchellh/gon
brew install mitchellh/gon/gon

# Write signing certificate to disk from environment variable.
CERT_FILE="$HOME/developer_id_certificate.p12"
echo -n "$APPLE_DEVELOPER_ID_CERT" | base64 --decode > "$CERT_FILE"

# In order to have all keychain interactions avoid an interactive user prompt, we need to control the password for the keychain in question, which means we need to create a new keychain into which we'll import the signing certificate and from which we'll later access this certificate during code signing.
EPHEMERAL_KEYCHAIN="ci-ephemeral-keychain"
EPHEMERAL_KEYCHAIN_PASSWORD="$(openssl rand -base64 100)"
security create-keychain -p "${EPHEMERAL_KEYCHAIN_PASSWORD}" "${EPHEMERAL_KEYCHAIN}"

# Import signing certificate into the keychain. (This is a pre-requisite for gon, which is invoked via goreleaser.)
EPHEMERAL_KEYCHAIN_FULL_PATH="$HOME/Library/Keychains/${EPHEMERAL_KEYCHAIN}-db"
security import "${CERT_FILE}" -k "${EPHEMERAL_KEYCHAIN_FULL_PATH}" -P "${APPLE_DEVELOPER_ID_CERT_PASS}" -T "$(command -v codesign)"

# Setting the partition list for this certificate's private key to include "apple-tool:" and "apple:" allows the codesign command to access this keychain item without an interactive user prompt. (codesign is invoked by gon.)
security set-key-partition-list -S "apple-tool:,apple:" -s -k "${EPHEMERAL_KEYCHAIN_PASSWORD}" "${EPHEMERAL_KEYCHAIN_FULL_PATH}"

# Make this new keychain the user's default keychain, so that codesign will be able to find this certificate when we specify it during signing.
security default-keychain -d "user" -s "${EPHEMERAL_KEYCHAIN_FULL_PATH}"
16 changes: 16 additions & 0 deletions .github/scripts/mac-sign-and-notarize.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env bash
set -eu

IS_SNAPSHOT="$1" # e.g. "true", "false"

if [[ "${IS_SNAPSHOT}" == "true" ]]; then
# This is a snapshot build —— skipping signing and notarization...
exit 0
fi

GON_CONFIG="$2" # e.g. "gon.hcl"
NEW_DMG_NAME="$3" # e.g. "./dist/syft-0.1.0.dmg"
ORIGINAL_DMG_NAME="./dist/output.dmg" # This should match dmg output_path in the gon config file.

gon "${GON_CONFIG}"
mv -v "${ORIGINAL_DMG_NAME}" "${NEW_DMG_NAME}"
13 changes: 4 additions & 9 deletions .github/scripts/update-version-file.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,9 @@ fi
echo "creating and publishing version file"

# create a version file for version-update checks
echo "${VERSION}" | tee ${DISTDIR}/VERSION
VERSION_FILE="${DISTDIR}/VERSION"
echo "${VERSION}" | tee "${VERSION_FILE}"

# upload the version file that supports the application version update check
docker run --rm \
-i \
-e AWS_DEFAULT_REGION=us-west-2 \
-e AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} \
-e AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} \
-v $(pwd)/${DISTDIR}/:/distmount \
amazon/aws-cli \
s3 cp /distmount/VERSION s3://toolbox-data.anchore.io/${BIN}/releases/latest/VERSION
export AWS_DEFAULT_REGION=us-west-2
aws s3 cp "${VERSION_FILE}" s3://toolbox-data.anchore.io/${BIN}/releases/latest/VERSION
8 changes: 2 additions & 6 deletions .github/workflows/acceptance-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ env:
jobs:
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
Build-Snapshot-Artifacts:
runs-on: ubuntu-latest
runs-on: macos-latest # We're creating these snapshot builds on macOS to be consistent with our release workflow's build process, which also takes place on macOS (due to code signing requirements).
steps:

- uses: actions/setup-go@v2
Expand All @@ -36,10 +36,6 @@ jobs:
${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ hashFiles('**/go.sum') }}-
${{ runner.os }}-go-${{ env.GO_VERSION }}-
- name: Bootstrap dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: make ci-bootstrap

- name: Import GPG key
id: import_gpg
uses: crazy-max/ghaction-import-gpg@v2
Expand Down Expand Up @@ -92,4 +88,4 @@ jobs:
path: snapshot

- name: Compare Anchore inline-scan results against snapshot build output
run: make compare-snapshot
run: make compare-snapshot
15 changes: 8 additions & 7 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ env:

jobs:
wait-for-checks:
runs-on: ubuntu-latest
runs-on: ubuntu-latest # This OS choice is arbitrary. None of the steps in this job are specific to either Linux or macOS.
steps:

- uses: actions/checkout@v2
Expand Down Expand Up @@ -71,7 +71,7 @@ jobs:
release:
needs: [ wait-for-checks ]
runs-on: ubuntu-latest
runs-on: macos-latest # Due to our code signing process, it's vital that we run our release steps on macOS.
steps:

- uses: actions/setup-go@v2
Expand All @@ -82,6 +82,7 @@ jobs:
with:
fetch-depth: 0

# We are expecting this cache to have been created during the "Build-Snapshot-Artifacts" job in the "Acceptance" workflow.
- name: Restore bootstrap cache
id: cache
uses: actions/cache@v2
Expand All @@ -94,10 +95,6 @@ jobs:
${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ hashFiles('Makefile') }}-
${{ runner.os }}-go-${{ env.GO_VERSION }}-
- name: Bootstrap dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: make ci-bootstrap

- name: Import GPG key
id: import_gpg
uses: crazy-max/ghaction-import-gpg@v2
Expand All @@ -121,6 +118,10 @@ jobs:
SIGNING_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
AWS_ACCESS_KEY_ID: ${{ secrets.TOOLBOX_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.TOOLBOX_AWS_SECRET_ACCESS_KEY }}
APPLE_DEVELOPER_ID_CERT: ${{ secrets.APPLE_DEVELOPER_ID_CERT }} # Used during macOS code signing.
APPLE_DEVELOPER_ID_CERT_PASS: ${{ secrets.APPLE_DEVELOPER_ID_CERT_PASS }} # Used during macOS code signing.
AC_USERNAME: ${{ secrets.ENG_CI_APPLE_ID }} # Used during macOS notarization.
AC_PASSWORD: ${{ secrets.ENG_CI_APPLE_ID_PASS }} # Used during macOS notarization.

- uses: 8398a7/action-slack@v3
with:
Expand All @@ -134,4 +135,4 @@ jobs:
- uses: actions/upload-artifact@v2
with:
name: artifacts
path: dist/**/*
path: dist/**/*
35 changes: 31 additions & 4 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ release:
# If set to true, will not auto-publish the release. This is done to allow us to review the changelog before publishing.
draft: true

signs:
- artifacts: checksum
args: ["--output", "${signature}", "--detach-sign", "${artifact}"]
# This ensures any macOS signed artifacts get included with the release.
extra_files:
- glob: "./dist/*.dmg"

builds:
- binary: grype
id: grype
env:
- CGO_ENABLED=0
goos:
Expand All @@ -32,6 +33,31 @@ builds:
-X github.com/anchore/grype/internal/version.gitCommit={{.Commit}}
-X github.com/anchore/grype/internal/version.buildDate={{.Date}}
-X github.com/anchore/grype/internal/version.gitTreeState={{.Env.BUILD_GIT_TREE_STATE}}
# For more info on this macOS build, see: https://github.com/mitchellh/gon#usage-with-goreleaser
- binary: grype
id: grype-macos
env:
- CGO_ENABLED=0
goos:
- darwin
goarch:
- amd64
# Set the modified timestamp on the output binary to the git timestamp (to ensure a reproducible build)
mod_timestamp: '{{ .CommitTimestamp }}'
ldflags: |
-w
-s
-extldflags '-static'
-X github.com/anchore/grype/internal/version.version={{.Version}}
-X github.com/anchore/grype/internal/version.gitCommit={{.Commit}}
-X github.com/anchore/grype/internal/version.buildDate={{.Date}}
-X github.com/anchore/grype/internal/version.gitTreeState={{.Env.BUILD_GIT_TREE_STATE}}
hooks:
post: ./.github/scripts/mac-sign-and-notarize.sh "{{.IsSnapshot}}" "gon.hcl" "./dist/grype_{{.Tag}}_{{.Target}}.dmg"

signs:
- artifacts: checksum
args: ["--output", "${signature}", "--detach-sign", "${artifact}"]

nfpms:
- license: "Apache 2.0"
Expand All @@ -51,7 +77,8 @@ brews:

archives:
- format: tar.gz
builds:
- grype # i.e. Linux only
format_overrides:
- goos: windows
format: zip

18 changes: 13 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ help:
ci-bootstrap: bootstrap
DEBIAN_FRONTEND=noninteractive sudo apt update && sudo -E apt install -y bc jq libxml2-utils

.PHONY:
ci-bootstrap-mac:
github_changelog_generator --version || sudo gem install github_changelog_generator

.PHONY: boostrap
bootstrap: ## Download and install all go dependencies (+ prep tooling in the ./tmp dir)
$(call title,Boostrapping dependencies)
Expand Down Expand Up @@ -172,8 +176,7 @@ compare:
changelog-release:
@echo "Last tag: $(SECOND_TO_LAST_TAG)"
@echo "Current tag: $(VERSION)"
@docker run -i --rm \
-v "$(shell pwd)":/usr/local/src/your-app ferrarimarco/github-changelog-generator \
@github_changelog_generator \
--user anchore \
--project $(BIN) \
-t ${GITHUB_TOKEN} \
Expand All @@ -187,8 +190,9 @@ changelog-release:

.PHONY: changelog-unreleased
changelog-unreleased: ## show the current changelog that will be produced on the next release (note: requires GITHUB_TOKEN set)
@docker run -it --rm \
-v "$(shell pwd)":/usr/local/src/your-app ferrarimarco/github-changelog-generator \
@docker run -it --rm \
-v "$(shell pwd)":/usr/local/src/your-app \
ferrarimarco/github-changelog-generator \
--user anchore \
--project $(BIN) \
-t ${GITHUB_TOKEN} \
Expand All @@ -204,8 +208,12 @@ changelog-unreleased: ## show the current changelog that will be produced on the
/CHANGELOG.md

.PHONY: release
release: clean-dist changelog-release ## Build and publish final binaries and packages
release: clean-dist ci-bootstrap-mac changelog-release ## Build and publish final binaries and packages. Intended to be run only on macOS.
$(call title,Publishing release artifacts)

# Prepare for macOS-specific signing process
.github/scripts/mac-prepare-for-signing.sh

# create a config with the dist dir overridden
echo "dist: $(DISTDIR)" > $(TEMPDIR)/goreleaser.yaml
cat .goreleaser.yaml >> $(TEMPDIR)/goreleaser.yaml
Expand Down
11 changes: 11 additions & 0 deletions gon.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
source = ["./dist/grype-macos_darwin_amd64/grype"] # The 'dist' directory path should ideally reference an env var, where the source of truth is the Makefile. I wasn't able to figure out how to solve this.
bundle_id = "com.anchore.toolbox.grype"

sign {
application_identity = "Developer ID Application: ANCHORE, INC. (9MJHKYX5AT)"
}

dmg {
output_path = "./dist/output.dmg"
volume_name = "Grype"
}

0 comments on commit 5725f26

Please sign in to comment.