# ARTICLE_LOADED
> cat devops-security.md
> rendering content...
status: READING_MODE
security_level: PUBLIC
engagement_tracking: ENABLED
article@tektik:/var/blog/devops-security$
cat metadata.json
"category": "Security"
"date": "2025-10-10"
"read_time": "10 dk okuma"
"author": "TekTık Yazılım DevOps Ekibi"
Security10 Ekim 2025

CI/CD Pipeline Security: DevSecOps Yaklaşımı

echo"Güvenli CI/CD pipeline tasarımı ve DevSecOps prensiplerinin entegrasyonu. Automated security testing, vulnerability management ve secure deployment stratejileri."
#DevSecOps#CI/CD#Security#Automation
10 dk okuma
TekTık Yazılım DevOps Ekibi
content@tektik:/var/articles/devops-security.md$
./render-article --format=html --style=cyber

CI/CD Pipeline Security: DevSecOps Yaklaşımı

Modern yazılım geliştirme süreçlerinde güvenlik, artık bir afterthought değil, development lifecycle'ının her aşamasına entegre edilmesi gereken temel bir bileşendir. DevSecOps yaklaşımı ile güvenlik kontrollerini CI/CD pipeline'larına nasıl entegre edeceğinizi ve automated security testing stratejileri oluşturmayı öğreneceksiniz.

İçindekiler

  1. DevSecOps Temelleri
  2. Secure CI/CD Pipeline Tasarımı
  3. Static Application Security Testing (SAST)
  4. Dynamic Application Security Testing (DAST)
  5. Container Security
  6. Infrastructure Security
  7. Secrets Management
  8. Compliance ve Audit

DevSecOps Temelleri {#devsecops-temelleri}

Shift-Left Security Prensibi

DevSecOps'un kalbi "shift-left" yaklaşımındadır - güvenliği development cycle'ın en başına taşımak:

text
graph LR
    A[Plan] --> B[Code]
    B --> C[Build]
    C --> D[Test]
    D --> E[Release]
    E --> F[Deploy]
    F --> G[Operate]
    G --> H[Monitor]
    
    S1[Security Planning] --> A
    S2[Secure Coding] --> B
    S3[Security Scanning] --> C
    S4[Security Testing] --> D
    S5[Security Validation] --> E
    S6[Secure Deployment] --> F
    S7[Runtime Security] --> G
    S8[Security Monitoring] --> H

DevSecOps Kültürü

yaml
# devsecops-principles.yaml
DevSecOps_Culture:
  shared_responsibility:
    - "Everyone is responsible for security"
    - "Security is not a bottleneck"
    - "Automate security wherever possible"
  
  collaboration:
    - "Dev, Ops, and Sec teams work together"
    - "Security requirements are clear and actionable"
    - "Fast feedback loops for security issues"
  
  automation:
    - "Security tests run automatically"
    - "Vulnerability remediation is automated where possible"
    - "Compliance checks are part of the pipeline"

Secure CI/CD Pipeline Tasarımı {#secure-cicd-pipeline}

GitHub Actions Security Pipeline

yaml
# .github/workflows/security-pipeline.yml
name: Security Pipeline

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  # Pre-commit security checks
  pre-commit-security:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
      with:
        fetch-depth: 0
    
    - name: Run TruffleHog OSS
      uses: trufflesecurity/trufflehog@main
      with:
        path: ./
        base: main
        head: HEAD
        extra_args: --debug --only-verified
    
    - name: Run git-secrets
      run: |
        git clone https://github.com/awslabs/git-secrets.git
        cd git-secrets && make install
        git secrets --register-aws
        git secrets --scan

  # Static Code Analysis
  sast:
    runs-on: ubuntu-latest
    needs: pre-commit-security
    steps:
    - uses: actions/checkout@v4
    
    - name: Run Semgrep
      uses: returntocorp/semgrep-action@v1
      with:
        config: >-
          p/security-audit
          p/secrets
          p/owasp-top-ten
          p/kubernetes
          p/dockerfile
    
    - name: Run CodeQL
      uses: github/codeql-action/init@v2
      with:
        languages: 'go, javascript, python'
        queries: security-extended,security-and-quality
    
    - name: Autobuild
      uses: github/codeql-action/autobuild@v2
    
    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v2

  # Dependency Vulnerability Scanning
  dependency-scan:
    runs-on: ubuntu-latest
    needs: pre-commit-security
    steps:
    - uses: actions/checkout@v4
    
    - name: Run Trivy vulnerability scanner in repo mode
      uses: aquasecurity/trivy-action@master
      with:
        scan-type: 'fs'
        scan-ref: '.'
        format: 'sarif'
        output: 'trivy-results.sarif'
    
    - name: Upload Trivy scan results to GitHub Security tab
      uses: github/codeql-action/upload-sarif@v2
      with:
        sarif_file: 'trivy-results.sarif'
    
    - name: Run Snyk to check for vulnerabilities
      uses: snyk/actions/node@master
      env:
        SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
      with:
        args: --severity-threshold=high

  # Container Security
  container-security:
    runs-on: ubuntu-latest
    needs: [sast, dependency-scan]
    steps:
    - uses: actions/checkout@v4
    
    - name: Build Docker image
      run: |
        docker build -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} .
    
    - name: Run Trivy container scan
      uses: aquasecurity/trivy-action@master
      with:
        image-ref: '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}'
        format: 'sarif'
        output: 'container-trivy-results.sarif'
    
    - name: Run Dockle security scanner
      run: |
        docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
          goodwithtech/dockle:latest ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
    
    - name: Run Docker Bench Security
      run: |
        docker run --rm --net host --pid host --userns host --cap-add audit_control \
          -e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
          -v /etc:/etc:ro \
          -v /usr/bin/containerd:/usr/bin/containerd:ro \
          -v /usr/bin/runc:/usr/bin/runc:ro \
          -v /usr/lib/systemd:/usr/lib/systemd:ro \
          -v /var/lib:/var/lib:ro \
          -v /var/run/docker.sock:/var/run/docker.sock:ro \
          --label docker_bench_security \
          docker/docker-bench-security

  # Infrastructure as Code Security
  iac-security:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    
    - name: Run Checkov
      uses: bridgecrewio/checkov-action@master
      with:
        directory: .
        framework: terraform,kubernetes,dockerfile
        output_format: sarif
        output_file_path: checkov-results.sarif
    
    - name: Run TFSec
      uses: aquasecurity/tfsec-action@v1.0.0
      with:
        soft_fail: true
    
    - name: Upload Checkov scan results
      uses: github/codeql-action/upload-sarif@v2
      with:
        sarif_file: checkov-results.sarif

  # Security Policy Validation
  policy-validation:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    
    - name: Run Open Policy Agent Tests
      run: |
        curl -L -o opa https://openpolicyagent.org/downloads/v0.57.0/opa_linux_amd64_static
        chmod 755 ./opa
        ./opa fmt --diff policies/
        ./opa test policies/
    
    - name: Run Conftest
      run: |
        docker run --rm -v $(pwd):/project openpolicyagent/conftest verify --policy policies/ k8s/

Static Application Security Testing (SAST) {#sast-implementation}

Semgrep Konfigürasyonu

yaml
# .semgrep.yml
rules:
  - id: hardcoded-secrets
    patterns:
      - pattern-either:
          - pattern: password = "..."
          - pattern: api_key = "..."
          - pattern: secret_key = "..."
          - pattern: private_key = "..."
    message: "Hardcoded secret detected"
    languages: [python, javascript, go]
    severity: ERROR
    
  - id: sql-injection
    patterns:
      - pattern-either:
          - pattern: db.exec("SELECT * FROM users WHERE id = " + $ID)
          - pattern: db.query("DELETE FROM " + $TABLE + " WHERE id = " + $ID)
    message: "Potential SQL injection vulnerability"
    languages: [python, javascript, go]
    severity: ERROR
    
  - id: command-injection
    patterns:
      - pattern-either:
          - pattern: exec.Command($CMD, ...)
          - pattern: os.system($CMD)
    message: "Potential command injection vulnerability"
    languages: [go, python]
    severity: WARNING

SonarQube Entegrasyonu

yaml
# sonarqube-analysis.yml
name: SonarQube Analysis

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  sonarqube:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
      with:
        fetch-depth: 0
    
    - name: Setup Go
      uses: actions/setup-go@v4
      with:
        go-version: '1.21'
    
    - name: Run tests with coverage
      run: |
        go test -coverprofile=coverage.out -covermode=atomic ./...
        go tool cover -func coverage.out
    
    - name: SonarQube Scan
      uses: sonarqube-quality-gate-action@master
      with:
        scanMetadataReportFile: target/sonar/report-task.txt
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}

Dynamic Application Security Testing (DAST) {#dast-implementation}

OWASP ZAP Integration

yaml
# dast-testing.yml
name: DAST Security Testing

on:
  deployment_status

jobs:
  dast:
    runs-on: ubuntu-latest
    if: github.event.deployment_status.state == 'success'
    steps:
    - name: Run OWASP ZAP Baseline Scan
      uses: zaproxy/action-baseline@v0.7.0
      with:
        target: ${{ github.event.deployment_status.target_url }}
        rules_file_name: '.zap/rules.tsv'
        cmd_options: '-a'
    
    - name: Run OWASP ZAP Full Scan
      uses: zaproxy/action-full-scan@v0.4.0
      with:
        target: ${{ github.event.deployment_status.target_url }}
        rules_file_name: '.zap/rules.tsv'
        cmd_options: '-a -j'
        allow_issue_writing: false
    
    - name: Archive ZAP Results
      uses: actions/upload-artifact@v3
      with:
        name: zap-results
        path: report_html.html

Custom Security Tests

python
# security_tests.py
import requests
import pytest
from urllib.parse import urljoin

class TestSecurityHeaders:
    def setup_method(self):
        self.base_url = "https://your-app.com"
        
    def test_security_headers(self):
        """Test for essential security headers"""
        response = requests.get(self.base_url)
        headers = response.headers
        
        # Test for Security Headers
        assert 'X-Frame-Options' in headers
        assert 'X-Content-Type-Options' in headers
        assert 'X-XSS-Protection' in headers
        assert 'Strict-Transport-Security' in headers
        assert 'Content-Security-Policy' in headers
        
        # Verify HSTS configuration
        assert 'max-age' in headers.get('Strict-Transport-Security', '')
        
        # Verify CSP is not too permissive
        csp = headers.get('Content-Security-Policy', '')
        assert 'unsafe-eval' not in csp
        assert 'unsafe-inline' not in csp or 'nonce-' in csp
    
    def test_no_sensitive_data_exposure(self):
        """Test for sensitive data in responses"""
        endpoints = ['/api/health', '/api/status', '/']
        
        for endpoint in endpoints:
            response = requests.get(urljoin(self.base_url, endpoint))
            content = response.text.lower()
            
            # Check for sensitive data
            sensitive_patterns = [
                'password', 'secret', 'key', 'token', 
                'api_key', 'private_key', 'credential'
            ]
            
            for pattern in sensitive_patterns:
                assert pattern not in content, f"Sensitive data '{pattern}' found in {endpoint}"
    
    def test_authentication_endpoints(self):
        """Test authentication security"""
        # Test login endpoint exists and requires HTTPS
        login_response = requests.get(urljoin(self.base_url, '/api/auth/login'))
        assert login_response.status_code in [200, 405]  # Should exist but may not accept GET
        
        # Test for rate limiting on auth endpoints
        auth_endpoint = urljoin(self.base_url, '/api/auth/login')
        responses = []
        
        for i in range(10):
            resp = requests.post(auth_endpoint, json={'username': 'test', 'password': 'test'})
            responses.append(resp.status_code)
        
        # Should have some rate limiting after multiple failed attempts
        assert 429 in responses or 403 in responses, "No rate limiting detected on auth endpoint"

Container Security {#container-security}

Secure Dockerfile Best Practices

text
# Secure Dockerfile example
FROM node:18-alpine as builder

# Create non-root user
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001

# Set working directory
WORKDIR /app

# Copy package files
COPY package.json package-lock.json ./

# Install dependencies
RUN npm ci --only=production && npm cache clean --force

# Copy source code
COPY . .

# Build application
RUN npm run build

# Production image
FROM node:18-alpine as runner

# Install security updates
RUN apk upgrade --no-cache

# Create non-root user
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001

WORKDIR /app

# Copy built application
COPY --from=builder --chown=nextjs:nodejs /app ./

# Remove unnecessary files
RUN rm -rf .git* README.md *.md

# Set security headers and configurations
ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1

# Use non-root user
USER nextjs

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD node healthcheck.js || exit 1

# Expose port
EXPOSE 3000

CMD ["npm", "start"]

Container Security Scanning

yaml
# container-security-scan.yml
name: Container Security Scan

on:
  push:
    branches: [ main ]

jobs:
  security-scan:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    
    - name: Build image
      run: docker build -t myapp:${{ github.sha }} .
    
    - name: Run Trivy vulnerability scanner
      uses: aquasecurity/trivy-action@master
      with:
        image-ref: 'myapp:${{ github.sha }}'
        format: 'table'
        exit-code: '1'
        ignore-unfixed: true
        vuln-type: 'os,library'
        severity: 'CRITICAL,HIGH'
    
    - name: Run Grype vulnerability scanner
      uses: anchore/scan-action@v3
      with:
        image: 'myapp:${{ github.sha }}'
        fail-build: true
        severity-cutoff: high
    
    - name: Run Syft SBOM generator
      uses: anchore/sbom-action@v0
      with:
        image: 'myapp:${{ 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

Infrastructure Security {#infrastructure-security}

Terraform Security Scanning

hcl
# security-policies.tf
# Security group with restrictive rules
resource "aws_security_group" "web" {
  name_description = "Web server security group"
  vpc_id          = aws_vpc.main.id

  # Only allow HTTPS inbound
  ingress {
    description = "HTTPS"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # Allow HTTP redirect to HTTPS
  ingress {
    description = "HTTP redirect"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # Restrictive outbound rules
  egress {
    description = "HTTPS outbound"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    description = "DNS"
    from_port   = 53
    to_port     = 53
    protocol    = "udp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name        = "web-sg"
    Environment = var.environment
  }
}

# S3 bucket with security configurations
resource "aws_s3_bucket" "app_data" {
  bucket = "myapp-data-${random_id.bucket_suffix.hex}"
}

resource "aws_s3_bucket_versioning" "app_data" {
  bucket = aws_s3_bucket.app_data.id
  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_s3_bucket_encryption" "app_data" {
  bucket = aws_s3_bucket.app_data.id

  server_side_encryption_configuration {
    rule {
      apply_server_side_encryption_by_default {
        sse_algorithm = "AES256"
      }
    }
  }
}

resource "aws_s3_bucket_public_access_block" "app_data" {
  bucket = aws_s3_bucket.app_data.id

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

OPA Policy Validation

text
# policies/security.rego
package kubernetes.admission

deny[msg] {
  input.request.kind.kind == "Pod"
  input.request.object.spec.containers[_].securityContext.runAsRoot == true
  msg := "Containers must not run as root"
}

deny[msg] {
  input.request.kind.kind == "Pod"
  not input.request.object.spec.containers[_].securityContext.readOnlyRootFilesystem
  msg := "Containers must have read-only root filesystem"
}

deny[msg] {
  input.request.kind.kind == "Pod"
  input.request.object.spec.containers[_].securityContext.privileged == true
  msg := "Privileged containers are not allowed"
}

deny[msg] {
  input.request.kind.kind == "Pod"
  input.request.object.spec.containers[_].image
  not starts_with(input.request.object.spec.containers[_].image, "myregistry.com/")
  msg := "Images must come from approved registry"
}

Secrets Management {#secrets-management}

Kubernetes Secrets Management

yaml
# external-secrets.yaml
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: vault-backend
  namespace: production
spec:
  provider:
    vault:
      server: "https://vault.company.com:8200"
      path: "secret"
      version: "v2"
      auth:
        kubernetes:
          mountPath: "kubernetes"
          role: "demo"
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: app-secrets
  namespace: production
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: app-secrets
    creationPolicy: Owner
  data:
  - secretKey: database-url
    remoteRef:
      key: myapp
      property: database_url
  - secretKey: api-key
    remoteRef:
      key: myapp
      property: api_key

CI/CD Secrets Security

yaml
# secrets-scanning.yml
name: Secrets Detection

on: [push, pull_request]

jobs:
  secret-scan:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
      with:
        fetch-depth: 0
    
    - name: Run TruffleHog
      uses: trufflesecurity/trufflehog@main
      with:
        path: ./
        base: ${{ github.event.repository.default_branch }}
        head: HEAD
    
    - name: Run GitLeaks
      uses: gitleaks/gitleaks-action@v2
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}
    
    - name: Run detect-secrets
      run: |
        pip install detect-secrets
        detect-secrets scan --all-files --baseline .secrets.baseline
        detect-secrets audit .secrets.baseline

Compliance ve Audit {#compliance-audit}

Security Compliance Pipeline

yaml
# compliance-check.yml
name: Compliance Check

on:
  schedule:
    - cron: '0 2 * * 1'  # Weekly on Mondays
  workflow_dispatch:

jobs:
  compliance:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    
    - name: Run CIS Kubernetes Benchmark
      run: |
        kubectl apply -f https://raw.githubusercontent.com/aquasecurity/kube-bench/main/job.yaml
        kubectl wait --for=condition=complete job/kube-bench --timeout=300s
        kubectl logs job/kube-bench > kube-bench-report.txt
    
    - name: Run Falco Security Monitoring
      run: |
        docker run --rm -v /var/run/docker.sock:/host/var/run/docker.sock \
          -v /dev:/host/dev -v /proc:/host/proc:ro -v /boot:/host/boot:ro \
          -v /lib/modules:/host/lib/modules:ro -v /usr:/host/usr:ro \
          falcosecurity/falco:latest --validate /etc/falco/falco_rules.yaml
    
    - name: Generate Security Report
      run: |
        python3 scripts/generate-security-report.py \
          --kube-bench kube-bench-report.txt \
          --trivy trivy-results.sarif \
          --output security-compliance-report.json
    
    - name: Upload Compliance Report
      uses: actions/upload-artifact@v3
      with:
        name: compliance-report
        path: security-compliance-report.json

Audit Logging Konfigürasyonu

yaml
# audit-policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
# Log all security-related events
- level: RequestResponse
  resources:
  - group: ""
    resources: ["secrets", "configmaps"]
  - group: "rbac.authorization.k8s.io"
    resources: ["roles", "rolebindings", "clusterroles", "clusterrolebindings"]

# Log all authentication events
- level: Request
  users: ["system:anonymous"]
  namespaces: ["kube-system", "default"]

# Log all privileged operations
- level: RequestResponse
  users: ["system:admin"]
  
# Log resource modifications
- level: Request
  verbs: ["create", "update", "patch", "delete"]
  resources:
  - group: "apps"
    resources: ["deployments", "daemonsets", "statefulsets"]

Sonuç

DevSecOps yaklaşımı ile güvenliği CI/CD pipeline'ınıza entegre etmek, modern yazılım geliştirme süreçlerinin vazgeçilmez bir parçasıdır. Bu rehberde ele aldığımız konular:

  • Shift-Left Security: Güvenliği development cycle'ın başına taşımak
  • Automated Security Testing: SAST, DAST ve container scanning
  • Infrastructure Security: IaC security ve policy validation
  • Secrets Management: Güvenli secret yönetimi ve rotation
  • Compliance: Automated compliance checking ve audit logging

Bu stratejileri uygulayarak, güvenli, hızlı ve sürdürülebilir software delivery pipeline'ları oluşturabilirsiniz.


TekTık Yazılım olarak, DevSecOps implementasyonu ve güvenli CI/CD pipeline tasarımı konularında profesyonel danışmanlık hizmeti vermekteyiz. İletişim sayfamızdan bizimle iletişime geçebilirsiniz.