Upgrade pipeline ของเราให้ Secure ขึ้นด้วย Trivy 🔍

Pitch Kantapit
odds.team
Published in
4 min readDec 2, 2023

สวัสดีครับทุก ๆ คน ช่วงผมนี้มีโอกาสได้อัพเกรดตัวเองด้าน DevSecOps มากขึ้นเลยอยากจะมาแชร์เกี่ยวกับเจ้า Trivy ว่ามันคืออะไรแล้วผมมีโอกาสนำไปประยุกต์ใช้ยังไงบ้างกับงานที่ทำอยู่

โดยต้องเกริ่นก่อนว่าผมมีโจทย์คือ ต้องการเพิ่มความ Security ให้กับ Jenkins pipeline ด้วยการเพิ่ม Images Scanner เข้าไปใน CI/CD แรก ๆ ก็เล็ง ๆ เจ้าตัว Anchore ไว้ครับ แต่! เดี๋ยวก่อนถ้าใช้ Anchore ทำไมถึงมีบทความ Trivy เกิดขึ้นมาได้หล่ะ 🤔 นั่นก็เพราะว่าาาาา พลามม 🎉 Surprise~~~

เพราะเจ้า Anchore จะไม่ Maintain ต่อแล้ว 😢 ทำให้ผมต้องหันหัวเรือไปหาที่พึ่งที่ใหม่ แต่เค้าก็ได้ทิ้งลายแทงไว้ว่า

“คุณไม่ลองไปใช้ Syft และ Grype ดูหล่ะ 🤓”

(จริง ๆ เค้าไม่ได้ไปไหนหรอก เพราะ Grype ก็ยังคง Base on Anchore Engine 🤣 แต่เพียงแค่ว่าต้องใช้ทั้ง Syft และ Grype เพื่อทำงานด้วยกัน)

https://github.com/anchore/anchore-engine

ผมก็ได้ลองไปดูเจ้าทั้ง 2 ตัวเหมือนกันทำให้ไปพบกับขุมทรัพย์ที่คาดไม่ถึง! นั่นก็คือ ภาพ ๆ นี้ 😎

https://blogs.cisco.com/developer/container_scanning_01

จากภาพเป็นการเปรียบเทียบ Trivy กับ ตัว Grype ซึ่งภาพนี้ทำให้ผมได้รู้จักกับเจ้า Trivy อย่างเป็นทางการนั่นเอง 🎊

แถม Trivy เองก็อยู่ใน CNCF Landscape ด้วยเอ้ออ

https://landscape.cncf.io/?selected=trivy

Trivy คืออะไร?

Trivy เป็น Vulnerability scanner ที่เป็น Open source แถมเจ้าตัวยังมาแบบ Stand alone แบบเดี่ยว ๆ เท่ ๆ เหมาะกับชาวเรา ๆ อีกด้วย เรียกว่าคนเดียวเอาอยู่ ซึ่งด้วยความที่เค้ามาแบบเดี่ยว ๆ ทำให้เราสามารถนำไป Implement เข้ากับ Pipeline ของเราได้ง่ายขึ้นด้วย สุดเฟี้ยวว ✨

โดยเราสามารถเล่นเจ้าตัว Trivy แบบเร็ว ๆ ด้วย Home Brew

brew install trivy

หรือใครอยากใช้ท่าอื่น ๆ ก็ตามนี้เลย Installation

หลังจาก Install เสร็จก็ลองเช็ค Version ดู

trivy --verison
ได้แบบนี้แล้วก็ลุยยยยย

Trivy Image Scan แบบเร็ว ๆ

ลอง Scan image ที่เรามี (ถ้าไม่แน่ใจว่าเรามี docker image อะไรใน local บ้างโดย docker images เช็คดูก่อนได้ครับ)

โดยผมจะลอง Scan Image ที่มีชื่อว่า webapp:latest ก็จะได้ว่า

trivy image webapp:latest

โอมเพี้ยง!

ผมใช้ Image จริง ๆ ขอปิดชื่อนิดนึงละกัน 🤣

ผลลัพธ์ที่ได้คือ มีสรุป Total มาให้เลยว่ามีช่องโหว่กี่ที่มี Severity Level เท่าไหร่ ดูดีสุด ๆ โดยเราก็สามารถเอา Code ในแถว Vulnerability ไป Search ดูได้เลยว่ามีวิธีแก้ยังไง เช่น CVE-2023–4806

โดยที่ Severity Level จะมีตั้งแต่ UNKNOWN, LOW, MEDIUM, HIGH, CRITICAL เลยครับก็ลองดูว่า ระบบของเรายอมรับได้ที่ Level ไหน

เรียบร้อยครับสำหรับ Trivy แต่เอ๊ะ ชื่อบทความนี้คือ “Upgrafe pipeline ให้ Sucure ขึ้น” นี่น่าใช่ครับบทความนี้ ยังไม่จบครับเดี๋ยวเราจะนำเจ้า Trivy ไป apply เข้ากับคุณพ่อบ้านของเราหรือ Jenkins นั่นเอง

Integrate Trivy เข้ากับ Jenkins กันน

ก่อนอื่นผมขอคิดไปเองก่อนแล้วกันว่าทุกคนมี Jenkins เป็นของตัวเองอยู่แล้วนะครับโดยผมจะแบ่งอธิบายเป็นส่วน ๆ นะครับ ✨

ส่วนแรกนะครับเราจะ ตั้งชื่อ agent และเซ็ต environment ให้กับ pipeline ก่อนนะครับ

pipeline {
agent { label 'trivy-101' }
environment {
REGISTRY_HOST= "your-registry-host"
REGISTRY_NAME = "your-registry-name"
TAG = 'your-images-tag'
}
}

ต่อมานะครับเนื่องจากถ้าเรา Scan image ด้วย Trivy เฉย ๆ pipeline ของเราจะไม่ failed หากตัว pipeline มีการเจอ Severity level และ Vulnerability type ที่เราไม่ยอมให้ผ่านไปได้เราจำเป็นที่จะต้องกำหนดค่าที่เราไม่อยากให้ผ่านไปได้ไว้ด้วยครับ

ท่าที่ผมจะไปคือจะไม่ใส่ค่า Severity level และ Vulnerability type ไปตรง ๆ ใน Command ครับเนื่องจากอาจมีบางครั้งที่เรา จะมีการเปลี่ยนค่า ใน pipeline ผมอยากทำให้เป็น Parameters ประเภท choice ครับ ก็จะได้แบบนี้ครับ (หรือใครจะใส่ลงไปใน Command เลยก็ได้นะครับไม่ว่ากัน 😆)

pipeline {
agent { label 'trivy-101' }
environment {
REGISTRY_HOST= "your-registry-host"
REGISTRY_NAME = "your-registry-name"
TAG = 'latest'
}
parameters {
choice(name: 'IMAGE_SEVERITY_LEVELS', choices:['UNKNOWN', 'LOW', 'MEDIUM', 'HIGH', 'CRITICAL'])
choice(name: 'VULNERABILITY_TYPES', choices:['os,library','os','library'])
}
}

ต่อมาเราก็ ทำการ Build image ตามปกติครับ

pipeline {
agent { label 'trivy-101' }
environment {
REGISTRY_HOST= "your-registry-host"
REGISTRY_NAME = "your-registry-name"
TAG = 'latest'
}
parameters {
choice(name: 'IMAGE_SEVERITY_LEVELS', choices:['UNKNOWN', 'LOW', 'MEDIUM', 'HIGH', 'CRITICAL'])
choice(name: 'VULNERABILITY_TYPES', choices:['os,library','os','library'])
}
stages {
stage("Build image") {
steps {
sh """
cd web-app
docker build \
-t ${REGISTRY_HOST}/${REGISTRY_NAME}:${TAG} \
.
"""
}
}
}
}
}

หลังจากที่เราทำการ Build image แล้วเราก็จะ Scan image เลยครับ

โดยเราจะทำการ ติดตั้ง curl ก่อน (ขั้นตอนนี้จะไม่มีก็ได้นะครับเผื่อ Container ที่ Run อยู่จะไม่มีเลยให้ติดตั้งไว้ก่อน)

sh "apk add curl"

Step ต่อมาก็ทำการ Install trivy ผ่าน curl ครับ

sh "curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v0.47.0"

และสุดท้ายก็จะเป็นการสั่งให้ทำการ Scan image โดยเราจะกำหนดว่าให้ exit code ออกมาเป็น 1 ถ้ามี Severity level และ Vulnerability type ที่เรายอมรับไม่ได้และให้ Pipeline failed

จะได้หน้าตาของ Jenkinsfileออกมาเป็นประมาณนี้ครับ

pipeline {
agent { label 'trivy-101' }
environment {
REGISTRY_HOST= "your-registry-host"
REGISTRY_NAME = "your-registry-name"
TAG = 'latest'
}
parameters {
choice(name: 'IMAGE_SEVERITY_LEVELS', choices:['UNKNOWN', 'LOW', 'MEDIUM', 'HIGH', 'CRITICAL'])
choice(name: 'VULNERABILITY_TYPES', choices:['os,library','os','library'])
}
stages {
stage("Build image") {
steps {
sh """
cd web-app
docker build \
-t ${REGISTRY_HOST}/${REGISTRY_NAME}:${TAG} \
.
"""
}
}
}
stage("Scan image"){
steps {
sh "apk add curl"
sh "curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v0.47.0"
sh "trivy image --severity ${params.IMAGE_SEVERITY_LEVELS} --vuln-type ${params.VULNERABILITY_TYPES} --exit-code 1 ${REGISTRY_HOST}:${TAG}"
}
}
}
}
}

หลังจากที่ Jenkins เจอ Jenkinsfile ของเราแล้วเข้าไปดูที่ Build with parameters จะเห็นหน้าตาให้เลือกแบบนี้เลยครับ

โดยผมจะลอง Build โดยให้ IMAGE_SEVERITY_LEVELS ที่ CRITICAL เท่านั้น ครับถ้าต่ำความนี้ ปล่อยผ่านได้และก็ให้หาช่องโหว่ที่ทั้ง OS และ Library ของ Image ครับเมื่อ Build แล้วก็จะได้หน้าตาประมาณนี้

ใน DevSecOps Stage จริง ๆ จะไม่ได้มีแค่ Scan Image ครับ และยังต้องเพิ่มอีกเยอะครับ 😵‍💫

ไหนดู Log หน่อยยยซิ

ดูดีใช้ได้ CRITICAL เป็น 0 (ถ้าเจอก็คงต้องไปแก้แทนที่จะมาเขียนบทความนี้แล้วแหละ แหะ ๆ 🤣)

https://twitter.com/kmagnc/status/1031034511738523648

Conclusion

สำหรับบทความนี้ ก็จะเป็นการเล่าตั้งแต่โจทย์ที่ผมได้รับมาและที่มาที่ไปว่าทำไมถึงมาเจอเจ้า Trivy นี้ได้และวิธีการใช้ Trivy แบบเร็ว ๆ และนำมาประยุกต์งานจริงซึ่งผมก็ยกตัวอย่างโดยการนำไปใช้กับ Jenkins ซึ่งในบทความนี้ไม่ได้แสดงให้เห็นถึงภาพรวมของ DevSecOps แต่เป็นเพียงแค่บางส่วนเท่านั้นครับ

จะเห็นได้ว่า Trivy เป็นหนึ่งในเครื่องมือในการหาช่องโหว่ของ Image ที่เราใช้แต่จริง ๆ แล้วความสามารถของ Trivy ยังมีมากกว่านี้ครับ ไม่ว่าจะเป็นการหา misconfigurations, secrets, SBOM in containers, Kubernetes, code repositories, clouds และ อื่น ๆ ครับที่ผมไม่ได้พูดถึงในบทความนี้ ถ้าหากสนใจก็สามารถศึกษาเพิ่มเติมได้ที่

--

--