action.yml 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  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 to write under; defaults to code-signing/<owner>/<repo> of the calling repo"
  28. required: false
  29. default: ""
  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. # An STS secret key with special characters does not survive the
  44. # pwsh -> make -> MSYS sh -> aws.exe chain, and SigV4 then signs with a
  45. # key that no longer matches, so the first S3 upload fails with
  46. # SignatureDoesNotMatch. Retries the assume until it comes back clean.
  47. # Same fix as DefinedNet/dnclient#867.
  48. special-characters-workaround: true
  49. # Overridden by the workaround above and kept for whenever that goes:
  50. # the default 12 rides out IAM trust-policy propagation, and once the
  51. # role is stable a real misconfiguration should fail fast.
  52. retry-max-attempts: 5
  53. - name: Sign .exe files
  54. if: inputs.role != '' && inputs.bucket != ''
  55. shell: sh
  56. env:
  57. SIGN_PATH: ${{ inputs.path }}
  58. BUCKET: ${{ inputs.bucket }}
  59. FUNCTION_NAME: ${{ inputs.function-name }}
  60. KEY_PREFIX: ${{ inputs.key-prefix }}
  61. run: |
  62. set -eu
  63. # Default the prefix to this repo so the S3 key attributes the sign correctly.
  64. # nebula-nightly runs this same action but writes under its own repo's prefix.
  65. KEY_PREFIX="${KEY_PREFIX:-code-signing/$GITHUB_REPOSITORY}"
  66. RUN="${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
  67. find "$SIGN_PATH" -name '*.exe' -print | while read -r path
  68. do
  69. rel=${path#"$SIGN_PATH"/}
  70. file=$(basename "$path")
  71. name=${file%.exe}
  72. prefix="${KEY_PREFIX}/${RUN}"
  73. src="${prefix}/unsigned/${rel}"
  74. dst="${prefix}/signed/${rel}"
  75. echo "::group::Sign ${rel}"
  76. echo "Uploading unsigned to s3://${BUCKET}/${src}"
  77. aws s3 cp --no-progress "$path" "s3://${BUCKET}/${src}" >/dev/null
  78. echo "Invoking ${FUNCTION_NAME} Lambda"
  79. payload=$(jq -nc \
  80. --arg s "$src" \
  81. --arg d "$dst" \
  82. --arg p "$name" \
  83. '{source_key: $s, dest_key: $d, program_name: $p}')
  84. meta=$(aws lambda invoke \
  85. --function-name "$FUNCTION_NAME" \
  86. --cli-binary-format raw-in-base64-out \
  87. --payload "$payload" \
  88. --output json \
  89. /tmp/sign-resp.json)
  90. if echo "$meta" | jq -e '.FunctionError != null' >/dev/null
  91. then
  92. echo "::endgroup::"
  93. echo "::error::code-signer Lambda failed for ${rel}"
  94. cat /tmp/sign-resp.json >&2
  95. exit 1
  96. fi
  97. echo "Downloading signed back to ${path}"
  98. aws s3 cp --no-progress "s3://${BUCKET}/${dst}" "$path" >/dev/null
  99. aws s3 rm "s3://${BUCKET}/${src}" >/dev/null 2>&1 || true
  100. aws s3 rm "s3://${BUCKET}/${dst}" >/dev/null 2>&1 || true
  101. # Sanity-check the bytes we got back actually carry an Authenticode
  102. # signature that this machine can validate end to end.
  103. status=$(powershell -NoProfile -Command "(Get-AuthenticodeSignature -FilePath '$path').Status" | tr -d '\r')
  104. if [ "$status" != "Valid" ]
  105. then
  106. echo "::endgroup::"
  107. echo "::error::${rel} signature status: ${status} (expected Valid)"
  108. exit 1
  109. fi
  110. echo "Signed ${rel} (sha256=$(jq -r '.sha256' /tmp/sign-resp.json), status=${status})"
  111. echo "::endgroup::"
  112. done