DevSecOps

Securitatea Pipeline-ului DevSecOps: Integrarea Securitatii in CI/CD

Nicu Constantin
--11 min lectura
#devsecops#ci-cd#pipeline-security#sast#dast#container-security

Securitatea Pipeline-ului DevSecOps: Integrarea Securitatii in CI/CD

DevSecOps reprezinta o schimbare fundamentala in modul in care organizatiile abordeaza securitatea - trecerea de la un control final inainte de deployment la o practica integrata pe tot parcursul ciclului de viata al dezvoltarii software. Pipeline-ul CI/CD este locul unde aceasta integrare prinde forma concreta.

Acest ghid ofera abordari practice pentru construirea pipeline-urilor securizate.

Arhitectura Pipeline-ului Securizat

┌─────────────────────────────────────────────────────────────────────────────┐
│                         Secure CI/CD Pipeline                                │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  ┌──────────┐   ┌──────────┐   ┌──────────┐   ┌──────────┐   ┌──────────┐  │
│  │  Code    │ → │  Build   │ → │  Test    │ → │  Deploy  │ → │ Monitor  │  │
│  │  Commit  │   │          │   │          │   │          │   │          │  │
│  └────┬─────┘   └────┬─────┘   └────┬─────┘   └────┬─────┘   └────┬─────┘  │
│       │              │              │              │              │         │
│       ▼              ▼              ▼              ▼              ▼         │
│  ┌──────────┐   ┌──────────┐   ┌──────────┐   ┌──────────┐   ┌──────────┐  │
│  │ Pre-     │   │ SAST     │   │ DAST     │   │ Config   │   │ Runtime  │  │
│  │ commit   │   │ SCA      │   │ Security │   │ Audit    │   │ Security │  │
│  │ Hooks    │   │ Secrets  │   │ Tests    │   │ CSPM     │   │ RASP     │  │
│  └──────────┘   └──────────┘   └──────────┘   └──────────┘   └──────────┘  │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

Etapa 1: Securitate Pre-Commit

Configurare Git Hooks

# .pre-commit-config.yaml
repos:
  # Detectie secrete
  - repo: https://github.com/Yelp/detect-secrets
    rev: v1.4.0
    hooks:
      - id: detect-secrets
        args: ['--baseline', '.secrets.baseline']
 
  # Scanare credentiale
  - repo: https://github.com/trufflesecurity/trufflehog
    rev: v3.63.0
    hooks:
      - id: trufflehog
        args: ['--only-verified']
 
  # Linting de securitate
  - repo: https://github.com/PyCQA/bandit
    rev: 1.7.5
    hooks:
      - id: bandit
        args: ['-r', 'src/', '-ll']
 
  # Linting Dockerfile
  - repo: https://github.com/hadolint/hadolint
    rev: v2.12.0
    hooks:
      - id: hadolint-docker
        args: ['--ignore', 'DL3008']
 
  # Securitate Terraform
  - repo: https://github.com/antonbabenko/pre-commit-terraform
    rev: v1.83.5
    hooks:
      - id: terraform_tfsec
      - id: terraform_checkov
 
  # Linting YAML (pentru manifeste K8s)
  - repo: https://github.com/adrienverge/yamllint
    rev: v1.32.0
    hooks:
      - id: yamllint
        args: ['-c', '.yamllint.yml']

Hook-uri Custom de Securitate

#!/bin/bash
# .git/hooks/pre-commit
 
echo "Se ruleaza verificarile de securitate..."
 
# Verificare pattern-uri de secrete hardcodate
SECRET_PATTERNS=(
    'password\s*=\s*["\047][^"\047]+'
    'api[_-]?key\s*=\s*["\047][^"\047]+'
    'secret\s*=\s*["\047][^"\047]+'
    'AWS[A-Z0-9]{16,}'
    'AKIA[A-Z0-9]{16}'
)
 
for pattern in "${SECRET_PATTERNS[@]}"; do
    if git diff --cached --name-only | xargs grep -lE "$pattern" 2>/dev/null; then
        echo "EROARE: Secret potential detectat cu pattern-ul: $pattern"
        echo "Te rugam sa elimini secretele inainte de commit"
        exit 1
    fi
done
 
# Verificare tipuri de fisiere sensibile
SENSITIVE_EXTENSIONS=(.pem .key .p12 .pfx .env .env.local)
for ext in "${SENSITIVE_EXTENSIONS[@]}"; do
    if git diff --cached --name-only | grep -E "\\${ext}$"; then
        echo "EROARE: Tip de fisier sensibil detectat: $ext"
        echo "Ia in considerare utilizarea unui sistem de management al secretelor"
        exit 1
    fi
done
 
echo "Verificarile de securitate pre-commit au trecut"

Etapa 2: Securitatea Build-ului

Pipeline Securizat GitHub Actions

# .github/workflows/secure-pipeline.yml
name: Secure CI/CD Pipeline
 
on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]
 
permissions:
  contents: read
  security-events: write
  actions: read
 
jobs:
  security-scan:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
 
      # SAST - Static Application Security Testing
      - name: Run SAST (Semgrep)
        uses: returntocorp/semgrep-action@v1
        with:
          config: >-
            p/security-audit
            p/secrets
            p/owasp-top-ten
          generateSarif: true
 
      - name: Upload SAST results
        uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: semgrep.sarif
 
      # SCA - Software Composition Analysis
      - name: Run SCA (Trivy)
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'fs'
          scan-ref: '.'
          format: 'sarif'
          output: 'trivy-sca.sarif'
          severity: 'CRITICAL,HIGH'
 
      - name: Upload SCA results
        uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: trivy-sca.sarif
 
      # Scanare secrete
      - name: Scan for secrets
        uses: trufflesecurity/trufflehog@main
        with:
          path: ./
          base: ${{ github.event.repository.default_branch }}
          head: HEAD
 
      # Conformitate licente
      - name: Check licenses
        run: |
          npm install -g license-checker
          license-checker --failOn 'GPL;AGPL' --summary
 
  build-and-scan:
    needs: security-scan
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
 
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
 
      - name: Build container image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: false
          load: true
          tags: app:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
 
      # Scanare imagine container
      - name: Scan container image
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: 'app:${{ github.sha }}'
          format: 'sarif'
          output: 'trivy-container.sarif'
          severity: 'CRITICAL,HIGH'
          exit-code: '1'
 
      - name: Upload container scan results
        uses: github/codeql-action/upload-sarif@v2
        if: always()
        with:
          sarif_file: trivy-container.sarif
 
      # Generare SBOM
      - name: Generate SBOM
        uses: anchore/sbom-action@v0
        with:
          image: app:${{ github.sha }}
          format: spdx-json
          output-file: sbom.spdx.json
 
      - name: Upload SBOM
        uses: actions/upload-artifact@v3
        with:
          name: sbom
          path: sbom.spdx.json
 
  security-tests:
    needs: build-and-scan
    runs-on: ubuntu-latest
    services:
      app:
        image: app:${{ github.sha }}
        ports:
          - 8080:8080
    steps:
      # DAST - Dynamic Application Security Testing
      - name: Run DAST (OWASP ZAP)
        uses: zaproxy/action-baseline@v0.9.0
        with:
          target: 'http://localhost:8080'
          rules_file_name: '.zap/rules.tsv'
          cmd_options: '-a'
 
      - name: Upload DAST results
        uses: actions/upload-artifact@v3
        with:
          name: zap-report
          path: report_html.html
 
      # Testare securitate API
      - name: Run API security tests
        run: |
          npm install -g @stoplight/spectral-cli
          spectral lint openapi.yaml --ruleset .spectral.yml
 
  deploy-staging:
    needs: security-tests
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - name: Deploy to staging
        run: |
          # Comenzi de deployment aici
          echo "Deploying to staging..."
 
      # Scanare securitate infrastructura
      - name: Scan infrastructure config
        uses: bridgecrewio/checkov-action@v12
        with:
          directory: ./terraform
          framework: terraform
          output_format: sarif
          output_file_path: checkov.sarif
 
      - name: Upload infrastructure scan
        uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: checkov.sarif

Pipeline Securizat GitLab CI

# .gitlab-ci.yml
stages:
  - security-scan
  - build
  - test
  - deploy
 
variables:
  DOCKER_DRIVER: overlay2
  SECURE_LOG_LEVEL: info
 
# Template-uri de scanare de securitate
include:
  - template: Security/SAST.gitlab-ci.yml
  - template: Security/Secret-Detection.gitlab-ci.yml
  - template: Security/Dependency-Scanning.gitlab-ci.yml
  - template: Security/Container-Scanning.gitlab-ci.yml
  - template: Security/License-Scanning.gitlab-ci.yml
 
# Override job SAST
sast:
  stage: security-scan
  variables:
    SAST_EXCLUDED_PATHS: "spec, test, tests, tmp, vendor"
    SEARCH_MAX_DEPTH: 10
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
 
# Job custom de securitate
security-audit:
  stage: security-scan
  image: node:18-alpine
  script:
    - npm audit --audit-level=high
    - npm run security:check
  allow_failure: false
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
 
# Scanare container cu Trivy
container-security:
  stage: security-scan
  image:
    name: aquasec/trivy:latest
    entrypoint: [""]
  script:
    - trivy image --exit-code 1 --severity HIGH,CRITICAL $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
 
# Scanare infrastructura
infra-security:
  stage: security-scan
  image:
    name: bridgecrew/checkov:latest
    entrypoint: [""]
  script:
    - checkov -d ./terraform --framework terraform --output cli --output junitxml --output-file-path ./checkov-results
  artifacts:
    reports:
      junit: checkov-results/results_junitxml.xml
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
      changes:
        - "terraform/**/*"
 
# Build cu context de securitate
build:
  stage: build
  image: docker:24
  services:
    - docker:24-dind
  script:
    - docker build
      --build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
      --build-arg VCS_REF=$CI_COMMIT_SHA
      --label "org.opencontainers.image.created=$(date -u +'%Y-%m-%dT%H:%M:%SZ')"
      --label "org.opencontainers.image.revision=$CI_COMMIT_SHA"
      -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
 
# Testare DAST
dast:
  stage: test
  image: owasp/zap2docker-stable
  script:
    - zap-baseline.py -t $STAGING_URL -r zap-report.html
  artifacts:
    paths:
      - zap-report.html
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

Etapa 3: Integrarea Testarii de Securitate

Configurare SAST (Semgrep)

# .semgrep.yml
rules:
  - id: hardcoded-secret
    patterns:
      - pattern-either:
          - pattern: $X = "..."
          - pattern: $X = '...'
    pattern-regex: (password|secret|api_key|token)\s*=\s*['"][^'"]+['"]
    message: Secret hardcodat detectat
    languages: [python, javascript, typescript, java]
    severity: ERROR
 
  - id: sql-injection
    patterns:
      - pattern: |
          $QUERY = f"SELECT ... {$INPUT} ..."
          ...
          cursor.execute($QUERY)
    message: Vulnerabilitate potentiala de SQL injection
    languages: [python]
    severity: ERROR
 
  - id: unsafe-deserialization
    pattern: pickle.loads($X)
    message: Deserializare nesigura cu pickle
    languages: [python]
    severity: ERROR
 
  - id: command-injection
    patterns:
      - pattern: subprocess.call($CMD, shell=True)
      - pattern: os.system($CMD)
    message: Potential command injection
    languages: [python]
    severity: ERROR
 
  # Regula custom pentru securitate AI/LLM
  - id: prompt-injection-risk
    patterns:
      - pattern: |
          prompt = f"... {$USER_INPUT} ..."
      - pattern: |
          prompt = "..." + $USER_INPUT + "..."
    message: Input utilizator direct in prompt - risc potential de prompt injection
    languages: [python]
    severity: WARNING

Configurare SCA (Dependabot + Renovate)

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "daily"
    open-pull-requests-limit: 10
    labels:
      - "dependencies"
      - "security"
    groups:
      security-patches:
        applies-to: security-updates
        patterns:
          - "*"
 
  - package-ecosystem: "pip"
    directory: "/"
    schedule:
      interval: "daily"
    groups:
      security-patches:
        applies-to: security-updates
 
  - package-ecosystem: "docker"
    directory: "/"
    schedule:
      interval: "weekly"
 
  - package-ecosystem: "terraform"
    directory: "/terraform"
    schedule:
      interval: "weekly"
// renovate.json
{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": [
    "config:base",
    ":semanticCommits",
    "security:openssf-scorecard"
  ],
  "vulnerabilityAlerts": {
    "enabled": true,
    "labels": ["security"]
  },
  "packageRules": [
    {
      "matchUpdateTypes": ["patch", "minor"],
      "matchCurrentVersion": "!/^0/",
      "automerge": true,
      "automergeType": "pr",
      "automergeStrategy": "squash"
    },
    {
      "matchDepTypes": ["devDependencies"],
      "automerge": true
    },
    {
      "matchPackagePatterns": ["*"],
      "matchUpdateTypes": ["major"],
      "labels": ["major-update"]
    }
  ],
  "osvVulnerabilityAlerts": true
}

Etapa 4: Securitatea Containerelor

Dockerfile Securizat

# Dockerfile cu bune practici de securitate
 
# Foloseste versiune specifica, nu latest
FROM node:20.10.0-alpine3.18 AS builder
 
# Creeaza utilizator non-root
RUN addgroup -g 1001 -S appgroup && \
    adduser -u 1001 -S appuser -G appgroup
 
WORKDIR /app
 
# Copiaza doar fisierele package mai intai pentru cache mai bun
COPY package*.json ./
 
# Instaleaza dependentele cu audit de securitate
RUN npm ci --only=production && \
    npm audit --audit-level=high
 
# Copiaza codul aplicatiei
COPY --chown=appuser:appgroup . .
 
# Build aplicatie
RUN npm run build
 
# Etapa de productie
FROM node:20.10.0-alpine3.18 AS production
 
# Update-uri de securitate
RUN apk update && \
    apk upgrade --no-cache && \
    apk add --no-cache dumb-init
 
# Creeaza utilizator non-root
RUN addgroup -g 1001 -S appgroup && \
    adduser -u 1001 -S appuser -G appgroup
 
WORKDIR /app
 
# Copiaza aplicatia compilata
COPY --from=builder --chown=appuser:appgroup /app/dist ./dist
COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules
COPY --from=builder --chown=appuser:appgroup /app/package.json ./
 
# Sterge fisierele inutile
RUN rm -rf /var/cache/apk/* /tmp/* /root/.npm
 
# Configurari de securitate
ENV NODE_ENV=production
ENV NPM_CONFIG_LOGLEVEL=warn
 
# Ruleaza ca utilizator non-root
USER appuser
 
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD node healthcheck.js || exit 1
 
# Foloseste dumb-init pentru gestionarea corecta a semnalelor
ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "dist/server.js"]
 
# Etichete
LABEL org.opencontainers.image.source="https://github.com/org/repo"
LABEL org.opencontainers.image.description="Descrierea aplicatiei"
LABEL org.opencontainers.image.licenses="MIT"

Politici de Securitate Kubernetes

# k8s/security-policies.yaml
 
# Pod Security Policy (pentru clustere mai vechi)
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: restricted
spec:
  privileged: false
  allowPrivilegeEscalation: false
  requiredDropCapabilities:
    - ALL
  volumes:
    - 'configMap'
    - 'emptyDir'
    - 'projected'
    - 'secret'
    - 'downwardAPI'
    - 'persistentVolumeClaim'
  hostNetwork: false
  hostIPC: false
  hostPID: false
  runAsUser:
    rule: 'MustRunAsNonRoot'
  seLinux:
    rule: 'RunAsAny'
  fsGroup:
    rule: 'RunAsAny'
  readOnlyRootFilesystem: true
 
---
# Politica de Retea
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: app-network-policy
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: myapp
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: api-gateway
      ports:
        - protocol: TCP
          port: 8080
  egress:
    - to:
        - podSelector:
            matchLabels:
              app: database
      ports:
        - protocol: TCP
          port: 5432
    - to:
        - namespaceSelector: {}
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - protocol: UDP
          port: 53
 
---
# Cote de Resurse
apiVersion: v1
kind: ResourceQuota
metadata:
  name: security-quota
  namespace: production
spec:
  hard:
    pods: "20"
    requests.cpu: "10"
    requests.memory: 20Gi
    limits.cpu: "20"
    limits.memory: 40Gi

Etapa 5: Conformitate ca si Cod

Policy as Code (OPA/Rego)

# policies/kubernetes.rego
 
package kubernetes.admission
 
# Refuza containerele care ruleaza ca root
deny[msg] {
    input.request.kind.kind == "Pod"
    container := input.request.object.spec.containers[_]
    not container.securityContext.runAsNonRoot
    msg := sprintf("Containerul %v nu trebuie sa ruleze ca root", [container.name])
}
 
# Cere limite de resurse
deny[msg] {
    input.request.kind.kind == "Pod"
    container := input.request.object.spec.containers[_]
    not container.resources.limits
    msg := sprintf("Containerul %v trebuie sa aiba limite de resurse", [container.name])
}
 
# Refuza containerele privilegiate
deny[msg] {
    input.request.kind.kind == "Pod"
    container := input.request.object.spec.containers[_]
    container.securityContext.privileged
    msg := sprintf("Containerul %v nu trebuie sa fie privilegiat", [container.name])
}
 
# Cere registre aprobate
deny[msg] {
    input.request.kind.kind == "Pod"
    container := input.request.object.spec.containers[_]
    not startswith(container.image, "gcr.io/my-project/")
    not startswith(container.image, "docker.io/library/")
    msg := sprintf("Containerul %v foloseste un registru neaprobat", [container.name])
}
 
# Cere eticheta de scanare de securitate
deny[msg] {
    input.request.kind.kind == "Deployment"
    not input.request.object.metadata.labels["security-scanned"]
    msg := "Deployment-ul trebuie sa aiba eticheta security-scanned"
}

Monitorizare si Alerte

# prometheus-alerts.yml
groups:
  - name: security-alerts
    rules:
      - alert: SecurityScanFailed
        expr: security_scan_status{result="failed"} > 0
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: Scanarea de securitate a esuat
          description: "Scanarea de securitate a esuat pentru {{ $labels.project }}"
 
      - alert: HighVulnerabilityDetected
        expr: security_vulnerabilities{severity="critical"} > 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: Vulnerabilitate critica detectata
          description: "Vulnerabilitate critica in {{ $labels.image }}"
 
      - alert: UnauthorizedDeployment
        expr: deployment_without_approval > 0
        for: 1m
        labels:
          severity: warning
        annotations:
          summary: Deployment fara aprobare de securitate
 
      - alert: SecretExposed
        expr: secret_scan_detections > 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: Secret expus in cod

Concluzie

Un pipeline CI/CD securizat necesita integrarea securitatii in fiecare etapa - de la hook-urile pre-commit pana la monitorizarea in runtime. Cheia este automatizarea: verificari de securitate care ruleaza automat, consistent si fara sa incetineasca dezvoltarea.

Concluzii principale:

  1. Shift left - Prinde problemele cat mai devreme posibil
  2. Automatizeaza totul - Verificarile manuale de securitate nu scaleaza
  3. Esueaza rapid - Blocheaza codul nesigur sa avanseze in pipeline
  4. Masoara si imbunatateste - Urmareste metricile de securitate in timp
  5. Echilibreaza securitatea si viteza - Securitatea nu ar trebui sa fie un bottleneck

La DeviDevs, ajutam organizatiile sa construiasca pipeline-uri CI/CD securizate care permit deployment-uri rapide si sigure. Contacteaza-ne pentru a discuta nevoile tale de DevSecOps.

Resurse Conexe


Sistemul tau AI e conform cu EU AI Act? Evaluare gratuita de risc - afla in 2 minute →

Ai nevoie de ajutor cu conformitatea EU AI Act sau securitatea AI?

Programeaza o consultatie gratuita de 30 de minute. Fara obligatii.

Programeaza un Apel

Weekly AI Security & Automation Digest

Get the latest on AI Security, workflow automation, secure integrations, and custom platform development delivered weekly.

No spam. Unsubscribe anytime.