Understanding DevSecOps
My DevSecOps Philosophy
My Real-World GitLab DevSecOps Pipeline
The .gitlab-ci.yml That Changed Our Security Posture
.gitlab-ci.yml That Changed Our Security Posturestages:
- pre-build
- build
- test
- security
- deploy-staging
- integration-test
- deploy-production
- post-deployment
variables:
DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_CERTDIR: "/certs"
DOCKER_TLS_VERIFY: 1
DOCKER_CERT_PATH: "$DOCKER_TLS_CERTDIR/client"
# Pre-build stage: Check for secrets early
secret-detection:
stage: pre-build
image: registry.gitlab.com/gitlab-org/security-products/secret-detection:latest
script:
- /analyzer run
artifacts:
reports:
secret_detection: gl-secret-detection-report.json
rules:
- if: $CI_COMMIT_BRANCH
# Build stage with built-in security
build-image:
stage: build
image: docker:20.10.16
services:
- docker:20.10.16-dind
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
rules:
- if: $CI_COMMIT_BRANCH
# Security stage with multiple parallel scans
sast:
stage: security
image: registry.gitlab.com/gitlab-org/security-products/sast:latest
script:
- /analyzer run
artifacts:
reports:
sast: gl-sast-report.json
rules:
- if: $CI_COMMIT_BRANCH
dependency-scanning:
stage: security
image: registry.gitlab.com/gitlab-org/security-products/dependency-scanning:latest
script:
- /analyzer run
artifacts:
reports:
dependency_scanning: gl-dependency-scanning-report.json
rules:
- if: $CI_COMMIT_BRANCH
container-scanning:
stage: security
image: registry.gitlab.com/gitlab-org/security-products/container-scanning:latest
variables:
DOCKER_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
script:
- /analyzer run
artifacts:
reports:
container_scanning: gl-container-scanning-report.json
rules:
- if: $CI_COMMIT_BRANCH
# Deploy to staging only if all security checks pass
deploy-to-staging:
stage: deploy-staging
image: alpine:latest
script:
- echo "Deploying to staging environment..."
- apk add --no-cache curl
- curl -X POST -F token=$STAGING_DEPLOY_TOKEN -F ref=master $STAGING_DEPLOY_HOOK
rules:
- if: $CI_COMMIT_BRANCH == "master"
# Dynamic scanning against the staging environment
dast:
stage: integration-test
image: registry.gitlab.com/gitlab-org/security-products/dast:latest
variables:
DAST_WEBSITE: https://staging.example.com
script:
- /analyzer run
artifacts:
reports:
dast: gl-dast-report.json
rules:
- if: $CI_COMMIT_BRANCH == "master"
# Production deployment with security gates
deploy-to-production:
stage: deploy-production
image: alpine:latest
script:
- echo "Deploying to production environment..."
- apk add --no-cache curl
- curl -X POST -F token=$PROD_DEPLOY_TOKEN -F ref=master $PROD_DEPLOY_HOOK
rules:
- if: $CI_COMMIT_BRANCH == "master"
when: manual
needs:
- job: dast
artifacts: truePractical Lessons I've Learned
The ROI of My DevSecOps Implementation
Last updated