DevSecOps: Seguretat en el Cicle de Vida del Desenvolupament
Introducció
DevSecOps ("Development + Security + Operations") integra la seguretat en cada etapa del cicle de vida del programari, en lloc de tractar-la com un pas final o una barrera per al llançament. La filosofia és "shift left": detectar i corregir vulnerabilitats el més aviat possible, quan el cost és menor.
flowchart LR
subgraph PLAN[Planificació]
TR[Threat Modeling\nSTRIDE]
REQS[Security\nRequirements]
end
subgraph CODE[Codi]
SAST[SAST\nSonarQube\nSemgrep]
SECRETS[Secret\nScanning\nGitLeaks]
REVIEW[Code\nReview]
end
subgraph BUILD[Build]
SCA[SCA\nDependency Check\nSnyk]
CONTAINER[Container\nScan\nTrivy]
end
subgraph TEST[Test]
DAST[DAST\nOWASP ZAP]
PENTEST[Pentest\nManual]
end
subgraph DEPLOY[Desplegament]
IaC[IaC Scan\nCheckov]
SIGN[Signing\nCosign]
end
subgraph RUN[Producció]
SIEM[SIEM\nMonitoratge]
RASP[RASP\nRuntime\nProtection]
end
PLAN --> CODE --> BUILD --> TEST --> DEPLOY --> RUN --> PLAN
style PLAN fill:#E3F2FD
style CODE fill:#E8F5E9
style BUILD fill:#FFF8E1
style TEST fill:#FBE9E7
style DEPLOY fill:#F3E5F5
style RUN fill:#E8EAF6
Pipeline CI/CD Segur
Estructura d'un pipeline GitLab CI
# .gitlab-ci.yml - Pipeline DevSecOps complet
stages:
- sast
- build
- scan
- test
- deploy
variables:
DOCKER_IMAGE: "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA"
# ==========================================
# ETAPA 1: Anàlisi estàtica de codi (SAST)
# ==========================================
semgrep-sast:
stage: sast
image: returntocorp/semgrep
script:
- semgrep --config=p/owasp-top-ten --config=p/python . --json > semgrep-report.json
- semgrep --config=p/owasp-top-ten --config=p/python . --error
artifacts:
reports:
sast: semgrep-report.json
allow_failure: false # Atura el pipeline si hi ha vulnerabilitats crítiques
# Detecció de secrets en el codi
gitleaks-scan:
stage: sast
image: zricethezav/gitleaks:latest
script:
- gitleaks detect --source . --report-path=gitleaks-report.json
artifacts:
paths:
- gitleaks-report.json
allow_failure: false
# ==========================================
# ETAPA 2: Build de la imatge Docker
# ==========================================
docker-build:
stage: build
image: docker:24
services:
- docker:24-dind
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build -t $DOCKER_IMAGE .
- docker push $DOCKER_IMAGE
# ==========================================
# ETAPA 3: Escaneig de la imatge
# ==========================================
trivy-scan:
stage: scan
image: aquasec/trivy:latest
script:
- trivy image --exit-code 1 --severity HIGH,CRITICAL $DOCKER_IMAGE
- trivy image --format json --output trivy-report.json $DOCKER_IMAGE
artifacts:
paths:
- trivy-report.json
# Escaneig de dependències
dependency-check:
stage: scan
image: owasp/dependency-check
script:
- /usr/share/dependency-check/bin/dependency-check.sh
--project "$CI_PROJECT_NAME"
--scan .
--format JSON
--out dependency-check-report.json
--failOnCVSS 7
artifacts:
paths:
- dependency-check-report.json
# ==========================================
# ETAPA 4: Tests dinàmics (DAST)
# ==========================================
zap-dast:
stage: test
image: zaproxy/zap-stable:latest
variables:
TARGET_URL: "http://staging.empresa.cat"
script:
- zap-baseline.py -t $TARGET_URL -J zap-report.json -r zap-report.html
artifacts:
paths:
- zap-report.html
- zap-report.json
allow_failure: true # DAST pot tenir falsos positius en entorn staging
# ==========================================
# ETAPA 5: Desplegament segur
# ==========================================
deploy-staging:
stage: deploy
script:
- # Signar la imatge Docker amb cosign
- cosign sign --key env://COSIGN_PRIVATE_KEY $DOCKER_IMAGE
- kubectl set image deployment/app app=$DOCKER_IMAGE
environment:
name: staging
only:
- develop
deploy-production:
stage: deploy
script:
- kubectl set image deployment/app app=$DOCKER_IMAGE
environment:
name: production
only:
- main
when: manual # Desplegament a producció sempre manual
Miniactivitat
En el pipeline anterior, hi ha tres punts on es pot bloquejar el pipeline (allow_failure: false). Per cada un, pensa:
- Per quin tipus de problemes és adequat bloquejar el pipeline completament?
- Quins casos podrien ser falsos positius i justificarien
allow_failure: true?
SAST: Anàlisi Estàtica del Codi
Semgrep
# Instal·lar i executar Semgrep localment
docker run --rm \
-v $(pwd):/src \
returntocorp/semgrep \
semgrep --config=p/owasp-top-ten /src
# Regles específiques per a Python (Django/Flask)
docker run --rm \
-v $(pwd):/src \
returntocorp/semgrep \
semgrep --config=p/django --config=p/flask /src
# Escriure una regla personalitzada
cat > custom-rules.yml << 'EOF'
rules:
- id: hardcoded-password
pattern: password = "..."
message: "Contrasenya hardcodejada detectada"
languages: [python, javascript]
severity: ERROR
- id: sql-injection-format-string
pattern: |
$DB.execute("... %s ..." % $INPUT)
message: "Possible SQL injection via format string"
languages: [python]
severity: ERROR
EOF
semgrep --config=custom-rules.yml /src
SonarQube
# docker-compose per a SonarQube local
version: '3'
services:
sonarqube:
image: sonarqube:community
container_name: sonarqube-NOMCOGNOM
ports:
- "9000:9000"
environment:
SONAR_ES_BOOTSTRAP_CHECKS_DISABLE: "true"
volumes:
- sonar_data:/opt/sonarqube/data
- sonar_logs:/opt/sonarqube/logs
volumes:
sonar_data:
sonar_logs:
# Analitzar un projecte Python amb SonarQube
docker run --rm \
-e SONAR_HOST_URL="http://localhost:9000" \
-e SONAR_LOGIN="<token>" \
-v $(pwd):/usr/src \
sonarsource/sonar-scanner-cli \
-Dsonar.projectKey=my-project \
-Dsonar.sources=.
Gestió de Secrets
Un dels errors més comuns: pujar credencials al repositori Git.
GitLeaks - Detecció de secrets
# Escanear el repositori complet (inclòs l'historial Git)
docker run --rm \
-v $(pwd):/path \
zricethezav/gitleaks:latest \
detect --source /path --report-path=/path/leaks-report.json
# Escanear únicament el codi no commitat
docker run --rm \
-v $(pwd):/path \
zricethezav/gitleaks:latest \
protect --staged --source /path
HashiCorp Vault - Gestió centralitzada de secrets
# docker-compose per a Vault en mode dev
services:
vault:
image: hashicorp/vault:latest
container_name: vault-NOMCOGNOM
ports:
- "8200:8200"
environment:
VAULT_DEV_ROOT_TOKEN_ID: "root-token-dev"
VAULT_DEV_LISTEN_ADDRESS: "0.0.0.0:8200"
cap_add:
- IPC_LOCK
# Guardar secrets en Vault (en lloc de .env o hardcoded)
export VAULT_ADDR="http://localhost:8200"
export VAULT_TOKEN="root-token-dev"
# Guardar credencials de la BBDD
vault kv put secret/myapp/database \
host="db.empresa.cat" \
username="app_user" \
password="s3cr3t_p4ssw0rd"
# Recuperar secrets en el codi
vault kv get -field=password secret/myapp/database
# Recuperar secrets de Vault en Python (exemple)
import hvac
client = hvac.Client(url='http://vault:8200', token=os.environ['VAULT_TOKEN'])
secret = client.secrets.kv.read_secret_version(path='myapp/database')
db_password = secret['data']['data']['password']
# La contrasenya MAI apareix al codi font
Infraestructura com a Codi (IaC) Segura
Checkov - Escaneig de Terraform/Kubernetes/Docker
# Escanejar fitxers Terraform per configuracions insegures
docker run --rm \
-v $(pwd):/tf \
bridgecrew/checkov:latest \
-d /tf --framework terraform
# Escanejar fitxers Kubernetes
docker run --rm \
-v $(pwd)/k8s:/k8s \
bridgecrew/checkov:latest \
-d /k8s --framework kubernetes
# Escanejar Dockerfiles
docker run --rm \
-v $(pwd):/src \
bridgecrew/checkov:latest \
-f /src/Dockerfile --framework dockerfile
# Exemple de Kubernetes deployment INSEGUR
# checkov detectarà aquests problemes:
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: app
image: myapp:latest # ❌ Tag "latest" no és reproducible
securityContext:
runAsRoot: true # ❌ Corrent com a root
privileged: true # ❌ Mode privilegiat
resources: {} # ❌ Sense límits de recursos
# Versió segura:
containers:
- name: app
image: myapp:1.2.3 # ✅ Tag específic
securityContext:
runAsNonRoot: true # ✅ No root
runAsUser: 1000
allowPrivilegeEscalation: false # ✅ No escalada
readOnlyRootFilesystem: true # ✅ FS read-only
resources:
limits:
memory: "512Mi" # ✅ Límits definits
cpu: "500m"
Contenidors Segurs en Producció
Signatura d'imatges amb Cosign
# Instal·lar cosign
docker run --rm gcr.io/projectsigstore/cosign:latest version
# Generar parella de claus per a la signatura
cosign generate-key-pair
# Signar una imatge Docker
cosign sign --key cosign.key myregistry/myapp:1.0.0
# Verificar la signatura
cosign verify --key cosign.pub myregistry/myapp:1.0.0
Política d'admissió Kubernetes (Kyverno)
# Política que exigeix imatges signades en producció
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-signed-images
spec:
validationFailureAction: enforce
rules:
- name: check-image-signature
match:
resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "myregistry/*"
attestors:
- entries:
- keys:
publicKeys: |-
-----BEGIN PUBLIC KEY-----
<cosign public key>
-----END PUBLIC KEY-----
Miniactivitat
Configura un pipeline mínim amb GitHub Actions o GitLab CI per a un projecte Python senzill que:
- Executi Semgrep en cada
git push - Generi un informe de vulnerabilitats com a artefacte
- Bloquegi el merge si hi ha vulnerabilitats de severitat HIGH o CRITICAL
Control de Versions i Branching Segur
gitGraph
commit id: "Initial commit"
branch develop
checkout develop
commit id: "Feature A"
branch feature/login
checkout feature/login
commit id: "Login screen"
commit id: "SAST passed"
checkout develop
merge feature/login id: "Merge after review"
branch release/1.0
checkout release/1.0
commit id: "Version bump"
commit id: "Final DAST scan"
checkout main
merge release/1.0 id: "Release 1.0" tag: "v1.0.0"
Protecció de branques (GitHub/GitLab)
# Regles recomanades per a la branca main/master:
# ✅ Requerir pull request reviews (mínim 1 revisor)
# ✅ Requerir CI/CD superat (SAST + tests)
# ✅ No permetre push directe
# ✅ No permetre force push
# ✅ Requerir historial lineal (no merge commits)
# ✅ No permetre eliminació de la branca
Exercici pràctic
Crea un projecte Python mínim amb una vulnerabilitat intencionada (SQL injection via format string) i:
- Configura Semgrep per detectar-la automàticament
- Configura un hook de pre-commit per blocar commits amb vulnerabilitats
- Simula un pipeline CI/CD (pots usar GitHub Actions free tier) amb les etapes:
- SAST (Semgrep)
- Build Docker
- Scan Trivy
- Documenta el procés en un informe
devsecops_NOMCOGNOM.md