action.yml 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. name: Code-sign Windows binaries
  2. description: >
  3. Sign every .exe under a given path in place via the DefinedNet code-signer
  4. Lambda. If `role` or `bucket` is empty, logs a notice and skips signing so
  5. forks and dev branches without AWS access still produce usable builds.
  6. inputs:
  7. path:
  8. description: "Directory whose .exe files should be signed in place"
  9. required: true
  10. role:
  11. description: "IAM role ARN to assume via OIDC; empty disables signing"
  12. required: false
  13. default: ""
  14. bucket:
  15. description: "S3 staging bucket the code-signer Lambda reads from; empty disables signing"
  16. required: false
  17. default: ""
  18. region:
  19. description: "AWS region for the role and Lambda"
  20. required: false
  21. default: "us-east-2"
  22. function-name:
  23. description: "Code-signer Lambda function name"
  24. required: false
  25. default: "code-signer"
  26. key-prefix:
  27. description: "S3 key prefix the caller is authorized to write under"
  28. required: false
  29. default: "code-signing/slackhq/nebula"
  30. runs:
  31. using: composite
  32. steps:
  33. - name: Skip notice
  34. if: inputs.role == '' || inputs.bucket == ''
  35. shell: sh
  36. run: echo "::notice::code-signer role or bucket not set; skipping code signing."
  37. - name: Configure AWS credentials
  38. if: inputs.role != '' && inputs.bucket != ''
  39. uses: aws-actions/configure-aws-credentials@v6
  40. with:
  41. role-to-assume: ${{ inputs.role }}
  42. aws-region: ${{ inputs.region }}
  43. # Default is 12 retries to ride out IAM trust-policy propagation; once
  44. # the role is stable we want a real misconfiguration to fail fast.
  45. retry-max-attempts: 5
  46. - name: Sign .exe files
  47. if: inputs.role != '' && inputs.bucket != ''
  48. shell: sh
  49. env:
  50. SIGN_PATH: ${{ inputs.path }}
  51. BUCKET: ${{ inputs.bucket }}
  52. FUNCTION_NAME: ${{ inputs.function-name }}
  53. KEY_PREFIX: ${{ inputs.key-prefix }}
  54. run: |
  55. set -eu
  56. RUN="${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
  57. find "$SIGN_PATH" -name '*.exe' -print | while read -r path
  58. do
  59. rel=${path#"$SIGN_PATH"/}
  60. file=$(basename "$path")
  61. name=${file%.exe}
  62. prefix="${KEY_PREFIX}/${RUN}"
  63. src="${prefix}/unsigned/${rel}"
  64. dst="${prefix}/signed/${rel}"
  65. echo "::group::Sign ${rel}"
  66. echo "Uploading unsigned to s3://${BUCKET}/${src}"
  67. aws s3 cp --no-progress "$path" "s3://${BUCKET}/${src}" >/dev/null
  68. echo "Invoking ${FUNCTION_NAME} Lambda"
  69. payload=$(jq -nc \
  70. --arg s "$src" \
  71. --arg d "$dst" \
  72. --arg p "$name" \
  73. '{source_key: $s, dest_key: $d, program_name: $p}')
  74. meta=$(aws lambda invoke \
  75. --function-name "$FUNCTION_NAME" \
  76. --cli-binary-format raw-in-base64-out \
  77. --payload "$payload" \
  78. --output json \
  79. /tmp/sign-resp.json)
  80. if echo "$meta" | jq -e '.FunctionError != null' >/dev/null
  81. then
  82. echo "::endgroup::"
  83. echo "::error::code-signer Lambda failed for ${rel}"
  84. cat /tmp/sign-resp.json >&2
  85. exit 1
  86. fi
  87. echo "Downloading signed back to ${path}"
  88. aws s3 cp --no-progress "s3://${BUCKET}/${dst}" "$path" >/dev/null
  89. aws s3 rm "s3://${BUCKET}/${src}" >/dev/null 2>&1 || true
  90. aws s3 rm "s3://${BUCKET}/${dst}" >/dev/null 2>&1 || true
  91. # Sanity-check the bytes we got back actually carry an Authenticode
  92. # signature that this machine can validate end to end.
  93. status=$(powershell -NoProfile -Command "(Get-AuthenticodeSignature -FilePath '$path').Status" | tr -d '\r')
  94. if [ "$status" != "Valid" ]
  95. then
  96. echo "::endgroup::"
  97. echo "::error::${rel} signature status: ${status} (expected Valid)"
  98. exit 1
  99. fi
  100. echo "Signed ${rel} (sha256=$(jq -r '.sha256' /tmp/sign-resp.json), status=${status})"
  101. echo "::endgroup::"
  102. done