feat: Initialize Zastava Webhook service with TLS and Authority authentication
- Added Program.cs to set up the web application with Serilog for logging, health check endpoints, and a placeholder admission endpoint. - Configured Kestrel server to use TLS 1.3 and handle client certificates appropriately. - Created StellaOps.Zastava.Webhook.csproj with necessary dependencies including Serilog and Polly. - Documented tasks in TASKS.md for the Zastava Webhook project, outlining current work and exit criteria for each task.
This commit is contained in:
@@ -36,6 +36,25 @@ src/
|
||||
|
||||
**Dependencies**: Authority (OpToks; DPoP/mTLS), MongoDB, Redis/NATS (bus), HTTP egress to Slack/Teams/Webhooks, SMTP relay for Email.
|
||||
|
||||
> **Configuration.** Notify.WebService bootstraps from `notify.yaml` (see `etc/notify.yaml.sample`). Use `storage.driver: mongo` with a production connection string; the optional `memory` driver exists only for tests. Authority settings follow the platform defaults—when running locally without Authority, set `authority.enabled: false` and supply `developmentSigningKey` so JWTs can be validated offline.
|
||||
|
||||
> **Plug-ins.** All channel connectors are packaged under `<baseDirectory>/plugins/notify`. The ordered load list must start with Slack/Teams before Email/Webhook so chat-first actions are registered deterministically for Offline Kit bundles:
|
||||
>
|
||||
> ```yaml
|
||||
> plugins:
|
||||
> baseDirectory: "/var/opt/stellaops"
|
||||
> directory: "plugins/notify"
|
||||
> orderedPlugins:
|
||||
> - StellaOps.Notify.Connectors.Slack
|
||||
> - StellaOps.Notify.Connectors.Teams
|
||||
> - StellaOps.Notify.Connectors.Email
|
||||
> - StellaOps.Notify.Connectors.Webhook
|
||||
> ```
|
||||
>
|
||||
> The Offline Kit job simply copies the `plugins/notify` tree into the air-gapped bundle; the ordered list keeps connector manifests stable across environments.
|
||||
|
||||
> **Authority clients.** Register two OAuth clients in StellaOps Authority: `notify-web-dev` (audience `notify.dev`) for development and `notify-web` (audience `notify`) for staging/production. Both require `notify.read` and `notify.admin` scopes and use DPoP-bound client credentials (`client_secret` in the samples). Reference entries live in `etc/authority.yaml.sample`, with placeholder secrets under `etc/secrets/notify-web*.secret.example`.
|
||||
|
||||
---
|
||||
|
||||
## 2) Responsibilities
|
||||
@@ -81,10 +100,32 @@ Notify subscribes to the **internal event bus** (produced by services, escaped J
|
||||
* `scanner.report.ready`:
|
||||
|
||||
```json
|
||||
{ "verdict":"fail|warn|pass",
|
||||
"delta": { "newCritical":1, "newHigh":2, "kev":["CVE-2025-..."] },
|
||||
"topFindings":[{"purl":"pkg:rpm/openssl","vulnId":"CVE-2025-...","severity":"critical"}],
|
||||
"links":{"ui":"https://ui/...","rekor":"https://rekor/..."} }
|
||||
{
|
||||
"reportId": "report-3def...",
|
||||
"verdict": "fail",
|
||||
"summary": {"total": 12, "blocked": 2, "warned": 3, "ignored": 5, "quieted": 2},
|
||||
"delta": {"newCritical": 1, "kev": ["CVE-2025-..."]},
|
||||
"links": {"ui": "https://ui/.../reports/report-3def...", "rekor": "https://rekor/..."},
|
||||
"dsse": { "...": "..." },
|
||||
"report": { "...": "..." }
|
||||
}
|
||||
```
|
||||
|
||||
Payload embeds both the canonical report document and the DSSE envelope so connectors, Notify, and UI tooling can reuse the signed bytes without re-serialising.
|
||||
|
||||
* `scanner.scan.completed`:
|
||||
|
||||
```json
|
||||
{
|
||||
"reportId": "report-3def...",
|
||||
"digest": "sha256:...",
|
||||
"verdict": "fail",
|
||||
"summary": {"total": 12, "blocked": 2, "warned": 3, "ignored": 5, "quieted": 2},
|
||||
"delta": {"newCritical": 1, "kev": ["CVE-2025-..."]},
|
||||
"policy": {"revisionId": "rev-42", "digest": "27d2..."},
|
||||
"findings": [{"id": "finding-1", "severity": "Critical", "cve": "CVE-2025-...", "reachability": "runtime"}],
|
||||
"dsse": { "...": "..." }
|
||||
}
|
||||
```
|
||||
|
||||
* `zastava.admission`:
|
||||
@@ -195,6 +236,8 @@ public interface INotifyConnector {
|
||||
|
||||
## 7) Data model (Mongo)
|
||||
|
||||
Canonical JSON Schemas for rules/channels/events live in `docs/notify/schemas/`. Sample payloads intended for tests/UI mock responses are captured in `docs/notify/samples/`.
|
||||
|
||||
**Database**: `notify`
|
||||
|
||||
* `rules`
|
||||
@@ -240,6 +283,14 @@ public interface INotifyConnector {
|
||||
|
||||
Base path: `/api/v1/notify` (Authority OpToks; scopes: `notify.admin` for write, `notify.read` for view).
|
||||
|
||||
*All* REST calls require the tenant header `X-StellaOps-Tenant` (matches the canonical `tenantId` stored in Mongo). Payloads are normalised via `NotifySchemaMigration` before persistence to guarantee schema version pinning.
|
||||
|
||||
Authentication today is stubbed with Bearer tokens (`Authorization: Bearer <token>`). When Authority wiring lands, this will switch to OpTok validation + scope enforcement, but the header contract will remain the same.
|
||||
|
||||
Service configuration exposes `notify:auth:*` keys (issuer, audience, signing key, scope names) so operators can wire the Authority JWKS or (in dev) a symmetric test key. `notify:storage:*` keys cover Mongo URI/database/collection overrides. Both sets are required for the new API surface.
|
||||
|
||||
Internal tooling can hit `/internal/notify/<entity>/normalize` to upgrade legacy JSON and return canonical output used in the docs fixtures.
|
||||
|
||||
* **Channels**
|
||||
|
||||
* `POST /channels` | `GET /channels` | `GET /channels/{id}` | `PATCH /channels/{id}` | `DELETE /channels/{id}`
|
||||
@@ -253,14 +304,18 @@ Base path: `/api/v1/notify` (Authority OpToks; scopes: `notify.admin` for write,
|
||||
|
||||
* **Deliveries**
|
||||
|
||||
* `GET /deliveries?tenant=...&since=...` → list
|
||||
* `POST /deliveries` → ingest worker delivery state (idempotent via `deliveryId`).
|
||||
* `GET /deliveries?since=...&status=...&limit=...` → list (most recent first)
|
||||
* `GET /deliveries/{id}` → detail (redacted body + metadata)
|
||||
* `POST /deliveries/{id}/retry` → force retry (admin)
|
||||
* `POST /deliveries/{id}/retry` → force retry (admin, future sprint)
|
||||
|
||||
* **Admin**
|
||||
|
||||
* `GET /stats` (per tenant counts, last hour/day)
|
||||
* `GET /healthz|readyz` (liveness)
|
||||
* `POST /locks/acquire` | `POST /locks/release` – worker coordination primitives (short TTL).
|
||||
* `POST /digests` | `GET /digests/{actionKey}` | `DELETE /digests/{actionKey}` – manage open digest windows.
|
||||
* `POST /audit` | `GET /audit?since=&limit=` – append/query structured audit trail entries.
|
||||
|
||||
**Ingestion**: workers do **not** expose public ingestion; they **subscribe** to the internal bus. (Optional `/events/test` for integration testing, admin‑only.)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user