Skip to main content
  1. Posts/

APIFlask Webhook Listener for Netbox

·258 words·2 mins·
netdevops blog netbox python api apiflask
Maximilian Thoma
Author
Maximilian Thoma
network engineer
Table of Contents

This little code snippet is the base of my Netbox Webhook Listener written in APIFlask. APIFlask is a package which based on Flask but specialized on REST API.

Github Gist: Netbox Webhook Listener written with APIFlask

APIFlask Documentation

Code
#

import hmac
from apiflask import APIFlask
import logging
from flask import request
from apiflask import Schema, abort
from apiflask.fields import String, Dict, DateTime, UUID

APP_NAME = "netbox-webhook-listener"
WEBHOOK_SECRET = "secret"


class WebhookResponse(Schema):
    result = String()


class WebhookData(Schema):
    username = String()
    data = Dict()
    event = String()
    timestamp = DateTime()
    model = String()
    request_id = UUID()
    snapshots = Dict()

app = APIFlask(__name__, title="Netbox Webhook Listener", version="1.0")

logger = logging.getLogger(APP_NAME)
logger.setLevel(logging.INFO)
formatter = logging.Formatter("%(asctime)s %(name)s %(levelname)s: %(message)s")
file_logging = logging.FileHandler(f"{APP_NAME}.log")
file_logging.setFormatter(formatter)
logger.addHandler(file_logging)


def do_something(data):
    logger.info("WebhookData received:")
    logger.info(f"Raw data: {data}")
    logger.info(f"Request ID: {data['request_id']}")
    logger.info(f"Username: {data['username']}")
    logger.info(f"Event: {data['event']}")
    logger.info(f"Timestamp: {data['timestamp']}")
    logger.info(f"Model: {data['model']}")
    logger.info(f"Data: {data['data']}")
    logger.info(f"URL in data: {data['data']['url']}")


@app.post('/webhook')
@app.input(WebhookData(partial=True))
@app.output(WebhookResponse)
def webhook(data):
    x_hook_signature = request.headers.get('X-Hook-Signature', None)
    content_length = int(request.headers.get('Content-Length', 0))

    if content_length > 1_000_000:
        # To prevent memory allocation attacks
        logger.error(f"Content too long ({content_length})")
        abort(400, "Content too long")

    if x_hook_signature:
        # Check signature
        raw_input = request.data
        input_hmac = hmac.new(key=WEBHOOK_SECRET.encode(), msg=raw_input, digestmod="sha512")
        if not hmac.compare_digest(input_hmac.hexdigest(), x_hook_signature):
            logger.error("Invalid message signature")
            abort(400, "Invalid message signature")

        logger.info("Message signature checked ok")
    else:
        logger.error("No message signature to check")
        abort(400, "No message signature to check")

    # Do something here
    do_something(data)

    return {"result": "ok"}


if __name__ == "__main__":
    app.run("0.0.0.0", 8000)

Use it in Netbox
#

Goto Webhooks and create a new webhook. Choose your content types and events. Set the URL and Secret to your Webhook listener.

Part1
shot1

Part2
shot2

Related

Automation: Netbox sync to CheckMK
·1055 words·5 mins
blog netbox python api check_mk
Script to sync Netbox assets to CheckMK
Check_MK: Use API on Check_MK 2.x to trigger tabula rasa for hosts
·166 words·1 min
blog python api check_mk
Sometimes if you must reinventory a huge amount of devices in Check_MK 2.
Ansible: Parse Cisco commando ouputs with TEXTFSM
·390 words·2 mins
netdevops blog cisco textfsm ansible
To parse outputs of commands in Ansible you can use TEXTFSM.