כשלים בלחיצת יד של SSL - אישור לקוח פגום

אתם צופים במסמכי התיעוד של Apigee Edge.
כדאי לעיין במסמכי התיעוד של Apigee X.
מידע

תיאור הבעיה

אפליקציית הלקוח מקבלת קוד סטטוס של HTTP‏ 503 עם ההודעה "השירות לא זמין" בתגובה לבקשת API. במעקב אחר ממשק המשתמש, אפשר לראות ש-error.cause הוא Received fatal alert: bad_certificate ב-Target Request Flow עבור בקשת ה-API שנכשלה.

אם יש לכם גישה ליומני Message Processor, תראו את הודעת השגיאה Received fatal alert: bad_certificate עבור בקשת ה-API שנכשלה. השגיאה הזו מתרחשת במהלך תהליך לחיצת היד של SSL בין מעבד ההודעות לבין שרת הקצה העורפי בהגדרת TLS דו-כיוונית.

הודעת השגיאה

אפליקציית הלקוח מקבלת את קוד התגובה הבא:

HTTP/1.1 503 Service Unavailable

בנוסף, יכול להיות שתופיע הודעת השגיאה הבאה:

{
 "fault": {
    "faultstring":"The Service is temporarily unavailable",
    "detail":{
        "errorcode":"messaging.adaptors.http.flow.ServiceUnavailable"
    }
 }
}

משתמשי Private Cloud יראו את השגיאה הבאה ביומנים של מעבד ההודעות /opt/apigee/var/log/edge-message-processor/system.log עבור בקשת ה-API הספציפית:

2017-10-23 05:28:57,813 org:org-name env:env-name api:apiproxy-name rev:revision-number messageid:message_id NIOThread@0 ERROR HTTP.CLIENT - HTTPClient$Context.handshakeFailed() : SSLClientChannel[C:IP address:port # Remote host:IP address:port #]@65461 useCount=1 bytesRead=0 bytesWritten=0 age=529ms lastIO=529ms handshake failed, message: Received fatal alert: bad_certificate

סיבות אפשריות

אלה הסיבות האפשריות לבעיה:

הסיבה תיאור הוראות לפתרון בעיות שרלוונטיות ל
No Client Certificate למאגר המפתחות שמשמש בנקודת היעד של שרת היעד אין אישור לקוח. משתמשים ב-Edge Private Cloud וב-Edge Public Cloud
אי התאמה של רשות האישורים רשות האישורים של אישור העלה (האישור הראשון בשרשרת האישורים) במאגר המפתחות של מעבד ההודעות לא תואמת לאף אחת מרשויות האישורים שמתקבלות על ידי שרת הקצה העורפי. משתמשים ב-Edge Private Cloud וב-Edge Public Cloud

שלבים נפוצים לאבחון

  1. מפעילים את האפשרות 'מעקב' בממשק המשתמש של Edge, מבצעים את הקריאה ל-API ומשחזרים את הבעיה.
  2. בתוצאות של מעקב ממשק המשתמש, עוברים בין השלבים וקובעים איפה התרחשה השגיאה. השגיאה הייתה מתרחשת בתהליך הבקשה של היעד.
  3. בודקים את הזרימה שבה מופיעה השגיאה. השגיאה אמורה להופיע כמו בדוגמה הבאה של מעקב:

    alt_text

  4. כפי שאפשר לראות בצילום המסך שלמעלה, הערך של error.cause הוא "Received fatal alert: bad_certificate".
  5. אם אתם משתמשי Private Cloud, אתם צריכים לפעול לפי ההוראות הבאות:
    1. כדי לקבל את מזהה ההודעה של בקשת ה-API שנכשלה, צריך לקבוע את הערך של כותרת השגיאה X-Apigee.Message-ID בשלב שמצוין על ידי AX בנתוני המעקב.
    2. מחפשים את מזהה ההודעה ביומן של מעבד ההודעות /opt/apigee/var/log/edge-message-processor/system.log ומנסים למצוא מידע נוסף על השגיאה:
      2017-10-23 05:28:57,813 org:org-name env:env-name api:apiproxy-name
      rev:revision-number messageid:message_id NIOThread@0 ERROR HTTP.CLIENT - HTTPClient$Context.handshakeFailed() :
      SSLClientChannel[C:IP address:port # Remote host:IP address:port #]@65461 useCount=1
      bytesRead=0 bytesWritten=0 age=529ms lastIO=529ms handshake failed, message: Received fatal alert: bad_certificate
      2017-10-23 05:28:57,813 org:org-name env:env-name api:apiproxy-name
      rev:revision-number messageid:message_id NIOThread@0 ERROR HTTP.CLIENT - HTTPClient$Context.handshakeFailed() : SSLInfo:
      KeyStore:java.security.KeyStore@52de60d9 KeyAlias:KeyAlias TrustStore:java.security.KeyStore@6ec45759
      2017-10-23 05:28:57,814 org:org-name env:env-name api:apiproxy-name
      rev:revision-number messageid:message_id NIOThread@0 ERROR ADAPTORS.HTTP.FLOW - RequestWriteListener.onException() :
      RequestWriteListener.onException(HTTPRequest@6071a73d)
      javax.net.ssl.SSLException: Received fatal alert: bad_certificate
      at sun.security.ssl.Alerts.getSSLException(Alerts.java:208) ~[na:1.8.0_101]
      at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1666) ~[na:1.8.0_101]
      at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1634) ~[na:1.8.0_101]
      at sun.security.ssl.SSLEngineImpl.recvAlert(SSLEngineImpl.java:1800) ~[na:1.8.0_101]
      at com.apigee.nio.NIOSelector$SelectedIterator.findNext(NIOSelector.java:496) [nio-1.0.0.jar:na]
      at com.apigee.nio.util.NonNullIterator.computeNext(NonNullIterator.java:21) [nio-1.0.0.jar:na]
      at com.apigee.nio.util.AbstractIterator.hasNext(AbstractIterator.java:47) [nio-1.0.0.jar:na]
      at com.apigee.nio.NIOSelector$2.findNext(NIOSelector.java:312) [nio-1.0.0.jar:na]
      at com.apigee.nio.NIOSelector$2.findNext(NIOSelector.java:302) [nio-1.0.0.jar:na]
      at com.apigee.nio.util.NonNullIterator.computeNext(NonNullIterator.java:21) [nio-1.0.0.jar:na]
      at com.apigee.nio.util.AbstractIterator.hasNext(AbstractIterator.java:47) [nio-1.0.0.jar:na]
      at com.apigee.nio.handlers.NIOThread.run(NIOThread.java:59) [nio-1.0.0.jar:na]

      בלוג של מעבד ההודעות היה דוח קריסות לשגיאה [Received fatal alert: bad_certificate], אבל לא היה בו מידע נוסף שמצביע על הסיבה לבעיה הזו.

  6. כדי לחקור את הבעיה הזו לעומק, צריך לתעד חבילות TCP/IP באמצעות הכלי tcpdump.
    1. אם אתם משתמשים ב-Private Cloud, אתם יכולים ללכוד את מנות ה-TCP/IP בשרת העורפי או במעבד ההודעות. מומלץ לתעד אותם בשרת העורפי, כי החבילות מפוענחות בשרת העורפי.
    2. אם אתם משתמשים בענן ציבורי, עליכם ללכוד את מנות ה-TCP/IP בשרת העורפי.
    3. אחרי שמחליטים איפה רוצים ללכוד חבילות TCP/IP, משתמשים בפקודה tcpdump שבהמשך כדי ללכוד חבילות TCP/IP.
    4. tcpdump -i any -s 0 host <IP address> -w <File name>

      אם אתם לוקחים את חבילות ה-TCP/IP במעבד ההודעות, צריך להשתמש בכתובת ה-IP הציבורית של שרת הקצה העורפי בפקודה tcpdump.

      אם יש כמה כתובות IP לשרת הקצה העורפי או למעבד ההודעות, צריך להשתמש בפקודה אחרת של tcpdump. מידע נוסף על הכלי הזה ועל וריאציות אחרות של הפקודה הזו מופיע במאמר בנושא tcpdump.

  7. מנתחים את חבילות ה-TCP/IP באמצעות הכלי Wireshark או כלי דומה שאתם מכירים.

הנה ניתוח של נתונים לדוגמה של חבילות TCP/IP באמצעות הכלי Wireshark:

alt_text

  1. ההודעה מספר 4 ב-tcpdump שלמעלה מראה שמעבד ההודעות (מקור) שלח הודעה מסוג Client Hello לשרת העורפי (יעד).
  2. הודעה מספר 5 מראה שהשרת העורפי מאשר את ההודעה Client Hello ממעבד ההודעות.
  3. השרת העורפי שולח את ההודעה Server Hello יחד עם האישור שלו, ואז מבקש מהלקוח לשלוח את האישור שלו בהודעה מספר 7.
  4. מעבד ההודעות משלים את האימות של האישור ומאשר את הודעת ServerHello של שרת הקצה העורפי בהודעה מספר 8.
  5. מעבד ההודעות שולח את האישור שלו לשרת העורפי בהודעה מספר 9.
  6. השרת העורפי מאשר את קבלת האישור של מעבד ההודעות בהודעה מספר 11.
  7. עם זאת, הוא שולח מיד התראה קריטית: אישור לא תקין למעבד ההודעות (הודעה מספר 12). השגיאה הזו מציינת שהאישור שנשלח על ידי מעבד ההודעות היה פגום, ולכן אימות האישור נכשל בשרת העורפי. כתוצאה מכך, לחיצת היד של SSL נכשלה והחיבור ייסגר.


    alt_text

  8. עכשיו נבדוק את הודעה מספר 9 כדי לראות את התוכן של האישור שנשלח על ידי מעבד ההודעות:


    alt_text

  9. כפי שאפשר לראות, שרת הקצה העורפי לא קיבל אישור מהלקוח (אורך האישור: 0). לכן, שרת הבק-אנד שולח את ההתראה הקריטית: אישור פגום.
  10. בדרך כלל זה קורה כשהלקוח, כלומר מעבד ההודעות (תהליך מבוסס-Java):
    1. אין לו אישור לקוח במאגר המפתחות, או;
    2. לא ניתן לשלוח אישור לקוח. מצב כזה יכול לקרות אם המערכת לא מוצאת אישור שהונפק על ידי אחת מרשויות האישורים המקובלות של שרת הקצה העורפי. כלומר, אם רשות האישורים של אישור העלה של הלקוח (כלומר, האישור הראשון בשרשרת) לא תואמת לאף אחת מרשויות האישורים המקובלות של שרת הקצה העורפי, מעבד ההודעות לא ישלח את האישור.

עכשיו נבחן כל אחת מהסיבות האלה בנפרד.

הסיבה: אין אישור לקוח

אבחון

אם אין אישור במאגר המפתחות שצוין בקטע SSL Info של נקודת היעד או של שרת היעד שמשמש בנקודת היעד, זו הסיבה לשגיאה הזו.

כדי לבדוק אם זו הסיבה לבעיה, פועלים לפי השלבים הבאים:

  1. כדי לקבוע את מאגר המפתחות שבו נעשה שימוש בנקודת הקצה של היעד או בשרת היעד עבור שרת ה-proxy הספציפי של ה-API, מבצעים את השלבים הבאים:
    1. מאחזרים את שם ההפניה של מאגר המפתחות מהרכיב Keystore בקטע SSLInfo בנקודת הקצה של היעד או בשרת היעד.

      הנה דוגמה לקטע SSLInfo בהגדרת נקודת קצה של יעד:

      <SSLInfo>
        <Enabled>true</Enabled>
        <ClientAuthEnabled>true</ClientAuthEnabled>
        <KeyStore>ref://myKeystoreRef</KeyStore>
        <KeyAlias>myKey</KeyAlias>
        <TrustStore>ref://myTrustStoreRef</TrustStore>
      </SSLInfo>
    2. בדוגמה שלמעלה, שם ההפניה למאגר המפתחות הוא myKeystoreRef.
    3. עוברים לממשק המשתמש של Edge ובוחרים באפשרות API Proxies -> Environment Configurations (שרתי proxy של API -> הגדרות סביבה).

      בוחרים בכרטיסייה קובצי עזר ומחפשים את שם ההפניה של מאגר המפתחות. רושמים את השם בעמודה Reference (הפניה) עבור ההפניה הספציפית למאגר המפתחות. זה יהיה השם של מאגר המפתחות.


      alt_text

    4. בדוגמה שלמעלה, אפשר לראות של-myKeystoreRef יש הפניה ל-myKeystore. לכן, שם ה-Keystore הוא myKeystore.
  2. בודקים אם מאגר המפתחות הזה מכיל את האישור באמצעות ממשק המשתמש של Edge או באמצעות List certs for keystore API.
  3. אם מאגר המפתחות מכיל אישורים, עוברים אל הסיבה: חוסר התאמה של רשות האישורים.
  4. אם מאגר המפתחות לא מכיל אישור, זו הסיבה לכך שמעבד ההודעות לא שולח את אישור הלקוח.

רזולוציה

  1. מוודאים שרשת אישורי הלקוח הנכונה והמלאה הועלתה למאגר המפתחות הספציפי במעבד ההודעות.

הגורם: אי התאמה של רשות האישורים

בדרך כלל, כשהשרת מבקש מהלקוח לשלוח את האישור שלו, הוא מציין את קבוצת המנפיקים או רשויות האישורים המקובלים. אם המנפיק או רשות האישורים של אישור העלה (כלומר, האישור הראשון בשרשרת האישורים) במאגר המפתחות של מעבד ההודעות לא תואם לאף אחת מרשויות האישורים שמקובלות על שרת הקצה העורפי, מעבד ההודעות (שהוא תהליך מבוסס Java) לא ישלח את האישור לשרת הקצה העורפי.

כדי לבדוק אם זה המצב, פועלים לפי השלבים הבאים:

  1. רשימת האישורים של keystore API
  2. מקבלים את הפרטים של כל אישור שהתקבל בשלב 1 באמצעות Get cert for keystore API.
  3. רושמים את הגורם שהנפיק את אישור העלה (כלומר, האישור הראשון בשרשרת האישורים) שמאוחסן ב-Keystore.

    דוגמה לתעודת עלה

    {
      "certInfo" : [ {
        "basicConstraints" : "CA:FALSE",
        "expiryDate" : 1578889324000,
        "isValid" : "Yes",
        "issuer" : "CN=MyCompany Test SHA2 CA G2, DC=testcore, DC=test, DC=dir, DC=mycompany, DC=com",
        "publicKey" : "RSA Public Key, 2048 bits",
        "serialNumber" : "65:00:00:00:d2:3e:12:d8:56:fa:e2:a9:69:00:06:00:00:00:d2",
        "sigAlgName" : "SHA256withRSA",
        "subject" : "CN=nonprod-api.mycompany.com, OU=ITS, O=MyCompany, L=MELBOURNE, ST=VIC, C=AU",
        "subjectAlternativeNames" : [ ],
        "validFrom" : 1484281324000,
        "version" : 3
      } ],
      "certName" : "nonprod-api.mycompany.com.key.pem-cert"
    }

    בדוגמה שלמעלה, הגורם שהנפיק את האישור הוא "CN=MyCompany Test SHA2 CA G2, DC=testcore, DC=test, DC=dir, DC=mycompany, DC=com"

  4. בודקים מה הרשימה של המנפיקים או רשויות האישורים שהשרת העורפי מקבל, באחת מהשיטות הבאות:

    טכניקה מספר 1: שימוש בפקודה openssl שמופיעה בהמשך:

    openssl s_client -host <backend server host name> -port <Backend port#> -cert <Client Certificate> -key <Client Private Key>
    

    מעיינים בקטע "Acceptable Client Certificate CA names" בפלט של הפקודה הזו, כמו שמוצג בהמשך:

    Acceptable client certificate CA names
    /C=AU/ST=VIC/L=MELBOURNE/O=MyCompany/OU=ITS/CN=nonprod-api.mycompany.com
    /C=AU/ST=VIC/L=MELBOURNE/O=MyCompany/OU=ITS/CN=nonprod-api.mycompany.com

    טכניקה מספר 2: בדיקת חבילת Certificate Request בחבילות TCP/IP, שבהן השרת העורפי מבקש מהלקוח לשלוח את האישור שלו:

    בדוגמה של מנות TCP/IP שמוצגת למעלה, Certificate Request המנה היא הודעה מספר 7. אפשר לעיין בקטע 'שמות ייחודיים' שבו מפורטות רשויות האישורים המקובלות של שרת הקצה העורפי.

    alt_text

  5. בודקים אם רשות האישורים שהתקבלה בשלב 3 זהה לרשימת המנפיקים או רשויות האישורים שהתקבלה בשלב 4. אם יש אי התאמה, מעבד ההודעות לא ישלח את אישור הלקוח לשרת העורפי.

    בדוגמה שלמעלה, אפשר לראות שגורם ההנפקה של אישור העלה של הלקוח במאגר המפתחות של מעבד ההודעות לא תואם לאף אחד מרשויות האישורים המקובלות של שרת הקצה העורפי. לכן, מעבד ההודעות לא שולח את אישור הלקוח לשרת הבק-אנד. כתוצאה מכך, לחיצת היד של SSL נכשלת ושרת הבק-אנד שולח את ההודעה Fatal alert: bad_certificate.

רזולוציה

  1. מוודאים שהאישור עם המנפיק/רשות האישורים שתואמים למנפיק/רשות האישורים של אישור העלה של הלקוח (האישור הראשון בשרשרת) מאוחסן במאגר האישורים המהימנים של שרת הקצה העורפי.
  2. בדוגמה שמתוארת בחוברת ההפעלה הזו, האישור עם המנפיק "issuer" : "CN=MyCompany Test SHA2 CA G2, DC=testcore, DC=test, DC=dir, DC=mycompany, DC=com" נוסף למאגר האישורים של שרת הקצה העורפי כדי לפתור את הבעיה.

אם הבעיה נמשכת, עוברים אל איסוף מידע לצורך אבחון.

איסוף פרטי אבחון

אם הבעיה נמשכת גם אחרי שמבצעים את ההוראות שלמעלה, צריך לאסוף את נתוני האבחון הבאים. אפשר ליצור קשר עם התמיכה של Apigee Edge ולשתף איתם את הקבצים:

  1. אם אתם משתמשים ב-Public Cloud, עליכם לספק את הפרטים הבאים:
    1. שם הארגון
    2. שם הסביבה
    3. שם ה-proxy ל-API
    4. השלמת פקודת curl לשחזור השגיאה
    5. קובץ פרטי העברה שבו מוצגת השגיאה
    6. חבילות TCP/IP שתועדו בשרת העורפי
  2. אם אתם משתמשים ב-Private Cloud, עליכם לספק את הפרטים הבאים:
    1. הודעת השגיאה המלאה שזוהתה
    2. חבילת proxy ל-API
    3. קובץ פרטי העברה שבו מוצגת השגיאה
    4. יומנים של מעבד בקשות /opt/apigee/var/log/edge-message-processor/logs/system.log
    5. חבילות TCP/IP שתועדו בשרת העורפי או במעבד ההודעות.
    6. הפלט של Get cert for keystore API.
  3. פרטים על החלקים בחוברת הזו שניסית להשתמש בהם וכל תובנה אחרת שתעזור לנו לפתור את הבעיה הזו במהירות.