receiver

package
v0.1.55 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 13, 2026 License: Apache-2.0 Imports: 9 Imported by: 6

Documentation

Overview

Package receiver provides secure CloudEvent webhook receivers for Chainguard events.

Overview

This package implements a CloudEvent handler that verifies webhook events sent by Chainguard are authentic and intended for your organization. It validates OIDC tokens embedded in webhook requests to ensure events come from Chainguard's webhook component and are addressed to the correct group.

Features

  • OIDC token verification using the Chainguard issuer
  • Group-based authorization to ensure events are intended for your organization
  • Message digest validation to prevent replay attacks
  • Integration with the CloudEvents SDK for standard event handling
  • Automatic HTTP status code responses for authentication and authorization failures

Security Model

Each webhook request includes an OIDC token in the Authorization header. The token contains:

  • A subject claim identifying the webhook component (must start with "webhook:")
  • A group identifier indicating the intended recipient organization
  • A message digest (SHA-256) of the event payload to prevent tampering

The receiver validates all three components before invoking your handler function.

Usage

Create a receiver by calling New with your OIDC issuer URL, group ID, and handler function. The returned handler can be used with the CloudEvents HTTP protocol:

handler, err := receiver.New(ctx, "https://issuer.enforce.dev", "my-group-id",
	func(ctx context.Context, event cloudevents.Event) error {
		// Process the verified event
		return nil
	})
if err != nil {
	log.Fatalf("failed to create receiver: %v", err)
}

// Use with CloudEvents HTTP receiver
c, err := cloudevents.NewClientHTTP()
if err != nil {
	log.Fatalf("failed to create client: %v", err)
}
if err := c.StartReceiver(ctx, handler); err != nil {
	log.Fatalf("failed to start receiver: %v", err)
}

Integration Patterns

The receiver integrates with the CloudEvents SDK's HTTP protocol. It expects:

  • An Authorization header with a Bearer token
  • A CloudEvent payload in the request body
  • The token's digest claim to match the SHA-256 hash of the event data

On authentication or authorization failure, the receiver returns an appropriate HTTP status code (401 Unauthorized or 403 Forbidden) without invoking your handler.

Error Handling

The receiver returns CloudEvents HTTP results for all authentication and authorization failures. These are automatically converted to HTTP responses by the CloudEvents SDK:

  • 401 Unauthorized: Missing Authorization header
  • 403 Forbidden: Invalid token, wrong group, wrong subject, or digest mismatch

Your handler function should return an error for processing failures. The CloudEvents SDK will convert these to appropriate HTTP responses.

Example

Example demonstrates creating a secure webhook receiver that verifies Chainguard events are authentic and intended for your organization.

package main

import (
	"context"

	"chainguard.dev/sdk/events/receiver"
	"github.com/chainguard-dev/clog"

	cloudevents "github.com/cloudevents/sdk-go/v2"
)

func main() {
	ctx := context.Background()

	// Create a receiver that verifies events from Chainguard's issuer
	// and ensures they are intended for your group.
	handler, err := receiver.New(ctx, "https://issuer.enforce.dev", "my-group-id",
		func(ctx context.Context, event cloudevents.Event) error {
			// Process the verified event
			clog.InfoContextf(ctx, "Received event: %s", event.Type())
			return nil
		})
	if err != nil {
		clog.FatalContextf(ctx, "failed to create receiver: %v", err)
	}

	// Use the handler with CloudEvents HTTP receiver
	c, err := cloudevents.NewClientHTTP()
	if err != nil {
		clog.FatalContextf(ctx, "failed to create client: %v", err)
	}

	// Start receiving events
	if err := c.StartReceiver(ctx, handler); err != nil {
		clog.FatalContextf(ctx, "failed to start receiver: %v", err)
	}
}
Example (CustomHandler)

Example_customHandler demonstrates using a custom handler function to process different event types.

package main

import (
	"context"

	"chainguard.dev/sdk/events/receiver"
	"github.com/chainguard-dev/clog"

	cloudevents "github.com/cloudevents/sdk-go/v2"
)

func main() {
	ctx := context.Background()

	handler, err := receiver.New(ctx, "https://issuer.enforce.dev", "my-group-id",
		func(ctx context.Context, event cloudevents.Event) error {
			// Handle different event types
			switch event.Type() {
			case "dev.chainguard.image.created":
				clog.InfoContextf(ctx, "New image created: %s", event.Subject())
			case "dev.chainguard.policy.violated":
				clog.InfoContextf(ctx, "Policy violation detected: %s", event.Subject())
			default:
				clog.InfoContextf(ctx, "Unknown event type: %s", event.Type())
			}
			return nil
		})
	if err != nil {
		clog.FatalContextf(ctx, "failed to create receiver: %v", err)
	}

	c, err := cloudevents.NewClientHTTP()
	if err != nil {
		clog.FatalContextf(ctx, "failed to create client: %v", err)
	}

	if err := c.StartReceiver(ctx, handler); err != nil {
		clog.FatalContextf(ctx, "failed to start receiver: %v", err)
	}
}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Handler

type Handler func(ctx context.Context, event cloudevents.Event) error

Handler is a function that handles a CloudEvent.

func New

func New(ctx context.Context, issuer, group string, fn Handler) (Handler, error)

New returns a new Handler that verifies the Event was sent by Chainguard, intended for the specified Group, then invokes the provided Handler.

TODO(jason): Accept options for configuring the issuer and accepted event types.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL