ข้ามไปยังเนื้อหา

การแก้ไขปัญหา Gotenberg ใน NextPDF

bridge จะล้มเหลวอย่างชัดเจนตั้งแต่เนิ่นๆ ทุกความล้มเหลวจะยก exception พร้อมชนิดที่ระบุและข้อความที่บอกสาเหตุ ใช้หน้านี้เป็นแคตาล็อกอ้างอิง สำหรับความล้มเหลวแต่ละกรณี คุณจะเห็นชนิดของ exception ส่วนของข้อความที่จะพบ เงื่อนไขที่กระตุ้นในเส้นทางโค้ดอย่างแน่นอน และวิธีแก้ไข

กลุ่มของ exception มีดังนี้

  • GotenbergConvertException — ความล้มเหลวในชั้นการแปลง (การกำหนดค่า การรับส่งข้อมูล หรือการตอบกลับ)
  • RuntimeException — ความล้มเหลวในชั้นการตรวจสอบความถูกต้องที่นโยบายความปลอดภัยยกขึ้นก่อนที่จะมีการรับส่งข้อมูลทางเครือข่ายใดๆ
  • ValueError — นามสกุลไฟล์ที่ไม่รู้จัก
  • InvalidSpkiPinException — สตริง pin ของ SubjectPublicKeyInfo (SPKI) ใน Transport Layer Security (TLS) ที่มีรูปแบบไม่ถูกต้อง
  • Type: GotenbergConvertException
  • Trigger: คุณเรียก convertFile() หรือ convertString() ในขณะที่ GotenbergConfig::isValid() เป็น false กรณีนี้เกิดขึ้นเมื่อ apiUrl เป็นสตริงว่าง
  • Fix: ระบุ URL แบบ HTTPS ที่ไม่เป็นค่าว่าง หากคุณสร้างการกำหนดค่าด้วย fromArray() โปรดทราบว่า api_url ที่ขาดหายไปหรือไม่ใช่สตริงจะถูกแทนที่ด้วย '' โดยไม่แสดงสัญญาณใดๆ ตรวจสอบความถูกต้องของแหล่งที่มาของการกำหนดค่าระหว่างการบูต

ความล้มเหลวเหล่านี้มาจากนโยบายความปลอดภัยซึ่งป้องกัน server-side request forgery (SSRF) bridge จะยกความล้มเหลวเหล่านี้ขึ้นก่อนส่งคำขอใดๆ แต่ละรายการเป็น RuntimeException ธรรมดา

  • Trigger: scheme ของ URL ที่กำหนดค่าไว้ไม่ใช่ https การตรวจสอบไม่คำนึงถึงตัวพิมพ์เล็กพิมพ์ใหญ่ ดังนั้น HTTPS:// จึงได้รับการยอมรับ
  • Fix: วาง Gotenberg หลัง TLS และกำหนดค่า endpoint เป็น HTTPS ระบบจะปฏิเสธ HTTP ธรรมดาแม้ในการพัฒนาบนเครื่องท้องถิ่น

URL ไม่ถูกต้อง แยกวิเคราะห์ไม่ได้: “Invalid Gotenberg API URL: unable to parse”

หัวข้อที่มีชื่อว่า “URL ไม่ถูกต้อง แยกวิเคราะห์ไม่ได้: “Invalid Gotenberg API URL: unable to parse””
  • Trigger: ไม่สามารถแยกวิเคราะห์ URL ให้เป็น scheme และ host ได้
  • Fix: ระบุ URL แบบสัมบูรณ์ที่ถูกต้องตามไวยากรณ์ เช่น https://gotenberg.example.com:3000

URL ต้องไม่ resolve ไปยังที่อยู่ IP แบบ private หรือ reserved: “Gotenberg API URL must not resolve to a private or reserved IP address”

หัวข้อที่มีชื่อว่า “URL ต้องไม่ resolve ไปยังที่อยู่ IP แบบ private หรือ reserved: “Gotenberg API URL must not resolve to a private or reserved IP address””
  • Trigger: host เป็น Internet Protocol (IP) literal แบบ private หรือ reserved หรือ เป็น hostname ที่ resolve (ผ่านระเบียน A/AAAA ทั้งหมด) ไปยังที่อยู่แบบ private หรือ reserved รายการใดรายการหนึ่ง การทำเช่นนี้จะบล็อกช่วงที่อยู่แบบ private จาก Request for Comments (RFC) 1918 ที่อยู่ loopback และที่อยู่ link-local
  • Fix: ชี้ bridge ไปยังที่อยู่สาธารณะที่ routable หรือที่อยู่ของบริการที่แบ่งเซกเมนต์อย่างเหมาะสม หากตั้งใจให้ Gotenberg อยู่บนเครือข่ายส่วนตัว SSRF guard ของ bridge จะปฏิเสธตามการออกแบบ เปิดให้เข้าถึงผ่านที่อยู่ที่ guard ยอมรับ แล้วป้องกันเส้นทางนั้นด้วยการควบคุมเครือข่ายและการพิสูจน์ตัวตน ดู /integrations/gotenberg/security-and-operations/

คำตอบ DNS ของ URL เปลี่ยนไปนับจากการตรวจสอบ อาจเป็นการโจมตีแบบ DNS rebinding: “Gotenberg API URL DNS answer changed since validation — possible DNS rebinding attack”

หัวข้อที่มีชื่อว่า “คำตอบ DNS ของ URL เปลี่ยนไปนับจากการตรวจสอบ อาจเป็นการโจมตีแบบ DNS rebinding: “Gotenberg API URL DNS answer changed since validation — possible DNS rebinding attack””
  • Trigger: ระหว่างการตรวจสอบความถูกต้องครั้งแรกกับคำขอ การ resolve Domain Name System (DNS) อีกครั้งส่งคืนที่อยู่ที่ไม่ได้อยู่ในชุดที่ผ่านการตรวจสอบความถูกต้องเดิม
  • Fix: นี่คือพฤติกรรมของ guard แบบ time-of-check/time-of-use ตรวจสอบ DNS ของ host สาเหตุที่ไม่ผิดปกติคือ load balancer ที่หมุนเวียนที่อยู่ สาเหตุที่เป็นอันตรายคือการโจมตีแบบ rebinding ใช้ที่อยู่ที่เสถียรหรือชื่อที่มีชุดระเบียนที่เสถียรสำหรับ endpoint ของ Gotenberg

นโยบายความปลอดภัยจะยกความล้มเหลวเหล่านี้ขึ้นก่อนส่งคำขอ แต่ละรายการเป็น RuntimeException ธรรมดา เว้นแต่จะระบุไว้

  • Type: GotenbergConvertException
  • Trigger: convertFile() ไม่สามารถ canonicalize เส้นทางได้ หรือเส้นทางที่ resolve แล้วไม่ใช่ไฟล์ปกติที่อ่านได้ ไดเรกทอรีจะกระตุ้นกรณีนี้เช่นกัน
  • Fix: ส่งเส้นทางไปยังไฟล์ที่มีอยู่และอ่านได้ เส้นทางจะถูก canonicalize ด้วย realpath() ก่อน และยังช่วยป้องกันการ traversal ด้วย

ขนาดไฟล์เกินขีดจำกัดสูงสุดที่อนุญาต: “File size ( bytes) exceeds maximum allowed size ( bytes)”

หัวข้อที่มีชื่อว่า “ขนาดไฟล์เกินขีดจำกัดสูงสุดที่อนุญาต: “File size ( bytes) exceeds maximum allowed size ( bytes)””
  • Trigger: อินพุตมีขนาดใหญ่กว่า maxFileSize (ค่าเริ่มต้น 52,428,800 ไบต์ = 50 MiB)
  • Fix: เพิ่ม maxFileSize หากเอกสารจำเป็นต้องใช้ขนาดนั้นจริง หรือปฏิเสธการอัปโหลดตั้งแต่ต้นทาง ตั้งค่าเพดานให้ต่ำที่สุดเท่าที่เอกสารจริงของคุณจะอนุญาต นี่คือขีดจำกัดทรัพยากรในตัวเพียงอย่างเดียวของ bridge

bridge จะตรวจสอบความถูกต้องของชื่อไฟล์ สำหรับการแปลงไฟล์ ชื่อไฟล์คือ basename ของเส้นทางที่ resolve แล้ว สำหรับ convertString() คือชื่อที่คุณส่งเข้าไป รายการต่อไปนี้แต่ละรายการเป็น RuntimeException:

ส่วนของข้อความตัวกระตุ้น
must not be emptyชื่อไฟล์ว่าง
path traversal sequences (..)ชื่อมี ..
forward slashesชื่อมี /
backslashesชื่อมี \
null bytesชื่อมีไบต์ NUL
control charactersชื่อมีอักขระควบคุม ASCII (0–31)
  • Fix: ส่ง basename ที่สะอาด สำหรับ convertString() ให้ระบุชื่อธรรมดา เช่น report.docx ระบบใช้ชื่อนี้สำหรับการตรวจหารูปแบบและเป็นชื่อไฟล์อัปโหลดแบบ multipart ไม่ใช่เป็นเส้นทาง

นามสกุลรูปแบบ office ที่ไม่รู้จัก: “Unknown office format extension:

หัวข้อที่มีชื่อว่า “นามสกุลรูปแบบ office ที่ไม่รู้จัก: “Unknown office format extension: ””
  • Type: ValueError
  • Trigger: นามสกุลไฟล์ไม่ใช่หนึ่งใน docx, xlsx, pptx, odt, ods, odp (ไม่คำนึงถึงตัวพิมพ์เล็กพิมพ์ใหญ่ ยอมรับจุดนำหน้าได้)
  • Fix: แปลงเฉพาะหกรูปแบบที่รู้จักเท่านั้น bridge ไม่รู้จักรูปแบบไบนารีรุ่นเก่า (.doc, .xls, .ppt), .rtf, .csv, ข้อความล้วน หรือรูปภาพ แปลงอินพุตเหล่านี้ให้เป็นรูปแบบที่รู้จักก่อนเรียก bridge หรือส่งผ่านเส้นทางอื่น

ความล้มเหลวของการรับส่งข้อมูลและการตอบกลับ

หัวข้อที่มีชื่อว่า “ความล้มเหลวของการรับส่งข้อมูลและการตอบกลับ”

รายการทั้งหมดนี้เป็น GotenbergConvertException

  • Trigger: PHP Standard Recommendation 18 (PSR-18) client (หรือ transport แบบ cURL-pinned) ยก exception ขึ้นขณะส่งคำขอ สาเหตุอาจเป็นการปฏิเสธการเชื่อมต่อ การหมดเวลา ความล้มเหลวของ TLS handshake หรือ pin ไม่ตรงกัน
  • Exception code: code ของ exception จาก client ชั้นล่าง
  • Cause: exception ดั้งเดิมของ PSR-18 client ถูกแนบมาเป็น exception ก่อนหน้า
  • Fix: ตรวจสอบสี่เรื่อง: ตรวจสอบว่าบริการเข้าถึงได้ด้วย isAvailable() ตรวจสอบเส้นทางเครือข่าย ตรวจสอบ TLS chain หากมีการกำหนดค่า pinning ไว้ ตรวจสอบว่า SubjectPublicKeyInfo (SPKI) ปัจจุบันของเซิร์ฟเวอร์ตรงกับ pin ที่กำหนดค่าไว้ pin ที่ไม่ตรงกันหลังการหมุนเวียนใบรับรองเป็นสาเหตุที่พบได้บ่อย ดูขั้นตอนการหมุนเวียนใน /integrations/gotenberg/security-and-operations/
  • Trigger: curl_exec ของ transport แบบ cURL-pinned ล้มเหลวด้วยหมายเลขข้อผิดพลาด cURL ที่ไม่เป็นศูนย์ หรือส่งคืน body ที่ไม่ใช่สตริง
  • Fix: หมายเลขข้อผิดพลาด cURL ระบุสาเหตุ (TLS, resolve, timeout, pin) ความล้มเหลวของ pinning จะปรากฏที่นี่เมื่อ CURLOPT_PINNEDPUBLICKEY ปฏิเสธใบรับรอง ยืนยันว่า pin ที่กำหนดค่าไว้และที่อยู่ที่ resolve แล้วเป็นปัจจุบัน

การแปลงล้มเหลวด้วยสถานะ HTTP: “Gotenberg conversion failed with HTTP : ”

หัวข้อที่มีชื่อว่า “การแปลงล้มเหลวด้วยสถานะ HTTP: “Gotenberg conversion failed with HTTP : ””
  • Trigger: สถานะของการตอบกลับไม่ใช่ 200 body จะถูกรวมไว้โดยตัดให้เหลือ 500 อักขระแรก พร้อมต่อท้ายด้วยจุดไข่ปลาเมื่อยาวกว่านั้น
  • Fix: อ่าน body ที่รวมมา ข้อความแสดงข้อผิดพลาดของ Gotenberg อธิบายว่าเหตุใดการแปลงจึงถูกปฏิเสธ ได้แก่ เนื้อหาเอกสารที่ไม่รองรับ ความล้มเหลวภายในของ LibreOffice หรือการปฏิเสธการพิสูจน์ตัวตนด้วย 401 หรือ 403 401/403 หมายความว่า apiKey ขาดหายไปหรือไม่ถูกต้อง 5xx เป็นความล้มเหลวฝั่งบริการและเป็นกรณีที่เหมาะกับการลองใหม่แบบมีขอบเขต

Content-Type ที่ไม่คาดคิดจาก Gotenberg: “Unexpected Content-Type from Gotenberg: (expected application/pdf)”

หัวข้อที่มีชื่อว่า “Content-Type ที่ไม่คาดคิดจาก Gotenberg: “Unexpected Content-Type from Gotenberg: (expected application/pdf)””
  • Trigger: สถานะคือ 200 แต่ Content-Type ของการตอบกลับไม่มี application/pdf
  • Fix: โดยทั่วไปหมายความว่า proxy หรือ gateway ส่งคืนหน้าข้อผิดพลาด HTML หรือหน้า redirect พร้อม 200 bridge ตั้งใจปิดการตาม redirect บน transport ที่ pinned ไว้ ดังนั้นการตอบกลับแบบ 3xx จึงไม่ถูกตามไปยัง host ที่ไม่ได้ตรวจสอบโดยเงียบๆ body ที่มาถึงพร้อม Content-Type ที่ผิดบ่งชี้ว่ามีบางอย่างระหว่างคุณกับ Gotenberg กำลังแทรกแซง ตรวจสอบเส้นทางเครือข่าย

body ของการตอบกลับไม่ขึ้นต้นด้วย header %PDF ข้อมูล PDF ไม่ถูกต้อง: “Response body does not start with %PDF header — invalid PDF data”

หัวข้อที่มีชื่อว่า “body ของการตอบกลับไม่ขึ้นต้นด้วย header %PDF ข้อมูล PDF ไม่ถูกต้อง: “Response body does not start with %PDF header — invalid PDF data””
  • Trigger: สถานะ 200 Content-Type ยอมรับได้ แต่ body ไม่ขึ้นต้นด้วยลายเซ็น %PDF
  • Fix: ต้นทางส่งคืนข้อมูลที่ไม่ใช่ไฟล์ Portable Document Format (PDF) แม้จะมี header ให้ถือว่าการตอบกลับไม่น่าเชื่อถือและตรวจสอบบริการ อย่าเขียน body ลงดิสก์ bridge ปฏิเสธที่จะส่งคืนเป็นผลลัพธ์
  • Type: InvalidSpkiPinException
  • Trigger: สตริง pin ที่กำหนดค่าไว้ไม่ขึ้นต้นด้วย sha256/ หรือ sha256//
  • Fix: จัดรูปแบบ pin แต่ละตัวเป็น sha256/<base64-encoded-spki-hash> transport ยังยอมรับรูปแบบ sha256//<base64> แบบ cURL-native ด้วย สร้างค่าจาก SubjectPublicKeyInfo ของใบรับรองเซิร์ฟเวอร์ ไม่ใช่จากใบรับรองทั้งใบ

ระบบแจ้งว่าไม่พร้อมใช้งานแต่บริการทำงานอยู่: “It says unavailable but the service is up”

หัวข้อที่มีชื่อว่า “ระบบแจ้งว่าไม่พร้อมใช้งานแต่บริการทำงานอยู่: “It says unavailable but the service is up””

isAvailable() ส่งคืน false โดยไม่มีการเรียกเครือข่ายใดๆ เมื่อ URL ว่าง ไม่ใช่ HTTPS หรือ resolve ไปยังที่อยู่แบบ private/reserved และยังส่งคืน false เมื่อเกิดข้อผิดพลาดเครือข่ายใดๆ หรือเมื่อ /health ส่งคืน 500 ขึ้นไป ในกรณีเหล่านั้นจะ catch ข้อผิดพลาดแทนการ throw ตรวจสอบตามลำดับนี้:

  1. URL ที่กำหนดค่าไว้ไม่ว่างและเป็น HTTPS
  2. host ไม่ resolve ไปยังที่อยู่แบบ private/reserved (SSRF guard ปฏิเสธแม้กระทั่งสำหรับการ probe)
  3. <apiUrl>/health เข้าถึงได้จาก host ของแอปพลิเคชันและส่งคืนสถานะที่ต่ำกว่า 500
  • /integrations/gotenberg/configuration/ — ตัวเลือกทั้งหมดและกฎการเลือก transport
  • /integrations/gotenberg/production-usage/ — นโยบายการลองใหม่และสัญญาการจัดการความล้มเหลว
  • /integrations/gotenberg/security-and-operations/ — โมเดล SSRF และการหมุนเวียน pin
  • /integrations/gotenberg/quickstart/ — ลำดับการ catch ที่ครบถ้วนในบริบท