GithubHelp home page GithubHelp logo

distributhor / workflow-webhook Goto Github PK

View Code? Open in Web Editor NEW
145.0 5.0 46.0 1.75 MB

A Github workflow action to call a webhook with payload data from the event. Support for JSON or URL encoded endpoints.

License: MIT License

Dockerfile 6.01% Shell 93.99%
workflow action webhook deployment continuous-deployment workflow-webhook

workflow-webhook's Introduction

Workflow Webhook Action

GitHub Release License

A Github workflow action to call a remote webhook endpoint with a JSON or form-urlencoded payload, and support for BASIC authentication. A hash signature is passed with each request, derived from the payload and a configurable secret token. The hash signature is identical to that which a regular Github webhook would generate, and sent in a header field named X-Hub-Signature. Therefore any existing Github webhook signature validation will continue to work. For more information on how to valiate the signature, see https://docs.github.com/webhooks/securing/.

By default, the values of the following GitHub workflow environment variables are sent in the payload: GITHUB_REPOSITORY, GITHUB_REF, GITHUB_HEAD_REF, GITHUB_SHA, GITHUB_EVENT_NAME and GITHUB_WORKFLOW. For more information on what is contained in these variables, see https://help.github.com/en/actions/automating-your-workflow-with-github-actions/using-environment-variables.

These values map to the payload as follows:

{
    "event": "GITHUB_EVENT_NAME",
    "repository": "GITHUB_REPOSITORY",
    "commit": "GITHUB_SHA",
    "ref": "GITHUB_REF",
    "head": "GITHUB_HEAD_REF",
    "workflow": "GITHUB_WORKFLOW"
}

If you are interested in receiving more comprehensive data about the GitHub event than just the above fields, then the action can be configured to send the whole JSON payload of the GitHub event, as per the GITHUB_EVENT_PATH variable in the environment variable documentation referenced above. The official documentation and reference for the payload itself can be found here: https://developer.github.com/webhooks/event-payloads/, and the details on how to configure it, is further down in the Usage section of this README.

Additional (custom) data can also be added/merged to the payload (see further down).

Usage

The following are example snippets for a Github yaml workflow configuration.

Send the JSON (default) payload to a webhook:

    - name: Invoke deployment hook
      uses: distributhor/workflow-webhook@v3
      with:
        webhook_url: ${{ secrets.WEBHOOK_URL }}
        webhook_secret: ${{ secrets.WEBHOOK_SECRET }}

Will deliver a payload with the following properties:

{
    "event": "push",
    "repository": "owner/project",
    "commit": "a636b6f0861bbee98039bf3df66ee13d8fbc9c74",
    "ref": "refs/heads/master",
    "head": "",
    "workflow": "Build and deploy",
    "requestID": "74b1912d19cfe780f1fada4b525777fd"
}

requestID contains a randomly generated identifier for each request.


Add additional data to the payload:

    - name: Invoke deployment hook
      uses: distributhor/workflow-webhook@v2
      with:
        webhook_url: ${{ secrets.WEBHOOK_URL }}
        webhook_secret: ${{ secrets.WEBHOOK_SECRET }}
        data: '{ "weapon": "hammer", "drink" : "beer" }'

The additional information will become available on a data property, and now look like:

{
    "event": "push",
    "repository": "owner/project",
    "commit": "a636b6f0861bbee98039bf3df66ee13d8fbc9c74",
    "ref": "refs/heads/master",
    "head": "",
    "workflow": "Build and deploy",
    "data": {
        "weapon": "hammer",
        "drink": "beer"
    },
    "requestID": "74b1912d19cfe780f1fada4b525777fd"
}

Send a form-urlencoded payload instead:

    - name: Invoke deployment hook
      uses: distributhor/workflow-webhook@v3
      with:
        webhook_type: 'form-urlencoded'
        webhook_url: ${{ secrets.WEBHOOK_URL }}
        webhook_secret: ${{ secrets.WEBHOOK_SECRET }}
        data: 'weapon=hammer&drink=beer'

Will set the Content-Type header to application/x-www-form-urlencoded and deliver:

"event=push&repository=owner/project&commit=a636b6f0....&weapon=hammer&drink=beer"

Finally, if you prefer to receive the whole original GitHub payload as JSON (as opposed to the default JSON snippet above), then configure the webhook with a webhook_type of json-extended:

    - name: Invoke deployment hook
      uses: distributhor/workflow-webhook@v3
      with:
        webhook_type: 'json-extended'
        webhook_url: ${{ secrets.WEBHOOK_URL }}
        webhook_secret: ${{ secrets.WEBHOOK_SECRET }}
        data: '{ "weapon": "hammer", "drink" : "beer" }'

You can still add custom JSON data, which will be available on a data property, included on the GitHub payload. Importantly, the sending of the whole GitHub payload is only supported as JSON, and not currently available as urlencoded form parameters.

Arguments

  webhook_url: "https://your.webhook"

Required. The HTTP URI of the webhook endpoint to invoke. The endpoint must accept an HTTP POST request.

  webhook_secret: "Y0uR5ecr3t"

Optional. The secret with which to generate the signature hash. If no secret is configured, then the URL itself will be used as the value with which to generate the signature hash. This is useful for use-cases where the webhook URL might be an obscure, random or temporary link. In general it is advisable to use a webhook secret.

  webhook_auth_type: "bearer"

The type of authentication to use when invoking the webhook URL. Valid values are basic, bearer and header. Defaults to basic if not specified. In addition, if no value is set for webhook_auth, then it is assumed that no authentication is required, and this value, even if configured, will have no effect. It only takes effect when used in conjunction with webhook_auth. The expectations for how each option behaves, is explained in the webhook_auth section below.

  webhook_auth: "username:password"
  webhook_auth: "Token:ABC"
  webhook_auth: "ABC"

The credentials to be used for authentication of the the endpoint. If not configured, authentication is assumed not to be required.

If the webhook_auth_type is set to basic, then this value is expected to be a username:password string, used for BASIC authentication against the endpoint. It must follow this format, as specified in the curl man pages, since it is passed verbatim to the curl -u option.

If the webhook_auth_type is set to bearer, then this value is expected to be an access token. It will set a header named Authorization with a value of Bearer ${webhook_auth}.

If the webhook_auth_type is set to header, then the expecation is to receive a string similar in format to basic, except that the delimiter (a colon) will delimit a header name and header value. For example a value of Token:ABC will set a header named Token with a value of ABC. If no colon is present (no delimiter specified) then it will default to setting a header named Authorization of which the value will be whatever was configured for ${webhook_auth}, ie (no 'bearer') prefix. As an example, when configured with a value of ABC, a header named Authorization will be set to the value ABC.

  webhook_type: "json | form-urlencoded | json-extended"

The default endpoint type is JSON. The argument is only required if you wish to send urlencoded form data. Otherwise it's optional.

  verbose: true

To enable verbose output in curl set the argument verbose to true. The default value is false. See also: curl docs on option -v.

โš ๏ธ Warning: This might lead to domain and IP leaking, as well as other security issues as the logs are public. See also #21 and #22.

  silent: true

To hide the output from curl set the argument silent to true. The default value is false.

  timeout: 30

To set a maximum time, in seconds, by which to establish an initial connection to the server. Once a connection has been established, the option is not used in any further way with regards to the duration of connection.

  max_time: 30

To set a maximum time, in seconds, by which the server needs to respond to the request. This also includes the time needed for the server to respond. May be used in combination with timeout.

  curl_opts: '--speed-limit 5000'
  curl_opts: '-H "X-Beverage: Beer"'

You can use curl_opts to pass in arbitrary options to the curl request. NOTE: this is an experimental feature and not guaranteed to work for all options. The string configured here will be passed in verbatim to curl, and it is quite easy to break things when using it. For simple curl options it should work, but for others it may not suffice. Also, take care with escaping characters in YAML.

  verify_ssl: false

To disable verification of SSL-certificates in curl set the argument verify_ssl to false. The default value is true. See also: curl docs on option -k.

  event_name: 'NAME'

Optional. A custom event name sent to the webhook endpoint

  data: "Additional JSON or URL encoded data"

Additional data to include in the payload. It is optional. This data will attempted to be merged 'as-is' with the existing payload, and is expected to already be sanitized and valid.

In the case of JSON, the custom data will be available on a property named data, and it will be run through a JSON validator. Invalid JSON will cause the action to break and exit. For example, using single quotes for JSON properties and values instead of double quotes, will show the following (somewhat confusing) message in your workflow output: Invalid numeric literal. Such messages are the direct output from the validation library https://stedolan.github.io/jq/. The supplied JSON must pass the validation run through jq.

Output

  response-body: 'The body of the webook response'

License

The MIT License (MIT). Please see License File for more information.

workflow-webhook's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

workflow-webhook's Issues

Non containerized version support

Hello

I have used self hosted runner which runs on windows instead of ubuntu. The runner is running successfully but when the actions gets triggered it throws following error

Run distributhor/workflow-webhook@v3
Error: Container action is only supported on Linux

Please help me with above error

Azure Pipeline Call with just Access Token

Am trying to call Azure pipeline with access token. Which means I need to pass only password in webhook_auth, something like webhook_auth: ":ACCESSTOKEN", and this fails with 401

v3.0.4 generates invalid signature

I've noticed that our webhook that uses this action stopped working. After some investigation, I've found out that the signature is not valid anymore. Testing with the Docker image directly, I've found that it works with v3.0.3, but not in v3.0.4. I assume it is related to the changes in entrypoint.sh.

We're using our homemade https://github.com/gbv/github-webhook-handler for handling webhooks from GitHub. The signature validation code is here and it works perfectly for webhook requests coming directly from GitHub.

Verify SSL certificates (by default)

Hi there, thank you for this project. I'm currently working on a server side implementation, that is supposed to receive a webhook and recreate a docker container.

While taking a look at your code, I've found multiple security problems. I don't think they are too bad for most people but still I feel like they should be addressed. I will open an issue for each of them, to help with organizing them, even though they are kind of related to each other. I'm sorry for spamming you with issues, but I think it's worth it.

The current implementation does not verify SSL certificates, if I correctly understand the curl docs on option -k. This parameter was introduced in 16b7ccd as a response to #3.

I can see how some people do not have the time, patience or even the option to use verifiable SSL certificates, but I do and so will others.

I believe there are two ways to fix this issue (both using an additional argument verify-ssl: [bool] similar to silent: [bool]):

  1. Remove -k-option by default and only add it, if an additional argument verify-ssl: false is added to the workflow config. This would have the benefit that the most secure option is used by default, but might break other peoples' workflows if they use unsigned certificates and would require them to update their workflow config.
  2. Add an additional argument verify-ssl: true to remove the -k -option. This would at least allow people like me to verify their SSL certificates, without breaking any existing workflows. If this route is taken, it should at least be mentioned in README.md in my opinion, to warn other users.

Personally I'd prefer option 1 as it will default new users' workflows to verify their SSL certificates, but I do understand if you don't want to break any existing workflows. Just keep in mind that fixing this now might be easier than fixing it when even more people use this workflow and it actually becomes a serious security issue for some.

bug: multiline responses cause an error 'Unable to process file command'

Latest feature of returning the results to output introduced a failure when there's a multiline response in v3.0.2.

As per this discussion and the official docs, multiline responses need to be handled accordingly.

Expected

Action to complete successfully.

Received

Webhook Request ID: a7c49923-05c1-4b44-8f8c-2aa661dc9ef5
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
...

Error: Unable to process file command 'output' successfully.
Error: Invalid format '***'

Support file uploads

I want to write an action that produces data that gets submitted to a webhook by workflows, but the data in general is too large to put in an action output (GitHub Actions produces an "out of memory" error). It would be useful to be able to upload a file to the webhook. I believe this can be done with curl using the -F flag (see https://everything.curl.dev/http/multipart#file-input).

response-body not showing when error encountered

If the endpoint returns an error status (like 500) then no response body is shown. If I manually hit the endpoint with the same data, I get an expected error message describing the problem with the payload.

The expectation here is that if there is response data and verbose is on, then I should see the response data even if the server returned a non-200 code.

(note: originally this issue was about it not showing in general. What was actually happening is that this other issue was making it seem like there was a general problem)

Prevent replay attacks

As mentioned in #21 a lot of information is published using --verbose/-v by default.

This includes the headers sent, including X-Hub-Signature and X-Hub-Signature-256. If I do understand the algorithm correctly this signature is used to verify the request body only.

Since the default payload is pretty predictable (source: README.md) :

{
    "event": "push",
    "repository": "owner/project",
    "commit": "a636b6f0861bbee98039bf3df66ee13d8fbc9c74",
    "ref": "refs/heads/master",
    "head": "",
    "workflow": "Build and deploy"
}

and the signature/ hash is logged publically, I do believe that it would be an easy one to replay this request. The server (at least my current implementation) would think this is a genuine request from GitHub.

There is no way to inject malicious data or similar, since that would change and therefore invalidate the signature, but this could make it way easier to DDoS my service that recreates a docker container. Just sending unauthorized requests should have minimal performance impact, but if a genuine message is replayed, my service would try to recreate the container over and over, which would hit really badly performance wise.

This is not too bad at the moment as not the complete URL is logged, but I do believe that many people use URLs similar to https://test.com/webhooks or similar predictable URLs. If #21 is taken care of and --verbose is not the default anymore, the issue would only affect people explicitly using the verbose: true option and their old workflow logs (but I believe those are deleted anyway after 3 months).

There are multiple ways to address this issue:

  1. Send a salt with each request:
    To prevent this problem from affecting me I will use an additional salt that is stored as a secret and is therefore private. This makes it impossible (or at least way harder) to replay the request, as part of the body is unknown.
        - name: Invoke deployment hook
          uses: distributhor/workflow-webhook@v1     
          env:
            webhook_url: ${{ secrets.WEBHOOK_URL }}
            webhook_secret: ${{ secrets.WEBHOOK_SECRET }}
            data: '{ "salt": "${{ secrets.WEBHOOK_SALT }}" }'
    Similarly adding other data, that is not publically available or predictable makes this attack way harder and therefore unreasonable. You could also add some sort of unique request ID (e.g. a random UUID) that is sent with each request.
  2. Verify that each request is only processed once on the receiving side:
    If the receiving side stores some identifier (e.g. the commit SHA, or even better aforementioned unique request ID) and makes sure that each request is only handled once, replay attacks are not useful anymore. It might be a good idea to state this somehere in README.md.

Add timeout option

Hi there,

I'm integrating this action (thanks!) but noticed since my endpoint isn't quite ready, that the action takes 2 minutes to fail.

curl: (28) Failed to connect to mydomain.com port 443 after 130061 ms: Operation timed out

Would you be open to adding an option for --connect-timeout with a friendly default of 10 seconds? That would help save time/expense when developing workflows...

403 Forbidden

I was getting a 403 error in the log. I thought this was because I had my web server only accept POST, but even with GET enabled, I still get the 403 error. It looks like it's doing a POST in any case. Turns out this is because the User-Agent header has duplicate text in it, which causes some web servers to block the request.

Can the docker image be cached?

This is a cool project. When implementing in a CI workflow, it seems that the docker image is built every time, although it never changes within my project. This adds an ~30sec step to each of my builds that could be avoided with caching.

Would it be possible to cache the built image as an artifact on Github Packages? This would greatly accelerate the build.

Request ID does not show in action logs

The webhook generates a requestId property in the body of the payload. This is great because it could be used to correlate the request in the system that is processing the webhook call. However, this requestId is not logged from workflow-webhook.

My suggestion is to log requestId (if not in silent mode) so that you can look at the Actions run to get the requestId and then correlate it to the invoked system's logs.

curl: option -: is unknown

Run distributhor/[email protected]
/usr/bin/docker run --name ghcriodistributhorworkflowwebhookcontainerv301_3[7]--workdir /github/workspace --rm -e "webhook_url" -e "webhook_secret" -e "INPUT_WEBHOOK_URL" -e "INPUT_WEBHOOK_SECRET" -e "INPUT_WEBHOOK_AUTH" -e "INPUT_WEBHOOK_TYPE" -e "INPUT_VERBOSE" -e "INPUT_SILENT" -e "INPUT_TIMEOUT" -e "INPUT_VERIFY_SSL" -e "INPUT_EVENT_NAME" -e "INPUT_DATA" -e "HOME" -e "GITHUB_JOB" -e "GITHUB_REF" -e "GITHUB_SHA" -e "GITHUB_REPOSITORY" -e "GITHUB_REPOSITORY_OWNER" -e "GITHUB_RUN_ID" -e "GITHUB_RUN_NUMBER" -e "GITHUB_RETENTION_DAYS" -e "GITHUB_RUN_ATTEMPT" -e "GITHUB_ACTOR" -e "GITHUB_TRIGGERING_ACTOR" -e "GITHUB_WORKFLOW" -e "GITHUB_HEAD_REF" -e "GITHUB_BASE_REF" -e "GITHUB_EVENT_NAME" -e "GITHUB_SERVER_URL" -e "GITHUB_API_URL" -e "GITHUB_GRAPHQL_URL" -e "GITHUB_REF_NAME" -e "GITHUB_REF_PROTECTED" -e "GITHUB_REF_TYPE" -e "GITHUB_WORKSPACE" -e "GITHUB_ACTION" -e "GITHUB_EVENT_PATH" -e "GITHUB_ACTION_REPOSITORY" -e "GITHUB_ACTION_REF" -e "GITHUB_PATH" -e "GITHUB_ENV" -e "GITHUB_STEP_SUMMARY" -e "GITHUB_STATE" -e "GITHUB_OUTPUT" -e "RUNNER_OS" -e "RUNNER_ARCH" -e "RUNNER_NAME" -e "RUNNER_TOOL_CACHE" -e "RUNNER_TEMP" -e "RUNNER_WORKSPACE" -e "ACTIONS_RUNTIME_URL" -e "ACTIONS_RUNTIME_TOKEN" -e "ACTIONS_CACHE_URL" -e GITHUB_ACTIONS=true -e CI=true -v "/var/run/docker.sock":"/var/run/docker.sock" -v "/home/runner/work/_temp/_github_home":"/github/home" -v "/home/runner/work/_temp/_github_workflow":"/github/workflow" -v "/home/runner/work/_temp/_runner_file_commands":"/github/file_commands" -v "/home/runner/work/feathr/feathr":"/github/workspace" ghcr.io/distributhor/workflow-webhook-container:v3.0.1
Webhook Request ID: 402485ab-f1cd-4cab-bf7f-b6c4f14d55b0
curl: option -: is unknown
curl: try 'curl --help' or 'curl --manual' for more information

Support for Self-Signed SSL Certificates

Hello,

I have a Jenkins server which is configured to use a Self-Signed SSL certificate. When I try to trigger a Jenkins Job using this action, it is failing due to the following error. Is it possible to override SSL validation?

curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.haxx.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not establish a secure connection to it.
To learn more about this situation and how to fix it, please visit the web page mentioned above.

Add option for custom headers or uuid for X-Github-Delivery

Usually regular github webhook send the X-Github-Delivery header with a UUID as a value and applications that integrate with github webhooks usually check for such value.

I did not see a way to modify the creation of a uuid or to send a custom request header with the uuid.

example of request from a webhook in github:

Accept: */*
content-type: application/json
User-Agent: GitHub-Hookshot/5ab4b44
X-GitHub-Delivery: 11eb8e80-3f7f-11ed-8e05-b5a152a893a5
X-GitHub-Event: pull_request```

curl command does not follow redirects

The current implementation of the curl command does not include the -L option to follow redirects. This behavior is causing issues with some Zero Trust integrations, which rely on handling redirects correctly to function properly.

Return the webhook response from the action

Description

I would like to be able to consume the response returned by the webhook action. Often times it includes details that I need to complete my automation task, or other information that can be surfaced. I've opened a pull request to add this functionality #38 .

Add log level between `--verbose` and `--silent`

I'd like to have another log level between --verbose/ -v and --silent/ -s.

Currently, it is possible to pick between -v (default) and -s with the silent: [bool] parameter in the workflow config. Both of these options don't fit my needs:

  • --verbose/ -v logs a lot of information to the publically available workflow runs, that I do not want to be available including the domain of the webhook_url that I (as suggested in README.md) explicitly used a secret for. Also the resolved IP address is logged. This opens the door for DDoS attacks and makes hacking the machine one step easier.
  • --silent/ -s doesn't log anything (but I think this is obvious ๐Ÿ˜„), not even the response code.

Not specifying either -v or -s does log a minimal amount of output which would be enough for me to debug most issues, but little enough to not compromise on privacy.

Example output (on windows, but it should be similar on alpine): curl: (22) The requested URL returned error: 403 Forbidden. That's all.

I think the best way to address this issue is to add another option verbose: [bool] similar to silent: [bool].

Once again (as in #20), I do believe this new log level should be the default, but your opinion and that of other users might vary. Depending on your opinion the default value could be verbose: true or verbose: false.

Having the new log level as the default would also make fixing #22 way less critical.

The GitHub actions extension gives incorrect feedback / errors

The official GitHub Actions for VSCode extension gives errors for a working configuration.

My step looks like this:

- name: Trigger Sync Template Webhook
  uses: distributhor/[email protected]
  env:
    webhook_url: ${{ secrets.TEMPLATE_WEBHOOK_URL }}

I get this message: Missing required inputs: 'webhook_url', 'webhook_secret'

It's worth noting that it says that the webhook_secret is a required input, the docs here say otherwise.

My fix is to do this:

- name: Trigger Sync Template Webhook
  uses: distributhor/[email protected]
  env:
    webhook_url: ${{ secrets.TEMPLATE_WEBHOOK_URL }}
  # For some reason I get the red wigglies if I don't have this
  # ...they're ignored, and the above variable is actually used
  with:
    webhook_url: unused
    webhook_secret: unused

The error

Screenshot 2023-10-30 at 10 08 06

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.