diff --git a/.github/actions/code-sign/action.yml b/.github/actions/code-sign/action.yml new file mode 100644 index 00000000..bfa1a9ec --- /dev/null +++ b/.github/actions/code-sign/action.yml @@ -0,0 +1,113 @@ +name: Code-sign Windows binaries +description: > + Sign every .exe under a given path in place via the DefinedNet code-signer + Lambda. If `role` or `bucket` is empty, logs a notice and skips signing so + forks and dev branches without AWS access still produce usable builds. + +inputs: + path: + description: "Directory whose .exe files should be signed in place" + required: true + role: + description: "IAM role ARN to assume via OIDC; empty disables signing" + required: false + default: "" + bucket: + description: "S3 staging bucket the code-signer Lambda reads from; empty disables signing" + required: false + default: "" + region: + description: "AWS region for the role and Lambda" + required: false + default: "us-east-2" + function-name: + description: "Code-signer Lambda function name" + required: false + default: "code-signer" + key-prefix: + description: "S3 key prefix the caller is authorized to write under" + required: false + default: "code-signing/slackhq/nebula" + +runs: + using: composite + steps: + - name: Skip notice + if: inputs.role == '' || inputs.bucket == '' + shell: sh + run: echo "::notice::code-signer role or bucket not set; skipping code signing." + + - name: Configure AWS credentials + if: inputs.role != '' && inputs.bucket != '' + uses: aws-actions/configure-aws-credentials@v6 + with: + role-to-assume: ${{ inputs.role }} + aws-region: ${{ inputs.region }} + # Default is 12 retries to ride out IAM trust-policy propagation; once + # the role is stable we want a real misconfiguration to fail fast. + retry-max-attempts: 5 + + - name: Sign .exe files + if: inputs.role != '' && inputs.bucket != '' + shell: sh + env: + SIGN_PATH: ${{ inputs.path }} + BUCKET: ${{ inputs.bucket }} + FUNCTION_NAME: ${{ inputs.function-name }} + KEY_PREFIX: ${{ inputs.key-prefix }} + run: | + set -eu + RUN="${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" + + find "$SIGN_PATH" -name '*.exe' -print | while read -r path + do + rel=${path#"$SIGN_PATH"/} + file=$(basename "$path") + name=${file%.exe} + prefix="${KEY_PREFIX}/${RUN}" + src="${prefix}/unsigned/${rel}" + dst="${prefix}/signed/${rel}" + + echo "::group::Sign ${rel}" + echo "Uploading unsigned to s3://${BUCKET}/${src}" + aws s3 cp --no-progress "$path" "s3://${BUCKET}/${src}" >/dev/null + + echo "Invoking ${FUNCTION_NAME} Lambda" + payload=$(jq -nc \ + --arg s "$src" \ + --arg d "$dst" \ + --arg p "$name" \ + '{source_key: $s, dest_key: $d, program_name: $p}') + meta=$(aws lambda invoke \ + --function-name "$FUNCTION_NAME" \ + --cli-binary-format raw-in-base64-out \ + --payload "$payload" \ + --output json \ + /tmp/sign-resp.json) + if echo "$meta" | jq -e '.FunctionError != null' >/dev/null + then + echo "::endgroup::" + echo "::error::code-signer Lambda failed for ${rel}" + cat /tmp/sign-resp.json >&2 + exit 1 + fi + + echo "Downloading signed back to ${path}" + aws s3 cp --no-progress "s3://${BUCKET}/${dst}" "$path" >/dev/null + + aws s3 rm "s3://${BUCKET}/${src}" >/dev/null 2>&1 || true + aws s3 rm "s3://${BUCKET}/${dst}" >/dev/null 2>&1 || true + + # Sanity-check the bytes we got back actually carry an Authenticode + # signature that this machine can validate end to end. + status=$(powershell -NoProfile -Command "(Get-AuthenticodeSignature -FilePath '$path').Status" | tr -d '\r') + if [ "$status" != "Valid" ] + then + echo "::endgroup::" + echo "::error::${rel} signature status: ${status} (expected Valid)" + exit 1 + fi + + echo "Signed ${rel} (sha256=$(jq -r '.sha256' /tmp/sign-resp.json), status=${status})" + echo "::endgroup::" + done diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 356ae363..e4ca2933 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -32,6 +32,9 @@ jobs: build-windows: name: Build Windows runs-on: windows-latest + permissions: + id-token: write + contents: read steps: - uses: actions/checkout@v6 @@ -54,6 +57,13 @@ jobs: mkdir build\dist\windows mv dist\windows\wintun build\dist\windows\ + - name: Code-sign + uses: ./.github/actions/code-sign + with: + path: build + role: ${{ secrets.DEFINED_CODE_SIGNER_ROLE }} + bucket: ${{ secrets.DEFINED_CODE_SIGNER_BUCKET }} + - name: Upload artifacts uses: actions/upload-artifact@v7 with: