Kubernetes Event Forwarder
A Kubernetes deployment that watches resource changes (ADDED/MODIFIED/DELETED) across multiple clusters and forwards them to an SNS topic with structured metadata for filtering. Uses kubeconfig contexts as the source of truth for which clusters and namespaces to watch.
The full Kubernetes object JSON is forwarded as the SNS message body, compressed with gzip and base64-encoded.
Features
- Multi-Cluster Support: Watch resources across multiple Kubernetes clusters using kubeconfig contexts
- Dynamic Resource Watching: Configure any namespaced resource type using
standard Kubernetes
apiVersionandkind - SNS Integration: Publish events with structured message attributes for precise filtering
- Compression: Events are compressed (gzip+base64) to reduce SNS message size
- Automatic Reconnection: Handles watch connection failures and reconnects automatically
Architecture
Threading Model
The forwarder uses a multi-threaded architecture:
-
Watcher threads (one per context + resource type): Each runs an independent LIST+Watch loop. Isolation ensures one slow/failing cluster doesn’t affect others.
-
Publisher thread (single, shared): Reads events from a queue and publishes to SNS. Decouples K8s API interaction from SNS latency (~150ms per publish), preventing watch loop stalls that could cause resourceVersion staleness.
Memory optimization: When SPOOL_DIR is set, messages are written to
temporary files instead of being held in memory. This reduces memory pressure
during event bursts (e.g., LIST operations or many resources created at once)
and enables recovery of unsent messages after restarts (e.g., after OOM kills).
LIST + Watch Pattern
The forwarder uses explicit LIST followed by Watch rather than
resource_version="0":
- LIST retrieves all current objects and a snapshot
resourceVersion - Watch starts from the LIST’s
resourceVersion(not object RVs) - On graceful watch timeout (300s), restart watch from last known RV (no re-list)
- On errors, fresh LIST to ensure current state
This is necessary because synthetic ADDED events from resource_version="0" have
unsorted, potentially-stale resourceVersions (they reflect when objects were last
modified). If the watch disconnects mid-stream, the tracked RV could be
arbitrarily old and may already be compacted by etcd, causing a 410 error
cascade.
Prerequisites
- Target cluster: ServiceAccount with watch permissions on the resources you want to monitor
- Deployment cluster: Kubernetes cluster to run the forwarder
- AWS credentials: SNS publish permissions (optional - logs events if not configured)
Deployment
The container image is built via Konflux CI/CD and published to
quay.io/hummingbird-ci/kubernetes-event-forwarder:latest.
Example Kubernetes manifests are provided in the kubernetes/
directory:
rbac.yaml- ServiceAccount, Role, RoleBinding for the target clustersecret.yaml- Kubeconfig and AWS credentialsconfigmap.yaml- Resource watch configurationdeployment.yaml- Forwarder deployment
Quick Start
-
On the target cluster (the one you want to watch), apply RBAC and create a token:
kubectl apply -f kubernetes/rbac.yaml kubectl create token kubernetes-event-forwarder --duration=8760h -
Update the example manifests with your values:
secret.yaml: cluster URL, token, AWS credentialsconfigmap.yaml: resources to watchdeployment.yaml: SNS topic ARN, AWS region
-
Apply the manifests to your deployment cluster:
kubectl apply -f kubernetes/secret.yaml kubectl apply -f kubernetes/configmap.yaml kubectl apply -f kubernetes/deployment.yaml
Prerequisites: Deploy hummingbird-events-topic first to create the SNS topic, then deploy the AWS resources (see below).
AWS Resources
The SAM template (template.yaml) provisions IAM resources for SNS publishing:
- IAM User (
${ResourcePrefix}-user) - Service account for the forwarder - IAM Policy (
${ResourcePrefix}-policy) - Grantssns:Publishto the SNS topic
Deploy using containerized AWS SAM CLI:
cd kubernetes-event-forwarder
sam build
sam deploy --guided # First deployment (interactive)
sam deploy # Subsequent deployments
After deployment, create access keys for the IAM user and store them securely.
SAM Parameters
| Parameter | Description | Default |
|---|---|---|
ResourcePrefix |
Prefix for resources | myapp-prod |
SnsTopicArn |
SNS topic ARN | (required) |
Resource naming: IAM resources follow {ResourcePrefix}-{type} pattern
(e.g., myapp-prod-user, myapp-prod-policy).
Usage
Configure resources to watch in config.yaml:
resources:
- apiVersion: v1
kind: Pod
- apiVersion: apps/v1
kind: Deployment
- apiVersion: v1
kind: ConfigMap
SNS Subscription Filter Examples:
Pod events in a specific namespace:
{
"source": ["kubernetes"],
"kind": ["Pod"],
"namespace": ["production"]
}
All deployment changes:
{
"source": ["kubernetes"],
"kind": ["Deployment"]
}
Deleted resources across all clusters:
{
"source": ["kubernetes"],
"event_type": ["DELETED"]
}
Development
See the main README for development workflows.
make kubernetes-event-forwarder/setup # Install dependencies
make check # Lint code (ruff)
make fmt # Format code
make test # Run unit tests
make coverage # Run tests with coverage
Configuration
The deployment is configured via environment variables:
| Variable | Description |
|---|---|
CONFIG_PATH |
Path to config YAML file |
CONFIG |
Inline config YAML (alternative) |
SNS_TOPIC_ARN |
SNS topic ARN (optional - logs if unset) |
SPOOL_DIR |
Optional spool directory for file-based message queue |
AWS_ACCESS_KEY_ID |
AWS access key ID |
AWS_SECRET_ACCESS_KEY |
AWS secret access key |
AWS_DEFAULT_REGION |
AWS region |
SENTRY_DSN |
Optional Sentry DSN |
Event Metadata
The forwarder extracts metadata from Kubernetes events and adds them as SNS message attributes:
| Attribute | Description | Example |
|---|---|---|
source |
Always "kubernetes" |
kubernetes |
cluster |
API server host | https://api.cluster:6443 |
namespace |
Object namespace | production |
api_version |
Resource API version | v1, apps/v1 |
kind |
Resource kind | Pod, Deployment |
event_type |
Event type | ADDED, MODIFIED, DELETED |
object_name |
Name of the object | nginx-7d8c4c9d6f |
content_encoding |
Message encoding | gzip+base64 |
Security & Limitations
Security:
- AWS credentials stored in Kubernetes Secret
- Kubeconfig credentials stored in Kubernetes Secret
- SNS topic follows least privilege principle (publish-only)
- Sentry integration for error tracking
Limitations:
- Only namespaced resources supported
- One thread per context + resource type combination
- SNS message size limit: 256 KB (after compression)
License
This project is licensed under the GNU General Public License v3.0 or later - see the LICENSE file for details.