Files
SnapOtter/apps/docs/th/guide/security.md
T

268 lines
29 KiB
Markdown
Raw Normal View History

---
description: "คู่มือการเสริมความแข็งแกร่งด้านความปลอดภัยสำหรับ SnapOtter ความปลอดภัยของคอนเทนเนอร์ การแยกเครือข่าย Docker secrets การปรับใช้ Kubernetes และอาร์ทิแฟกต์ด้านการปฏิบัติตามข้อกำหนด"
i18n_source_hash: 9ff337fa0417
i18n_provenance: machine
i18n_output_hash: 06f28d0ea62d
i18n_hash_version: 2
---
# Security & Hardening {#security-hardening}
SnapOtter ประมวลผลไฟล์ทั้งหมดบนโครงสร้างพื้นฐานของคุณ มันส่งการวิเคราะห์ผลิตภัณฑ์และรายงานการล่มแบบไม่ระบุตัวตนที่ไม่มีเนื้อหาโดยค่าเริ่มต้น เพื่อช่วยปรับปรุงโครงการ มันไม่เคยส่งไฟล์, ชื่อไฟล์, เนื้อหาไฟล์, เอาต์พุต OCR, เมตาดาตาของรูปภาพ หรือข้อความในเอกสารของคุณ ข้อเสนอแนะเสริมจะถูกส่งหลังจากผู้ใช้ส่งเท่านั้น เฉพาะเมื่อเปิดใช้การวิเคราะห์ และฟิลด์ข้อมูลติดต่อจะรวมอยู่ด้วยเฉพาะเมื่อมีความยินยอมด้านการติดต่ออย่างชัดเจน ผู้ดูแลระบบสามารถปิดการวิเคราะห์และการเก็บข้อเสนอแนะได้ในคลิกเดียวภายใต้ Settings > System > Privacy โดยไม่ต้อง build ใหม่ การประมวลผลไฟล์อยู่ภายในคอนเทนเนอร์ของคุณเสมอ
คอนเทนเนอร์รันเป็นผู้ใช้ที่ไม่ใช่ root โดยเฉพาะ (`snapotter`) โดยตัด Linux capabilities ทั้งหมดออก ยกเว้นชุดขั้นต่ำที่จำเป็น สำหรับนโยบายการเปิดเผยช่องโหว่ฉบับเต็มและสถาปัตยกรรมความปลอดภัย ดู [SECURITY.md](https://github.com/snapotter-hq/SnapOtter/blob/main/SECURITY.md) บน GitHub
## การชุบแข็งตู้คอนเทนเนอร์ {#container-hardening}
ไฟล์เขียน Canonical [CPU](https://github.com/snapotter-hq/SnapOtter/blob/main/docker/docker-compose.yml) และ [GPU](https://github.com/snapotter-hq/SnapOtter/blob/main/docker/docker-compose-gpu.yml) เป็นแหล่งที่มาของความจริง อย่าคัดลอกตัวอย่างย่อไปสู่การใช้งานจริง ปรับใช้ไฟล์จากแท็ก release ที่คุณตรวจสอบแล้ว
สแต็กทั้งสองใช้การควบคุมต่อไปนี้:
- ขีดจำกัดหน่วยความจำ, การสลับ, CPU และ PID มีการประมวลผลแบบเนทีฟแบบควบคุมไม่ได้
- ทุกบริการจะลดความสามารถของ Linux ทั้งหมด แอปพลิเคชันเพิ่มกลับเฉพาะ `CHOWN, SETUID, SETGID, DAC_OVERRIDE, FOWNER, KILL` สำหรับการเป็นเจ้าของโวลุ่ม การลดการระบุตัวตน `gosu` ทางเดียว และการส่งต่อสัญญาณที่สวยงาม PostgreSQL และ Redis ได้รับเฉพาะส่วนย่อยที่ต้องการสำหรับจุดเข้าใช้งานอย่างเป็นทางการเท่านั้น
- `security_opt: [no-new-privileges:true]` ป้องกันไม่ให้กระบวนการในแอปพลิเคชัน, PostgreSQL และคอนเทนเนอร์ Redis ได้รับสิทธิพิเศษเพิ่มเติม สิ่งนี้ยังคงเข้ากันได้กับ `gosu`: จุดเข้าใช้งานเริ่มต้นในฐานะรูท เตรียมวอลุ่ม และส่งไปยังผู้ใช้ `snapotter` เฉพาะเท่านั้น
- อินพุตรูปภาพ PostgreSQL และ Redis ถูกตรึงโดยไดเจสต์ นอกจากนี้ ควรปักหมุดแอปพลิเคชันไว้ที่แท็ก Release ที่ได้รับการยืนยันหรือแยกย่อย แทนที่จะปักหมุด `latest`
- การตรวจสอบประสิทธิภาพการทำงาน, การหมุนเวียนบันทึก JSON แบบมีขอบเขต, Redis AOF ที่คงทน และนโยบายการรีสตาร์ทจะถูกกำหนดไว้ที่ส่วนกลางในไฟล์ Canonical
สำหรับการปรับใช้ผ่านอินเทอร์เน็ต ให้ผูกพอร์ต 1349 เข้ากับลูปแบ็คและยุติ TLS ที่พร็อกซีย้อนกลับที่ได้รับการดูแล สร้างข้อมูลรับรอง PostgreSQL และ Redis ที่ไม่ซ้ำกัน จัดเก็บข้อมูลลับในไฟล์ที่ได้รับการป้องกันหรือเครื่องมือจัดการความลับ และเปลี่ยนรหัสผ่านผู้ดูแลระบบเริ่มต้นทันที
### ทำไม `read_only` ถึงไม่ได้ตั้งค่า {#why-read-only-is-not-set}
ไม่ได้ตั้งค่า `read_only: true` เนื่องจากการรีแมป PUID/PGID เขียนไปยัง `/etc/passwd` และ `/etc/group` เมื่อเริ่มต้น หากคุณใช้แฟล็ก `--user` ของ Docker หรือ Kubernetes `runAsUser` แทน PUID/PGID คุณสามารถเปิดใช้งานระบบไฟล์รูทแบบอ่านอย่างเดียวได้อย่างปลอดภัย
## การแยกเครือข่าย {#network-isolation}
การประมวลผลไฟล์เป็นแบบโลคัล แต่การติดตั้งเริ่มต้นคือ **ไม่ใช่ระบบที่ไม่มีทางออก** การวิเคราะห์ผลิตภัณฑ์แบบไม่เปิดเผยตัวตนใช้ PostHog และการรายงานข้อขัดข้องจะใช้ Sentry เมื่อเปิดใช้งานการวัดและส่งข้อมูลทางไกล ตั้งค่า `SNAPOTTER_TELEMETRY=0` (หรือปิดใช้งานการวิเคราะห์ภายใต้การตั้งค่า > ระบบ > ความเป็นส่วนตัว) เพื่อปิดทั้งสองอย่าง SnapOtter จะไม่รวมไฟล์ที่อัพโหลด ชื่อไฟล์ เอาต์พุต OCR ข้อความในเอกสาร หรือเนื้อหาไฟล์อื่น ๆ ในเหตุการณ์เหล่านั้น
การรับส่งข้อมูลขาออกอื่นๆ ขับเคลื่อนด้วยฟีเจอร์: ดาวน์โหลดการติดตั้งชุด AI/โมเดล อินพุตรีลีสที่เซ็นชื่อ; การนำเข้า URL ดึง URL สาธารณะที่ผู้ใช้ร้องขอ และการกำหนดค่า OIDC, SAML, OpenTelemetry, webhooks, พื้นที่เก็บข้อมูลที่เข้ากันได้กับ S3 หรือการบูรณาการที่คล้ายกันที่กำหนดค่าไว้อย่างชัดเจน จะติดต่อกับปลายทางที่ผู้ดูแลระบบเลือก การดาวน์โหลดโมเดลขณะรันไทม์ถูกปิดใช้งานโดยค่าเริ่มต้น ตั้งค่า `SNAPOTTER_ALLOW_MODEL_DOWNLOAD=1` เฉพาะเมื่อต้องการเปิดใช้การดาวน์โหลดสำรองอัตโนมัติอย่างชัดเจน [การนำเข้าบันเดิลออฟไลน์](/th/guide/deployment) สามารถจัดเตรียมฟีเจอร์ AI โดยไม่ต้องมีโมเดลรันไทม์ขาออก
**คำแนะนำไฟร์วอลล์:**
|สถานการณ์|กฎขาออก|
|---|---|
|มีช่องว่างอากาศ|ตั้งค่า `SNAPOTTER_TELEMETRY=0` และ `SNAPOTTER_ALLOW_MODEL_DOWNLOAD=0` ใช้การนำเข้าบันเดิล AI ออฟไลน์ ปิดใช้งานการนำเข้า URL และการผสานรวมภายนอก จากนั้นบล็อกทางออก|
|การวัดและส่งข้อมูลทางไกลเริ่มต้น|อนุญาตตำแหน่งข้อมูล PostHog และ Sentry ที่แสดงโดยบันทึกของเบราว์เซอร์/เครือข่ายของคุณ ปิดใช้งานการวัดและส่งข้อมูลทางไกลหากนโยบายไม่อนุญาต|
|จำเป็นต้องมีชุด AI|ระหว่างการติดตั้ง ให้อนุญาต HTTPS เป็น `huggingface.co, *.xethub.hf.co, cdn-lfs.huggingface.co, github.com, objects.githubusercontent.com, storage.googleapis.com, pypi.org, files.pythonhosted.org` จากนั้นจึงบล็อกโฮสต์เหล่านั้น|
|การบูรณาการภายนอก|อนุญาตเฉพาะปลายทาง OIDC/SAML/OTLP/webhook/object-storage ที่ผู้ดูแลระบบกำหนดค่าไว้เท่านั้น|
ไฟล์เก็บถาวรแบบบันเดิลให้บริการจากพื้นที่จัดเก็บ Xet ของ Hugging Face ซึ่งถ่ายโอนผ่านตำแหน่งข้อมูล `*.xethub.hf.co` แบบขนาน และเป็นสิ่งที่ทำให้การดาวน์โหลดบันเดิลหลาย GB รวดเร็ว หากไฟร์วอลล์ของคุณอนุญาต `huggingface.co` แต่บล็อก `*.xethub.hf.co` การติดตั้งยังคงสำเร็จแต่กลับไปดาวน์โหลดแบบสตรีมเดี่ยวที่ช้ากว่า ดังนั้นให้อนุญาตโฮสต์ Xet ให้อยู่ในเส้นทางที่รวดเร็ว การติดตั้งแบบออฟไลน์โดยสมบูรณ์สามารถข้ามทั้งหมดนี้ได้และใช้ [การนำเข้าชุดออฟไลน์](/th/guide/deployment) แทน
สำหรับการกำหนดค่าพร็อกซีย้อนกลับ (Nginx, Traefik, Caddy, Cloudflare Tunnels) โปรดดู [คู่มือการปรับใช้](/th/guide/deployment#reverse-proxy)
## Docker Secrets {#docker-secrets}
สำหรับการปรับใช้ในโปรดักชัน ควรหลีกเลี่ยงการส่งความลับเป็นตัวแปรสภาพแวดล้อมแบบข้อความธรรมดา entrypoint รองรับข้อกำหนด `_FILE` ของ Docker: เมานต์ความลับเป็นไฟล์แล้วตั้งค่าตัวแปร `_FILE` ที่สอดคล้องกันให้เป็นพาธของมัน
**ความลับที่รองรับ:**
| ตัวแปร | `_FILE` ที่เทียบเท่า |
|---|---|
| `DEFAULT_PASSWORD` | `DEFAULT_PASSWORD_FILE` |
| `COOKIE_SECRET` | `COOKIE_SECRET_FILE` |
| `OIDC_CLIENT_SECRET` | `OIDC_CLIENT_SECRET_FILE` |
| `S3_ACCESS_KEY_ID` | `S3_ACCESS_KEY_ID_FILE` |
| `S3_SECRET_ACCESS_KEY` | `S3_SECRET_ACCESS_KEY_FILE` |
| `SNAPOTTER_LICENSE_KEY` | `SNAPOTTER_LICENSE_KEY_FILE` |
**ตัวอย่างกับ Docker Compose secrets:**
```yaml
services:
SnapOtter:
image: snapotter/snapotter:latest
environment:
- AUTH_ENABLED=true
- DEFAULT_USERNAME=admin
- DEFAULT_PASSWORD_FILE=/run/secrets/snapotter_password
- COOKIE_SECRET_FILE=/run/secrets/cookie_secret
secrets:
- snapotter_password
- cookie_secret
secrets:
snapotter_password:
file: ./secrets/snapotter_password.txt
cookie_secret:
file: ./secrets/cookie_secret.txt
```
::: tip
Docker Compose secrets (โดยไม่มี Swarm) ต้องใช้ Compose v2.23 ขึ้นไป
:::
## Kubernetes Deployment {#kubernetes-deployment}
entrypoint ตรวจจับเมื่อคอนเทนเนอร์รันเป็น non-root อยู่แล้ว (เช่น ผ่าน `runAsUser` ของ Kubernetes) และข้ามการลดสิทธิ์ด้วย gosu โดยอัตโนมัติ ในกรณีนั้นมันไม่สามารถ chown วอลุ่มที่เมานต์ได้เอง ดังนั้นมันจะตรวจสอบว่าวอลุ่มเขียนได้ และออกก่อนกำหนดพร้อมคำแนะนำที่นำไปปฏิบัติได้หากเขียนไม่ได้ ดู [Storage permissions](/th/guide/deployment#storage-permissions) สำหรับ `fsGroup` และการตั้งค่าแบบ UID ต่างถิ่น (TrueNAS, OpenShift)
**SecurityContext ของ Pod ที่แนะนำ:**
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: snapotter
spec:
replicas: 1
selector:
matchLabels:
app: snapotter
template:
metadata:
labels:
app: snapotter
spec:
securityContext:
runAsNonRoot: true
runAsUser: 999
runAsGroup: 999
fsGroup: 999
containers:
- name: snapotter
image: snapotter/snapotter:latest
ports:
- containerPort: 1349
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: [ALL]
resources:
requests:
cpu: "1"
memory: 2Gi
limits:
cpu: "4"
memory: 6Gi
livenessProbe:
httpGet:
path: /api/v1/health
port: 1349
initialDelaySeconds: 60
periodSeconds: 30
timeoutSeconds: 5
readinessProbe:
httpGet:
path: /api/v1/health
port: 1349
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
volumeMounts:
- name: data
mountPath: /data
- name: workspace
mountPath: /tmp/workspace
volumes:
- name: data
persistentVolumeClaim:
claimName: snapotter-data
- name: workspace
emptyDir:
medium: Memory
sizeLimit: 2Gi
```
เนื่องจาก `runAsUser: 999` ถูกตั้งค่าที่ระดับ pod entrypoint จึงข้าม gosu ทั้งหมด สิ่งนี้อนุญาต capabilities `allowPrivilegeEscalation: false` และ `drop: [ALL]` โดยไม่ขัดแย้งกัน
สำหรับการกำหนดขนาดทรัพยากร ดู [Hardware Requirements](/th/guide/deployment#hardware-requirements)
## สำรองและกู้คืน {#backup-and-recovery}
สแต็ก Compose ที่ใช้งานจริงจะกำหนดสี่วอลุ่ม หยุดข้อมูลเข้าและปล่อยให้งานที่ใช้งานอยู่เสร็จสิ้นก่อนทำการสำรองข้อมูลแบบประสานงาน ดังนั้น PostgreSQL, Redis และสถานะไฟล์จะอธิบายจุดเวลาเดียวกัน
|ปริมาณ|สารบัญ|การบำบัดฟื้นฟู|
|---|---|---|
|`SnapOtter-pgdata`|ผู้ใช้ PostgreSQL การตั้งค่า ไปป์ไลน์ งาน ข้อมูลเมตาของไฟล์ และบันทึกการตรวจสอบ|วิกฤต; ใช้การถ่ายโอนข้อมูลแบบลอจิคัลที่รวดเร็วเมื่อล้มเหลวสำหรับการกู้คืนแบบพกพา|
|`SnapOtter-data`|ออบเจ็กต์ไลบรารี บันทึก และสถานะ AI ที่บันทึกไว้ (`/data/files, /data/logs, /data/ai, /data/ai/venv`)|สำรองข้อมูลทั้งหมด เพื่อประหยัดพื้นที่ จงละเว้นสถานะ AI ทั้งหมดอย่างจงใจ และติดตั้งบันเดิลใหม่|
|`SnapOtter-redisdata`|Redis AOF สำหรับสถานะคิว BullMQ ที่ทนทาน|สำรองข้อมูลหลังจากหยุดแอปชั่วคราวและบังคับให้ `SAVE`; จำเป็นต้องกลับมาทำงานต่อคิวอย่างแน่นอน|
|`SnapOtter-workspace`|คีย์การจัดเก็บอ็อบเจ็กต์ชั่วคราว (`/tmp/workspace/uploads, /tmp/workspace/outputs`)|อย่าสำรองข้อมูลหลังจากที่งานทั้งหมดหมดหรือยกเลิก อย่าทิ้งมันในขณะที่งานกำลังทำงานอยู่|
โดยปกติแล้ว เขียนคำนำหน้าชื่อวอลุ่มด้วยชื่อโปรเจ็กต์ แก้ไขวอลลุมแหล่งที่มาจริงจากคอนเทนเนอร์ที่เมาท์ แทนที่จะสมมติว่าชื่อที่แสดง เช่น `SnapOtter-data` เป็นชื่อวอลลุม Docker
### สำรองฐานข้อมูล {#database-backup}
ใช้รูปแบบไฟล์เก็บถาวรที่กำหนดเองของ PostgreSQL และตรวจสอบไฟล์เก็บถาวรก่อนที่จะดำเนินการกับการสำรองข้อมูลว่าเสร็จสมบูรณ์:
```bash
docker exec SnapOtter-postgres \
pg_dump --format=custom --no-owner -U snapotter snapotter > snapotter.dump
test -s snapotter.dump
docker exec -i SnapOtter-postgres pg_restore --list < snapotter.dump >/dev/null
# Restore only into a fresh/disposable target first; any SQL error fails the command.
docker exec -i SnapOtter-postgres \
pg_restore --exit-on-error --clean --if-exists --no-owner \
-U snapotter -d snapotter < snapotter.dump
```
ทดสอบการสำรองข้อมูลทุกครั้งโดยการกู้คืนลงในสแต็กแยก ตรวจสอบบันทึกฐานข้อมูลและเช็คซัมไฟล์ และเริ่มต้นแอปพลิเคชัน `tests/qa/backup-restore-drill.sh` ของที่เก็บจะปล่อยเกตโดยอัตโนมัติกับ `QA_IMAGE` ที่ชัดเจน
หากแพลตฟอร์มของคุณใช้สแนปช็อตปริมาณที่สอดคล้องกับข้อขัดข้องแทน ให้หยุดทั้งสแต็กก่อนและสแน็ปช็อตวอลุ่มที่สำคัญทั้งหมดเป็นชุดเดียว สำเนาไดเรกทอรีข้อมูล PostgreSQL แบบ Raw จากคอนเทนเนอร์ที่ทำงานอยู่ไม่ใช่การสำรองข้อมูลแบบลอจิคัลที่รองรับ
### ไฟล์และคิวสำรอง {#file-and-queue-backup}
หยุดแอปพลิเคชันชั่วคราวก่อนจับปริมาณไฟล์และคิว ใช้ `docker inspect` เพื่อแก้ไขชื่อวอลุ่มจริง บังคับให้ Redis คงสถานะปัจจุบันไว้ และเก็บถาวรโดยคงความเป็นเจ้าของและสิทธิ์ไว้:
```bash
docker stop SnapOtter
docker exec SnapOtter-redis redis-cli -a "$REDIS_PASSWORD" --no-auth-warning SAVE
docker stop SnapOtter-redis
DATA_VOLUME="$(docker inspect SnapOtter --format '{{range .Mounts}}{{if eq .Destination "/data"}}{{.Name}}{{end}}{{end}}')"
REDIS_VOLUME="$(docker inspect SnapOtter-redis --format '{{range .Mounts}}{{if eq .Destination "/data"}}{{.Name}}{{end}}{{end}}')"
install -d -m 700 backup
docker run --rm -v "$DATA_VOLUME:/source:ro" -v "$PWD/backup:/backup" \
alpine:3.22@sha256:14358309a308569c32bdc37e2e0e9694be33a9d99e68afb0f5ff33cc1f695dce tar czf /backup/snapotter-data.tar.gz -C /source .
docker run --rm -v "$REDIS_VOLUME:/source:ro" -v "$PWD/backup:/backup" \
alpine:3.22@sha256:14358309a308569c32bdc37e2e0e9694be33a9d99e68afb0f5ff33cc1f695dce tar czf /backup/snapotter-redis.tar.gz -C /source .
sha256sum backup/snapotter-*.tar.gz > backup/SHA256SUMS
```
รีสตาร์ท Redis ก่อนแอปพลิเคชัน หากคุณตั้งใจยกเว้น `/data/ai` ให้ลบทรีย่อย AI ทั้งหมด แทนที่จะเก็บบันทึก `installed.json` โดยไม่มีโมเดลหรือสภาพแวดล้อมเสมือน เก็บไฟล์สำรองเข้ารหัส ควบคุมการเข้าถึง และแยกจากโฮสต์ที่ใช้งาน SnapOtter
## สิ่งประดิษฐ์การปฏิบัติตามข้อกำหนด {#compliance-artifacts}
SnapOtter แต่ละรุ่นมีอาร์ติแฟกต์ด้านความปลอดภัยต่อไปนี้:
| สิ่งประดิษฐ์ | รูปแบบ | จะหาได้ที่ไหน |
|---|---|---|
| ปล่อยเรื่องผูกมัด | การรับรอง Canonical JSON + GitHub | [GitHub Release](https://github.com/snapotter-hq/SnapOtter/releases) เนื้อหา: `snapotter-v{version}-release-subjects.json` |
| เก็บถาวร SBOM | CycloneDX และ SPDX JSON | เนื้อหาที่เผยแพร่: `snapotter-v{version}-archive-linux-{arch}-sbom.{cdx,spdx}.json` |
| รูปภาพ SBOM | CycloneDX และ SPDX JSON | เนื้อหาที่เผยแพร่: `snapotter-v{version}-image-linux-{arch}-sbom.{cdx,spdx}.json` |
| การสแกนช่องโหว่ | Trivy JSON | เผยแพร่เนื้อหาที่มีคำนำหน้า `archive-linux-{arch}` หรือ `image-linux-{arch}` ที่ตรงกัน |
| การสแกนช่องโหว่ | SARIF | แท็บ [GitHub Security](https://github.com/snapotter-hq/SnapOtter/security) |
| การวิเคราะห์แบบคงที่ | CodeQL (JS/TS + Python) | แท็บ [GitHub Security](https://github.com/snapotter-hq/SnapOtter/security) รันรายสัปดาห์ + ต่อ PR |
| การตรวจสอบการพึ่งพา | GitHub เนทิฟ | การตรวจสอบต่อ PR ล้มเหลวในการเพิ่มที่มีความรุนแรงสูง |
| การตรวจสอบการพึ่งพา Python | pip-audit | CI รันบันทึกทุกครั้งที่กด |
| นโยบายความปลอดภัย | Markdown | [SECURITY.md](https://github.com/snapotter-hq/SnapOtter/blob/main/SECURITY.md) ในพื้นที่เก็บข้อมูล |
| การอัปเดตการพึ่งพา | Dependabot | PR รายสัปดาห์อัตโนมัติสำหรับ npm, pip, Docker, Actions |
**เรียกใช้การสแกนของคุณเอง:**
ดาวน์โหลดรายการหัวข้อการเผยแพร่และตรวจสอบว่าได้รับการยืนยันโดยเวิร์กโฟลว์การเผยแพร่:
```bash
2026-07-27 22:09:31 +08:00
gh attestation verify snapotter-v2.2.0-release-subjects.json \
--repo snapotter-hq/SnapOtter \
--signer-workflow snapotter-hq/SnapOtter/.github/workflows/release.yml
```
ไฟล์ Manifest จะบันทึก `releaseTag`, `releaseCommit` และ `workflowTriggerCommit` แยกกัน ตรวจสอบว่า `releaseCommit` เป็นคอมมิตที่ลอกออกจากแท็กที่ไม่เปลี่ยนรูปแบบ จากนั้นตรวจสอบการแยกย่อย SHA-256 ของไฟล์เก็บถาวร รูปภาพ SBOM หรือการสแกนที่คุณใช้โดยเทียบกับรายการใน `subjects` ความแตกต่างนี้มีเจตนา: การตรวจสอบการคอมมิตรีลีสที่สร้างขึ้นใหม่จะไม่เปลี่ยนเอกลักษณ์การคอมมิตในข้อมูลรับรอง OIDC ของเวิร์กโฟลว์
คุณยังสามารถสแกน SBOM ที่ดาวน์โหลดมาหรือรูปภาพได้โดยตรง:
```bash
# Scan with Grype using the CycloneDX SBOM
2026-07-27 22:09:31 +08:00
grype sbom:snapotter-v2.2.0-image-linux-amd64-sbom.cdx.json
# Scan with Trivy using the SPDX SBOM
2026-07-27 22:09:31 +08:00
trivy sbom snapotter-v2.2.0-image-linux-amd64-sbom.spdx.json
# Scan the Docker image directly
2026-07-27 22:09:31 +08:00
trivy image snapotter/snapotter:2.2.0
```
::: info
รูปภาพ SBOMs และการสแกนสะท้อนถึงรูปภาพเฉพาะทางสถาปัตยกรรมที่เผยแพร่สำหรับรุ่นนั้น ไฟล์เก็บถาวร SBOMs และการสแกนจะอธิบายไฟล์เก็บถาวรที่สร้างไว้ล่วงหน้าแยกกัน บันเดิลโมเดล AI ที่ติดตั้งหลังจากการปรับใช้จะไม่รวมอยู่ใน SBOMs เหล่านี้ เนื่องจากมีการดาวน์โหลดขณะรันไทม์
:::