mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-10 10:49:35 +00:00
feat(security): enforce scan policies as a pre-deploy gate (#719)
Policies with block_on_deploy=1 now scan every stack image before docker compose up runs and reject the deploy with HTTP 409 on violation. The UI opens a dialog listing offending images; admins can override per deploy with ?ignorePolicy=true, and every bypass is recorded in the audit log with the originating route, actor, policy, and image list. When Trivy is not installed on the target node the gate fails open with a warning notification, so teams are never locked out by tooling state. Post-deploy and scheduled scans still evaluate matching policies and dispatch warnings on violations to surface drift on long-running stacks. Public API additions: policy and suppression CRUD under /api/security, plus the documented 409 block-response shape on all deploy paths.
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
---
|
||||
title: "Deploy Enforcement"
|
||||
description: "Block deploys that violate a scan policy before docker compose up runs, with an admin bypass path and full audit trail."
|
||||
---
|
||||
|
||||
Deploy enforcement is the pre-flight half of Sencho's vulnerability workflow. When a [scan policy](/features/vulnerability-scanning#scan-policies) with **Block on deploy** enabled matches a stack, Sencho scans every image referenced by the stack's compose file before starting any container. If any image meets or exceeds the policy's severity threshold, the deploy is rejected and the compose stack is never brought up. Detection always continues post-deploy (and on schedule), so images that develop new vulnerabilities after the initial deploy still surface through alerts.
|
||||
|
||||
<Note>
|
||||
Deploy enforcement requires a **Skipper** or **Admiral** license. Policies on Community are evaluation-only and cannot block deploys.
|
||||
</Note>
|
||||
|
||||
## How enforcement runs
|
||||
|
||||
Sencho applies the pre-flight gate on every code path that starts a compose stack:
|
||||
|
||||
- **Deploy** and **Redeploy** from the stack page
|
||||
- **Update** (re-pull + redeploy)
|
||||
- **Deploy from Git source** and **Apply** on a managed git source
|
||||
- **Template deploy** from the App Store
|
||||
- **Recreate** from the stack actions menu
|
||||
|
||||
On every one of these actions, Sencho:
|
||||
|
||||
1. Looks up the most specific enabled policy that matches the stack on the target node.
|
||||
2. If the policy has **Block on deploy** off, lets the deploy proceed and evaluates the post-deploy scan against the policy for alerting.
|
||||
3. If **Block on deploy** is on, enumerates the stack's images with `docker compose config --images`, runs a pre-flight Trivy scan against each one, and compares the highest severity in each scan against the policy threshold.
|
||||
4. If every image is below the threshold, the deploy proceeds exactly as before. A post-deploy drift scan still runs in the background.
|
||||
5. If any image violates the threshold, the deploy is rejected with HTTP `409 Conflict` and the stack never starts. The UI opens a dialog listing the offending images.
|
||||
|
||||
Pre-flight scans use the same 24-hour digest cache as on-demand scans, so the second deploy of the same image does not pay the full scan time.
|
||||
|
||||
## What the block dialog shows
|
||||
|
||||
<Frame>
|
||||
<img src="/images/vulnerability-scanning/deploy-blocked-dialog.png" alt="AlertDialog shown when a deploy is blocked, listing the policy name and the offending images with severity chips" />
|
||||
</Frame>
|
||||
|
||||
The dialog shows:
|
||||
|
||||
- The policy that fired, with its max severity.
|
||||
- Every image that violated the threshold, with its severity chip and the counts of critical and high findings.
|
||||
- A **Close** button that dismisses the dialog without deploying.
|
||||
- A **Deploy anyway** button when the current user is an admin (see bypass below); the button is disabled and relabeled **Admin required to bypass** for non-admin roles.
|
||||
|
||||
Clicking a violation row's severity chip takes you to the scan details sheet for that image, where you can review every finding and decide whether to upgrade the base image or [suppress](/features/cve-suppressions) a specific CVE.
|
||||
|
||||
## Bypassing a block
|
||||
|
||||
Blocks can be overridden by admins on a per-deploy basis. The button is only visible when:
|
||||
|
||||
- The current user has the `admin` role.
|
||||
- The deploy came from the UI (the bypass flag is ignored when the caller role is not admin server-side, so non-admin sessions cannot forge the header).
|
||||
|
||||
Every bypass is recorded in the [Audit Log](/features/audit-log) with:
|
||||
|
||||
- `method` and `path` of the originating request (so you can tell whether the bypass came from a deploy, update, template, or git-apply call).
|
||||
- `username` of the admin who bypassed.
|
||||
- A `policy.bypass` summary with the stack name, policy name, violation count, and the list of image references that were bypassed.
|
||||
|
||||
API callers can pass `?ignorePolicy=true` on any deploy endpoint to request a bypass. The flag is only honored when the token's session resolves to an admin user; API tokens without admin scope cannot bypass a policy. See the [Security API reference](/api-reference/security#bypassing-a-block) for the exact request shape.
|
||||
|
||||
## Drift detection keeps running
|
||||
|
||||
Deploy enforcement prevents a new deploy from introducing known vulnerabilities at the front door. Long-running stacks whose images were clean at deploy time can still develop new CVEs as upstream feeds update. Two surfaces catch this:
|
||||
|
||||
- **Post-deploy scans** run automatically on every successful deploy (whether the pre-flight gate was tripped or not). When a post-deploy scan violates an enabled policy, Sencho dispatches a warning alert and the scan details sheet shows a policy-violation banner.
|
||||
- **[Scheduled fleet scans](/features/vulnerability-scanning#scheduled-fleet-scans)** re-scan every image on a node at a cron schedule you pick. Any scan that violates a matching policy produces the same warning alert and banner, so you never need to babysit running stacks.
|
||||
|
||||
Neither drift mechanism blocks, stops, or quarantines a running stack automatically. The gate is intentionally limited to deploy time.
|
||||
|
||||
## FAQ
|
||||
|
||||
**CI pipelines feel slower after I enabled a block policy.**
|
||||
|
||||
Only the first deploy of a new image pays the full Trivy runtime. Sencho caches scan results by image digest for 24 hours, so repeat deploys of the same image hit the cache in under a second. For greenfield CI where every deploy ships a new image tag, budget 30 to 120 seconds per image on the first deploy depending on image size and whether Trivy's vulnerability database is already seeded on the node.
|
||||
|
||||
**The gate let a deploy through even though I have a block policy.**
|
||||
|
||||
Check the following in order:
|
||||
|
||||
1. Open **Settings → Security** on the target node and confirm Trivy is installed. Sencho fails open when Trivy is missing, dispatching a warning alert instead of blocking. [Install Trivy](/operations/trivy-setup) to enforce the policy.
|
||||
2. Confirm the policy is enabled and the stack pattern matches the stack name. `prod-*` matches `prod-api` but not `production-api`. An empty pattern matches every stack on the node.
|
||||
3. Check the highest severity in the latest scan for each image. If no image reached the threshold, the gate correctly allowed the deploy.
|
||||
|
||||
**A deploy is blocked and I cannot bypass as a non-admin.**
|
||||
|
||||
Only users with the `admin` role can bypass a block. Ask an admin to review the violations and either bypass the single deploy, upgrade the base image, or suppress the offending CVE with an expiry.
|
||||
|
||||
**The block dialog mentions an image I do not recognize.**
|
||||
|
||||
Pre-flight enumerates images via `docker compose config --images`, which expands any `extends`, `env_file`, or variable substitution in the compose file. An image pulled by a dependency you did not author may appear in the list. Review the stack's compose file and confirm the reference.
|
||||
|
||||
**I want a block policy that only applies to a subset of my fleet.**
|
||||
|
||||
Combine two mechanisms: scope the policy to a specific node (policies scoped to a node win over global ones) and tighten the stack pattern glob. For example, a policy with `stack_pattern=prod-*` scoped to your production node fires only on `prod-*` stacks deployed to that node.
|
||||
@@ -129,7 +129,9 @@ Failures are typically transient (registry timeouts, missing credentials) and do
|
||||
Scan policies require a **Skipper** or **Admiral** license.
|
||||
</Note>
|
||||
|
||||
Policies let you define severity thresholds that the post-deploy scanner evaluates against. When a deploy's scan exceeds a policy's threshold, Sencho dispatches an alert. The policy's **Block on deploy** toggle controls the alert severity: warning when off, critical when on.
|
||||
Policies let you define severity thresholds that govern whether a stack can deploy at all. A policy with **Block on deploy** enabled runs a pre-flight scan on every image in the stack before `docker compose up` executes; if any image meets or exceeds the threshold, the deploy is rejected with a dialog listing the offending images. Policies with **Block on deploy** disabled still evaluate every post-deploy and scheduled scan, and dispatch warning alerts when the threshold is exceeded.
|
||||
|
||||
See [Deploy Enforcement](/features/deploy-enforcement) for the full pre-flight flow, admin bypass path, and audit-log behavior.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/vulnerability-scanning/security-settings.png" alt="Security section of Settings showing the scan policies list with add policy button" />
|
||||
@@ -144,7 +146,7 @@ Go to **Settings → Security** and click **Add Policy**.
|
||||
| **Name** | A descriptive label (e.g. "Production critical block"). |
|
||||
| **Stack pattern** | Optional glob against stack names (e.g. `prod-*`). Leave empty to match every stack. |
|
||||
| **Max severity** | The threshold. If a scan finds any vulnerability at or above this severity, the policy fires. |
|
||||
| **Block on deploy** | When enabled, policy violations are dispatched as critical (error) alerts. When disabled, they are dispatched as warnings. |
|
||||
| **Block on deploy** | When enabled, deploys are rejected before `docker compose up` runs if any image violates the threshold. When disabled, the policy still evaluates post-deploy and scheduled scans and dispatches warning alerts on violations. |
|
||||
| **Enabled** | Disabled policies are skipped during evaluation. |
|
||||
|
||||
### Policy scoping
|
||||
@@ -157,6 +159,41 @@ When multiple policies match a deploy, Sencho picks the most specific one:
|
||||
|
||||
Only one policy is evaluated per deploy; use a single tight pattern rather than overlapping policies for clarity.
|
||||
|
||||
### Example policies
|
||||
|
||||
**Block criticals and highs in production.** A tight gate that keeps known vulnerable base images out of your production fleet.
|
||||
|
||||
- **Name:** `prod-high-gate`
|
||||
- **Stack pattern:** `prod-*`
|
||||
- **Max severity:** `HIGH`
|
||||
- **Block on deploy:** On
|
||||
- **Enabled:** On
|
||||
|
||||
**Alert on criticals in staging, never block.** Lets engineers iterate without friction while still surfacing critical findings for triage.
|
||||
|
||||
- **Name:** `staging-critical-alert`
|
||||
- **Stack pattern:** `staging-*`
|
||||
- **Max severity:** `CRITICAL`
|
||||
- **Block on deploy:** Off
|
||||
- **Enabled:** On
|
||||
|
||||
### Creating a policy via the API
|
||||
|
||||
Policy CRUD endpoints are documented in the [Security API reference](/api-reference/security). A typical create call from CI looks like this:
|
||||
|
||||
```bash
|
||||
curl -X POST https://your-sencho-instance:3000/api/security/policies \
|
||||
-H "Authorization: Bearer YOUR_API_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "prod-high-gate",
|
||||
"stack_pattern": "prod-*",
|
||||
"max_severity": "HIGH",
|
||||
"block_on_deploy": 1,
|
||||
"enabled": 1
|
||||
}'
|
||||
```
|
||||
|
||||
## SBOM generation
|
||||
|
||||
<Note>
|
||||
@@ -312,6 +349,14 @@ Enable **Developer Mode** under **Settings → Developer** and trigger the faili
|
||||
|
||||
When a post-deploy scan fails for a specific image (for example because Trivy could not resolve a private registry pull), Sencho dispatches a warning-level alert through your configured notification channels. The deploy itself is never blocked by a scan failure.
|
||||
|
||||
### A deploy was blocked by a policy I did not expect
|
||||
|
||||
The block dialog names the policy that fired and lists every image that violated the threshold. Open **Settings → Security → Scan Policies** and review the matching policy: check the stack pattern glob and the max severity. If the policy should not apply, tighten the pattern (for example `staging-*` instead of `*`) or turn **Block on deploy** off to keep the evaluation in alert-only mode. Admins can also bypass a single deploy with the **Deploy anyway** button; every bypass is recorded in the [Audit Log](/features/audit-log) with the actor, policy, and violation list.
|
||||
|
||||
### Trivy is not installed and a deploy with a block policy went through
|
||||
|
||||
Sencho fails open when Trivy is not installed on the target node, so users are never locked out by tooling state. A warning alert is dispatched through your configured notification channels with the message `Pre-deploy scan for "<stack>" skipped: Trivy not installed on this node`. Install Trivy from **Settings → Security** to enforce the policy; see [Installing Trivy](/operations/trivy-setup) for options.
|
||||
|
||||
### Compare button is disabled
|
||||
|
||||
Scan comparison is a Skipper feature; on Community, the Compare button stays disabled with a tooltip explaining the upgrade path. If your license is Skipper or Admiral, make sure you have ticked exactly two completed scans: selecting zero, one, or three scans leaves the button disabled. If you have only one scan for an image, trigger a second scan from the Resources Hub (or wait for a scheduled scan), then return to the Scan history page and tick both.
|
||||
|
||||
Reference in New Issue
Block a user