Security DevSecOps DevOps

DevSecOps — Security พื้นฐานที่ DevOps ต้องรู้ 🔐DevSecOps — The Security Fundamentals Every DevOps Engineer Needs 🔐

By Anirach Mingkhwan DevOps & Vibe Coding 2026 · 23 min read
DevSecOps — Security พื้นฐานที่ DevOps ต้องรู้
ในบทความนี้
  1. DevSecOps คืออะไร?
  2. OWASP Top 10 — ช่องโหว่ยอดฮิต
  3. 🔑 Secrets Management — อย่าเก็บ secret ใน code!
  4. 🐳 Container Security
  5. 🔒 SSL/TLS — เข้ารหัส Traffic
  6. 🔍 Security Scanning ใน CI/CD
  7. 🏰 Server Hardening
  8. 🔐 Supply Chain Security
  9. 📋 Security Checklist สำหรับ DevOps
  10. สรุป
In this post
  1. What Is DevSecOps?
  2. OWASP Top 10 — The Greatest Hits
  3. 🔑 Secrets Management — Never Keep a Secret in Your Code!
  4. 🐳 Container Security
  5. 🔒 SSL/TLS — Encrypting Traffic
  6. 🔍 Security Scanning in CI/CD
  7. 🏰 Server Hardening
  8. 🔐 Supply Chain Security
  9. 📋 A Security Checklist for DevOps
  10. Summary

ตลอด 9 โพสต์ที่ผ่านมาเราเรียนรู้ DevOps ตั้งแต่ Git, Docker, K8s, IaC, Monitoring ไปจนถึง Linux

แต่ถ้า deploy ทุกอย่างขึ้น production แล้ว โดน hack? ข้อมูล user หลุด? Server ถูกยึด? 😱

Security ไม่ใช่ "เรื่องของทีม security" — มันเป็น เรื่องของทุกคนที่ deploy code ขึ้น production นี่คือ DevSecOps — ฝัง security เข้าไปในทุกขั้นตอนของ DevOps 🔐🐕


DevSecOps คืออะไร?

DevSecOps = Development + Security + Operations — แทนที่จะเช็ค security ตอนท้าย ให้ทำตั้งแต่ต้น:

📝 Plan
Threat modeling
→
💻 Code
Secret scanning
→
🔨 Build
SAST + SCA
→
🧪 Test
DAST + Pen test
→
🚀 Deploy
Image scanning
→
📊 Monitor
Runtime protection
💡 Shift Left: ยิ่งเจอ bug/vulnerability เร็ว ยิ่งแก้ถูก — หาตอน code ถูกกว่าหาตอน production 100 เท่า!

OWASP Top 10 — ช่องโหว่ยอดฮิต

OWASP Top 10 คือรายการช่องโหว่ที่พบบ่อยที่สุดใน web applications:

#ช่องโหว่ตัวอย่างป้องกัน
A01Broken Access ControlUser A เข้าถึงข้อมูล User BRBAC, JWT validation
A02Cryptographic FailuresPassword เก็บ plain textbcrypt, AES-256, TLS
A03InjectionSQL Injection, XSSParameterized queries, sanitize
A04Insecure Designไม่ rate limit loginThreat modeling, design review
A05Security MisconfigurationDebug mode on productionHardening, IaC scan
A06Vulnerable Componentsใช้ library เก่าที่มี CVESCA, Dependabot
A07Auth FailuresWeak password, no MFAMFA, strong password policy
A08Data Integrity FailuresUntrusted CI/CD pluginsVerify signatures, pin versions
A09Logging Failuresไม่ log login attemptsAudit logging, SIEM
A10SSRFServer เรียก internal API ให้Allowlist, network segmentation

🔑 Secrets Management — อย่าเก็บ secret ใน code!

Secrets Management คือการจัดการข้อมูลลับ (passwords, API keys, tokens, certificates) อย่างปลอดภัย ข้อผิดพลาดที่พบบ่อยที่สุดคือ hardcode secrets ใน source code แล้ว commit เข้า Git — แม้จะลบภายหลัง Git history ยังเก็บไว้! ตัวอย่างที่เห็นบ่อย:

❌ อย่าทำแบบนี้เด็ดขาด!
# ❌ WRONG — secret ใน code
DATABASE_URL="postgres://admin:P@[email protected]:5432/mydb"
API_KEY="sk-1234567890abcdef"

# ❌ WRONG — secret ใน Dockerfile
ENV AWS_SECRET_KEY=AKIAxxxxxxxxxxx

# ❌ WRONG — secret ใน docker-compose.yml
environment:
  - DB_PASSWORD=mysecretpassword

✅ วิธีที่ถูกต้อง

แทนที่จะ hardcode ให้ใช้ environment variables หรือ secrets manager (AWS Secrets Manager, HashiCorp Vault, GitHub Secrets) เก็บ secrets แยกจาก code ทำให้เปลี่ยน secrets ได้โดยไม่ต้องแก้ code และ secrets ไม่เข้า Git:

# 1. Environment Variables (ง่ายสุด)
export DB_PASSWORD="$(cat /run/secrets/db_password)"

# 2. Docker Secrets
echo "mysecretpassword" | docker secret create db_password -
# → ใช้ใน docker-compose:
services:
  app:
    secrets:
      - db_password
secrets:
  db_password:
    external: true

# 3. HashiCorp Vault (production-grade)
vault kv put secret/myapp db_password="P@ssw0rd"
vault kv get secret/myapp

# 4. AWS Secrets Manager / GCP Secret Manager
aws secretsmanager get-secret-value --secret-id myapp/db_password

# 5. .env file (dev only — อย่า commit!)
# .gitignore ต้องมี:
.env
.env.local
*.pem
*.key

Git Secret Scanning

แม้จะระวังแล้ว บางครั้ง secrets ก็หลุดเข้า Git ได้ Secret scanning ช่วยตรวจจับอัตโนมัติ — ทั้ง pre-commit hook (ป้องกันก่อน commit) และ CI pipeline (ตรวจซ้ำทุก PR) เครื่องมือเช่น Gitleaks และ GitHub Secret Scanning สแกนหา patterns ของ API keys, tokens, passwords:

# ติดตั้ง git-secrets ป้องกัน commit secrets
git secrets --install
git secrets --register-aws     # ตรวจ AWS keys

# ใช้ truffleHog scan repo ทั้งหมด
trufflehog git https://github.com/myorg/myrepo

# ใช้ gitleaks
gitleaks detect --source . --verbose

# GitHub: เปิด Secret Scanning (Settings → Security)
# → แจ้งเตือนทันทีถ้ามี secret หลุดเข้า repo

🐳 Container Security

Containers มีความเสี่ยงด้าน security หลายจุด — base image อาจมี vulnerabilities, Dockerfile อาจ expose secrets, runtime อาจให้สิทธิ์มากเกินไป (root access) การ secure containers ต้องทำทั้ง 3 ระดับ: build time (Dockerfile best practices), registry (image scanning), และ runtime (least privilege, read-only filesystem):

Dockerfile Best Practices

Dockerfile ที่ปลอดภัยต้อง: ใช้ specific version tags (ไม่ใช่ latest), ใช้ non-root user, ใช้ multi-stage builds เพื่อลดขนาดและ attack surface, และอย่า COPY secrets เข้า image:

# ❌ BAD — ใช้ root, image ใหญ่, ไม่ pin version
FROM node:latest
COPY . .
RUN npm install
CMD ["node", "app.js"]

# ✅ GOOD — non-root, minimal image, pinned version, multi-stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

FROM node:20-alpine
RUN addgroup -S app && adduser -S app -G app
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY --chown=app:app . .
USER app
EXPOSE 3000
HEALTHCHECK CMD curl -f http://localhost:3000/health || exit 1
CMD ["node", "app.js"]

Image Scanning

Image scanning ตรวจหา known vulnerabilities (CVEs) ใน Docker images — ทั้ง OS packages และ application dependencies เครื่องมือเช่น Trivy (ฟรี, เร็ว), Snyk, หรือ AWS ECR scanning ควรรันทุกครั้งที่ build image ใหม่ใน CI pipeline:

# Trivy — scan Docker image
trivy image myapp:latest
# → แสดง CVE ทั้งหมดในทุก layer

# Trivy scan ก่อน push ใน CI/CD
trivy image --exit-code 1 --severity HIGH,CRITICAL myapp:latest
# → fail build ถ้าเจอ HIGH หรือ CRITICAL

# Snyk Container
snyk container test myapp:latest

# Docker Scout (built-in)
docker scout cves myapp:latest

Runtime Security

แม้ image จะ clean แต่ runtime ก็ต้อง secure — ใช้ read-only filesystem (ป้องกัน malware เขียนไฟล์), drop capabilities (ลดสิทธิ์ที่ไม่จำเป็น), ตั้ง resource limits (ป้องกัน DoS), และ no-new-privileges (ป้องกัน privilege escalation):

# ใช้ read-only filesystem
docker run --read-only --tmpfs /tmp myapp

# จำกัด resources
docker run --memory=512m --cpus=1.0 myapp

# Drop capabilities
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE myapp

# No new privileges
docker run --security-opt=no-new-privileges myapp

# Kubernetes Pod Security
apiVersion: v1
kind: Pod
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    fsGroup: 1000
  containers:
  - name: app
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop: ["ALL"]

🔒 SSL/TLS — เข้ารหัส Traffic

SSL/TLS เข้ารหัส traffic ระหว่าง client กับ server — ถ้าไม่มี ข้อมูลทั้งหมด (passwords, credit cards, personal data) ถูกส่งแบบ plaintext ใครก็ดักอ่านได้! ปัจจุบัน HTTPS เป็น mandatory — browsers แสดง "Not Secure" สำหรับ HTTP, Google ลด SEO ranking, และ modern APIs ส่วนใหญ่บังคับ HTTPS ใช้ Let's Encrypt ได้ฟรี:

# Let's Encrypt — SSL ฟรี!
# ติดตั้ง certbot
sudo apt install certbot python3-certbot-nginx

# ขอ certificate
sudo certbot --nginx -d example.com -d www.example.com

# Auto-renew (certbot ตั้ง cron ให้อัตโนมัติ)
sudo certbot renew --dry-run

# ตรวจสอบ SSL
curl -vI https://example.com 2>&1 | grep -A5 "SSL connection"
openssl s_client -connect example.com:443 -brief

Nginx SSL Config (Best Practice)

Config ด้านล่างแสดง Nginx SSL ที่ได้เกรด A+ จาก SSL Labs — ใช้ TLS 1.2+ เท่านั้น (ปิด SSLv3, TLS 1.0, 1.1 ที่มีช่องโหว่), เปิด HSTS (บังคับ HTTPS), และ redirect HTTP → HTTPS อัตโนมัติ:

server {
    listen 443 ssl http2;
    server_name example.com;

    # Certificate
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Modern TLS only
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;

    # Security headers
    add_header Strict-Transport-Security "max-age=63072000" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "DENY" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Content-Security-Policy "default-src 'self'" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
}

🔍 Security Scanning ใน CI/CD

แทนที่จะตรวจ security ด้วยมือ ให้ automate ทุกอย่างใน CI/CD pipeline — ทุก PR จะถูกสแกนอัตโนมัติ ถ้าพบ critical vulnerabilities → PR merge ไม่ได้ ครอบคลุม 5 ด้าน: secret scanning, dependency audit, SAST, container scanning, และ IaC scanning:

GitHub Actions Security Pipeline

ตัวอย่าง pipeline ที่รัน security scans ทั้งหมดแบบ parallel — Gitleaks หา secrets, npm audit หา vulnerable dependencies, CodeQL หา code vulnerabilities, Trivy สแกน Docker image, และ tfsec สแกน Terraform configs:

# .github/workflows/security.yml
name: Security Scan
on: [push, pull_request]

jobs:
  security:
    runs-on: ubuntu-latest
    steps:
      # 1. Secret Scanning
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Gitleaks
        uses: gitleaks/gitleaks-action@v2

      # 2. Dependency Scanning (SCA)
      - name: npm audit
        run: npm audit --audit-level=high

      # 3. Static Analysis (SAST)
      - name: CodeQL Analysis
        uses: github/codeql-action/analyze@v3

      # 4. Container Scanning
      - name: Trivy scan
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: myapp:${{ github.sha }}
          severity: HIGH,CRITICAL
          exit-code: 1

      # 5. Infrastructure Scanning
      - name: tfsec (Terraform)
        uses: aquasecurity/[email protected]

Security Testing Types

การทำ security testing ใน CI/CD มีหลายแบบที่ทำงานร่วมกันเป็น defense in depth แต่ละประเภทมีจุดแข็งและจุดอ่อนต่างกัน การเข้าใจความแตกต่างจะช่วยให้เราเลือกใช้ tools ได้เหมาะสมและไม่ทำ security testing ซ้ำซ้อนหรือเสียเวลาโดยเปล่าประโยชน์

Typeตัวย่อทำอะไรเครื่องมือ
Static AnalysisSASTวิเคราะห์ source codeCodeQL, SonarQube, Semgrep
Dependency ScanSCAตรวจ library ที่มี CVEDependabot, Snyk, npm audit
Dynamic TestingDASTโจมตีแอปจริงๆOWASP ZAP, Burp Suite
Container Scan—ตรวจ Docker imageTrivy, Snyk Container
IaC Scan—ตรวจ Terraform/K8s configtfsec, Checkov, kube-bench
Secret Scan—หา secrets ที่หลุดGitleaks, TruffleHog

SAST (Static Application Security Testing) ทำงานโดยวิเคราะห์ source code โดยไม่ต้องรัน application จริง ข้อดีคือเร็วและสามารถหาช่องโหว่ได้ตั้งแต่เริ่มเขียน code แต่ข้อเสียคืออาจมี false positives สูงและไม่สามารถตรวจหาปัญหาที่เกิดจาก runtime behavior ได้ SCA (Software Composition Analysis) เน้นไปที่ third-party dependencies ซึ่งในปัจจุบันคิดเป็นส่วนใหญ่ของ modern applications

DAST (Dynamic Application Security Testing) ทำงานโดยจำลองการโจมตี application ที่กำลังรันอยู่ ข้อดีคือสามารถหาปัญหาที่ SAST หาไม่ได้ เช่น configuration issues หรือ runtime-specific vulnerabilities แต่ข้อเสียคือใช้เวลานานและต้องมี running application Container scanning, IaC scanning, และ secret scanning เป็น specialized tools สำหรับ cloud-native environments ที่ complement SAST/DAST แต่ focus ไปที่ attack vectors ใหม่ๆ ที่เกิดจาก containerization และ infrastructure as code


🏰 Server Hardening

Server Hardening คือกระบวนการลด attack surface ของ server โดยการปิดใช้งาน services ที่ไม่จำเป็น กำหนด security configurations และติดตั้ง defensive mechanisms ต่างๆ เป็นขั้นตอนสำคัญที่ทุก server ที่จะเปิดให้ public access ต้องทำก่อน deploy applications ขึ้นไป

หลักการของ server hardening ยึดตาม defense in depth และ principle of least privilege นั่นคือสร้างการป้องกันหลายชั้น และให้สิทธิ์เฉพาะที่จำเป็นเท่านั้น การ hardening ที่ดีต้องสมดุลระหว่างความปลอดภัยและ usability - ป้องกันดี แต่ไม่ทำให้การบริหารจัดการ server ยากจนเกินไป

#!/bin/bash
# server-hardening.sh — Basic hardening for Ubuntu

# 1. Update everything
sudo apt update && sudo apt upgrade -y

# 2. Create non-root user
sudo adduser deploy
sudo usermod -aG sudo deploy

# 3. SSH hardening
sudo sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
sudo sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo systemctl restart sshd

# 4. Firewall
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp      # SSH
sudo ufw allow 80/tcp      # HTTP
sudo ufw allow 443/tcp     # HTTPS
sudo ufw enable

# 5. Fail2ban — บล็อก brute force
sudo apt install fail2ban -y
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo systemctl enable --now fail2ban

# 6. Automatic security updates
sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure -plow unattended-upgrades

# 7. Disable unused services
sudo systemctl disable bluetooth
sudo systemctl disable cups

Script นี้ครอบคลุม hardening พื้นฐานที่จำเป็น ขั้นแรกคือการ update ระบบให้เป็นปัจจุบันเพื่อได้ security patches ล่าสุด การสร้าง non-root user สำหรับการจัดการประจำป้องกันไม่ให้ต้องใช้ root account ซึ่งมีสิทธิ์เต็มระบบ SSH hardening ปิดการ login ด้วย root และบังคับให้ใช้ SSH keys แทน passwords ซึ่งป้องกัน brute force attacks ได้ดีกว่า

UFW (Uncomplicated Firewall) เป็น interface ที่ใช้งานง่ายสำหรับ iptables ซึ่งเป็น Linux firewall การตั้งค่าให้ deny incoming ทั้งหมดแล้วอนุญาตเฉพาะ ports ที่จำเป็นเป็นการใช้ default deny principle Fail2ban ทำหน้าที่ monitor logs แล้ว automatically ban IP addresses ที่มี suspicious activities เช่น failed login attempts หลายครั้ง

Automatic security updates ช่วยให้ระบบได้รับ security patches โดยอัตโนมัติโดยไม่ต้องรอให้ administrator มา manually update การ disable unused services ลด attack surface โดยการปิด services ที่ไม่จำเป็น เช่น Bluetooth หรือ printing services บน server ที่ไม่ต้องใช้ features เหล่านี้ ซึ่งเป็นการประยุกต์ใช้หลักการ minimize attack surface


🔐 Supply Chain Security

ช่องโหว่ไม่ได้มาจาก code เราเสมอ — มาจาก dependencies ที่เราใช้!

Supply Chain Security เป็นเรื่องที่ได้รับความสนใจมากขึ้นในช่วงหลังๆ เนื่องจาก modern applications ส่วนใหญ่ใช้ open source libraries และ dependencies จำนวนมาก แต่ละ library อาจมี sub-dependencies อีกหลายต่อ ทำให้เกิด dependency tree ที่ซับซ้อนและยากต่อการควบคุม

ปัญหาคือ dependencies เหล่านี้กลายเป็น attack vector ใหม่ที่ attackers สามารถใช้โจมตีได้ ไม่ว่าจะเป็นการ inject malicious code เข้าไปใน popular packages (เช่น กรณี event-stream ใน npm) การใช้ typosquatting ล่อให้ developers install packages ที่มีชื่อคล้ายๆ หรือการ compromise maintainer accounts ของ popular libraries การป้องกัน supply chain attacks ต้องใช้แนวทางหลายด้าน

# 1. Pin versions (อย่าใช้ latest!)
# ❌ BAD
FROM node:latest
npm install express

# ✅ GOOD
FROM node:20.11.1-alpine3.19@sha256:abc123...
npm install [email protected]

# 2. Lock files — commit เสมอ!
# package-lock.json (Node.js)
# Pipfile.lock (Python)
# go.sum (Go)
# Gemfile.lock (Ruby)

# 3. npm audit / pip audit
npm audit --production
pip audit

# 4. Dependabot (GitHub)
# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
    open-pull-requests-limit: 10

# 5. ตรวจสอบ package ก่อนติดตั้ง
npm info suspicious-package
npx is-my-node-vulnerable

การ pin versions เป็นวิธีการป้องกันที่สำคัญที่สุด การใช้ `latest` tags หรือ wildcard versions (เช่น `^1.0.0`) อาจดูสะดวก แต่เสี่ยงต่อการได้ malicious updates โดยไม่ได้ตั้งใจ การใช้ SHA256 digests ใน container images ยิ่งเพิ่มความมั่นใจว่าเราได้ image ที่เป็นตัวเดียวกันกับที่เราเทสไว้

Lock files เป็นกลไกสำคัญในการทำ reproducible builds และป้องกัน unexpected dependency changes แต่หลายๆ ทีมลืม commit lock files เข้า Git หรือ ignore พวกมัน ทำให้แต่ละครั้งที่ build อาจได้ dependency versions ที่ต่างกัน npm audit และ Dependabot ช่วยตรวจสอบ known vulnerabilities และสร้าง automated pull requests เพื่อ update vulnerable packages แต่ต้องระวังเรื่อง alert fatigue และต้องมีกระบวนการ review updates อย่างถูกต้อง


📋 Security Checklist สำหรับ DevOps

Security checklist เป็นเครื่องมือสำคัญในการทำให้ security practices เป็น systematic และ repeatable ไม่ใช่แค่การพึ่งพา tribal knowledge หรือความจำของแต่ละคน การมี checklist ที่ชัดเจนช่วยให้ทีมสามารถ audit security posture ได้เป็นระยะ และช่วยให้ onboarding สมาชิกใหม่ทำได้ง่ายขึ้น

Checklist นี้ออกแบบมาให้ครอบคลุมพื้นที่สำคัญของ DevSecOps โดยแบ่งเป็นหมวดหมู่ตาม technical domains แต่ละรายการควรจะมี acceptance criteria ที่ชัดเจน และ verification method ที่สามารถ automate ได้เมื่อเป็นไปได้ ไม่ควรใช้ checklist นี้เป็น one-size-fits-all แต่ควร customize ตามความต้องการและ threat model ของแต่ละองค์กร

🔐 Secrets

  • ☐ ไม่มี secrets ใน code/Git
  • ☐ ใช้ Vault / Secrets Manager
  • ☐ เปิด Git secret scanning
  • ☐ Rotate secrets เป็นประจำ

🐳 Containers

  • ☐ ไม่รัน container เป็น root
  • ☐ ใช้ minimal base image (Alpine)
  • ☐ Pin image versions + digest
  • ☐ Scan images ก่อน deploy

🌐 Network

  • ☐ HTTPS ทุก endpoint (Let's Encrypt)
  • ☐ Security headers ครบ
  • ☐ Firewall — เปิดเฉพาะ port ที่จำเป็น
  • ☐ Database ไม่เปิด public

🔑 Access

  • ☐ SSH key only (ปิด password)
  • ☐ ปิด root login
  • ☐ MFA ทุก service (GitHub, AWS, etc.)
  • ☐ Least privilege — ให้สิทธิ์เท่าที่จำเป็น

🔄 CI/CD

  • ☐ SAST + SCA ใน pipeline
  • ☐ Image scan ก่อน push
  • ☐ IaC scan (tfsec, Checkov)
  • ☐ Signed commits + artifacts

📊 Monitoring

  • ☐ Audit logging ทุก action สำคัญ
  • ☐ Alert เมื่อ login ผิดพลาดหลายครั้ง
  • ☐ Monitor CVE ใน dependencies
  • ☐ Incident response plan พร้อม

การใช้ checklist อย่างมีประสิทธิภาพต้องมีการ review และ update เป็นระยะ เพราะ threat landscape เปลี่ยนแปลงตลอดเวลา และ technology stacks ก็พัฒนาไปเรื่อยๆ นอกจากนี้ควรมีการ track compliance rate และ identify ข้อที่ทีมทำได้ยากหรือ skip บ่อยๆ เพื่อ improve processes หรือ tooling การมี automated checking เมื่อเป็นไปได้จะช่วยลด manual workload และเพิ่มความน่าเชื่อถือของการ audit


สรุป

DevSecOps ไม่ใช่แค่ buzzword หรือ trend ที่จะผ่านไป แต่เป็น evolution ที่จำเป็นของ software development ในยุคที่ cyber threats เพิ่มขึ้นทั้งความซับซ้อนและความถี่ การนำ security เข้ามาเป็นส่วนหนึ่งของ development workflow ไม่ใช่แค่การเพิ่ม steps ใน pipeline แต่เป็นการเปลี่ยน mindset ของทั้งทีม

สิ่งสำคัญที่สุดคือการเริ่มต้นจากพื้นฐาน ไม่ต้องทำทุกอย่างพร้อมกัน เริ่มจากการไม่ commit secrets เข้า Git ใช้ HTTPS ทุกที่ และติดตั้ง basic security scanning ใน CI/CD แล้วค่อยๆ เพิ่ม sophistication ไปเรื่อยๆ การสร้าง security culture ที่ดีสำคัญกว่าการมี expensive security tools

  • DevSecOps = ฝัง security ในทุกขั้นตอน ไม่ใช่แค่ตอนท้าย (Shift Left)
  • OWASP Top 10 = ช่องโหว่ที่พบบ่อยสุด — ต้องรู้และป้องกัน
  • Secrets = อย่าเก็บใน code! ใช้ Vault, Secrets Manager, environment variables
  • Container Security = non-root, minimal image, scan ด้วย Trivy
  • SSL/TLS = HTTPS ทุก endpoint + security headers ครบ
  • CI/CD Security = SAST + SCA + image scan + secret scan ใน pipeline
  • Supply Chain = pin versions, lock files, Dependabot
  • Server Hardening = firewall, fail2ban, no root, auto updates 🔐🐕

DevOps & Vibe Coding series นี้เริ่มจาก Git basics และตามด้วย Docker, Kubernetes, Infrastructure as Code, Monitoring, จนมาถึง Security ในโพสต์สุดท้าย แต่ความจริงแล้ว security ไม่ใช่ "เรื่องสุดท้าย" แต่ควรเป็นเรื่องที่อยู่ใน DNA ของทุก practices ที่เราได้เรียนรู้มา ไม่ว่าจะเป็นการเขียน Dockerfiles, การออกแบบ Kubernetes manifests, หรือการเขียน Terraform configs

การเป็น DevOps Engineer ที่ดีในยุคปัจจุบันต้องคิด security first ไม่ใช่ security last เพราะในโลกที่ everything is connected และ attacks automated การมี security mindset จะช่วยป้องกันปัญหาใหญ่ที่อาจเกิดขึ้นกับธุรกิจและผู้ใช้งานของเรา 🔐🐕

Across the previous nine posts we have worked through DevOps from Git, Docker, K8s and IaC to Monitoring and Linux.

But what happens once all of it is deployed to production and you get hacked? User data leaks? The server is taken over? 😱

Security is not "the security team's business" — it is the business of everyone who deploys code to production. This is DevSecOps — security embedded in every stage of DevOps 🔐🐕


What Is DevSecOps?

DevSecOps = Development + Security + Operations — instead of checking security at the very end, you do it from the very beginning:

📝 Plan
Threat modeling
→
💻 Code
Secret scanning
→
🔨 Build
SAST + SCA
→
🧪 Test
DAST + Pen test
→
🚀 Deploy
Image scanning
→
📊 Monitor
Runtime protection
💡 Shift Left: the sooner you find a bug or a vulnerability, the cheaper it is to fix — catching it at code time costs 100 times less than catching it in production!

OWASP Top 10 — The Greatest Hits

The OWASP Top 10 is the list of the vulnerabilities most commonly found in web applications:

#VulnerabilityExamplePrevention
A01Broken Access ControlUser A can reach User B's dataRBAC, JWT validation
A02Cryptographic FailuresPasswords stored in plain textbcrypt, AES-256, TLS
A03InjectionSQL Injection, XSSParameterized queries, sanitize
A04Insecure DesignNo rate limit on loginThreat modeling, design review
A05Security MisconfigurationDebug mode on productionHardening, IaC scan
A06Vulnerable ComponentsAn old library carrying a CVESCA, Dependabot
A07Auth FailuresWeak password, no MFAMFA, strong password policy
A08Data Integrity FailuresUntrusted CI/CD pluginsVerify signatures, pin versions
A09Logging FailuresLogin attempts are never loggedAudit logging, SIEM
A10SSRFThe server calls an internal API on the attacker's behalfAllowlist, network segmentation

🔑 Secrets Management — Never Keep a Secret in Your Code!

Secrets management is the safe handling of confidential material — passwords, API keys, tokens, certificates. The most common mistake by far is to hardcode secrets into source code and commit them to Git — and even if you delete them later, Git history still keeps them! Here is what that usually looks like:

❌ Never, ever do this!
# ❌ WRONG — secret in the code
DATABASE_URL="postgres://admin:P@[email protected]:5432/mydb"
API_KEY="sk-1234567890abcdef"

# ❌ WRONG — secret in the Dockerfile
ENV AWS_SECRET_KEY=AKIAxxxxxxxxxxx

# ❌ WRONG — secret in docker-compose.yml
environment:
  - DB_PASSWORD=mysecretpassword

✅ The Right Way

Instead of hardcoding, use environment variables or a secrets manager (AWS Secrets Manager, HashiCorp Vault, GitHub Secrets). Keeping secrets outside the code means you can rotate them without touching the code, and they never enter Git:

# 1. Environment Variables (the simplest)
export DB_PASSWORD="$(cat /run/secrets/db_password)"

# 2. Docker Secrets
echo "mysecretpassword" | docker secret create db_password -
# → use it in docker-compose:
services:
  app:
    secrets:
      - db_password
secrets:
  db_password:
    external: true

# 3. HashiCorp Vault (production-grade)
vault kv put secret/myapp db_password="P@ssw0rd"
vault kv get secret/myapp

# 4. AWS Secrets Manager / GCP Secret Manager
aws secretsmanager get-secret-value --secret-id myapp/db_password

# 5. .env file (dev only — never commit it!)
# .gitignore must contain:
.env
.env.local
*.pem
*.key

Git Secret Scanning

However careful you are, secrets sometimes slip into Git anyway. Secret scanning catches them automatically — both as a pre-commit hook (stopping them before the commit) and in the CI pipeline (re-checking every PR). Tools such as Gitleaks and GitHub Secret Scanning look for the patterns of API keys, tokens and passwords:

# install git-secrets to stop secrets being committed
git secrets --install
git secrets --register-aws     # check for AWS keys

# use truffleHog to scan the whole repo
trufflehog git https://github.com/myorg/myrepo

# use gitleaks
gitleaks detect --source . --verbose

# GitHub: turn on Secret Scanning (Settings → Security)
# → alerts you the moment a secret lands in the repo

🐳 Container Security

Containers carry security risk at several points — the base image may hold vulnerabilities, the Dockerfile may expose secrets, the runtime may hand out far too much privilege (root access). Securing containers means working at all three levels: build time (Dockerfile best practices), registry (image scanning) and runtime (least privilege, read-only filesystem):

Dockerfile Best Practices

A safe Dockerfile must use specific version tags (not latest), run as a non-root user, use multi-stage builds to shrink both the size and the attack surface — and never COPY secrets into the image:

# ❌ BAD — runs as root, huge image, no pinned version
FROM node:latest
COPY . .
RUN npm install
CMD ["node", "app.js"]

# ✅ GOOD — non-root, minimal image, pinned version, multi-stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

FROM node:20-alpine
RUN addgroup -S app && adduser -S app -G app
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY --chown=app:app . .
USER app
EXPOSE 3000
HEALTHCHECK CMD curl -f http://localhost:3000/health || exit 1
CMD ["node", "app.js"]

Image Scanning

Image scanning hunts for known vulnerabilities (CVEs) inside Docker images — both OS packages and application dependencies. Tools such as Trivy (free, fast), Snyk or AWS ECR scanning should run every time a new image is built in the CI pipeline:

# Trivy — scan Docker image
trivy image myapp:latest
# → lists every CVE in every layer

# Trivy scan before pushing, inside CI/CD
trivy image --exit-code 1 --severity HIGH,CRITICAL myapp:latest
# → fail the build on any HIGH or CRITICAL

# Snyk Container
snyk container test myapp:latest

# Docker Scout (built-in)
docker scout cves myapp:latest

Runtime Security

Even a clean image needs a secure runtime — use a read-only filesystem (so malware cannot write files), drop capabilities (remove privileges you do not need), set resource limits (to blunt DoS) and enable no-new-privileges (to block privilege escalation):

# use a read-only filesystem
docker run --read-only --tmpfs /tmp myapp

# cap the resources
docker run --memory=512m --cpus=1.0 myapp

# Drop capabilities
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE myapp

# No new privileges
docker run --security-opt=no-new-privileges myapp

# Kubernetes Pod Security
apiVersion: v1
kind: Pod
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    fsGroup: 1000
  containers:
  - name: app
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop: ["ALL"]

🔒 SSL/TLS — Encrypting Traffic

SSL/TLS encrypts the traffic between client and server — without it, everything (passwords, credit cards, personal data) travels as plaintext and anyone on the path can read it! Today HTTPS is mandatory — browsers label HTTP pages "Not Secure", Google lowers their SEO ranking, and most modern APIs simply require HTTPS. Let's Encrypt gives you a certificate for free:

# Let's Encrypt — free SSL!
# install certbot
sudo apt install certbot python3-certbot-nginx

# request the certificate
sudo certbot --nginx -d example.com -d www.example.com

# Auto-renew (certbot sets the cron job up for you)
sudo certbot renew --dry-run

# check the SSL
curl -vI https://example.com 2>&1 | grep -A5 "SSL connection"
openssl s_client -connect example.com:443 -brief

Nginx SSL Config (Best Practice)

The config below is the Nginx SSL setup that earns an A+ from SSL Labs — TLS 1.2 and above only (SSLv3, TLS 1.0 and 1.1, all of them vulnerable, are switched off), HSTS enabled (forcing HTTPS), and an automatic HTTP → HTTPS redirect:

server {
    listen 443 ssl http2;
    server_name example.com;

    # Certificate
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Modern TLS only
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;

    # Security headers
    add_header Strict-Transport-Security "max-age=63072000" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "DENY" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Content-Security-Policy "default-src 'self'" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
}

🔍 Security Scanning in CI/CD

Rather than checking security by hand, automate all of it in the CI/CD pipeline — every PR is scanned automatically, and if a critical vulnerability turns up the PR cannot be merged. Five areas are covered: secret scanning, dependency audit, SAST, container scanning and IaC scanning:

GitHub Actions Security Pipeline

Here is a pipeline that runs every security scan in parallel — Gitleaks for secrets, npm audit for vulnerable dependencies, CodeQL for code vulnerabilities, Trivy for the Docker image, and tfsec for the Terraform configs:

# .github/workflows/security.yml
name: Security Scan
on: [push, pull_request]

jobs:
  security:
    runs-on: ubuntu-latest
    steps:
      # 1. Secret Scanning
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Gitleaks
        uses: gitleaks/gitleaks-action@v2

      # 2. Dependency Scanning (SCA)
      - name: npm audit
        run: npm audit --audit-level=high

      # 3. Static Analysis (SAST)
      - name: CodeQL Analysis
        uses: github/codeql-action/analyze@v3

      # 4. Container Scanning
      - name: Trivy scan
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: myapp:${{ github.sha }}
          severity: HIGH,CRITICAL
          exit-code: 1

      # 5. Infrastructure Scanning
      - name: tfsec (Terraform)
        uses: aquasecurity/[email protected]

Security Testing Types

Security testing in CI/CD comes in several kinds that work together as defense in depth. Each has its own strengths and blind spots, and understanding the differences is what lets you pick the right tools instead of duplicating coverage and wasting time for nothing.

TypeAcronymWhat it doesTools
Static AnalysisSASTAnalyzes the source codeCodeQL, SonarQube, Semgrep
Dependency ScanSCAFinds libraries carrying a CVEDependabot, Snyk, npm audit
Dynamic TestingDASTActually attacks the running appOWASP ZAP, Burp Suite
Container Scan—Inspects the Docker imageTrivy, Snyk Container
IaC Scan—Inspects Terraform/K8s configtfsec, Checkov, kube-bench
Secret Scan—Hunts for leaked secretsGitleaks, TruffleHog

SAST (Static Application Security Testing) works by analyzing source code without ever running the application. The upside is speed, and the ability to find holes from the moment you start writing code; the downside is a high false-positive rate and blindness to anything that only shows up in runtime behavior. SCA (Software Composition Analysis) focuses on third-party dependencies, which today make up the bulk of most modern applications.

DAST (Dynamic Application Security Testing) works by simulating attacks against a running application. The upside is that it finds what SAST cannot — configuration issues, runtime-specific vulnerabilities; the downside is that it takes a long time and needs a running application. Container scanning, IaC scanning and secret scanning are specialized tools for cloud-native environments: they complement SAST and DAST, but focus on the newer attack vectors that containerization and infrastructure as code have opened up.


🏰 Server Hardening

Server hardening is the process of shrinking a server's attack surface — disabling services you do not need, setting security configurations and installing defensive mechanisms. It is a step every server that will face the public must go through before any application is deployed onto it.

The principles behind hardening are defense in depth and least privilege: build several layers of protection, and grant only the privileges that are actually needed. Good hardening balances security against usability — protecting the machine well without making it so awkward to administer that nobody wants to touch it.

#!/bin/bash
# server-hardening.sh — Basic hardening for Ubuntu

# 1. Update everything
sudo apt update && sudo apt upgrade -y

# 2. Create non-root user
sudo adduser deploy
sudo usermod -aG sudo deploy

# 3. SSH hardening
sudo sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
sudo sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo systemctl restart sshd

# 4. Firewall
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp      # SSH
sudo ufw allow 80/tcp      # HTTP
sudo ufw allow 443/tcp     # HTTPS
sudo ufw enable

# 5. Fail2ban — block brute force
sudo apt install fail2ban -y
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo systemctl enable --now fail2ban

# 6. Automatic security updates
sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure -plow unattended-upgrades

# 7. Disable unused services
sudo systemctl disable bluetooth
sudo systemctl disable cups

This script covers the hardening basics. The first step is updating the system so it carries the latest security patches. Creating a non-root user for day-to-day administration removes the need to work from the root account, which holds full control of the machine. SSH hardening disables root login and forces SSH keys instead of passwords, which defends against brute-force attacks far better.

UFW (Uncomplicated Firewall) is a friendly interface to iptables, the Linux firewall. Denying all incoming traffic and then allowing only the ports you need is the default-deny principle in practice. Fail2ban monitors the logs and automatically bans IP addresses showing suspicious activity, such as repeated failed login attempts.

Automatic security updates keep the system patched without waiting for an administrator to do it by hand. Disabling unused services shrinks the attack surface by turning off what the machine does not need — Bluetooth, say, or printing services on a server that will never use them. That is the minimize-attack-surface principle applied.


🔐 Supply Chain Security

Vulnerabilities do not always come from our own code — they come from the dependencies we use!

Supply chain security has drawn a great deal more attention lately, because most modern applications lean on a large number of open source libraries and dependencies. Each library may pull in several sub-dependencies of its own, producing a dependency tree that is both complex and hard to control.

The problem is that these dependencies have become a new attack vector. Attackers inject malicious code into popular packages (the event-stream case on npm, for instance), use typosquatting to lure developers into installing packages with confusingly similar names, or compromise the maintainer accounts of popular libraries. Defending against supply chain attacks takes several approaches at once.

# 1. Pin versions (never use latest!)
# ❌ BAD
FROM node:latest
npm install express

# ✅ GOOD
FROM node:20.11.1-alpine3.19@sha256:abc123...
npm install [email protected]

# 2. Lock files — always commit them!
# package-lock.json (Node.js)
# Pipfile.lock (Python)
# go.sum (Go)
# Gemfile.lock (Ruby)

# 3. npm audit / pip audit
npm audit --production
pip audit

# 4. Dependabot (GitHub)
# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
    open-pull-requests-limit: 10

# 5. inspect a package before you install it
npm info suspicious-package
npx is-my-node-vulnerable

Pinning versions is the single most important defense. Using `latest` tags or wildcard versions (`^1.0.0`, say) may look convenient, but it risks pulling in a malicious update without meaning to. Using SHA256 digests for container images goes further still, guaranteeing that the image you get is the exact one you tested.

Lock files are the key mechanism for reproducible builds and for preventing unexpected dependency changes — yet plenty of teams forget to commit them to Git, or ignore them outright, so every build may resolve to different dependency versions. npm audit and Dependabot check for known vulnerabilities and open automated pull requests to update the vulnerable packages, but watch out for alert fatigue: you need a proper process for reviewing those updates.


📋 A Security Checklist for DevOps

A security checklist is the tool that turns security practice into something systematic and repeatable, rather than something that depends on tribal knowledge and whatever each person happens to remember. A clear checklist lets the team audit its security posture at regular intervals, and it makes onboarding new members considerably easier.

This checklist is designed to cover the important ground in DevSecOps, grouped by technical domain. Every item should have clear acceptance criteria and a verification method that can be automated wherever possible. Do not treat it as one-size-fits-all — customize it to the needs and the threat model of your own organization.

🔐 Secrets

  • ☐ No secrets in the code or in Git
  • ☐ Vault / Secrets Manager in use
  • ☐ Git secret scanning switched on
  • ☐ Secrets rotated on a regular schedule

🐳 Containers

  • ☐ No container runs as root
  • ☐ A minimal base image (Alpine)
  • ☐ Image versions pinned, with a digest
  • ☐ Images scanned before deploy

🌐 Network

  • ☐ HTTPS on every endpoint (Let's Encrypt)
  • ☐ A complete set of security headers
  • ☐ Firewall — only the necessary ports open
  • ☐ The database is not exposed publicly

🔑 Access

  • ☐ SSH keys only (passwords disabled)
  • ☐ Root login disabled
  • ☐ MFA on every service (GitHub, AWS, etc.)
  • ☐ Least privilege — only the rights actually needed

🔄 CI/CD

  • ☐ SAST + SCA in the pipeline
  • ☐ Image scan before push
  • ☐ IaC scan (tfsec, Checkov)
  • ☐ Signed commits + artifacts

📊 Monitoring

  • ☐ Audit logging on every significant action
  • ☐ An alert when logins fail repeatedly
  • ☐ CVE monitoring across dependencies
  • ☐ An incident response plan ready to go

Using a checklist well means reviewing and updating it periodically, because the threat landscape shifts constantly and technology stacks keep moving too. Beyond that, track the compliance rate and identify the items the team finds hard or skips often, so you can improve the process or the tooling behind them. Automated checking, wherever it is possible, cuts the manual workload and makes the audit itself more trustworthy.


Summary

DevSecOps is not a buzzword or a passing trend. It is a necessary evolution of software development in an era where cyber threats are growing in both sophistication and frequency. Bringing security into the development workflow is not merely a matter of adding steps to a pipeline — it is a change of mindset across the whole team.

The most important thing is to start from the fundamentals. You do not have to do everything at once. Begin by not committing secrets to Git, use HTTPS everywhere, and install basic security scanning in CI/CD — then add sophistication gradually. Building a good security culture matters more than owning expensive security tools.

  • DevSecOps = security embedded at every stage, not bolted on at the end (Shift Left)
  • OWASP Top 10 = the most commonly found vulnerabilities — know them and defend against them
  • Secrets = never keep them in the code! Use Vault, a Secrets Manager, environment variables
  • Container Security = non-root, minimal image, scanned with Trivy
  • SSL/TLS = HTTPS on every endpoint + a complete set of security headers
  • CI/CD Security = SAST + SCA + image scan + secret scan in the pipeline
  • Supply Chain = pin versions, lock files, Dependabot
  • Server Hardening = firewall, fail2ban, no root, automatic updates 🔐🐕

This DevOps & Vibe Coding series began with Git basics and went on through Docker, Kubernetes, Infrastructure as Code and Monitoring, arriving at Security in this final post. In truth, though, security is not "the last thing" — it should live in the DNA of every practice we have covered, whether you are writing Dockerfiles, designing Kubernetes manifests or writing Terraform configs.

Being a good DevOps engineer today means thinking security first, not security last. In a world where everything is connected and attacks are automated, a security mindset is what keeps a large problem from landing on your business and your users 🔐🐕

🎉 ครบ 10 โพสต์! DevOps & Vibe Coding 2026 Series Complete! 🚀
บทความจากซีรีส์ DevOps & Vibe Coding 2026
🎉 All 10 posts done! DevOps & Vibe Coding 2026 Series Complete! 🚀
An article from the DevOps & Vibe Coding 2026 series
← Previous
Linux Command Line — คำสั่งพื้นฐานที่ DevOps ต้องรู้
Next →
Software Testing — ทดสอบยังไงให้มั่นใจก่อน Deploy