รักษาความปลอดภัย Supply Chain บน Linux: คู่มือ SBOM, Sigstore, SLSA และ DevSecOps ฉบับปี 2026

คู่มือปฏิบัติจริงสำหรับ Supply Chain Security บน Linux ครอบคลุมการสร้าง SBOM ด้วย Syft สแกนช่องโหว่ด้วย Grype ลงนามด้วย Sigstore/Cosign กรอบงาน SLSA และการสร้าง DevSecOps Pipeline พร้อมตัวอย่างโค้ดจริงที่ใช้งานได้ทันที

บทนำ: ทำไมความปลอดภัยของซัพพลายเชนซอฟต์แวร์จึงสำคัญขนาดนี้ในปี 2026

ถ้าคุณยังคิดว่าแอปพลิเคชันของคุณถูกเขียนขึ้นมา "จากศูนย์" ล่ะก็ ลองมองให้ดีอีกทีนะครับ ในโลกของการพัฒนาซอฟต์แวร์สมัยใหม่ ทุกระบบล้วนพึ่งพาไลบรารีโอเพนซอร์ส เครื่องมือบิลด์ ระบบ CI/CD และ container images จากบุคคลที่สาม ความซับซ้อนตรงนี้แหละที่กลายเป็นช่องทางให้ผู้โจมตีแทรกซึมระบบขนาดใหญ่ได้โดยที่องค์กรไม่รู้ตัวเลย

ลองนึกถึงเหตุการณ์สำคัญที่เปลี่ยนมุมมองของอุตสาหกรรมต่อ supply chain security ดูครับ การโจมตี SolarWinds ในปี 2020 ผู้โจมตีฝัง backdoor ลงในซอฟต์แวร์ Orion ก่อนที่จะถูกส่งไปยังลูกค้ากว่า 18,000 ราย รวมถึงหน่วยงานรัฐบาลสหรัฐระดับสูงสุด ต่อมาในปี 2021 ช่องโหว่ Log4Shell ใน Apache Log4j เผยให้เห็นว่าไลบรารีขนาดเล็กที่ฝังอยู่ในระบบนับล้านสามารถกลายเป็นระเบิดเวลาได้อย่างไร

และที่น่ากลัวที่สุด? เหตุการณ์ XZ Utils backdoor ในปี 2024 ที่ผู้โจมตีอดทนสร้างความน่าเชื่อถือในโปรเจกต์โอเพนซอร์สนานกว่า 2 ปี เพื่อแทรก backdoor ลงใน SSH daemon อันนี้พูดตรงๆ เลยว่าน่าขนลุกมาก

ในปี 2026 แนวโน้มได้เปลี่ยนจาก "ตั้งรับเมื่อถูกโจมตี" (reactive) ไปสู่ "ป้องกันเชิงรุก" (proactive) อย่างชัดเจน คำสั่ง EO 14028 ของสหรัฐอเมริกากำหนดให้หน่วยงานรัฐต้องใช้ Software Bill of Materials (SBOM) สำหรับซอฟต์แวร์ทุกชิ้น กรอบงาน SLSA กลายเป็นมาตรฐานอุตสาหกรรม และ Sigstore ก็ได้รับการยอมรับอย่างกว้างขวางในฐานะโครงสร้างพื้นฐานสำหรับการลงนามซอฟต์แวร์แบบ keyless

มาเลยครับ บทความนี้จะพาคุณผ่านทุกแง่มุมของ Linux Software Supply Chain Security อย่างละเอียด ตั้งแต่การสร้าง SBOM ด้วย Syft ไปจนถึงการสร้าง DevSecOps pipeline ที่ครบวงจร

ทำความเข้าใจ SBOM: รากฐานของความปลอดภัยซัพพลายเชน

SBOM คืออะไร

Software Bill of Materials (SBOM) พูดง่ายๆ ก็คือ "รายการส่วนผสม" ของซอฟต์แวร์ คล้ายกับฉลากบนกล่องอาหารที่บอกว่ามีอะไรบ้างอยู่ข้างใน SBOM ที่ดีจะประกอบด้วยข้อมูลเหล่านี้สำหรับแต่ละ component:

  • ชื่อและเวอร์ชันของ package/library
  • ผู้ผลิต (supplier) และผู้เขียน (author)
  • ใบอนุญาตการใช้งาน (license)
  • hash ของไฟล์ (checksums)
  • ความสัมพันธ์กับ component อื่น (dependencies)
  • ข้อมูล vulnerability ที่ทราบ

SPDX เทียบกับ CycloneDX

มาตรฐาน SBOM ที่ได้รับการยอมรับตอนนี้มีสองรูปแบบหลักครับ

SPDX (Software Package Data Exchange) เป็นมาตรฐาน ISO/IEC 5962:2021 พัฒนาโดย Linux Foundation ออกแบบมาเพื่อการแชร์ข้อมูล license และ compliance เป็นหลัก รองรับรูปแบบ tag-value, JSON, YAML, RDF และ spreadsheet โดย SPDX 2.3 เพิ่มการรองรับ security vulnerabilities และ SBOM relationships อย่างครบครัน

CycloneDX พัฒนาโดย OWASP มุ่งเน้นด้าน security โดยเฉพาะ มี VEX (Vulnerability Exploitability eXchange) ในตัว เหมาะมากสำหรับ DevSecOps pipeline มีโครงสร้าง JSON/XML ที่กระชับ แถม tool ecosystem ยังหลากหลายอีกด้วย

ในทางปฏิบัติสำหรับปี 2026 ต้องบอกว่า CycloneDX ได้รับความนิยมมากกว่าในกระบวนการ DevSecOps เนื่องจาก tooling ที่สมบูรณ์กว่า ส่วน SPDX มักถูกใช้เวลาต้องรายงานต่อหน่วยงานรัฐบาล

ทำไม SBOM จึงกลายเป็นข้อบังคับ

ตัวเลขนี้น่าสนใจมากครับ ตามการศึกษาของ Ponemon Institute ที่เผยแพร่ในปี 2025 องค์กรที่ใช้ SBOM อย่างเป็นระบบมี Mean Time to Remediate (MTTR) สำหรับช่องโหว่ใน dependencies สั้นกว่าองค์กรที่ไม่มี SBOM ถึง 264 วัน เลยทีเดียว เมื่อเกิดช่องโหว่อย่าง Log4Shell องค์กรที่มี SBOM พร้อมสามารถระบุได้ภายในชั่วโมงว่าระบบใดบ้างที่โดน ขณะที่องค์กรที่ไม่มีต้องใช้เวลาเป็นสัปดาห์หรือเดือนในการสำรวจ

นอกจากคำสั่ง EO 14028 ของสหรัฐฯ แล้ว สหภาพยุโรปยังกำหนดผ่าน Cyber Resilience Act ให้ผู้ผลิตซอฟต์แวร์ต้องจัดทำ SBOM สำหรับผลิตภัณฑ์ทุกชิ้นที่วางจำหน่ายในตลาด EU ภาคเอกชนเองก็เริ่มกำหนดให้ vendor ต้องส่งมอบ SBOM เป็นส่วนหนึ่งของสัญญาจัดซื้อจัดจ้างแล้ว

การสร้าง SBOM ด้วย Syft

การติดตั้ง Syft บน Linux

Syft เป็นเครื่องมือโอเพนซอร์สจาก Anchore สำหรับสร้าง SBOM รองรับ filesystems, container images, archives และ directories ได้ครบ ติดตั้งง่ายมากครับ ทำได้หลายวิธี:

# ติดตั้งผ่าน installation script (แนะนำ)
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin

# ตรวจสอบเวอร์ชัน
syft version

# ติดตั้งผ่าน package manager บน Ubuntu/Debian
wget https://github.com/anchore/syft/releases/download/v1.19.0/syft_1.19.0_linux_amd64.deb
sudo dpkg -i syft_1.19.0_linux_amd64.deb

# ติดตั้งผ่าน Homebrew (บน Linux ที่มี Linuxbrew)
brew install syft

การสร้าง SBOM จาก Filesystem

การสร้าง SBOM จากไดเรกทอรีของโปรเจกต์ทำได้ตรงไปตรงมามาก:

# สร้าง SBOM จาก directory ปัจจุบัน (รูปแบบ table สำหรับดูทั่วไป)
syft dir:/path/to/your/project

# สร้าง SBOM รูปแบบ JSON
syft dir:/path/to/your/project -o json > sbom.json

# สร้าง SBOM รูปแบบ SPDX JSON
syft dir:/path/to/your/project -o spdx-json > sbom.spdx.json

# สร้าง SBOM รูปแบบ CycloneDX JSON (แนะนำสำหรับ DevSecOps)
syft dir:/path/to/your/project -o cyclonedx-json > sbom.cdx.json

# สร้าง SBOM รูปแบบ CycloneDX XML
syft dir:/path/to/your/project -o cyclonedx-xml > sbom.cdx.xml

# สร้าง SBOM พร้อมระบุ version ของแอปพลิเคชัน
syft dir:/path/to/your/project -o cyclonedx-json \
  --source-name "my-application" \
  --source-version "v2.3.1" \
  > sbom.cdx.json

การสร้าง SBOM จาก Container Image

ส่วนนี้สำคัญมากในกระบวนการ DevSecOps เพราะ container image คือสิ่งที่ถูก deploy จริงๆ:

# สร้าง SBOM จาก container image บน Docker Hub
syft ubuntu:22.04 -o cyclonedx-json > ubuntu-sbom.cdx.json

# สร้าง SBOM จาก image ใน local Docker daemon
syft docker:my-app:latest -o cyclonedx-json > my-app-sbom.cdx.json

# สร้าง SBOM จาก image ใน OCI registry
syft registry:ghcr.io/myorg/my-app:v1.2.3 -o cyclonedx-json > my-app-sbom.cdx.json

# สร้าง SBOM จาก image tar archive
syft docker-archive:my-image.tar -o spdx-json > sbom.spdx.json

# สร้าง SBOM พร้อม output หลายรูปแบบพร้อมกัน
syft registry:ghcr.io/myorg/my-app:v1.2.3 \
  -o cyclonedx-json=sbom.cdx.json \
  -o spdx-json=sbom.spdx.json \
  -o syft-json=sbom.syft.json

การกำหนดค่า Syft ด้วยไฟล์ config

สำหรับการใช้งานใน CI/CD pipeline ผมแนะนำให้สร้างไฟล์ config เพื่อความสม่ำเสมอ แบบนี้ทุกคนในทีมก็จะได้ SBOM ออกมาในรูปแบบเดียวกัน:

# .syft.yaml
output:
  - "cyclonedx-json"
  - "spdx-json"

file:
  metadata:
    digests:
      - "sha256"
      - "sha512"

package:
  search-unindexed-archives: true
  search-indexed-archives: true

catalogers:
  enabled:
    - dpkgdb
    - rpm
    - go-module-binary
    - python
    - javascript-package
    - javascript-lock
    - java-archive
    - rust-cargo-lock
    - ruby-gemfile

การสแกนช่องโหว่ด้วย Grype

การติดตั้งและทำความเข้าใจ Grype

Grype เป็นเครื่องมือสแกนช่องโหว่จาก Anchore เช่นกัน ทำงานร่วมกับ Syft ได้อย่างราบรื่นมาก (ไม่น่าแปลกใจเลยเพราะมาจากทีมเดียวกัน) สามารถสแกนทั้ง SBOM files และ container images โดยตรง โดยฐานข้อมูลช่องโหว่รวบรวมมาจากหลายแหล่ง ได้แก่ NVD, GitHub Security Advisories, Red Hat, Debian, Ubuntu และอื่นๆ

# ติดตั้ง Grype
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin

# ตรวจสอบเวอร์ชันและอัปเดต vulnerability database
grype version
grype db update

# ดูข้อมูล database ปัจจุบัน
grype db status

การสแกน SBOM และ Container Images

# สแกนจาก SBOM file ที่สร้างไว้แล้ว (เร็วที่สุด ไม่ต้องวิเคราะห์ซ้ำ)
grype sbom:./sbom.cdx.json

# สแกน container image โดยตรง
grype ubuntu:22.04

# สแกน image จาก registry
grype registry:ghcr.io/myorg/my-app:v1.2.3

# สแกนและแสดงเฉพาะ High และ Critical severity
grype sbom:./sbom.cdx.json --fail-on high

# สแกนและ output เป็น JSON สำหรับ automated processing
grype sbom:./sbom.cdx.json -o json > vulnerabilities.json

# สแกนและ output เป็น table (อ่านง่ายกว่า)
grype sbom:./sbom.cdx.json -o table

# สแกนพร้อมกำหนด severity threshold (pipeline จะ fail ถ้าเจอ critical)
grype sbom:./sbom.cdx.json --fail-on critical
echo "Exit code: $?"

การตีความผลลัพธ์และการจัดการ False Positives

เรื่องนี้สำคัญมากครับ Grype รองรับการสร้าง ignore rules เพื่อจัดการกับ false positives หรือช่องโหว่ที่ยังไม่มี fix ไม่งั้น pipeline คุณจะ fail ตลอดเวลาโดยไม่จำเป็น:

# .grype.yaml - ไฟล์ config สำหรับ Grype
ignore:
  # ละเว้น CVE ที่ทราบว่าไม่ส่งผลกระทบต่อระบบของเรา
  - vulnerability: CVE-2023-44487
    reason: "HTTP/2 rapid reset attack - mitigated by load balancer"

  # ละเว้นตาม package และ version range
  - package:
      name: openssl
      version: "< 3.0.0"
      type: rpm
    reason: "Legacy system - scheduled for upgrade Q2 2026"

  # ละเว้น vulnerability ที่ fix ยังไม่พร้อมใช้งาน
  - vulnerability: CVE-2024-12345
    fix-state: "not-fixed"

# กำหนด severity ที่ต้องการ fail
fail-on-severity: high

# อัปเดต database อัตโนมัติ
db:
  auto-update: true

การผสาน Grype เข้ากับ CI/CD

นี่คือ script ที่ใช้งานจริงได้เลยใน CI/CD pipeline ครับ:

#!/bin/bash
# scan-and-gate.sh - Script สำหรับใช้ใน CI/CD pipeline

set -euo pipefail

IMAGE_NAME="${1:-}"
SBOM_FILE="${2:-sbom.cdx.json}"
SEVERITY_THRESHOLD="${3:-high}"

echo "=== Linux Secure Ops: Vulnerability Gate ==="
echo "Image: ${IMAGE_NAME}"
echo "Severity Threshold: ${SEVERITY_THRESHOLD}"

# อัปเดต database ล่าสุด
grype db update

# สแกนและบันทึกผลลัพธ์
SCAN_FAILED=0
grype sbom:"${SBOM_FILE}" \
  -o json \
  --fail-on "${SEVERITY_THRESHOLD}" \
  > scan-results.json 2>&1 || SCAN_FAILED=$?

# แสดงสรุปผล
jq -r '
  .matches |
  group_by(.vulnerability.severity) |
  .[] |
  "\(.[0].vulnerability.severity): \(length) vulnerabilities"
' scan-results.json

if [ "${SCAN_FAILED}" -ne 0 ]; then
  echo "FAILED: Found vulnerabilities at or above ${SEVERITY_THRESHOLD} severity"
  echo "Pipeline blocked - review and remediate before deployment"
  exit 1
fi

echo "PASSED: No blocking vulnerabilities found"

การลงนามและยืนยันด้วย Sigstore

ภาพรวมของระบบนิเวศ Sigstore

Sigstore เป็นโครงการโอเพนซอร์สที่มีเป้าหมายทำให้การลงนามซอฟต์แวร์เป็นเรื่องง่าย โดยไม่ต้องจัดการ private key อย่างซับซ้อน (ซึ่งพูดตามตรงคือปวดหัวมากสำหรับหลายๆ ทีม) ประกอบด้วยสามส่วนหลัก:

  • Cosign - เครื่องมือ command-line สำหรับลงนามและตรวจสอบ container images, artifacts และ attestations
  • Fulcio - Certificate Authority (CA) ที่ออก short-lived certificates โดยยืนยันตัวตนผ่าน OIDC จาก identity provider เช่น GitHub, Google, Microsoft
  • Rekor - Transparency log ที่บันทึกการลงนามทุกครั้งแบบ immutable คล้ายกับ Certificate Transparency log สำหรับ TLS certificates

กระบวนการ "keyless signing" ทำงานแบบนี้ครับ: นักพัฒนายืนยันตัวตนผ่าน OIDC จากนั้น Fulcio ออก short-lived certificate (อายุแค่ 10 นาที) แล้ว Cosign ก็ลงนาม artifact ด้วย private key ชั่วคราว บันทึก signature ลงใน Rekor แล้วก็ทิ้ง private key ไปเลย ทั้งหมดนี้เกิดขึ้นโดยอัตโนมัติ เจ๋งมากจริงๆ

การติดตั้งและใช้งาน Cosign

# ติดตั้ง Cosign บน Linux
curl -O -L https://github.com/sigstore/cosign/releases/download/v2.4.1/cosign-linux-amd64
chmod +x cosign-linux-amd64
sudo mv cosign-linux-amd64 /usr/local/bin/cosign

# ตรวจสอบการติดตั้ง
cosign version

# ติดตั้งผ่าน Go (สำหรับนักพัฒนา)
go install github.com/sigstore/cosign/v2/cmd/cosign@latest

Keyless Signing ด้วย OIDC

# ลงนาม container image แบบ keyless (ต้องการ OIDC token)
# ในสภาพแวดล้อม CI/CD เช่น GitHub Actions จะใช้ OIDC token อัตโนมัติ
cosign sign --yes ghcr.io/myorg/my-app:v1.2.3

# ลงนามพร้อมระบุ annotations เพิ่มเติม
cosign sign --yes \
  --annotations "git-commit=$(git rev-parse HEAD)" \
  --annotations "build-date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
  --annotations "repo=https://github.com/myorg/my-app" \
  ghcr.io/myorg/my-app:v1.2.3

# ลงนามแบบใช้ key-pair (สำหรับกรณีที่ต้องการจัดการ key เอง)
# สร้าง key pair
cosign generate-key-pair

# ลงนามด้วย private key
cosign sign --key cosign.key ghcr.io/myorg/my-app:v1.2.3

การตรวจสอบ Signature

# ตรวจสอบ signature แบบ keyless (ระบุ OIDC issuer และ identity)
cosign verify \
  --certificate-identity "https://github.com/myorg/my-app/.github/workflows/release.yml@refs/heads/main" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  ghcr.io/myorg/my-app:v1.2.3

# ตรวจสอบและแสดงผลในรูปแบบ JSON
cosign verify \
  --certificate-identity "https://github.com/myorg/my-app/.github/workflows/release.yml@refs/heads/main" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  ghcr.io/myorg/my-app:v1.2.3 \
  -o json | jq .

# ตรวจสอบด้วย public key
cosign verify --key cosign.pub ghcr.io/myorg/my-app:v1.2.3

การแนบและตรวจสอบ SBOM Attestation

นอกจากลงนาม image แล้ว Cosign ยังรองรับการแนบ attestations ด้วย ซึ่งก็คือข้อมูลที่ลงนามแล้วผูกติดกับ image เช่น SBOM หรือ SLSA provenance:

# แนบ SBOM เข้ากับ image ในรูปแบบ attestation
cosign attest --yes \
  --predicate sbom.cdx.json \
  --type cyclonedx \
  ghcr.io/myorg/my-app:v1.2.3

# แนบ SPDX SBOM
cosign attest --yes \
  --predicate sbom.spdx.json \
  --type spdxjson \
  ghcr.io/myorg/my-app:v1.2.3

# ตรวจสอบและดึง SBOM attestation
cosign verify-attestation \
  --certificate-identity "https://github.com/myorg/my-app/.github/workflows/release.yml@refs/heads/main" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  --type cyclonedx \
  ghcr.io/myorg/my-app:v1.2.3 \
  | jq -r '.payload | @base64d | fromjson | .predicate'

เฟรมเวิร์ก SLSA: มาตรฐานการรับประกันคุณภาพซอฟต์แวร์

SLSA คืออะไรและทำไมจึงสำคัญ

SLSA (Supply-chain Levels for Software Artifacts) ออกเสียงว่า "salsa" เหมือนซอสเม็กซิกันนั่นแหละครับ เป็นกรอบงานที่พัฒนาโดย Google และปัจจุบันดูแลโดย OpenSSF SLSA ให้คะแนน "ระดับความน่าเชื่อถือ" ของกระบวนการ build ตั้งแต่ L0 ถึง L3:

  • SLSA L0 (No guarantees) - ไม่มีการรับประกันใดๆ เป็นจุดเริ่มต้นสำหรับทุกโปรเจกต์
  • SLSA L1 (Provenance exists) - มี provenance document แม้จะยังไม่ได้รับการยืนยันจากระบบอิสระ ต้องใช้ scripted build process ที่ทำซ้ำได้
  • SLSA L2 (Hosted build, signed provenance) - Build ทำบน hosted CI/CD system และ provenance ถูกลงนามโดย build service อัตโนมัติ ผู้ใช้ตรวจสอบย้อนกลับได้ว่า artifact มาจากที่ไหน
  • SLSA L3 (Hardened build, non-falsifiable provenance) - ระดับสูงสุด Build environment ถูก harden อย่างเต็มที่ provenance ไม่สามารถถูกปลอมแปลงได้แม้แต่โดย build service เอง

Build Provenance คืออะไร

Provenance คือเอกสารที่ระบุว่า artifact ถูกสร้างมาจากอะไร อย่างไร ที่ไหน โดยมีการลงนามจาก build platform ประกอบด้วย:

  • Source code ที่ใช้ (commit hash, repository URL)
  • Builder ที่ใช้ (เช่น GitHub Actions runner)
  • Build instructions (workflow file)
  • Dependencies ที่ใช้ในการ build
  • Timestamp และข้อมูล environment

การบรรลุ SLSA Level 2 และ 3 ด้วย GitHub Actions

โปรเจกต์ slsa-framework/slsa-github-generator ให้ reusable workflows ที่สร้าง SLSA provenance ระดับ L3 ได้เลย:

# ติดตั้ง slsa-verifier สำหรับตรวจสอบ provenance
curl -O -L https://github.com/slsa-framework/slsa-verifier/releases/download/v2.7.0/slsa-verifier-linux-amd64
chmod +x slsa-verifier-linux-amd64
sudo mv slsa-verifier-linux-amd64 /usr/local/bin/slsa-verifier

# ตรวจสอบ provenance ของ artifact
slsa-verifier verify-artifact \
  --provenance-path provenance.intoto.jsonl \
  --source-uri github.com/myorg/my-app \
  --source-tag v1.2.3 \
  my-app-linux-amd64

# ตรวจสอบ container image provenance
slsa-verifier verify-image \
  --source-uri github.com/myorg/my-app \
  ghcr.io/myorg/my-app:v1.2.3

สำหรับ Go binary สามารถใช้ slsa-github-generator ร่วมกับ GitHub Actions ได้ดังนี้:

# .github/workflows/slsa-go-release.yml
name: SLSA Go Release
on:
  push:
    tags:
      - 'v*'

permissions: read-all

jobs:
  build:
    permissions:
      id-token: write
      contents: write
      actions: read
    uses: slsa-framework/slsa-github-generator/.github/workflows/[email protected]
    with:
      go-version: "1.23"
      config-file: ".slsa-goreleaser.yml"

in-toto Attestation Framework: รักษาความปลอดภัยทั้ง Pipeline

ภาพรวมของ in-toto

in-toto เป็น framework ที่ออกแบบมาเพื่อปกป้องกระบวนการ software supply chain ทั้งสาย ตั้งแต่ source code จนถึง deployment แนวคิดหลักคือการกำหนด "layout" ที่ระบุว่าใคร (functionaries) สามารถทำอะไร (steps) ในกระบวนการ build และทุก step ต้องมีหลักฐานที่ลงนามแล้ว

in-toto ทำงานร่วมกับ SLSA โดยเป็นส่วนหนึ่งของ attestation specification ที่ SLSA ใช้ Predicate types ที่รองรับมีหลายแบบ:

  • SLSA Provenance - ข้อมูลการ build ระบุที่มาของ artifact
  • CycloneDX/SPDX - SBOM ของซอฟต์แวร์
  • Vulnerability Scan - ผลการสแกนช่องโหว่
  • Test Results - ผลการทดสอบ
  • Code Review - หลักฐานการ review โค้ด

โครงสร้างของ in-toto Attestation

in-toto attestation ใช้รูปแบบ DSSE (Dead Simple Signing Envelope) ที่ห่อหุ้ม statement ข้างในจะมี subject (สิ่งที่ attestation อ้างถึง) และ predicate (เนื้อหาจริงๆ) มาดูตัวอย่างกันครับ:

{
  "_type": "https://in-toto.io/Statement/v1",
  "predicateType": "https://slsa.dev/provenance/v1",
  "subject": [
    {
      "name": "my-app-linux-amd64",
      "digest": {
        "sha256": "a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4"
      }
    }
  ],
  "predicate": {
    "buildDefinition": {
      "buildType": "https://slsa-framework.github.io/github-actions-buildtypes/workflow/v1",
      "externalParameters": {
        "workflow": {
          "ref": "refs/tags/v1.2.3",
          "repository": "https://github.com/myorg/my-app",
          "path": ".github/workflows/release.yml"
        }
      },
      "resolvedDependencies": [
        {
          "uri": "git+https://github.com/myorg/my-app@refs/tags/v1.2.3",
          "digest": {
            "gitCommit": "abc123def456789"
          }
        }
      ]
    },
    "runDetails": {
      "builder": {
        "id": "https://github.com/actions/runner/github-hosted"
      },
      "metadata": {
        "invocationId": "https://github.com/myorg/my-app/actions/runs/123456789",
        "startedOn": "2026-02-17T10:00:00Z",
        "finishedOn": "2026-02-17T10:05:30Z"
      }
    }
  }
}

การใช้ in-toto ร่วมกับ Cosign

Cosign รองรับ in-toto attestation โดยตรง ทำให้แนบ attestation หลายประเภทเข้ากับ container image เดียวกันได้สบายๆ:

# สร้าง in-toto attestation สำหรับ vulnerability scan
cat > vuln-attestation.json << 'ATTESTATION'
{
  "_type": "https://in-toto.io/Statement/v1",
  "predicateType": "https://cosign.sigstore.dev/attestation/vuln/v1",
  "subject": [
    {
      "name": "ghcr.io/myorg/my-app",
      "digest": {
        "sha256": "abc123..."
      }
    }
  ],
  "predicate": {
    "scanner": {
      "uri": "pkg:github/anchore/[email protected]",
      "version": "0.88.0",
      "result": {
        "critical": 0,
        "high": 2,
        "medium": 5,
        "low": 12
      }
    },
    "metadata": {
      "scanStartedOn": "2026-02-17T10:03:00Z",
      "scanFinishedOn": "2026-02-17T10:03:45Z"
    }
  }
}
ATTESTATION

# แนบ attestation เข้ากับ image
cosign attest --yes \
  --predicate vuln-attestation.json \
  --type vuln \
  ghcr.io/myorg/my-app:v1.2.3

การสร้าง DevSecOps Pipeline ที่สมบูรณ์

ภาพรวมของ Pipeline

ถึงตรงนี้เราได้เรียนรู้เครื่องมือแต่ละตัวแล้ว ทีนี้มาดูกันว่าจะรวมทุกอย่างเข้าด้วยกันเป็น pipeline ที่สมบูรณ์ได้ยังไง ลำดับขั้นตอนควรเป็นแบบนี้ครับ:

  1. Source code checkout พร้อม commit verification
  2. Dependency audit และ license compliance check
  3. Build application ด้วย hermetic build environment
  4. สร้าง container image
  5. สร้าง SBOM ด้วย Syft
  6. สแกนช่องโหว่ด้วย Grype
  7. ลงนาม image ด้วย Cosign (keyless)
  8. แนบ SBOM เป็น attestation
  9. สร้าง SLSA provenance
  10. Push ไปยัง registry
  11. ตรวจสอบ signature และ provenance ก่อน deploy

GitHub Actions Workflow ที่สมบูรณ์

นี่คือ GitHub Actions workflow ที่ผสานรวมทุกเครื่องมือเข้าด้วยกัน ค่อนข้างยาวหน่อยนะครับ แต่ทุก step มีเหตุผลทั้งหมด:

# .github/workflows/secure-release.yml
name: Secure Supply Chain Release

on:
  push:
    tags:
      - 'v*.*.*'

permissions:
  contents: read
  packages: write
  id-token: write
  attestations: write

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

jobs:
  # ===== Job 1: Build and Test =====
  build:
    name: Build Application
    runs-on: ubuntu-24.04
    outputs:
      image-digest: ${{ steps.build.outputs.digest }}
      image-tags: ${{ steps.meta.outputs.tags }}

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version: ${{ env.GO_VERSION }}
          cache: true

      - name: Run dependency audit
        run: |
          go mod verify
          go list -m all | \
            grep -v "^$(go list -m)" | \
            sort > dependency-list.txt
          echo "Dependencies verified successfully"

      - name: Run tests
        run: |
          go test -v -race -coverprofile=coverage.out ./...
          go tool cover -html=coverage.out -o coverage.html

      - name: Log in to Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract Docker metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=sha,prefix=sha-

      - name: Build and push container image
        id: build
        uses: docker/build-push-action@v6
        with:
          context: .
          platforms: linux/amd64,linux/arm64
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
          provenance: true
          sbom: false

  # ===== Job 2: SBOM Generation and Vulnerability Scanning =====
  sbom-scan:
    name: SBOM and Vulnerability Scan
    runs-on: ubuntu-24.04
    needs: build

    steps:
      - name: Install Syft
        run: |
          curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh \
            | sh -s -- -b /usr/local/bin v1.19.0

      - name: Install Grype
        run: |
          curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh \
            | sh -s -- -b /usr/local/bin v0.88.0

      - name: Log in to Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Generate SBOM (CycloneDX and SPDX)
        run: |
          IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}"
          DIGEST="${{ needs.build.outputs.image-digest }}"

          syft "${IMAGE}@${DIGEST}" \
            -o cyclonedx-json=sbom.cdx.json \
            -o spdx-json=sbom.spdx.json \
            --source-name "${{ github.repository }}" \
            --source-version "${{ github.ref_name }}"

          echo "SBOM generated successfully"
          echo "Components found: $(jq '.components | length' sbom.cdx.json)"

      - name: Scan for vulnerabilities
        id: vulnerability-scan
        run: |
          grype db update

          grype sbom:./sbom.cdx.json \
            -o json \
            > vulnerability-report.json

          echo "=== Vulnerability Summary ==="
          jq -r '.matches | group_by(.vulnerability.severity) | .[] |
            "\(.[0].vulnerability.severity): \(length)"' \
            vulnerability-report.json

          CRITICAL_COUNT=$(jq '[.matches[] |
            select(.vulnerability.severity == "Critical")] | length' \
            vulnerability-report.json)

          if [ "${CRITICAL_COUNT}" -gt 0 ]; then
            echo "FAILED: Found ${CRITICAL_COUNT} Critical vulnerabilities"
            exit 1
          fi

          echo "PASSED: No Critical vulnerabilities found"

      - name: Upload SBOM artifacts
        uses: actions/upload-artifact@v4
        with:
          name: sbom-files
          path: |
            sbom.cdx.json
            sbom.spdx.json
            vulnerability-report.json

  # ===== Job 3: Sign and Attest =====
  sign-attest:
    name: Sign Image and Create Attestations
    runs-on: ubuntu-24.04
    needs: [build, sbom-scan]

    steps:
      - name: Install Cosign
        uses: sigstore/cosign-installer@v3
        with:
          cosign-release: 'v2.4.1'

      - name: Log in to Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Download SBOM artifacts
        uses: actions/download-artifact@v4
        with:
          name: sbom-files

      - name: Sign container image (keyless)
        run: |
          IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}"
          DIGEST="${{ needs.build.outputs.image-digest }}"

          cosign sign --yes \
            --annotations "repo=${{ github.server_url }}/${{ github.repository }}" \
            --annotations "git-commit=${{ github.sha }}" \
            --annotations "git-tag=${{ github.ref_name }}" \
            --annotations "build-date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
            --annotations "workflow=${{ github.workflow }}" \
            "${IMAGE}@${DIGEST}"

          echo "Image signed successfully"

      - name: Attach SBOM attestation
        run: |
          IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}"
          DIGEST="${{ needs.build.outputs.image-digest }}"

          cosign attest --yes \
            --predicate sbom.cdx.json \
            --type cyclonedx \
            "${IMAGE}@${DIGEST}"

          echo "SBOM attestation attached successfully"

      - name: Attach vulnerability scan attestation
        run: |
          IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}"
          DIGEST="${{ needs.build.outputs.image-digest }}"

          cosign attest --yes \
            --predicate vulnerability-report.json \
            --type vuln \
            "${IMAGE}@${DIGEST}"

      - name: Generate GitHub Attestation (SLSA Provenance)
        uses: actions/attest-build-provenance@v2
        with:
          subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          subject-digest: ${{ needs.build.outputs.image-digest }}
          push-to-registry: true

  # ===== Job 4: Deployment Verification =====
  deploy-verify:
    name: Verify Before Deploy
    runs-on: ubuntu-24.04
    needs: [build, sign-attest]
    environment: production

    steps:
      - name: Install verification tools
        run: |
          curl -O -L https://github.com/sigstore/cosign/releases/download/v2.4.1/cosign-linux-amd64
          chmod +x cosign-linux-amd64
          sudo mv cosign-linux-amd64 /usr/local/bin/cosign

          curl -O -L https://github.com/slsa-framework/slsa-verifier/releases/download/v2.7.0/slsa-verifier-linux-amd64
          chmod +x slsa-verifier-linux-amd64
          sudo mv slsa-verifier-linux-amd64 /usr/local/bin/slsa-verifier

      - name: Log in to Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Verify image signature
        run: |
          IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}"
          DIGEST="${{ needs.build.outputs.image-digest }}"
          WORKFLOW_REF="${{ github.server_url }}/${{ github.repository }}/.github/workflows/secure-release.yml@${{ github.ref }}"

          cosign verify \
            --certificate-identity "${WORKFLOW_REF}" \
            --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
            "${IMAGE}@${DIGEST}"

          echo "Signature verified successfully"

      - name: Verify SLSA provenance
        run: |
          IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}"
          DIGEST="${{ needs.build.outputs.image-digest }}"

          slsa-verifier verify-image \
            --source-uri "github.com/${{ github.repository }}" \
            --source-tag "${{ github.ref_name }}" \
            "${IMAGE}@${DIGEST}"

          echo "SLSA provenance verified successfully"

      - name: Deploy to production
        run: |
          echo "All security checks passed - proceeding with deployment"
          # kubectl set image deployment/my-app \
          #   my-app="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ needs.build.outputs.image-digest }}"

GitLab CI Pipeline ทางเลือก

สำหรับทีมที่ใช้ GitLab CI/CD ก็ไม่ต้องน้อยใจนะครับ สามารถสร้าง pipeline เทียบเท่าได้เลย:

# .gitlab-ci.yml
stages:
  - build
  - sbom
  - scan
  - sign
  - deploy

variables:
  REGISTRY: registry.gitlab.com
  IMAGE_PATH: ${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG}
  COSIGN_VERSION: "v2.4.1"
  SYFT_VERSION: "v1.19.0"
  GRYPE_VERSION: "v0.88.0"

build-image:
  stage: build
  image: docker:26
  services:
    - docker:26-dind
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker build -t ${IMAGE_PATH} .
    - docker push ${IMAGE_PATH}
    - docker inspect --format='{{index .RepoDigests 0}}' ${IMAGE_PATH} > image-digest.txt
  artifacts:
    paths:
      - image-digest.txt
  only:
    - tags

generate-sbom:
  stage: sbom
  image: anchore/syft:latest
  script:
    - IMAGE_DIGEST=$(cat image-digest.txt)
    - syft ${IMAGE_DIGEST} -o cyclonedx-json=sbom.cdx.json -o spdx-json=sbom.spdx.json
    - echo "Components found $(jq '.components | length' sbom.cdx.json)"
  artifacts:
    paths:
      - sbom.cdx.json
      - sbom.spdx.json
  only:
    - tags

vulnerability-scan:
  stage: scan
  image: anchore/grype:latest
  script:
    - grype db update
    - grype sbom:./sbom.cdx.json -o json > vulnerability-report.json
    - CRITICAL=$(jq '[.matches[] | select(.vulnerability.severity=="Critical")] | length' vulnerability-report.json)
    - HIGH=$(jq '[.matches[] | select(.vulnerability.severity=="High")] | length' vulnerability-report.json)
    - echo "Critical ${CRITICAL} - High ${HIGH}"
    - if [ "$CRITICAL" -gt 0 ]; then echo "FAILED - Critical vulnerabilities found"; exit 1; fi
  artifacts:
    paths:
      - vulnerability-report.json
    reports:
      cyclonedx: vulnerability-report.json
  only:
    - tags

sign-image:
  stage: sign
  image: ubuntu:24.04
  id_tokens:
    SIGSTORE_ID_TOKEN:
      aud: sigstore
  script:
    - apt-get update && apt-get install -y curl jq
    - curl -O -L https://github.com/sigstore/cosign/releases/download/${COSIGN_VERSION}/cosign-linux-amd64
    - chmod +x cosign-linux-amd64 && mv cosign-linux-amd64 /usr/local/bin/cosign
    - IMAGE_DIGEST=$(cat image-digest.txt)
    - cosign sign --yes --identity-token=${SIGSTORE_ID_TOKEN} ${IMAGE_DIGEST}
    - cosign attest --yes --identity-token=${SIGSTORE_ID_TOKEN} --predicate sbom.cdx.json --type cyclonedx ${IMAGE_DIGEST}
    - echo "Image signed and SBOM attested successfully"
  only:
    - tags

deploy-production:
  stage: deploy
  image: bitnami/kubectl:latest
  environment:
    name: production
  script:
    - IMAGE_DIGEST=$(cat image-digest.txt)
    - echo "Deploying verified image ${IMAGE_DIGEST}"
    # - kubectl set image deployment/my-app my-app=${IMAGE_DIGEST}
  when: manual
  only:
    - tags

แนวปฏิบัติที่ดีที่สุดสำหรับ Linux Supply Chain Security

1. การจัดเก็บ SBOM แบบรวมศูนย์

การมี SBOM อย่างเดียวยังไม่พอครับ ต้องจัดการให้ดีด้วย ผมแนะนำให้ใช้ระบบจัดเก็บ SBOM แบบรวมศูนย์อย่าง Dependency-Track ซึ่งเป็นแพลตฟอร์มโอเพนซอร์สจาก OWASP รองรับทั้ง CycloneDX และ SPDX แสดงผลช่องโหว่แบบ real-time และที่สำคัญคือแจ้งเตือนเมื่อมี CVE ใหม่ที่กระทบ component ที่คุณใช้อยู่:

# ติดตั้ง Dependency-Track ด้วย Docker Compose
cat > docker-compose-dt.yml << 'EOF'
version: '3'
services:
  dtrack-apiserver:
    image: dependencytrack/apiserver:4.11.7
    environment:
      - ALPINE_DATABASE_MODE=external
      - ALPINE_DATABASE_URL=jdbc:postgresql://postgres:5432/dtrack
      - ALPINE_DATABASE_DRIVER=org.postgresql.Driver
      - ALPINE_DATABASE_USERNAME=dtrack
      - ALPINE_DATABASE_PASSWORD=changeme
    ports:
      - "8080:8080"
    depends_on:
      - postgres

  dtrack-frontend:
    image: dependencytrack/frontend:4.11.7
    environment:
      - API_BASE_URL=http://localhost:8080
    ports:
      - "8081:8080"

  postgres:
    image: postgres:16-alpine
    environment:
      - POSTGRES_DB=dtrack
      - POSTGRES_USER=dtrack
      - POSTGRES_PASSWORD=changeme
    volumes:
      - postgres-data:/var/lib/postgresql/data

volumes:
  postgres-data:
EOF

docker compose -f docker-compose-dt.yml up -d

# อัปโหลด SBOM ไปยัง Dependency-Track ผ่าน API
curl -X PUT \
  "http://localhost:8080/api/v1/bom" \
  -H "X-Api-Key: ${DT_API_KEY}" \
  -H "Content-Type: application/json" \
  -d "{
    \"projectName\": \"my-app\",
    \"projectVersion\": \"v1.2.3\",
    \"autoCreate\": true,
    \"bom\": \"$(base64 -w 0 sbom.cdx.json)\"
  }"

2. Policy Enforcement ด้วย Kyverno บน Kubernetes

ตรงนี้สำคัญมากครับ ถ้าคุณใช้ Kubernetes อยู่ คุณต้องมั่นใจว่า image ที่ไม่ได้รับการยืนยันจะไม่สามารถ deploy ได้เด็ดขาด Kyverno เป็น policy engine แบบ Kubernetes-native ที่รองรับ image verification ด้วย Cosign โดยตรง:

# kyverno-policy-verify-image.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-image-signature
  annotations:
    policies.kyverno.io/title: Verify Image Signature
    policies.kyverno.io/description: >-
      Images must be signed by our CI/CD pipeline using Sigstore keyless signing.
      Only signed and attested images are allowed in production.
spec:
  validationFailureAction: Enforce
  background: false
  rules:
    - name: check-image-signature
      match:
        any:
          - resources:
              kinds:
                - Pod
              namespaces:
                - production
                - staging
      verifyImages:
        - imageReferences:
            - "ghcr.io/myorg/*"
          attestors:
            - entries:
                - keyless:
                    subject: "https://github.com/myorg/*/.github/workflows/secure-release.yml@*"
                    issuer: "https://token.actions.githubusercontent.com"
                    rekor:
                      url: https://rekor.sigstore.dev
          attestations:
            - predicateType: https://cyclonedx.org/bom/v1.4
              conditions:
                - all:
                    - key: "{{ components | length(@) }}"
                      operator: GreaterThan
                      value: "0"

3. Dependency Update Automation ด้วย Renovate

การอัปเดต dependency อัตโนมัติเป็นอีกเรื่องที่หลายทีมมองข้าม แต่จริงๆ แล้วสำคัญมากในการรักษา supply chain security เครื่องมืออย่าง Renovate จะคอยตรวจจับ dependency ที่ล้าสมัยหรือมีช่องโหว่ แล้วสร้าง pull request อัปเดตให้อัตโนมัติ:

// renovate.json
{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": [
    "config:recommended",
    ":dependencyDashboard",
    "security:openssf-scorecard"
  ],
  "vulnerabilityAlerts": {
    "enabled": true,
    "labels": ["security", "dependencies"],
    "automerge": false
  },
  "osvVulnerabilityAlerts": true,
  "packageRules": [
    {
      "matchDepTypes": ["dependencies"],
      "matchUpdateTypes": ["patch"],
      "automerge": true,
      "automergeType": "pr"
    },
    {
      "matchPackagePatterns": ["*"],
      "matchUpdateTypes": ["major"],
      "labels": ["major-update"],
      "reviewers": ["security-team"]
    }
  ],
  "schedule": ["before 6am on monday"],
  "prHourlyLimit": 5,
  "prConcurrentLimit": 10
}

4. Supply Chain Threat Modeling

การทำ threat modeling สำหรับ supply chain ควรพิจารณาจุดเสี่ยงหลักเหล่านี้ครับ (จากประสบการณ์ บอกได้เลยว่าหลายข้อเกิดขึ้นจริงบ่อยกว่าที่คิด):

  • Dependency Confusion Attack - ผู้โจมตีสร้าง package ชื่อเดียวกับ internal package บน public registry ป้องกันได้โดยใช้ --require-hashes ใน pip, npm ci แทน npm install และกำหนด registry scope อย่างชัดเจน
  • Typosquatting - Package ที่ชื่อคล้ายกับ popular package แต่แอบฝัง malicious code ควรตรวจสอบชื่อ package อย่างระมัดระวัง และใช้ allowlist สำหรับ approved packages
  • Build System Compromise - CI/CD runner ถูก compromise ป้องกันด้วย ephemeral runners, least privilege และ hermetic builds
  • Maintainer Account Takeover - บัญชีผู้ดูแล package ถูกยึด ควรใช้ 2FA/MFA เสมอ และติดตาม release patterns ที่ผิดปกติ
  • Malicious Pull Request - PR ที่แอบแฝง malicious code ต้องมี code review process ที่เข้มงวดและใช้ automated security scanning ทุก PR

5. การตรวจสอบ OpenSSF Scorecard

OpenSSF Scorecard เป็นเครื่องมือที่ช่วยประเมินความปลอดภัยของ project โอเพนซอร์สที่คุณใช้เป็น dependency ก่อนจะเพิ่ม dependency ใหม่ ลองเช็ค scorecard ก่อนก็ดีครับ:

# ติดตั้ง scorecard
curl -O -L https://github.com/ossf/scorecard/releases/download/v5.1.0/scorecard-linux-amd64
chmod +x scorecard-linux-amd64
sudo mv scorecard-linux-amd64 /usr/local/bin/scorecard

# ตรวจสอบคะแนนความปลอดภัยของ dependency
export GITHUB_AUTH_TOKEN=ghp_your_token_here
scorecard --repo=github.com/anchore/syft --format=json | \
  jq '.checks[] | {name: .name, score: .score, reason: .reason}'

# ตรวจสอบ project ของตัวเอง
scorecard --repo=github.com/myorg/my-app --format=json > scorecard-report.json

# แสดงเฉพาะ checks ที่ score ต่ำกว่า 5 (ต้องปรับปรุง)
jq '.checks[] | select(.score < 5) | {name: .name, score: .score}' scorecard-report.json

6. Hermetic Builds และ Distroless Images

Hermetic build คือการ build ที่ผลลัพธ์ออกมาเหมือนกันทุกครั้ง ไม่ว่าจะ build ที่ไหน ช่วยป้องกันปัญหา "ของผมมัน run ได้นะ" และลดความเสี่ยงจาก dependency substitution attacks ส่วน distroless image ก็ช่วยลด attack surface ได้เยอะมาก:

# Dockerfile สำหรับ hermetic build
# ใช้ digest แทน tag เพื่อป้องกัน tag mutation
FROM golang:1.23.6@sha256:927112936cd3a7de2b2b5c337d0a8b3e6f1f1e6c94a7d4c14d36cf1c4e3ab12 AS builder

WORKDIR /app

# Copy go module files ก่อน (layer caching)
COPY go.mod go.sum ./

# Download dependencies พร้อม verification
RUN go mod download -x && go mod verify

# Copy source code
COPY . .

# Build แบบ static binary พร้อม hardening flags
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
    go build \
    -trimpath \
    -ldflags="-w -s -X main.version=${VERSION} -X main.commit=${COMMIT}" \
    -o /app/my-app \
    ./cmd/my-app

# ใช้ distroless base image (ไม่มี shell ลด attack surface)
FROM gcr.io/distroless/static-debian12:nonroot@sha256:3ae7f0201fee13b777a3e1abe5e7e5ef4c9d4e3d1d6a35d04e7f3c7d67b2e9a

COPY --from=builder /app/my-app /my-app

USER nonroot:nonroot
ENTRYPOINT ["/my-app"]

7. การตรวจสอบ Container Image ด้วย Trivy

นอกจาก Grype แล้ว Trivy จาก Aqua Security ก็เป็นตัวเลือกยอดนิยมอีกตัวครับ จริงๆ แล้วหลายทีมใช้ทั้งสองตัวร่วมกันเพื่อลด false negatives เพราะแต่ละเครื่องมือก็มีจุดแข็งต่างกัน:

# ติดตั้ง Trivy บน Linux
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v0.58.1

# สแกน container image
trivy image --severity HIGH,CRITICAL ghcr.io/myorg/my-app:v1.2.3

# สร้าง SBOM ด้วย Trivy (รูปแบบ CycloneDX)
trivy image --format cyclonedx --output trivy-sbom.cdx.json ghcr.io/myorg/my-app:v1.2.3

# สแกนและ output เป็น SARIF สำหรับ GitHub Security tab
trivy image --format sarif --output trivy-results.sarif ghcr.io/myorg/my-app:v1.2.3

# สแกน filesystem สำหรับ secrets และ misconfigurations
trivy fs --security-checks vuln,secret,config /path/to/project

สรุป: ก้าวสู่อนาคตของ Linux Supply Chain Security

ถ้าอ่านมาถึงตรงนี้ได้ ขอบอกเลยว่าคุณเอาจริงเรื่อง supply chain security แล้วครับ ความปลอดภัยของ software supply chain ไม่ใช่เรื่อง "nice to have" อีกต่อไป แต่เป็นส่วนสำคัญของกระบวนการพัฒนาซอฟต์แวร์ที่รับผิดชอบ ในปี 2026 เครื่องมืออย่าง Syft, Grype, Cosign/Sigstore, SLSA และ in-toto ได้กลายเป็น standard stack ที่องค์กรชั้นนำทั่วโลกนำมาใช้แล้ว

สิ่งที่ควรเริ่มทำทันทีครับ:

  1. เริ่มสร้าง SBOM ในทุก build - ไม่ว่าจะใช้ Syft หรือเครื่องมืออื่น การมี inventory ของ software components คือพื้นฐานของทุกอย่าง เมื่อเกิดช่องโหว่ใหม่ คุณจะระบุผลกระทบได้ภายในนาทีแทนที่จะเป็นสัปดาห์
  2. ผสาน vulnerability scanning เข้าใน CI/CD - ด้วย Grype หรือ Trivy กำหนด policy ที่ชัดเจนว่า severity level ไหนที่จะ block การ deploy
  3. เปิดใช้งาน keyless signing - ด้วย Cosign เพื่อให้ทุก artifact มี cryptographic proof ของแหล่งที่มา ยืนยันได้ว่าใครสร้างและมาจากไหน
  4. มุ่งสู่ SLSA Level 2 ในระยะสั้น - แล้วค่อยๆ ไต่ขึ้นสู่ Level 3 ในระยะยาว การมี build provenance ที่น่าเชื่อถือช่วยสร้างความมั่นใจให้ผู้ใช้ได้เยอะมาก
  5. บังคับใช้ policy ใน runtime - ใน Kubernetes ด้วย Kyverno หรือ OPA/Gatekeeper เพื่อให้มั่นใจว่ามีเพียง verified images เท่านั้นที่ถูก deploy

แนวโน้มที่น่าจับตาในอนาคตอันใกล้ก็มีเช่น การขยายตัวของ Sigstore ไปสู่ภาษาโปรแกรมมิ่งอื่นๆ อย่าง Python PyPI และ Rust crates.io ที่เริ่มรองรับ Sigstore signing แล้ว รวมถึงการพัฒนา VEX ให้สมบูรณ์ขึ้นเพื่อลด noise จาก false positives และการนำ AI/ML มาช่วย threat detection ใน build pipelines

สำหรับทีมที่เพิ่งเริ่ม การเริ่มต้นอาจดูซับซ้อนหน่อยครับ แต่ขอให้จำไว้ว่า supply chain attack ที่ประสบความสำเร็จนั้นมีต้นทุนที่สูงกว่าการลงทุนป้องกันมากมายนัก ตัวอย่างจาก SolarWinds แสดงให้เห็นว่าผลกระทบสามารถส่งผลต่อลูกค้าหลายหมื่นรายและใช้เวลาเป็นปีในการฟื้นตัว ในขณะที่เครื่องมือทั้งหมดที่กล่าวถึงในบทความนี้เป็นโอเพนซอร์สและเริ่มต้นได้ภายในวันเดียว

เส้นทางสู่ secure software supply chain ไม่ใช่จุดหมายปลายทาง แต่เป็นการเดินทางอย่างต่อเนื่อง เริ่มจากการทำ inventory ของ software components วัดระดับความปลอดภัยปัจจุบัน แล้วค่อยๆ ยกระดับตาม SLSA framework ได้เลยครับ

โลกของ Linux supply chain security กำลังพัฒนาอย่างรวดเร็ว การติดตามข่าวสารจาก OpenSSF, CNCF Security TAG และ NIST SSDF จะช่วยให้ทีมของคุณก้าวทันภัยคุกคามและแนวปฏิบัติที่ดีที่สุดในยุค DevSecOps อยู่เสมอครับ

เกี่ยวกับผู้เขียน Editorial Team

Our team of expert writers and editors.