DevSecOps on Scaleway: Wiring SAST and DAST Into a Pipeline That Survives Production
Sécurité

DevSecOps on Scaleway: Wiring SAST and DAST Into a Pipeline That Survives Production

June 28, 20267 min readScalewayDevSecOpsSAST

How to build a complete DevSecOps chain on Scaleway: secret scanning, SAST, SCA, IaC checks and DAST on ephemeral environments, with a pipeline you can copy.

Why DevSecOps looks different on Scaleway

Teams that move to Scaleway usually do so for two reasons: European data residency and a pricing model that stays predictable. What they often discover afterwards is that the security tooling ecosystem is thinner than on the hyperscalers. There is no managed equivalent of Amazon Inspector or Google Security Command Center that will silently scan your images and workloads for you. Scaleway gives you excellent primitives — IAM with applications and policies, Secret Manager, Private Networks, Audit Trail, Cockpit for observability, Kapsule for managed Kubernetes — but the assembly is yours.

That is not a drawback. It forces the security controls into the place where they belong: the delivery pipeline, versioned alongside the code. This article describes a concrete DevSecOps chain built around SAST, SCA, IaC scanning and DAST on Scaleway, with the trade-offs that matter when you actually have to run it every day.

The four families of scanning, and what each is actually good at

Before wiring anything into CI, be honest about what each class of tool detects. Most failed DevSecOps rollouts come from expecting SAST to find business logic flaws, or DAST to catch a vulnerable transitive dependency.

ControlWhat it findsWhere it runsNoise levelBlocking policy
Secret scanningCommitted API keys, Scaleway secret keys, tokensPre-commit + every pushVery lowBlock immediately
SASTInjection patterns, unsafe deserialization, weak crypto, path traversalMerge requestMedium to highBlock on new HIGH findings only
SCAKnown CVEs in dependencies and base imagesMR + nightly rescanLow, but high volumeBlock on fixable HIGH/CRITICAL
IaC scanningPublic security groups, unencrypted volumes, over-broad IAM policiesMR on the infra repoLowBlock on HIGH
DASTAuth bypass, misconfigured headers, exposed endpoints, real-world exploitabilityEphemeral review envMediumReport first, block later

Note the last column. A pipeline that blocks on everything from day one gets bypassed within a fortnight. Start with secrets and IaC as hard gates — they are cheap and almost noise-free — then progressively harden SAST and SCA using a diff-based approach where only findings introduced by the merge request cause a failure.

SAST that developers do not disable

Semgrep has become the pragmatic default for polyglot codebases: rules are readable YAML, the community ruleset covers most languages, and semgrep ci natively performs a diff scan against the target branch. SonarQube remains relevant if you already run it for code quality and want a single quality gate, but its container needs a database and a Scaleway Instance or a Kapsule deployment to host it — a real operational cost. For narrower stacks, language-native tools (gosec, Bandit, Brakeman) are faster and produce fewer false positives.

Two rules make SAST sustainable. First, output SARIF and let your forge render findings inline in the merge request; a security report nobody opens is dead weight. Second, keep suppressions in the code (// nosemgrep: rule-id — reason) rather than in a central exclusion file, so that a reviewer sees the justification next to the line it protects.

Dependencies, images and the Scaleway Container Registry

Software composition analysis is where you get the highest return per minute of CI. Trivy handles both filesystem scanning (lockfiles, licences) and image scanning, and Grype is a solid alternative. Scan the image after the build but before the push, so a vulnerable artefact never reaches your Scaleway registry namespace at rg.fr-par.scw.cloud.

Two practices matter more than the choice of scanner. Pin base images by digest rather than tag, so a rebuild is reproducible and a scan result stays meaningful. And generate an SBOM at build time, store it as an artefact, and rescan it nightly: a container that was clean on Monday is not clean on Friday, and CVE disclosure does not wait for your next release.

Terraform and IaC scanning against Scaleway resources

Checkov and Trivy's config scanner both understand the Scaleway Terraform provider well enough to catch the classic mistakes: an scaleway_instance_security_group with an inbound rule open to 0.0.0.0/0 on port 22, an object storage bucket left public, a Kapsule pool exposed on a public network when a Private Network would do. Combine that with an IAM policy review: Scaleway's model of applications, groups and policies makes least privilege easy, but the default reflex of granting ProjectManager to a CI application is depressingly common. Give the pipeline application only ContainerRegistryFullAccess and KubernetesFullAccess scoped to the right project, and rotate its API key.

DAST needs a real environment — make it ephemeral

DAST is where most pipelines stop, because it requires a running application. On Scaleway you have two good options. For containerised HTTP services, Serverless Containers can spin up a per-merge-request deployment in seconds and scale to zero afterwards, which keeps the cost of review environments negligible. For anything with stateful dependencies, deploy a namespace per merge request on Kapsule and tear it down on merge.

Then run OWASP ZAP against it. The baseline scan is a passive crawl that takes a couple of minutes and is safe to run on every MR. The full active scan takes far longer and should run nightly against a dedicated staging environment. If your service is an API, use zap-api-scan.py with your OpenAPI specification: it gives ZAP the endpoint inventory it cannot discover by crawling, and dramatically improves coverage. Nuclei is a useful complement for templated checks on exposed infrastructure — misconfigured headers, default panels, known CVEs on the edge.

The hard part of DAST is authentication. Budget time for it: a ZAP context file with a login script and a session token, or a pre-issued JWT injected via a header. Without it, you are scanning your login page and nothing else.

A pipeline you can copy

stages: [secrets, sast, build, review, dast]

variables:
  REGISTRY: rg.fr-par.scw.cloud/my-namespace
  IMAGE: $REGISTRY/api:$CI_COMMIT_SHORT_SHA

gitleaks:
  stage: secrets
  image: zricethezav/gitleaks:latest
  script: ["gitleaks detect --source . --redact --exit-code 1"]

semgrep:
  stage: sast
  image: semgrep/semgrep:latest
  script:
    - semgrep ci --sarif --output semgrep.sarif
  artifacts:
    reports: { sast: semgrep.sarif }

checkov:
  stage: sast
  image: bridgecrew/checkov:latest
  script:
    - checkov -d infra/ --framework terraform --compact --hard-fail-on HIGH

build-and-scan:
  stage: build
  image: quay.io/buildah/stable
  script:
    - buildah bud -t $IMAGE .
    - trivy image --severity HIGH,CRITICAL --ignore-unfixed --exit-code 1 $IMAGE
    - trivy image --format cyclonedx -o sbom.json $IMAGE
    - echo "$SCW_SECRET_KEY" | buildah login -u nologin --password-stdin rg.fr-par.scw.cloud
    - buildah push $IMAGE
    - cosign sign --key env://COSIGN_KEY $IMAGE
  artifacts:
    paths: [sbom.json]

deploy-review:
  stage: review
  script:
    - kubectl create ns review-$CI_MERGE_REQUEST_IID --dry-run=client -o yaml | kubectl apply -f -
    - helm upgrade --install api ./chart -n review-$CI_MERGE_REQUEST_IID --set image=$IMAGE
  environment:
    name: review/$CI_MERGE_REQUEST_IID
    url: https://mr-$CI_MERGE_REQUEST_IID.review.example.com
    on_stop: stop-review

zap-baseline:
  stage: dast
  image: ghcr.io/zaproxy/zaproxy:stable
  script:
    - zap-api-scan.py -t $CI_ENVIRONMENT_URL/openapi.json -f openapi -r zap.html -I
  artifacts:
    paths: [zap.html]
  allow_failure: true

The -I flag and allow_failure: true on the DAST job are deliberate: you observe before you block. Once the baseline is stable and false positives are filtered in a ZAP rules file, flip it to blocking.

Closing the loop at runtime

Scanning stops at deployment; attacks do not. On Kapsule, three controls give disproportionate value. Enable Pod Security Admission in restricted mode on application namespaces, which kills privileged containers and host mounts by default. Apply default-deny NetworkPolicies so that a compromised pod cannot pivot laterally — Kapsule's CNI supports them natively. And ship audit logs and Falco events into Cockpit, where Scaleway already aggregates your metrics and logs into managed Grafana, Loki and Prometheus endpoints.

Complete the picture with Scaleway Secret Manager instead of Kubernetes Secrets in plain etcd, Audit Trail for API-level traceability of who changed what in your project, and short-lived API keys for CI applications. None of this is exotic; the discipline is in doing it consistently.

What actually makes it stick

The technical assembly above takes a few days. Making it survive a year takes three organisational decisions. Define an explicit SLA per severity — critical fixed in days, high in weeks — and track the backlog like any other technical debt. Give teams a documented exception process with an expiry date, otherwise they will invent an undocumented one. And measure the pipeline itself: if the security stages add more than a couple of minutes to a merge request, engineers will find a way around them, and no amount of policy will change that.

DevSecOps on Scaleway is less turnkey than on AWS or GCP, but the result is more portable and easier to audit: every control lives in your repositories, not in a provider console. For teams that chose Scaleway for sovereignty reasons in the first place, that is exactly the right trade-off.

← Back to blog