383 lines
14 KiB
Markdown
383 lines
14 KiB
Markdown
# Notification Flow
|
|
|
|
## Overview
|
|
|
|
The Notification Flow describes how StellaOps delivers alerts and notifications to users through multiple channels including email, Slack, Microsoft Teams, and webhooks. Notifications are triggered by system events such as scan completions, policy violations, new advisories, and scheduled reports.
|
|
|
|
**Business Value**: Timely notifications ensure security teams are immediately aware of critical issues without constantly monitoring dashboards.
|
|
|
|
## Actors
|
|
|
|
| Actor | Type | Role |
|
|
|-------|------|------|
|
|
| Event Source | Service | Emits triggering events |
|
|
| Scheduler | Service | Manages scheduled notifications |
|
|
| Notify | Service | Routes and delivers notifications |
|
|
| Channel Adapters | Components | Email, Slack, Teams, Webhook |
|
|
| User | Human | Receives notifications |
|
|
|
|
## Prerequisites
|
|
|
|
- Notification channels configured in tenant settings
|
|
- Channel credentials (SMTP, Slack webhook, Teams connector)
|
|
- User notification preferences set
|
|
|
|
## Supported Channels
|
|
|
|
| Channel | Protocol | Configuration |
|
|
|---------|----------|---------------|
|
|
| Email | SMTP/SMTPS | `smtp.host`, `smtp.port`, `smtp.user`, `smtp.password` |
|
|
| Slack | Webhook | `slack.webhook_url` |
|
|
| Microsoft Teams | Connector | `teams.webhook_url` |
|
|
| Webhook | HTTP POST | `webhook.url`, `webhook.headers` |
|
|
| PagerDuty | API | `pagerduty.routing_key` |
|
|
|
|
## Flow Diagram
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────────────┐
|
|
│ Notification Flow │
|
|
└─────────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
┌──────────┐ ┌───────────┐ ┌────────┐ ┌─────────────────────────────────────────┐
|
|
│ Event │ │ Scheduler │ │ Notify │ │ Channel Adapters │
|
|
│ Source │ │ │ │ │ │ Email │ Slack │ Teams │ Webhook │ PD │
|
|
└────┬─────┘ └─────┬─────┘ └───┬────┘ └────┬───┴───┬───┴───┬───┴────┬────┴──┬──┘
|
|
│ │ │ │ │ │ │ │
|
|
│ Emit event │ │ │ │ │ │ │
|
|
│─────────────────────────>│ │ │ │ │ │
|
|
│ │ │ │ │ │ │ │
|
|
│ │ │ Match │ │ │ │ │
|
|
│ │ │ rules │ │ │ │ │
|
|
│ │ │───┐ │ │ │ │ │
|
|
│ │ │ │ │ │ │ │ │
|
|
│ │ │<──┘ │ │ │ │ │
|
|
│ │ │ │ │ │ │ │
|
|
│ │ │ Route to │ │ │ │ │
|
|
│ │ │ channels │ │ │ │ │
|
|
│ │ │───────────>│ │ │ │ │
|
|
│ │ │ │ │ │ │ │
|
|
│ │ │ │ Send │ │ │ │
|
|
│ │ │ │ email │ │ │ │
|
|
│ │ │ │──┐ │ │ │ │
|
|
│ │ │ │ │ │ │ │ │
|
|
│ │ │ │<─┘ │ │ │ │
|
|
│ │ │ │ │ │ │ │
|
|
│ │ │───────────────────>│ │ │ │
|
|
│ │ │ │ │ │ │ │
|
|
│ │ │ │ │ Post │ │ │
|
|
│ │ │ │ │ msg │ │ │
|
|
│ │ │ │ │──┐ │ │ │
|
|
│ │ │ │ │ │ │ │ │
|
|
│ │ │ │ │<─┘ │ │ │
|
|
│ │ │ │ │ │ │ │
|
|
│ │ │───────────────────────────>│ │ │
|
|
│ │ │ │ │ │ │ │
|
|
│ │ │ │ │ │ Post │ │
|
|
│ │ │ │ │ │ card │ │
|
|
│ │ │ │ │ │──┐ │ │
|
|
│ │ │ │ │ │ │ │ │
|
|
│ │ │ │ │ │<─┘ │ │
|
|
│ │ │ │ │ │ │ │
|
|
│ │ │ Log │ │ │ │ │
|
|
│ │ │ delivery │ │ │ │ │
|
|
│ │ │───┐ │ │ │ │ │
|
|
│ │ │ │ │ │ │ │ │
|
|
│ │ │<──┘ │ │ │ │ │
|
|
│ │ │ │ │ │ │ │
|
|
```
|
|
|
|
## Step-by-Step
|
|
|
|
### 1. Event Emission
|
|
|
|
Various services emit notification-triggering events:
|
|
|
|
```json
|
|
{
|
|
"event_type": "scan.complete",
|
|
"event_id": "evt-123456",
|
|
"timestamp": "2024-12-29T10:30:00Z",
|
|
"tenant_id": "acme-corp",
|
|
"payload": {
|
|
"scan_id": "scan-7f3a9b2c-...",
|
|
"image": "docker.io/library/nginx:1.25",
|
|
"verdict": "FAIL",
|
|
"violations": 3,
|
|
"severity_breakdown": {
|
|
"critical": 1,
|
|
"high": 2
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### 2. Notification Rule Matching
|
|
|
|
Notify service matches event against configured rules:
|
|
|
|
```yaml
|
|
# Notification Rules
|
|
rules:
|
|
- name: critical-scan-failure
|
|
description: Alert on critical vulnerabilities
|
|
event_type: scan.complete
|
|
conditions:
|
|
- field: payload.verdict
|
|
operator: eq
|
|
value: FAIL
|
|
- field: payload.severity_breakdown.critical
|
|
operator: gt
|
|
value: 0
|
|
channels:
|
|
- type: slack
|
|
config_ref: security-team-slack
|
|
urgency: high
|
|
- type: email
|
|
config_ref: security-leads
|
|
- type: pagerduty
|
|
config_ref: on-call-rotation
|
|
|
|
- name: daily-scan-summary
|
|
description: Daily summary of all scans
|
|
event_type: scheduled.daily_summary
|
|
channels:
|
|
- type: email
|
|
config_ref: security-distribution
|
|
```
|
|
|
|
### 3. Channel Routing
|
|
|
|
Notify determines which channels to activate:
|
|
|
|
| Rule Match | Channel | Priority |
|
|
|------------|---------|----------|
|
|
| critical-scan-failure | slack | HIGH |
|
|
| critical-scan-failure | email | HIGH |
|
|
| critical-scan-failure | pagerduty | HIGH |
|
|
|
|
### 4. Message Rendering
|
|
|
|
Each channel adapter renders the message in appropriate format:
|
|
|
|
#### Email Template
|
|
```html
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<body>
|
|
<h1>🚨 Critical Vulnerability Detected</h1>
|
|
<p>A scan has failed with critical vulnerabilities.</p>
|
|
|
|
<table>
|
|
<tr><th>Image</th><td>docker.io/library/nginx:1.25</td></tr>
|
|
<tr><th>Verdict</th><td style="color:red">FAIL</td></tr>
|
|
<tr><th>Critical</th><td>1</td></tr>
|
|
<tr><th>High</th><td>2</td></tr>
|
|
</table>
|
|
|
|
<p><a href="https://console.stellaops.local/scans/scan-7f3a9b2c-...">View Details</a></p>
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
#### Slack Block Kit
|
|
```json
|
|
{
|
|
"blocks": [
|
|
{
|
|
"type": "header",
|
|
"text": {
|
|
"type": "plain_text",
|
|
"text": "🚨 Critical Vulnerability Detected"
|
|
}
|
|
},
|
|
{
|
|
"type": "section",
|
|
"fields": [
|
|
{"type": "mrkdwn", "text": "*Image:*\n`nginx:1.25`"},
|
|
{"type": "mrkdwn", "text": "*Verdict:*\n:x: FAIL"},
|
|
{"type": "mrkdwn", "text": "*Critical:*\n1"},
|
|
{"type": "mrkdwn", "text": "*High:*\n2"}
|
|
]
|
|
},
|
|
{
|
|
"type": "actions",
|
|
"elements": [
|
|
{
|
|
"type": "button",
|
|
"text": {"type": "plain_text", "text": "View Details"},
|
|
"url": "https://console.stellaops.local/scans/scan-7f3a9b2c-..."
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
#### Teams Adaptive Card
|
|
```json
|
|
{
|
|
"type": "AdaptiveCard",
|
|
"version": "1.4",
|
|
"body": [
|
|
{
|
|
"type": "TextBlock",
|
|
"text": "🚨 Critical Vulnerability Detected",
|
|
"weight": "bolder",
|
|
"size": "large"
|
|
},
|
|
{
|
|
"type": "FactSet",
|
|
"facts": [
|
|
{"title": "Image", "value": "nginx:1.25"},
|
|
{"title": "Verdict", "value": "FAIL"},
|
|
{"title": "Critical", "value": "1"},
|
|
{"title": "High", "value": "2"}
|
|
]
|
|
}
|
|
],
|
|
"actions": [
|
|
{
|
|
"type": "Action.OpenUrl",
|
|
"title": "View Details",
|
|
"url": "https://console.stellaops.local/scans/scan-7f3a9b2c-..."
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### 5. Delivery
|
|
|
|
Each adapter delivers to its channel:
|
|
|
|
| Channel | Protocol | Retry Policy |
|
|
|---------|----------|--------------|
|
|
| Email | SMTP | 3 retries, exponential backoff |
|
|
| Slack | HTTPS POST | 3 retries, 1s/2s/4s |
|
|
| Teams | HTTPS POST | 3 retries, 1s/2s/4s |
|
|
| Webhook | HTTPS POST | 5 retries, configurable |
|
|
| PagerDuty | HTTPS POST | 5 retries, 2s/4s/8s/16s/32s |
|
|
|
|
### 6. Delivery Logging
|
|
|
|
Notify logs delivery status to `notify.delivery_log`:
|
|
|
|
```json
|
|
{
|
|
"delivery_id": "dlv-789abc",
|
|
"event_id": "evt-123456",
|
|
"channel": "slack",
|
|
"status": "delivered",
|
|
"attempts": 1,
|
|
"delivered_at": "2024-12-29T10:30:02Z",
|
|
"response": {"ok": true}
|
|
}
|
|
```
|
|
|
|
## Data Contracts
|
|
|
|
### Notification Event Schema
|
|
|
|
```typescript
|
|
interface NotificationEvent {
|
|
event_type: string;
|
|
event_id: string;
|
|
timestamp: string;
|
|
tenant_id: string;
|
|
payload: Record<string, unknown>;
|
|
metadata?: {
|
|
source_service: string;
|
|
correlation_id?: string;
|
|
};
|
|
}
|
|
```
|
|
|
|
### Channel Configuration Schema
|
|
|
|
```typescript
|
|
interface ChannelConfig {
|
|
type: 'email' | 'slack' | 'teams' | 'webhook' | 'pagerduty';
|
|
name: string;
|
|
enabled: boolean;
|
|
config: EmailConfig | SlackConfig | TeamsConfig | WebhookConfig | PagerDutyConfig;
|
|
}
|
|
|
|
interface EmailConfig {
|
|
smtp_host: string;
|
|
smtp_port: number;
|
|
smtp_user?: string;
|
|
smtp_password?: string;
|
|
smtp_tls: boolean;
|
|
from_address: string;
|
|
to_addresses: string[];
|
|
}
|
|
|
|
interface SlackConfig {
|
|
webhook_url: string;
|
|
channel?: string;
|
|
username?: string;
|
|
icon_emoji?: string;
|
|
}
|
|
|
|
interface WebhookConfig {
|
|
url: string;
|
|
method: 'POST' | 'PUT';
|
|
headers?: Record<string, string>;
|
|
auth?: {
|
|
type: 'basic' | 'bearer' | 'api_key';
|
|
credentials: string;
|
|
};
|
|
}
|
|
```
|
|
|
|
## Event Types
|
|
|
|
| Event Type | Source | Description |
|
|
|------------|--------|-------------|
|
|
| `scan.complete` | Scanner | Scan finished with results |
|
|
| `scan.failed` | Scanner | Scan execution failed |
|
|
| `policy.violation` | Policy | Policy rule triggered |
|
|
| `advisory.new` | Concelier | New advisory ingested |
|
|
| `advisory.update` | Concelier | Advisory modified |
|
|
| `vex.issued` | VexLens | New VEX statement |
|
|
| `exception.expiring` | Policy | Exception about to expire |
|
|
| `scheduled.daily_summary` | Scheduler | Daily digest |
|
|
| `scheduled.weekly_report` | Scheduler | Weekly report |
|
|
|
|
## Error Handling
|
|
|
|
| Error | Recovery |
|
|
|-------|----------|
|
|
| SMTP connection failed | Retry with backoff, queue for later |
|
|
| Slack webhook 429 | Respect Retry-After header |
|
|
| Teams connector 502 | Retry up to 3 times |
|
|
| Webhook timeout | Retry with increased timeout |
|
|
| Invalid recipient | Skip recipient, log error |
|
|
|
|
## Observability
|
|
|
|
### Metrics
|
|
|
|
| Metric | Type | Labels |
|
|
|--------|------|--------|
|
|
| `notify_events_received_total` | Counter | `event_type` |
|
|
| `notify_deliveries_total` | Counter | `channel`, `status` |
|
|
| `notify_delivery_latency_ms` | Histogram | `channel` |
|
|
| `notify_retries_total` | Counter | `channel`, `reason` |
|
|
|
|
### Key Log Events
|
|
|
|
| Event | Level | Fields |
|
|
|-------|-------|--------|
|
|
| `notify.event.received` | INFO | `event_type`, `event_id` |
|
|
| `notify.rule.matched` | DEBUG | `rule_name`, `channels` |
|
|
| `notify.delivery.attempt` | DEBUG | `channel`, `attempt` |
|
|
| `notify.delivery.success` | INFO | `channel`, `delivery_id` |
|
|
| `notify.delivery.failed` | WARN | `channel`, `error` |
|
|
|
|
## Related Flows
|
|
|
|
- [Scan Submission Flow](02-scan-submission-flow.md) - Triggers scan notifications
|
|
- [Advisory Drift Re-scan Flow](11-advisory-drift-rescan-flow.md) - Advisory notifications
|
|
- [Exception Approval Workflow](17-exception-approval-workflow.md) - Exception notifications
|