OpenVPN עם תעודות CA Cert

אחד מהכלים האהובים עלי (אחרי Bash כמובן) בעולם הקוד הפתוח הוא OpenVPN. כלי נוח שמאפשר הקמת VPN בצורה פשוטה (אבל גמישה מאוד) בין 2 מחשבים או אתרים.

בעוד השימוש ב-Pre-Shared Key מאפשר פשטות, השימוש ב-TLS מאפשר הזדהות חזקה שאינה מבוססת ססמה, אלא על תעודות SSL. שימוש בתעודות SSL מאפשר זיהוי בצורה חד-חד ערכית על פי רוב, ללא צורך בהפצת הסיסמא לכל מען דבאי. אבל, פה גם טמונה בעיה.

זיהוי של משתמש על בסיס קבלת תעודה חתומה ע"י גורם מאושר, גורם ל-OpenVPN לנטות לכיוון עבודה עם CA עצמאי. במצב זה, המשתמש מייצר את התעודותיו בעצמו. המצב מסתבך כאשר רוצים לעבוד עם תעודות שמפיק רשם ידוע (נאמר Thawte). ייתכן מצב שמשתמש יוכל להתחבר לשרת שלנו עם תעודה שנחתמה ע"י , פשוט כי אנו סומכים על כל התעודות שנחתמו ע"י Thawte.

מסתבר שב-OpenVPN חשבו על הבעיה, והוסיפו שתי אפשרויות: tls-verify ו-tls-remote. שימוש ב-tls-remote, מאפשר הגבלת שם התעודה המרוחקת לשם ה-X509 מתוך התעודה.
בעוד השימוש ב-tls-verify, מאפשר הרצת סקריפט חיצוני שמקבל את שם התעודה (CN), ומחזיר return code של 0 או 1, אשר קובע האם התעודה תתקבל.

נתחיל ביצירת התעודת עבור השרת והלקוח. יש לייצר קודם כל בקשה לחתימה (CSR):

openssl req -newkey rsa:1024 -keyout server.key -out server.csr
openssl req -newkey rsa:1024 -keyout client.key -out client.csr

תוכן ה-CSR משמש ליצירת התעודה באתר CACert. באתר מדריכים והסברים כיצד לצור את התעודות.

בשלב זה גם כדאי יהיה להוריד ולשמור את ה-RootCA Cert של CACert. התעודה זמינה בקישור הבא. את התעודה שנוריד, נשמור במקום מרכזי בשרת ובלקוח, בד"כ בספריה

/etc/ssl/certs

הגדרות הלקוח

לאחר שהעתקנו את ה-RootCA ותעודת המשתמש למחשב הלקוח, נגדיר את הלקוח כך:

client
remote [server ip address]
ca /etc/ssl/certs/CACert-root.crt
cert /etc/ssl/private/user@mydomain.org.crt
key /etc/ssl/private/user@mydomain.org.key
tls-remote /CN=server.mydomain.org
dev tun
ping 15
ping-restart 45

הגדרות מתקדמות יותר, לפי הצורך כמובן.
ה"קסם" מתרחש בהגדרה tls-remote, שאומרת שהלקוח יתחבר רק לשרת אשר יזהה את עצמו עם תעודה, שהשם שלה הוא server.mydomain.org

הגדרות שרת

בעוד הלקוח מתחבר רק לשרת אחד, לשרת עשויים להתחבר כמה משתמשים. כדי לאפשר זאת, יש לזהות 2 דברים:

  • למשתמש יש תעודה שחתומה ע"י CACert, בדיקה זו מתבצעת ע"י המנגנונים הפנימיים של OpenVPN
  • המשתמש מאושר להתחבר לשרת לפי שם תעודת הלקוח שלו. שלב זה מתבצע ע"י סקריפט חיצוני שמוגדר תחת האפשרות tls-verify

קובץ קונפיגורציה של שרת יראה כך:

server 10.0.0.0 255.255.255.0
ca /etc/ssl/certs/CACert-root.crt
cert /etc/ssl/private/server.crt
key /etc/ssl/private/server.key
dh /etc/ssl/dh1024.pem
script-security 2
tls-verify tls-verify.sh
dev tun
ping 15
ping-restart 45

את הסקריפט tls-verify.sh נשים ב

/etc/openvpn

הסקריפט verify-cn מקבל פרמטרים סטנדרטיים מ-OpenVPN, בפורמט הבא (מתוך man openvpn):

certificate_depth X509_NAME_oneline

כאשר certificate_depth מייצג את עומק התעודה: החל מה-RootCA ועד תעודת המשתמש, בעומק 0. תעודה רגילה שחתומה ע"י CACert תתחיל בעומק 1, שייצג את התעודה החותמת (של CACert), ותסתיים בעומק 0, תעודת הלקוח. השדה  X509_NAME_online מייצג את שם התעודה בעומק הנוכחי.

סקריפט tls-verify.sh עשוי להראות כך:

#!/bin/bash
domain=mydomain.org
CA="/O=Root_CA/OU=http://www.cacert.org/CN=CA_Cert_Signing_Authority/emailAddress=support@cacert.org"
if [ $# -ne 2 ]; then echo "usage: tls-verify.sh [depth] [x509 name]"; exit 1; fi
case "$1" in
1) if [ "$2" == "$CA" ]; then exit 0; fi ;;
0) echo $2 | grep -q $
domain && exit 0 ;;
esac
exit 1

המשתנה domain מגדיר את שם הדומיין שממנו יתקבלו תעודות משתמש. שם תעודת לקוח סטנדרטי שמונפק ע"י CACert יראה כך:

/CN=CAcert WoT User/emailAddress=user@mydomain.org

השימוש ב-CA="/O=Root_CA…cacert.org" יגביל את ההתחברות לתעודות שנחתמו ע"י CACert.
השימוש ב-domain=mydomain.org יגביל את ההתחברות רק למשתמש עם תעודה המכילה את mydomain.org בשם התעודה.

אפשרות אחרת היא שימוש בקובץ אשר יכיל רשימת שמות משתמשים. במקרה זה, ניתן להשתמש בסקריפט:

#!/bin/bash
users_file=/etc/openvpn/users.txt
CA="/O=Root_CA/OU=http://www.cacert.org/CN=CA_Cert_Signing_Authority/emailAddress=support@cacert.org"
if [ $# -ne 2 ]; then echo "usage: tls-verify.sh [depth] [x509 name]"; exit 1; fi
case "$1" in
1) if [ "$2" == "$CA" ]; then exit 0; fi ;;
0) echo $2 | grep -q -f $
users_file && exit 0 ;;
esac
exit 1

במקרה זה, תאומת שם התעודה במלואה מול הקובץ המצויין בסקריפט.

כל שנשאר עכשיו הוא להפעיל את שני צדי ה-VPN ולנסות את הקונפיגורציה החדשה. בשימוש בהגדרות שמעל, ניתן לבדוק את התקשורת ע"י פקודת ping פשוטה מהלקוח:


# ping 10.0.0.1 -c 1
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=2.62 ms
...
1 packets transmitted, 1 received, 0% packet loss, time 0ms

בהצלחה!

מאזין ל: Amy Winehouse – You know I'm no good

64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=2.62 ms

הפוסט הזה פורסם בתאריך כללי. קישור קבוע.

2 תגובות על OpenVPN עם תעודות CA Cert

  1. מאת צפריר כהן:‏

    נקודה קטנה אחת: האם צריך לערב רשות מאשרת (certification authority) חיצונית?

    בשביל ה־VPN הפרטי שלי, שמשמש כל מיני מחשבים שלי, התשובה היא כמובן שלילית. יצרתי לי "רשות מאשרת" פרטית (לפי הדוגמה של easy-ca שמגיעה עם openvpn. אבל לא חסרות דרכים אחרות).

    מה שאני צריך לדאוג להפיץ בצורה אמינה:

    1. את המפתח (הציבורי) של הרשות המאשרת שלי לכל לקוח
    2. ליצור ברשות המאשרת שלי מפתח (סודי) לכל לקוח ולהעביר אותו ללקוח.

    רק מי שיש לו תעודה חתומה מזוהה כלקוח.

    במקום (2) הייתי יכול ליצור את המפתח הסודי אצל הלקוח ולהעביר אותו לחתימה אצל השרת. זה יותר בטוח, אבל במקרה שלי יש לי ערוצים בטוח (זמני – חיבור ssh) בין השרת ללקוח, ולכן אני מעדיף את הדרך הפשוטה יותר.

    אם מדובר רק על שרת openvpn אחד עם מעט משתמשים, ואם פרוצדורת ההתקנה שלך כוולת ממילא התקנת תעודה של רשות האישור, כדאי לשקול לעבוד עם רשות אישור פרטית כדי לפשט את ההתקנה.

  2. מאת כתריאל:‏

    אני מסכים לגבי הנקודה כי CA מקומי יספיק בוודאי לרשת קטנה.
    הדוגמה שמובאת בפוסט מתייחסת ל-CACert, אבל כמובן שתתאים לכל CA אחר.
    החשיבה שלי בכתיבת הפוסט הייתה יותר לכיוון חברה אשר כבר יש לה תעודות שהונפקו, או שיש לה ICA שנחתם ע"י CA מסחרי/חיצוני.
    במקרה כזה, שימוש בתשתית קיימת יותר קל.

    מזווית אישית, אני מעדיף להשתמש ב-CA אחד לכל צרכי, ובחרתי ב-CACert כי הוא חופשי ופשוט לשימוש.

כתיבת תגובה

האימייל שלך לא יוצג בבלוג. (*) שדות חובה מסומנים

*

תגי HTML מותרים: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>