Webhooks
Webhooks are a great way to get notified about important events that occur within Kolide, allowing you to react to them programmatically.
How to Receive Webhooks
To receive webhooks in Kolide, you must register your HTTP(s) endpoint in the Kolide admin UI.
To register a new endpoint, you can browse directly to the webhooks settings screen, or follow these steps:
- Click your user avatar in the upper-right corner of the Kolide UI.
- In the dropdown menu, click Settings.
- In the menu on the left, click Developers.
- In the sub-menu that appears, click Webhooks.
- On the next screen, click Add Endpoint.
- In the modal that appears, provide the URL of the publicly accessible HTTPS endpoint where Kolide should send webhooks.
- Subscribe to the events you wish to receive on this endpoint. For more information about these events please see the Supported Events section in this document.
- Click Save
Verifying Webhook Authenticity
Before trusting the contents of a webhook sent by Kolide, you should verify that the webhook is authentic.
In addition to the JSON payload, each HTTP POST
request sent to your endpoint will include an HTTP header called Authorization
.
Here is an example of a full request from the test event you can send from the Webhooks UI.
POST / HTTP/1.1
Host: example.com
User-Agent: Ruby
Content-Length: 143
Accept: application/json
Accept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Authorization: c71fd8b49e0736962d16f8410fa0d6f5bb27d7ef35cd1c0b6d4da0b8568921ac
Content-Type: application/json
X-Forwarded-For: 2601:19c:4a00:141e:2852:5759:61:fee7
X-Forwarded-Host: example.com
X-Forwarded-Proto: https
{
"event": "webhook.test",
"id": "01HDVY6MBEYST7797YFFH6QK7C",
"timestamp": "2023-10-28T20:04:28Z",
"data": {
"message": "This is a test webhook event"
}
}
The signature in Authorization
is a SHA256 HMAC hexdigest of the JSON
payload in the HTTP body, signed with the webhook endpoint’s secret. This signing
secret can be obtained in the webhook settings screen.
To obtain the secret, click “Reveal Secret” on the webhooks settings screen.
Preliminary Verification
In addition to the signature in the Authorization header, each webhook request includes a header named X-Kolide-Webhook-Identifier
. The value of this header contains a static identifying value unique to the webhook endpoint.
While you should not rely solely on this value to verify the authenticity of the request, this value can be useful to perform preliminary verification of the request. For example, if you need to restrict traffic through a web application firewall (WAF), reading this header can be simpler than verifying the signature of the request body, with the full signature verification still being done by the final endpoint receiving the webhook request.
This identifier can be found in the webhook settings screen along with the signing secret.
Webhook Verification Code Examples
Verifying Signatures in Ruby
To consume and verify webhooks in Ruby, you can use the following example as a starting point.
require 'sinatra'
require 'openssl'
SIGNING_SECRET = "<YOUR_SIGNING_SECRET>"
post '/echo' do
data = request.body.read
actual_signature = request.get_header 'HTTP_AUTHORIZATION'
expected_hmac = OpenSSL::HMAC.hexdigest('sha256', SIGNING_SECRET, data)
if expected_hmac == actual_signature
puts "OK"
status 200
else
puts "Payload signature not verified"
puts "Expected: #{expected_hmac}"
puts " Actual: #{actual_signature}"
status 400
end
end
Verifying Signatures in Go
To consume and verify webhooks in Go, you can use the following example as a starting point.
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"io/ioutil"
"log"
"net/http"
)
func main() {
server := signedHandler{signingSecret: "<SIGNING SECRET>"}
http.HandleFunc("/", server.ServeHTTP)
log.Fatal(http.ListenAndServe(":8765", nil))
}
type signedHandler struct {
signingSecret string
}
func (s *signedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Fatal("unable to read body")
}
// verify that the signature is correct
signature := r.Header.Get("Authorization")
expectedSig, signatureValid := validMac(body, []byte(signature), []byte(s.signingSecret))
if !signatureValid {
fmt.Printf("request is not properly signed, expected: %s, got: %s", expectedSig, signature)
}
// respond to the request
fmt.Fprintf(w, "OK")
}
func validMac(message, messageMAC, key []byte) (expected string, match bool) {
mac := hmac.New(sha256.New, key)
mac.Write(message)
expectedMAC := mac.Sum(nil)
decodedActualMAC, err := hex.DecodeString(string(messageMAC))
if err != nil {
log.Fatal("unable to decode hex")
}
return hex.EncodeToString(expectedMAC), hmac.Equal(decodedActualMAC, expectedMAC)
}
Supported Events
The following is a complete list of supported events your webhook endpoint can subscribe to.
audit_log.recorded
Added On: October 25th, 2024
Description: This event is sent when an administrator or system action is recorded in the audit log
{
"event": "audit_log.recorded",
"id": "01JA67B1DYJCKJ1J73T0F5EWGR",
"timestamp": "2024-10-14T19:16:05Z",
"data": {
"actor_name": "Fred Flintstone",
"actor_type": "User",
"actor_email": "fred@theflintstones.com",
"ip_address": "86.121.117.128",
"description": "Enabled check 'iTerm2 - Require Secure Keyboard Entry to Be Enabled'"
}
}
admin_users.created
Added On: June 21st, 2024
Description: This event is sent when a new admin user is created.
{
"event": "admin_users.created",
"id": "01HDW1V4TDE1B3KCAMR8V7Z4S5",
"timestamp": "2024-06-21T21:28:17Z",
"data": {
"user_id": 1,
"name": "Fred Flintstone",
"email": "fred@theflintstones.com",
"access": "Admin",
"admin_user_url": "https://api.kolide.com/admin_users/1"
}
}
auth_logs.success
Added On: March 11th, 2024
Description: This event is sent when an end-user successfully completes an SSO authentication through Kolide.
{
"event": "auth_logs.success",
"id": "01HDW1V4TDE1B3KCAMR8V7Z4S5",
"timestamp": "2024-03-11T21:28:17Z",
"data": {
"auth_log_url": "https://api.kolide.com/auth_logs/id3374648",
"browser_name": "Chrome",
"browser_version": "122.0.0.0",
"device_id": 1,
"device_name": "Caitlins-M1",
"launcher_version": "1.4.0",
"person_id": 3
}
}
auth_logs.failure
Added On: May 21st, 2024
Description: This event is sent when an end-user attempts to authenticate through Kolide but never successfully finishes the authentication attempt. This may happen when the SSO session times out or the user navigates away from the authentication page.
{
"event": "auth_logs.failure",
"id": "01HDW1V4TDE1B3KCAMR8V7Z4S5",
"timestamp": "2024-05-14T21:28:17Z",
"data": {
"auth_log_url": "https://api.kolide.com/auth_logs/id3374648",
"browser_name": "Chrome",
"browser_version": "122.0.0.0",
"device_id": 1,
"device_name": "Caitlins-M1",
"launcher_version": "1.4.0",
"person_id": 3
}
}
devices.created
Added On: December 17th, 2019
Description: This event is sent when a device enrolls in Kolide.
{
"event": "devices.created",
"id": "01HDVY8MA3CQ48HJ356FP8M2P6",
"timestamp": "2023-10-28T20:05:33Z",
"data": {
"device_id": 1,
"device_name": "Jasons-MacBook-Pro"
}
}
devices.registered
Added On: October 27th, 2023
Description: This event is sent when an end-user successfully registers a device.
{
"event": "devices.registered",
"id": "01HDVYMVM58H3RMBA3CR16M4PP",
"timestamp": "2023-10-28T20:12:14Z",
"data": {
"device_id": 1,
"device_name": "Jasons-MacBook-Pro",
"device_url": "https://api.kolide.com/devices/1",
"registered_owner": {
"id": 1,
"name": "Casper McFadden",
"email": "ghost@kolide.co",
"owner_url": "https://api.kolide.com/people/1"
}
}
}
devices.destroyed
Added On: December 17th, 2019
Description: This event is sent when a device is removed in Kolide.
{
"event": "devices.destroyed",
"id": "01HDW1TX10N3BS6WJYFP2D421D",
"timestamp": "2023-10-28T21:07:58Z",
"data": {
"device_id": 1,
"device_name": "Jasons-MacBook-Pro-2"
}
}
devicetrust.statuschanged
Added On: September 11th, 2024
Description: This event is sent when a device trust status changes in Kolide.
{
"event": "device_trust.status_changed",
"id": "01J7EXHMF0FCA14EW04JASYCRP",
"timestamp": "2024-09-10T21:30:59Z",
"data": {
"device_id": 9,
"device_name": "Matts-Kolide-MacBook-Pro",
"device_status": "blocked",
"device_url": "http://api.kolide.com/devices/9"
}
}
issues.new
Added On: October 26th, 2021
Description: This event is sent when a new issue is detected by Kolide.
{
"event": "issues.new",
"id": "01HDW0TFR12Q0DJFVY7Y0DBQ0N",
"timestamp": "2023-10-28T20:50:15Z",
"data": {
"issue_id": 2,
"title": null,
"check_id": 71,
"device": {
"id": 1,
"name": "Jasons-MacBook-Pro-2"
},
"check": {
"id": 71,
"name": "macOS Firewall - Require macOS Firewall is Enabled",
"tags": []
}
}
}
issues.resolved
Added On: October 26th, 2021
Description: This event is sent when an issue resolved in Kolide.
{
"event": "issues.resolved",
"id": "01HDW1E30VMSHFFP4MXVCSF7BC",
"timestamp": "2023-10-28T21:00:58Z",
"data": {
"issue_id": 2,
"title": "macOS Firewall is Disabled",
"device": {
"id": 1,
"name": "Jasons-MacBook-Pro-2"
},
"check": {
"id": 71,
"name": "macOS Firewall - Require macOS Firewall is Enabled",
"tags": []
}
}
}
requests.issue_exemption
Added On: October 27th, 2023
Description: This is event is sent when an end-user requests an exemption for one or many related issues.
{
"event": "requests.issue_exemption",
"id": "01HDW1P1BE8PHC7Y6RQTCFBTWR",
"timestamp": "2023-10-28T21:05:18Z",
"data": {
"device_id": 1,
"device_name": "Jasons-MacBook-Pro-2",
"person_email": "ghost@kolide.co",
"message": "I need to keep my device on an old version of macOS because I am a developer testing backwards compatibility.",
"issues": [
{
"issue_id": 1,
"issue_url": "https://api.kolide.com/issues/1"
}
]
}
}
requests.registration
Added On: October 27th, 2023
Description: This is event is sent when an end-user requests approval to register a new device.
{
"event": "requests.registration",
"id": "01HDW1V4TDE1B3KCAMR8V7Z4S5",
"timestamp": "2023-10-28T21:08:06Z",
"data": {
"device": {
"id": 2,
"name": "Jasons-MacBook-Pro-2",
"url": "https://api.kolide.com/devices/2"
},
"requester": {
"id": 1,
"email": "ghost@kolide.co",
"url": "https://api.kolide.com/people/1"
},
"message": "I was recently provisioned a new computer to replace my laptop which no longer works."
}
}