TDCTF Academy Logo TDCTF ACADEMY

GitLab CI Security

Pendahuluan

GitLab CI/CD adalah platform CI/CD terintegrasi yang merupakan bagian dari GitLab - baik GitLab.com (SaaS) maupun self-managed GitLab. GitLab CI menawarkan pipeline yang fleksibel dengan fitur keamanan bawaan seperti SAST, DAST, secret detection, container scanning, dan dependency scanning melalui template ready-to-use.

Keamanan pipeline GitLab CI sangat krusial karena pipeline sering memiliki akses ke production credentials, artifact build, dan infrastruktur cloud. Artikel ini membahas struktur .gitlab-ci.yml, manajemen CI/CD variables, secret detection, template keamanan, runner security, dan contoh pipeline aman yang lengkap.

Struktur .gitlab-ci.yml

File .gitlab-ci.yml adalah jantung dari GitLab CI. File ini mendefinisikan pipeline yang terdiri dari stages, jobs, dan konfigurasi runner.

Contoh Struktur Dasar

stages:
- build
- test
- deploy

variables:
IMAGE_TAG: $CI_COMMIT_SHORT_SHA

build-job:
stage: build
script:
- docker build -t app:$IMAGE_TAG .
artifacts:
paths:
- dist/

test-job:
stage: test
script:
- npm install
- npm test
dependencies:
- build-job

deploy-job:
stage: deploy
script:
- ./deploy.sh
environment: production
only:
- main

Beberapa elemen penting dari struktur di atas:

  • stages: Menentukan urutan eksekusi pipeline.
  • variables: Variabel yang tersedia di seluruh job.
  • artifacts: File yang diteruskan antar job.
  • environment: Menautkan job ke environment tertentu (production, staging).
  • only/except: Aturan kapan job dijalankan.

CI/CD Variables: Masked, Protected, dan File

GitLab CI menyediakan beberapa jenis variabel untuk menyimpan informasi sensitif:

1. Masked Variables

Variabel yang dimasked akan disembunyikan di job log. GitLab mengganti nilai variabel dengan [MASKED] di output log.

# Setting di UI: Settings > CI/CD > Variables
# Key: DEPLOY_TOKEN
# Value: glpat-xxxxxxxx
# Type: Variable
# Masked: Yes

Keterbatasan masking:

  • Hanya variabel dengan panjang minimal 8 karakter yang bisa dimasked.
  • Variabel tidak boleh berisi spasi atau karakter yang mudah dikenali regex.
  • Masking tidak sempurna - variabel yang di-encode (base64, URL encoding) bisa lolos.

2. Protected Variables

Protected variables hanya tersedia di pipeline yang berjalan di protected branch atau protected tag. Ini mencegah developer tidak berwenang mengakses production credentials.

# Setting di UI: Settings > CI/CD > Variables
# Key: PROD_DB_PASSWORD
# Value: s3cr3t!
# Protected: Yes
# Masked: Yes
# Environment: production

3. File Variables

Untuk nilai yang sangat panjang (seperti SSH key atau sertifikat), gunakan tipe File - variabel akan disimpan sebagai file sementara di runner.

# Setting di UI
# Key: DEPLOY_SSH_KEY
# Value: (private key content)
# Type: File

Akses di pipeline:

deploy-job:
script:
- chmod 600 $DEPLOY_SSH_KEY
- ssh -i $DEPLOY_SSH_KEY user@server "deploy.sh"

Best Practices Variabel

  • Jangan pernah mencampur variabel development dan production dalam satu project.
  • Gunakan group-level variables untuk variabel yang dibagikan ke beberapa project.
  • Rotasi variabel secara berkala - gunakan GitLab API untuk otomatisasi.
  • Audit akses variabel secara rutin.

Secret Detection

GitLab memiliki Secret Detection bawaan yang dapat mendeteksi credentials yang tidak sengaja di-commit ke repository. Fitur ini menggunakan pola regex untuk mendeteksi berbagai jenis secret.

Mengaktifkan Secret Detection

include:
- template: Jobs/Secret-Detection.gitlab-ci.yml

secret_detection:
stage: test
variables:
SECRET_DETECTION_HISTORIC_SCAN: "true"

Dengan SECRET_DETECTION_HISTORIC_SCAN: "true", GitLab juga memindai commit lama - bukan hanya commit terbaru.

Secret Detection Rules Kustom

GitLab memungkinkan Anda menambahkan custom rules untuk secret detection:

# .gitlab/secret_detection_rules.toml
[[rules]]
id = "custom-api-key"
description = "Custom API Key detection"
path = ".*\\.key$"
regex = "(?i)myapp-api-key-[a-zA-Z0-9]{32}"

Template SAST dan DAST

GitLab menyediakan template siap pakai untuk berbagai jenis security scanning melalui include: template.

SAST (Static Application Security Testing)

include:
- template: Jobs/SAST.gitlab-ci.yml

stages:
- test
- security

sast:
stage: security
variables:
SAST_EXCLUDED_ANALYZERS: "eslint, nodejs-scan"
SAST_EXCLUDED_PATHS: "vendor, node_modules, tests"
SEARCH_MAX_DEPTH: 8

Template SAST GitLab secara otomatis mendeteksi bahasa pemrograman yang digunakan dan menjalankan analyzer yang sesuai (Semgrep, SpotBugs untuk Java, brakeman untuk Ruby, bandit untuk Python).

DAST (Dynamic Application Security Testing)

include:
- template: DAST.gitlab-ci.yml

stages:
- build
- test
- dast

dast:
stage: dast
variables:
DAST_WEBSITE: https://staging.example.com
DAST_BROWSER_SCAN: "true"
DAST_FULL_SCAN: "true"
environment:
name: staging

DAST GitLab menggunakan tool analisis dinamis untuk memindai aplikasi yang sedang berjalan. Mode browser scan menggunakan headless browser untuk aplikasi JavaScript-heavy, sementara full scan mengaktifkan pengujian yang lebih agresif.

Container Scanning

include:
- template: Jobs/Container-Scanning.gitlab-ci.yml

container_scanning:
stage: security
variables:
CI_APPLICATION_REPOSITORY: $CI_REGISTRY_IMAGE
CI_APPLICATION_TAG: $CI_COMMIT_SHORT_SHA
DOCKER_IMAGE: alpine:latest

Container scanning menggunakan Trivy di backend untuk memindai kerentanan di image Docker.

Dependency Scanning

include:
- template: Jobs/Dependency-Scanning.gitlab-ci.yml

dependency_scanning:
stage: security
variables:
DS_DEFAULT_ANALYZERS: "gemnasium"
DS_EXCLUDED_PATHS: "test, spec, vendor"

Dependency scanning memeriksa library dan package yang digunakan untuk kerentanan yang diketahui. Gemnasium analyzer mendeteksi kerentanan di berbagai ekosistem seperti npm, pip, Maven, dan Composer.

License Compliance

include:
- template: Jobs/License-Scanning.gitlab-ci.yml

license_scanning:
stage: security
variables:
LICENSE_MANAGEMENT_LICENSE_FILE: "license-report.json"

License compliance memeriksa lisensi dari setiap dependency yang digunakan, memastikan kepatuhan terhadap kebijakan organisasi.

Auto DevOps GitLab

Auto DevOps adalah pendekatan batteries-included yang mengonfigurasi pipeline lengkap secara otomatis - dari build hingga deploy ke Kubernetes, termasuk semua security scanning.

Mengaktifkan Auto DevOps

  1. Buka project di GitLab.
  2. Pergi ke Settings > CI/CD > Auto DevOps.
  3. Centang Default to Auto DevOps pipeline.

Atau aktifkan melalui file .gitlab-auto-devops.yml:

# .gitlab-auto-devops.yml
auto_devops:
script:
- echo "Auto DevOps enabled"

Contoh Pipeline Auto DevOps Lengkap

# .gitlab-ci.yml dengan Auto DevOps terintegrasi

variables:
SECURE_LOG_LEVEL: info

include:
- template: Auto-DevOps.gitlab-ci.yml

# Override konfigurasi template Auto DevOps

stages:
- build
- test
- security
- review
- dast
- staging
- canary
- production
- performance
- cleanup

build:
stage: build
image: docker:24.0.5
services:
- docker:24.0.5-dind
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA

sast:
stage: security
variables:
SAST_EXCLUDED_ANALYZERS: "eslint"

dast:
stage: dast
variables:
DAST_WEBSITE: https://review-$CI_ENVIRONMENT_SLUG.example.com
DAST_FULL_SCAN: "true"

secret_detection:
stage: security

container_scanning:
stage: security
variables:
CI_APPLICATION_REPOSITORY: $CI_REGISTRY_IMAGE
CI_APPLICATION_TAG: $CI_COMMIT_SHORT_SHA

dependency_scanning:
stage: security

license_scanning:
stage: security

deploy:
stage: production
environment:
name: production
only:
- main
script:
- kubectl set image deployment/my-app app=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA

Pipeline ini secara otomatis menjalankan: SAST, DAST, Secret Detection, Container Scanning, Dependency Scanning, dan License Compliance - tanpa perlu konfigurasi manual.

Runner Security

GitLab CI menggunakan runner untuk mengeksekusi job. Ada dua jenis runner: shared dan specific.

Shared vs Specific Runner

Aspek Shared Runner Specific Runner
Available to Semua project di instance/group Project tertentu saja
Cost Gratis hingga batas tertentu (GitLab.com) Dikelola sendiri
Security Isolasi via VM/container Kontrol penuh
Tag shared docker, macos, dll

Security Best Practices untuk Runner

# .gitlab-ci.yml - gunakan tag untuk memilih runner yang tepat
test-job:
tags:
- docker
script:
- npm test

deploy-job:
tags:
- production
script:
- ./deploy.sh

Untuk self-managed runner:

  1. Gunakan Docker executor - setiap job berjalan di container baru tanpa akses ke host.
# config.toml untuk Docker executor
[[runners]]
name = "docker-runner-01"
url = "https://gitlab.com/"
token = "xxxxxxxx"
executor = "docker"
[runners.docker]
image = "ubuntu:22.04"
privileged = false
disable_cache = false
volumes = ["/cache"]
shm_size = 0
  1. Nonaktifkan privileged mode - jangan pernah memberikan akses root penuh ke container runner.

  2. Batasi concurrent jobs - cegah satu runner mengeksekusi terlalu banyak job bersamaan.

[[runners]]
limit = 2 # maksimal 2 job concurrent
concurrent = 2
  1. Gunakan runner ephemeral - hancurkan runner setelah setiap job untuk mencegah data bocor.

  2. Firewall ketat - runner hanya boleh mengakses GitLab server dan resource yang diperlukan.

  3. Update rutin - pastikan GitLab Runner selalu diperbarui ke versi terbaru.

Contoh Pipeline Secure Lengkap

Berikut adalah contoh pipeline production-grade yang mengintegrasikan semua fitur keamanan:

# .gitlab-ci.yml - Secure Pipeline

image: node:18-alpine

stages:
- lint
- security
- test
- build
- deploy

variables:
NPM_CONFIG_CACHE: $CI_PROJECT_DIR/.npm
NODE_ENV: test

# Cache untuk mempercepat instalasi
cache:
key: $CI_COMMIT_REF_SLUG
paths:
- .npm/
- node_modules/

lint-job:
stage: lint
script:
- npm ci
- npm run lint
- npm run format:check

# Template keamanan dari GitLab
include:
- template: Jobs/SAST.gitlab-ci.yml
- template: Jobs/Secret-Detection.gitlab-ci.yml
- template: Jobs/Dependency-Scanning.gitlab-ci.yml

sast:
stage: security
variables:
SAST_EXCLUDED_ANALYZERS: "eslint, nodejs-scan"
SAST_EXCLUDED_PATHS: "node_modules, test"

secret_detection:
stage: security

dependency_scanning:
stage: security

unit-test:
stage: test
script:
- npm ci
- npm test -- --coverage
artifacts:
reports:
junit: junit.xml
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml

build:
stage: build
image: docker:24.0.5
services:
- docker:24.0.5-dind
variables:
DOCKER_HOST: tcp://docker:2375
DOCKER_TLS_CERTDIR: ""
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
only:
- main

container-scan:
stage: security
image: docker:24.0.5
services:
- docker:24.0.5-dind
variables:
DOCKER_HOST: tcp://docker:2375
DOCKER_TLS_CERTDIR: ""
script:
- apk add --no-cache curl
- curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- trivy image --severity CRITICAL,HIGH --exit-code 0 $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
- trivy image --severity CRITICAL --exit-code 1 $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
dependencies:
- build
only:
- main

deploy-staging:
stage: deploy
variables:
NODE_ENV: staging
script:
- echo "Deploying to staging server..."
- chmod 400 $STAGING_SSH_KEY
- ssh -o StrictHostKeyChecking=no -i $STAGING_SSH_KEY [email protected] "docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA && docker-compose up -d"
environment:
name: staging
url: https://staging.example.com
only:
- main
needs: ["unit-test", "sast", "secret_detection", "dependency_scanning", "container-scan"]

deploy-production:
stage: deploy
variables:
NODE_ENV: production
script:
- echo "Deploying to production server..."
- chmod 400 $PRODUCTION_SSH_KEY
- ssh -o StrictHostKeyChecking=no -i $PRODUCTION_SSH_KEY [email protected] "docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA && docker-compose up -d"
environment:
name: production
url: https://app.example.com
only:
- main
needs: ["deploy-staging"]
when: manual

Pipeline di atas menerapkan:

  1. SAST, Secret Detection, Dependency Scanning melalui template GitLab.
  2. Container scanning dengan Trivy - pipeline gagal jika ada kerentanan CRITICAL.
  3. Stage terpisah untuk keamanan - security gate sebelum deploy.
  4. Deployment manual ke production - membutuhkan persetujuan manusia.
  5. SSH key sebagai File variable - aman dan terproteksi.
  6. Hanya branch main yang bisa deploy ke production.

Kesimpulan

Keamanan GitLab CI/CD membutuhkan pendekatan berlapis. Mulai dari manajemen variabel yang aman (masked, protected, file variables), pemanfaatan template SAST/DAST/secret scanning, hingga konfigurasi runner yang aman. GitLab Auto DevOps menyediakan fondasi keamanan yang solid, tetapi tetap membutuhkan penyesuaian sesuai kebutuhan organisasi. Dengan mengintegrasikan security scanning di setiap stage pipeline, Anda dapat mendeteksi dan memperbaiki kerentanan sebelum mencapai production.

PADA HALAMAN INI