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:
2025-10-19 18:36:22 +03:00
parent 7e2fa0a42a
commit 5ce40d2eeb
966 changed files with 91038 additions and 1850 deletions

View File

@@ -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, adminonly.)