השאירו את CA השורש של ה-VPN לא מקוון ועדיין הנפיקו אישורים
החזיקו את מפתח השורש מחוץ לרשת, חתמו על אישורי עלה עם CA ביניים, פרסמו רשימת ביטול בזמן ובדקו את השרשרת לפני שהלקוח הראשון מתחבר.
ה-VPN שלכם עונה לחיבורים בשעה 3 לפנות בוקר בעוד המכונה שחותמת על האישורים שלו כבויה במגירה. לקוח שמאמת את אישור השרת לא יוצר קשר עם ה-CA שלכם לעולם, ולכן ה-CA לא צריך להיות נגיש לאף חיבור. החלק שדורש תכנון הוא היום שבו יש להנפיק, לחדש או לבטל אישור בעוד מפתח השורש נשאר נעול.
מה חיבור באמת מאמת
כאשר לקוח OpenVPN מתחבר, הוא עובר את שרשרת החתימות מעלה מאישור העלה של השרת עד אישור CA שהוא כבר סומך עליו, בודק את חלון התוקף ואם הפרופיל אומר כך, עיין ברשימת ביטול. הוא לעולם לא מחפש את CA השורש שלכם. השרת עושה את התמונה המראה עבור אישורי לקוח. בזמן החיבור נוכחים רק העלה, הביניים וחבילת האמון שהפיצו קודם.
מה שבאמת נכשל הוא אחד משלושה דברים: מפתח החתימה אינו זמין בזמן ההנפקה, רשימת ביטול התיישנה, או שחבילת האמון של הלקוח ישנה מהשרשרת שהשרת מציג. הראשון הוא בעיית תהליך; השניים האחרים הם בעיות הפצה. אף אחד מהם אינו דורש שמפתח השורש יהיה מקוון, ושירות VPN מנוהל שדורש אישור פרטי לאימות המנהרה רק מעביר את סמכות החתימה לספק. Azure נקודה-לאתר עובד ההפך: אתם מעלים אישור שורש ואישורים שנחתמו תחתיו נחשבים מהימנים, אז שאלו מי חותם, מי מאמת ואיך אישור נמשך מוקדם.
בנו את השורש על מארח שלעולם לא מקבל כתובת
בשורש לא מקוון משתמשים לעתים רחוקות: כדי לחתום על אישור ה-CA הביניים, ומאוחר יותר כדי לחדש או לבטל אותו. כל השאר קורה רמה אחת למטה. עשו את עבודת השורש במכונה שנשמרת מחוץ לרשת, אם אפשר ממערכת חיה, ותעדו מי היה בחדר. הפקודות מניחות OpenSSL 1.1.1 או חדש יותר, כי -addext אינו זמין בגרסאות בנייה ישנות.
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out root.key
openssl req -x509 -new -key root.key -sha256 -days 7300 \
-subj "/CN=Example VPN Root CA" -out root.crt \
-addext "basicConstraints=critical,CA:TRUE" \
-addext "keyUsage=critical,keyCertSign,cRLSign"
צרו את הביניים במארח החתימה והביאו רק את בקשתו למכונה הלא מקוונת. השורש חותם על הבקשה הזו באורך נתיב אפס, כך שה-CA הביניים רשאי לחתום על אישורי נקודת קצה אך אינו רשאי ליצור CA נוסף תחתיו. הרחבה אחת זו מונעת ממארח חתימה שנפרץ להטביע סמכות שוות ערך לשורש. השתמשו ב-ECDSA רק אם כל לקוח מריץ OpenVPN 2.4 או חדש יותר; אחרת צרו מפתחות RSA ושמרו על אותו זרם.
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out intermediate.key
openssl req -new -key intermediate.key -subj "/CN=Example VPN Intermediate CA" -out intermediate.csr
openssl x509 -req -in intermediate.csr -CA root.crt -CAkey root.key \
-CAcreateserial -days 3650 -sha256 -extfile intermediate.ext -out intermediate.crt
intermediate.ext מכיל את basicConstraints=critical,CA:TRUE,pathlen:0, keyUsage=critical,keyCertSign,cRLSign ואת authorityKeyIdentifier=keyid:always. החזירו את מפתח השורש לכספת שלו לפני שהמארח החתימה מתחבר מחדש. אם השורש צריך להיות מסוגל לבטל את הביניים, חתמו עליו עם openssl ca -config root.cnf -extensions intermediate_ca מול index.txt ברמת השורש במקום ב-openssl x509 -req, כדי שמספר הסידורי יירשם; לאחר מכן שלחו את רשימת השורש שנוצרה בתוך פרופיל הלקוח, שם crl-verify מבוסס קובץ קורא אותה בעת האתחול. שלחו ללקוחות חבילה עם שני אישורי ה-CA; עלה בלבד לא יאומת מול השורש.
הנפיקו עלים מה-CA הביניים, עם מסד נתונים
חתימה עם openssl x509 -req עובדת עד שצריך לבטל, כי שום דבר לא רושם את המספר הסידורי. השתמשו ב-openssl ca מהאישור הראשון כדי שמספר סידורי, תפוגה ומצב ביטול יחיו במסד נתונים אחד. צרו את קובצי מסד הנתונים פעם אחת, ואז כוונו את התצורה אליהם:
mkdir -p /etc/vpn-ca/intermediate/newcerts
touch /etc/vpn-ca/intermediate/index.txt
echo 1000 > /etc/vpn-ca/intermediate/serial
echo 1000 > /etc/vpn-ca/intermediate/crlnumber
[ ca ]
default_ca = CA_default
[ CA_default ]
dir = /etc/vpn-ca/intermediate
database = $dir/index.txt
new_certs_dir = $dir/newcerts
serial = $dir/serial
crlnumber = $dir/crlnumber
certificate = $dir/intermediate.crt
private_key = $dir/intermediate.key
default_md = sha256
default_days = 365
default_crl_days = 30
unique_subject = no
policy = policy_any
[ policy_any ]
commonName = supplied
[ server_cert ]
basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature
extendedKeyUsage = serverAuth
subjectAltName = @server_names
[ client_cert ]
basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature
extendedKeyUsage = clientAuth
[ server_names ]
DNS.1 = vpn.example.com
שורת שימוש המפתח מניחה מפתח ECDSA; הוסיפו keyEncipherment עבור RSA. תנו לכל לקוח שם נפוץ משלו, כי OpenVPN מזהה חיבור לפי השדה הזה, ושני מחשבים ניידים שחולקים CN=vpn-client לא ניתנים להבחנה ביומנים, בכללי client-config-dir או בכל דחיפה לכל משתמש, ואתם מאבדים את הייחוס שהופך החלטת ביטול להגנתית. הנפיקו ואמתו עלה כך:
openssl req -new -key alice.key -subj "/CN=alice-laptop" -out alice.csr
openssl ca -config intermediate.cnf -extensions client_cert -in alice.csr -out alice.crt -batch
openssl verify -CAfile ca-bundle.crt -untrusted intermediate.crt alice.crt
החזיקו את מפתח החתימה מאחורי גבול מדיניות במקום להעתיק אותו למארח ה-VPN: שירות קטן שמאמת את הבקשה, בודק את השם הנפוץ מול רשימת מורשים ומחזיר רק את האישור. תעדו כל מספר סידורי; היומן הוא החוליה היחידה בין אישור לאדם שביקש אותו.
פרסמו ביטול בלי להעיר את השורש
אישורי לקוח מבוטלים על ידי הביניים, לעולם לא על ידי השורש. רשימת השורש חשובה רק כששורפים ביניים. כוונו את השרת לרשימת הביניים עם crl-verify. CRL מבוסס קובץ נטען בעת הפעלת השירות, אז טענו מחדש לאחר כל יצירה מחדש; צורת התיקייה קוראת מצב בזמן החיבור ועולה קובץ אחד לכל מספר סידורי.
בטלו וצרו מחדש בשתי פקודות מאותו מסד נתונים:
openssl ca -config intermediate.cnf -revoke /etc/vpn-ca/intermediate/newcerts/1001.pem
openssl ca -config intermediate.cnf -gencrl -out /etc/openvpn/crl.pem
ואז שימו לב לשעון. CRL נושא שדה עדכון הבא, וכשהתאריך הזה עובר, עמית שמאמת דוחה את הרשימה במקום להתעלם ממנה. יצירה מחדש חודשית עם default_crl_days = 30 משאירה מרווח; שנתית לא. תוקף עלים קצר יותר הוא הבקרה האמינה יותר, כי ביטול חל רק במקום שבו מתייעצים ברשימה.
בדקו את השרשרת, ואז כתבו את הטקס
שחזרו ארבעה כשלים לפני שסומכים על התכנון. אמתו את השרשרת עם openssl verify -CAfile ca-bundle.crt -untrusted intermediate.crt server.crt. התחברו עם אישור לקוח שפג תוקפו וודאו שהשרת דוחה אותו. בטלו לקוח ניסוי, טענו מחדש וודאו שהרשימה נכנסת לתוקף. הקדימו את שעון המארח ביום, כי שעון שגוי יכול להנפיק ולפסול אישורים שנראים תקפים במקום אחר.
שורש לא מקוון הוא בקרה רק אם קיימת הניירת: שני אנשים בשם לטקס, תיעוד של כל מספר סידורי שנחתם, גיבוי מוצפן שנשמר בנפרד מהמכונה, ובדיקת שחזור על מארח מבודד. חתמו על ביניים רזרבי במהלך אותו טקס כדי שמארח חתימה מת לא יאלץ אתכם לפתוח את השורש בחצות.
לפני שאתם קוראים לזה גמור, ודאו חמישה דברים: מפתח השורש אינו על מארח מחובר לרשת, מפתח הביניים חי רק על מארח החתימה, חבילת הלקוח נושאת את שני אישורי ה-CA, ל-CRL יש עדכון בא עתידי, ולמישהו יש רשומת יומן לכל תפוגה בשרשרת. אם אחד חסר, התצוגה שנראית לא מקוונת היא פשוט לא מתועדת.