Skip to content

Commit

Permalink
Merge branch 'develop' into improv/event_handler_api_gateway_strip_pr…
Browse files Browse the repository at this point in the history
…efixes
  • Loading branch information
leandrodamascena authored Aug 1, 2023
2 parents 2a7e2b6 + 0916bc5 commit 373aada
Show file tree
Hide file tree
Showing 181 changed files with 6,112 additions and 3,644 deletions.
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ body:
- "3.8"
- "3.9"
- "3.10"
- "3.11"
validations:
required: true
- type: dropdown
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/static_typing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ body:
- "3.8"
- "3.9"
- "3.10"
- "3.11"
validations:
required: true
- type: input
Expand Down
82 changes: 82 additions & 0 deletions .github/actions/seal-restore/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
name: "Restore sealed source code"
description: "Restore sealed source code and confirm integrity hash"

# PROCESS
#
# 1. Exports artifact name using Prefix + GitHub Run ID (unique for each release trigger)
# 2. Compress entire source code as tarball OR given files
# 3. Create and export integrity hash for tarball
# 4. Upload artifact
# 5. Remove archive

# USAGE
#
# - name: Seal and upload
# id: seal_source_code
# uses: ./.github/actions/seal
# with:
# artifact_name_prefix: "source"
#
# - name: Restore sealed source code
# uses: ./.github/actions/seal-restore
# with:
# integrity_hash: ${{ needs.seal_source_code.outputs.integrity_hash }}
# artifact_name: ${{ needs.seal_source_code.outputs.artifact_name }}

# NOTES
#
# To be used together with .github/actions/seal

inputs:
integrity_hash:
description: "Integrity hash to verify"
required: true
artifact_name:
description: "Sealed artifact name to restore"
required: true

runs:
using: "composite"
steps:
- id: adjust-path
run: echo "${{ github.action_path }}" >> $GITHUB_PATH
shell: bash

- name: Download artifacts
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
with:
name: ${{ inputs.artifact_name }}
path: .

- id: integrity_hash
name: Create integrity hash for downloaded artifact
run: |
HASH=$(sha256sum "${ARTIFACT_NAME}.tar" | awk '{print $1}')
echo "current_hash=${HASH}" >> "$GITHUB_OUTPUT"
env:
ARTIFACT_NAME: ${{ inputs.artifact_name }}
shell: bash

- id: verify_hash
name: Verify sealed artifact integrity hash
run: test "${CURRENT_HASH}" = "${PROVIDED_HASH}" || exit 1
env:
ARTIFACT_NAME: ${{ inputs.artifact_name }}
PROVIDED_HASH: ${{ inputs.integrity_hash }}
CURRENT_HASH: ${{ steps.integrity_hash.outputs.current_hash }}
shell: bash

# Restore and overwrite tarball in current directory
- id: overwrite
name: Extract tarball
run: tar -xvf "${ARTIFACT_NAME}".tar
env:
ARTIFACT_NAME: ${{ inputs.artifact_name }}
shell: bash

- name: Remove archive
run: rm -f "${ARTIFACT_NAME}.tar"
env:
ARTIFACT_NAME: ${{ inputs.artifact_name }}
shell: bash
93 changes: 93 additions & 0 deletions .github/actions/seal/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
name: "Seal and hash source code"
description: "Seal and export source code as a tarball artifact along with its integrity hash"

# PROCESS
#
# 1. Exports artifact name using Prefix + GitHub Run ID (unique for each release trigger)
# 2. Compress entire source code as tarball OR given files
# 3. Create and export integrity hash for tarball
# 4. Upload artifact
# 5. Remove archive

# USAGE
#
# - name: Seal and upload
# id: seal_source_code
# uses: ./.github/actions/seal
# with:
# artifact_name_prefix: "source"

inputs:
files:
description: "Files to seal separated by space"
required: false
artifact_name_prefix:
description: "Prefix to use when exporting artifact"
required: true

outputs:
integrity_hash:
description: "Source code integrity hash"
value: ${{ steps.integrity_hash.outputs.integrity_hash }}
artifact_name:
description: "Artifact name containTemporary branch created with staged changed"
value: ${{ steps.export_artifact_name.outputs.artifact_name }}

runs:
using: "composite"
steps:
- id: adjust-path
run: echo "${{ github.action_path }}" >> $GITHUB_PATH
shell: bash

- id: export_artifact_name
name: Export final artifact name
run: echo "artifact_name=${ARTIFACT_PREFIX}-${GITHUB_RUN_ID}" >> "$GITHUB_OUTPUT"
env:
GITHUB_RUN_ID: ${{ github.run_id }}
ARTIFACT_PREFIX: ${{ inputs.artifact_name_prefix }}
shell: bash

# By default, create a tarball of the current directory minus .git
# otherwise it breaks GH Actions when restoring it
- id: compress_all
if: ${{ !inputs.files }}
name: Create tarball for entire source
run: tar --exclude-vcs -cvf "${ARTIFACT_NAME}".tar *
env:
ARTIFACT_NAME: ${{ steps.export_artifact_name.outputs.artifact_name }}
shell: bash

# If a list of files are given, then create a tarball for those only
- id: compress_selected_files
if: ${{ inputs.files }}
name: Create tarball for selected files
run: tar --exclude-vcs -cvf "${ARTIFACT_NAME}".tar "${FILES}"
env:
FILES: ${{ inputs.files }}
ARTIFACT_NAME: ${{ steps.export_artifact_name.outputs.artifact_name }}
shell: bash

- id: integrity_hash
name: Create and export integrity hash for tarball
run: |
HASH=$(sha256sum "${ARTIFACT_NAME}.tar" | awk '{print $1}')
echo "integrity_hash=${HASH}" >> "$GITHUB_OUTPUT"
env:
ARTIFACT_NAME: ${{ steps.export_artifact_name.outputs.artifact_name }}
shell: bash

- name: Upload artifacts
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
with:
if-no-files-found: error
name: ${{ steps.export_artifact_name.outputs.artifact_name }}
path: ${{ steps.export_artifact_name.outputs.artifact_name }}.tar
retention-days: 1

- name: Remove archive
run: rm -f "${ARTEFACT_NAME}.tar"
env:
ARTIFACT_NAME: ${{ steps.export_artifact_name.outputs.artifact_name }}
shell: bash
67 changes: 67 additions & 0 deletions .github/actions/upload-release-provenance/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: "Upload provenance attestation to release"
description: "Download and upload newly generated provenance attestation to latest release."

# PROCESS
#
# 1. Downloads provenance attestation artifact generated earlier in the release pipeline
# 2. Updates latest GitHub draft release pointing to newly git release tag
# 3. Uploads provenance attestation file to latest GitHub draft release

# USAGE
#
# - name: Upload provenance
# id: upload-provenance
# uses: ./.github/actions/upload-release-provenance
# with:
# release_version: ${{ needs.seal.outputs.RELEASE_VERSION }}
# provenance_name: ${{needs.provenance.outputs.provenance-name}}
# github_token: ${{ secrets.GITHUB_TOKEN }}

# NOTES
#
# There are no outputs.
#

inputs:
provenance_name:
description: "Provenance artifact name to download"
required: true
release_version:
description: "Release version (e.g., 2.20.0)"
required: true
github_token:
description: "GitHub token for GitHub CLI"
required: true

runs:
using: "composite"
steps:
- id: adjust-path
run: echo "${{ github.action_path }}" >> $GITHUB_PATH
shell: bash

- id: download-provenance
name: Download newly generated provenance
uses: actions/download-artifact@9782bd6a9848b53b110e712e20e42d89988822b7 # v3.0.1
with:
name: ${{ inputs.provenance_name }}

- id: sync-release-tag
name: Update draft release tag to release commit tag
run: |
CURRENT_DRAFT_RELEASE=$(gh release list | awk '{ if ($2 == "Draft") print $1}')
gh release edit "${CURRENT_DRAFT_RELEASE}" --tag v"${RELEASE_VERSION}"
env:
RELEASE_VERSION: ${{ inputs.release_version }}
GH_TOKEN: ${{ inputs.github_token }}
shell: bash

- id: upload-provenance
name: Upload provenance to release tag
# clobber flag means overwrite release asset if available (eventual consistency, retried failed steps)
run: gh release upload --clobber v"${RELEASE_VERSION}" "${PROVENANCE_FILE}"
env:
RELEASE_VERSION: ${{ inputs.release_version }}
PROVENANCE_FILE: ${{ inputs.provenance_name }}
GH_TOKEN: ${{ inputs.github_token }}
shell: bash
134 changes: 134 additions & 0 deletions .github/actions/verify-provenance/verify_provenance.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
#!/bin/bash
set -uo pipefail # prevent accessing unset env vars, prevent masking pipeline errors to the next command

#docs
#title :verify_provenance.sh
#description :This script will download and verify a signed Powertools for AWS Lambda (Python) release build with SLSA Verifier
#author :@heitorlessa
#date :July 1st 2023
#version :0.1
#usage :bash verify_provenance.sh {release version}
#notes :Meant to use in GitHub Actions or locally (MacOS, Linux, WSL).
#os_version :Ubuntu 22.04.2 LTS
#==============================================================================

# Check if RELEASE_VERSION is provided as a command line argument
if [[ $# -eq 1 ]]; then
export readonly RELEASE_VERSION="$1"
else
echo "ERROR: Please provider Powertools release version as a command line argument."
echo "Example: bash verify_provenance.sh 2.20.0"
exit 1
fi

export readonly ARCHITECTURE=$(uname -m | sed 's/x86_64/amd64/g') # arm64, x86_64 ->amd64
export readonly OS_NAME=$(uname -s | tr '[:upper:]' '[:lower:]') # darwin, linux
export readonly SLSA_VERIFIER_VERSION="2.3.0"
export readonly SLSA_VERIFIER_CHECKSUM_FILE="SHA256SUM.md"
export readonly SLSA_VERIFIER_BINARY="./slsa-verifier-${OS_NAME}-${ARCHITECTURE}"

export readonly RELEASE_BINARY="aws_lambda_powertools-${RELEASE_VERSION}-py3-none-any.whl"
export readonly ORG="aws-powertools"
export readonly REPO="powertools-lambda-python"
export readonly PROVENANCE_FILE="multiple.intoto.jsonl"

export readonly FILES=("${SLSA_VERIFIER_BINARY}" "${SLSA_VERIFIER_CHECKSUM_FILE}" "${PROVENANCE_FILE}" "${RELEASE_BINARY}")

function debug() {
TIMESTAMP=$(date -u "+%FT%TZ") # 2023-05-10T07:53:59Z
echo ""${TIMESTAMP}" DEBUG - [*] $1"
}

function error() {
cleanup
TIMESTAMP=$(date -u "+%FT%TZ") # 2023-05-10T07:53:59Z
echo ""${TIMESTAMP}" ERROR - [!] $1"
echo ""${TIMESTAMP}" ERROR - [!] exiting"
exit 1
}

function download_slsa_verifier() {
readonly SLSA_URL="https://github.com/slsa-framework/slsa-verifier/releases/download/v${SLSA_VERIFIER_VERSION}/slsa-verifier-${OS_NAME}-${ARCHITECTURE}"
# debug "Downloading SLSA Verifier for - Binary: slsa-verifier-${OS_NAME}-${ARCHITECTURE}"
debug "Downloading SLSA Verifier binary: ${SLSA_URL}"
curl \
--location \
--fail \
--silent \
-O "${SLSA_URL}" || error "Failed to download SLSA Verifier binary"

readonly SLSA_CHECKSUM_URL="https://raw.githubusercontent.com/slsa-framework/slsa-verifier/f59b55ef2190581d40fc1a5f3b7a51cab2f4a652/${SLSA_VERIFIER_CHECKSUM_FILE}"
debug "Downloading SLSA Verifier checksums"
curl \
--location \
--fail \
--silent \
-O "${SLSA_CHECKSUM_URL}" || error "Failed to download SLSA Verifier binary checksum file"

debug "Verifying SLSA Verifier binary integrity"
CURRENT_HASH=$(sha256sum "${SLSA_VERIFIER_BINARY}" | awk '{print $1}')
if [[ $(grep "${CURRENT_HASH}" "${SLSA_VERIFIER_CHECKSUM_FILE}") ]]; then
debug "SLSA Verifier binary integrity confirmed"
chmod +x "${SLSA_VERIFIER_BINARY}"
else
error "Failed integrity check for SLSA Verifier binary: ${SLSA_VERIFIER_BINARY}"
fi
}

function download_provenance() {
readonly PROVENANCE_URL="https://github.com/${ORG}/${REPO}/releases/download/v${RELEASE_VERSION}/${PROVENANCE_FILE}"
debug "Downloading attestation: ${PROVENANCE_URL}"

curl \
--location \
--fail \
--silent \
-O ${PROVENANCE_URL} || error "Failed to download provenance. Does the release already exist?"
}

function download_release_artifact() {
debug "Downloading ${RELEASE_VERSION} release from PyPi"
python -m pip download \
--only-binary=:all: \
--no-deps \
--quiet \
aws-lambda-powertools=="${RELEASE_VERSION}"
}

function verify_provenance() {
debug "Verifying attestation with slsa-verifier"
"${SLSA_VERIFIER_BINARY}" verify-artifact \
--provenance-path "${PROVENANCE_FILE}" \
--source-uri github.com/${ORG}/${REPO} \
${RELEASE_BINARY}
}

function cleanup() {
debug "Cleaning up previously downloaded files"
rm -f "${SLSA_VERIFIER_BINARY}"
rm -f "${SLSA_VERIFIER_CHECKSUM_FILE}"
rm -f "${PROVENANCE_FILE}"
rm -f "${RELEASE_BINARY}"
echo "${FILES[@]}" | xargs -n1 echo "Removed file: "
}

function main() {
download_slsa_verifier
download_provenance
download_release_artifact
verify_provenance
cleanup
}

main

# Lessons learned
#
# 1. If source doesn't match provenance
#
# FAILED: SLSA verification failed: source used to generate the binary does not match provenance: expected source 'awslabs/aws-lambda-powertools-python', got 'heitorlessa/aws-lambda-powertools-test'
#
# 2. Avoid building deps during download in Test registry endpoints
#
# FAILED: Could not find a version that satisfies the requirement poetry-core>=1.3.2 (from versions: 1.2.0)
#
Loading

0 comments on commit 373aada

Please sign in to comment.