alerts

package
v0.10.2 Latest Latest
Warning

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

Go to latest
Published: Jul 27, 2026 License: MIT Imports: 18 Imported by: 0

Documentation

Overview

Package alerts implements the cert-expiry alerter: a periodic scanner that detects host certificates approaching their expiry without having been auto-renewed and fans the event out to one or more sinks (audit log, webhook, Prometheus gauge).

State lives in the cert_alerts table — one row per host — so a given (host, not_after) pair fires at most once even if the scan tick fires many times before someone rotates the cert.

Index

Constants

View Source
const AuditActionCertExpiring = "cert.expiring"

Audit action emitted by the cert-expiry alerter. Kept here as a constant so receivers (parsers, dashboards) can match against a stable identifier.

View Source
const SignatureHeader = "X-Nebula-Signature"

SignatureHeader is the HTTP header webhook receivers should inspect to authenticate the request body. Exported for documentation tests.

Variables

This section is empty.

Functions

This section is empty.

Types

type Alert

type Alert struct {
	HostID             string
	HostName           string
	NetworkID          string
	CAID               string
	Fingerprint        string
	NotAfter           time.Time
	SecondsUntilExpiry float64
}

Alert is the structured event a Sink receives.

type AuditSink

type AuditSink struct {
	Store store.Store
}

AuditSink writes each Alert to the store's audit log as a structured `cert.expiring` entry. Always-on by design: the audit log is the no-config-required fallback documented in the issue.

func (*AuditSink) Notify

func (a *AuditSink) Notify(ctx context.Context, ev Alert) error

Notify appends an audit entry whose details field is a small JSON blob — enough for `/api/v1/audit-log` consumers to extract host_id, ca_id, not_after, and seconds_until_expiry without a join.

type Scanner

type Scanner struct {
	Store     store.Store
	Threshold time.Duration
	Interval  time.Duration
	Sinks     []Sink
	Logger    *slog.Logger
}

Scanner is a periodic cert-expiry watchdog. Wire one up at server start, call StartLoop with a cancellable context, and it ticks every Interval until the context is canceled.

func (*Scanner) Run

func (s *Scanner) Run(ctx context.Context) error

Run performs a single sweep: enumerate enrolled hosts' current certs, filter to those whose remaining lifetime is shorter than Threshold, and emit one Alert per (host, not_after) pair that has not yet been alerted.

Errors from individual sinks are logged but do not stop the loop; the dedup record is still written so retries happen at the next threshold crossing (cert rotation) rather than every tick.

func (*Scanner) StartLoop

func (s *Scanner) StartLoop(ctx context.Context)

StartLoop runs Scan immediately, then on every Interval tick until ctx is canceled. Returns once the loop exits.

type Sink

type Sink interface {
	Notify(ctx context.Context, a Alert) error
}

Sink is the interface implemented by alert delivery backends — audit log, webhook, Prometheus gauge, etc. A sink's Notify call is expected to be fast and best-effort; the scanner records the alert as fired regardless of individual sink failures so a flaky webhook does not block the audit log entry or trigger duplicate delivery on the next scan tick.

type WebhookSink

type WebhookSink struct {
	URL        string
	HMACSecret string
	HTTPClient *http.Client
	// AllowPrivate disables the request-time private-address guard,
	// mirroring alerts.allow_private_webhook for intentional internal
	// sinks. Only consulted when HTTPClient is nil — a caller-supplied
	// client manages its own transport policy.
	AllowPrivate bool
}

WebhookSink POSTs every alert as JSON to URL with an HMAC-SHA256 signature in the X-Nebula-Signature header (sha256=<hex>). If HMACSecret is empty, the signature header is omitted — useful for trusted-internal alertmanager endpoints behind mTLS where signing is redundant.

Unless AllowPrivate is set, deliveries are SSRF-guarded at request time: the dialer rejects connections to private/loopback/link-local addresses after DNS resolution, and redirects re-apply the same check per hop. This complements the config-load check in validateWebhookURL, which cannot see what a hostname resolves to at delivery time or where a redirect points.

func (*WebhookSink) Notify

func (w *WebhookSink) Notify(ctx context.Context, ev Alert) error

Jump to

Keyboard shortcuts

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