כיצד ExpressVPN שומר על שרתי האינטרנט שלה טלאים ומאובטחים

שרת ExpressVPN עולה מהאפר.

מאמר זה מסביר את גישתו של ExpressVPN ניהול תיקוני אבטחה עבור התשתית המפעילה את אתר ExpressVPN (לא שרתי ה- VPN). באופן כללי, הגישה שלנו לביטחון היא:

  1. הפוך מערכות מאוד קשה לפרוץ.
  2. צמצם את הנזק הפוטנציאלי אם מערכת נפרצת באופן היפותטי ומכירה בעובדה כי מערכות מסוימות אינן ניתנות לאבטחה מושלמת. בדרך כלל זה מתחיל בשלב התכנון האדריכלי, בו אנו ממזערים את הגישה של יישום.
  3. צמצם את משך הזמן שמערכת יכולה להישאר בסכנה.
  4. אמת נקודות אלה עם מזיקים קבועים, פנימיים וחיצוניים כאחד.

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

כאן אנו מסבירים כיצד אנו משיגים את הדברים הבאים:

  1. ודא שכל השרתים טלאים במלואם ולעולם לא יותר מ- 24 שעות מאחורי פרסומי CVE.
  2. וודא כי אף פעם לא נעשה שימוש בשרת במשך יותר מ- 24 שעות, ובכך לשים גבול עליון למשך הזמן שתוקף יכול להתמיד בו.

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

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

כיצד אנו משתמשים בספרי משחק ותשובות ענן

תשתית האינטרנט של ExpressVPN מתארחת ב- AWS (בניגוד לשרתי ה- VPN שלנו הפועלים על חומרה ייעודית) ואנחנו עושים שימוש רב בתכונות שלה בכדי לאפשר בנייה מחדש..

כל תשתית האינטרנט שלנו מסופקת עם Cloudformation, ואנחנו מנסים להפוך אוטומטית לתהליכים רבים ככל שאפשר. עם זאת, אנו מוצאים שעבודה עם תבניות ענן של Cloudformation איננה נעימה למדי בגלל הצורך בחזרה, הקריאות הכללית הגרועה וההגבלות של תחביר JSON או YAML..

כדי להקל על כך אנו משתמשים ב- DSL שנקרא cloudformation-ruby-dsl המאפשר לנו לכתוב הגדרות תבניות ב- Ruby ולייצא תבניות Cloudformation ב- JSON.

בפרט, ה- DSL מאפשר לנו לכתוב סקריפטים של נתוני משתמשים כתסריטים רגילים המומרים ל- JSON באופן אוטומטי (ולא לעבור את התהליך הכואב של הפיכת כל שורה של סקריפט למחרוזת JSON תקפה).

תפקיד גנרי Ansible שנקרא Cloudformation-infrastructure דואג להעביר את התבנית בפועל לקובץ זמני, שמשמש אז על ידי מודול ה- Cloudformation Ansible:

- שם: 'render {{component}} stack cloudformation json'
מעטפת: 'אודם "{{template_name | ברירת מחדל (רכיב)}}. rb" הרחב - שם המחסנית {{stack}} - אזור {{aws_region}} > {{tempfile_path}} '
טוען:
chdir: ../cloudformation/templates
שונה_שעה: שקר

- שם: 'צור / עדכן {{component}} מחסנית'
עננים:
stack_name: '{{stack}} - {{xv_env_name}} - {{component}}'
מדינה: הווה
אזור: '{{aws_region}}'
תבנית: '{{tempfile_path}}'
template_parameters: '{{template_parameters | ברירת מחדל ({})}} '
stack_policy: '{{stack_policy}}'
הרשמה: cf_result

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

לאחר מכן אנו משתמשים בטריק מעט מכוער אך שימושי כדי להפוך את הפלט של מודול הענן לתצורות משתנות לתפקידים הבאים. עלינו להשתמש בגישה זו מכיוון ש- Ansible אינה מאפשרת ליצור משתנים עם שמות דינמיים:

- כולל: _tempfile.yml
- עותק:
תוכן: '{{רכיב | regex_replace ("-", "_")}} _ מחסנית: {{cf_result.stack_outputs | to_json}} '
dest: '{{tempfile_path}}. json'
no_log: נכון
שונה_שעה: שקר

- include_vars: '{{tempfile_path}}. json'

עדכון קבוצת הסינון האוטומטי EC2

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

Cloudformation מנצרת את כל הבנייה מחדש, ואנחנו מפעילים את ספר ההשמעה Ansible המתואר לעיל כל 24 שעות כדי לבנות מחדש את כל המופעים, תוך שימוש במשאב AutoScalingRollingUpdate UpdatePolicy של AWS :: AutoScaling :: AutoScalingGroup Group.

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

משאב 'AppLaunchConfiguration', הקלד: 'AWS :: AutoScaling :: LaunchConfiguration',
נכסים: {
KeyName: param ('AppServerKey'),
ImageId: param ('AppServerAMI'),
InstanceType: param ('AppServerInstanceType'),
קבוצות אבטחה: [
param ('SecurityGroupApp'),
],
IamInstanceProfile: param ('RebuildIamInstanceProfile'),
InstanceMonitoring: נכון,
מיפוי BlockDevice: [
{
שם המכשיר: '/ dev / sda1', # נפח שורש
Ebs: {
VolumeSize: param ('AppServerStorageSize'),
VolumeType: param ('AppServerStorageType'),
DeleteOnTermination: נכון,
},
},
],
UserData: base64 (אינטרפולציה (קובץ ('סקריפטים / app_user_data.sh')),
}

אם אנו מבצעים עדכון כלשהו לסקריפט נתוני המשתמש, אפילו תגובה, תצורת ההשקה תיחשב כמשנה, ו- Cloudformation תעדכן את כל המקרים בקבוצה Scaling Auto כך שתציית לתצורת ההשקה החדשה..

בזכות Cloudformation-ruby-dsl ופונקציית השירות הבין-אינטראקטיבי שלה, אנו יכולים להשתמש בהתייחסויות Cloudformation בתסריט app_user_data.sh:

readonly build_timestamp ="{{param ('RebuildTimestamp')}}"

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

ווים של מחזור החיים

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

השימוש במחברי מחזור החיים מאפשר לנו להתקיים באותו מחזור חיים של מופע, גם כאשר אנו מפעילים את העדכון באמצעות Cloudformation וגם כאשר מתרחש אירוע קנה מידה אוטומטי (לדוגמא, כאשר מופע נכשל בבדיקת הבריאות EC2 ונפסק). אנו לא משתמשים באות cfn ובמדיניות העדכון האוטומטי של WaitOnResourceSignals כיוון שהם מיושמים רק כאשר Cloudformation מפעילה עדכון..

כשקבוצה לקנה מידה אוטומטי יוצרת מופע חדש, וו מחזור החיים EC2_INSTANCE_LAUNCHING מופעל, והוא מציב את המופע באופן אוטומטי במצב המתנה: המתן.

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

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

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

אנו רוצים שיצירת מופע חדש תיכשל ברגע שנבין שהוא לעולם לא יהפוך לבריא. כדי להשיג זאת, הגדרנו מלכודת ERR בסקריפט נתוני המשתמש יחד עם set -o errtrace כדי להתקשר לפונקציה השולחת פעולה של מחזור החיים של ABANDON כך שמופע לקוי יכול להסתיים בהקדם האפשרי.

סקריפטים של נתוני משתמשים

סקריפט נתוני המשתמש אחראי להתקנת כל התוכנות הנדרשות במופע. השתמשנו בהצלחה במקרים של Ansible to provision ו- Capistrano כדי לפרוס יישומים במשך זמן רב, ולכן אנו משתמשים בהם גם כאן, ומאפשרים את ההבדל המינימלי בין פריסות רגילות לבנייה מחדש..

סקריפט נתוני המשתמש בודק את מאגר היישומים שלנו מ- Github, הכולל סקריפטים להקצאת Ansible, ואז מריץ את Ansible ו- Capistrano הצביע על localhost.

בבדיקת הקוד עלינו להיות בטוחים כי הגרסה הפרושה של היישום כרגע נפרסת במהלך הבנייה מחדש. סקריפט הפריסה של Capistrano כולל משימה שמעדכנת קובץ ב- S3 המאחסן את ה- SHA של הפרוץ הנוכחי. כאשר הבנייה מחדש מתרחשת, המערכת מרימה את ההתחייבות שאמורה לפרוס מאותו קובץ.

עדכוני תוכנה מיושמים על ידי הפעלת שדרוג ללא השגחה בקדמת הבמה עם הפקודה -d שדרוג ללא השגחה. לאחר השלמתו, מופע מופעל מחדש ומתחיל בבדיקות הבריאות.

התמודדות עם סודות

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

אנו רוצים להימנע מאחסון סודות בזיכרון המתמשך של המופע. הסוד היחיד שאנו שומרים בדיסק הוא מפתח ה- Github SSH, אך לא ביטוי הסיסמה שלו. גם אנחנו לא שומרים את סיסמת הכספת Ansible.

עם זאת, עלינו להעביר ביטויים סיסמא אלה ל- SSH ו- Ansible בהתאמה, וזה אפשרי רק במצב אינטראקטיבי (כלומר, כלי השירות מבקש מהמשתמש להזין את ביטויי הסיסמה באופן ידני) מסיבה טובה - אם ביטוי סיסמה הוא חלק מפקודה הוא שנשמרו בהיסטוריית הקונכיות ויכולים להיות גלויים לכל המשתמשים במערכת אם הם מפעילים ps. אנו משתמשים בכלי הציפיות כדי להפוך את האינטראקציה לאוטומטית עם כלים אלה:

מצפה << EOF
cd $ {repo_dir}
spawn make ansible_local env = $ {deploy_env} stack = $ {stack} host host = $ {server_hostname}
קבע פסק זמן 2
צפו לסיסמת הכספת
שלח "$ {vault_password} \ r"
קבע פסק זמן 900
מצפה {
"בלתי ניתן להשגה = 0 נכשל = 0" {
יציאה 0
}
eof {
יציאה 1
}
פסק זמן {
יציאה 1
}
}
EOF

מפעילה את הבנייה מחדש

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

אנו משיגים זאת על ידי קביעת מדיניות ערימה מגבילה בערימות Cloudformation שלנו כך שרק המשאבים הדרושים לבנייה מחדש מתעדכנים:

{
"הצהרה" : [
{
"השפעה" : "להתיר",
"פעולה" : "עדכון: שנה",
"המנהל": "*",
"משאב" : [
"LogicalResourceId / * AutoScalingGroup"
]
},
{
"השפעה" : "להתיר",
"פעולה" : "עדכון: החלף",
"המנהל": "*",
"משאב" : [
"LogicalResourceId / * LaunchConfiguration"
]
}
]
}

כאשר עלינו לבצע עדכוני תשתית בפועל, עלינו לעדכן באופן ידני את מדיניות הערימה כדי לאפשר עדכונים למשאבים אלה במפורש.

מכיוון ששמות המארח וה- IP של השרתים שלנו משתנים מדי יום, יש לנו סקריפט המעדכן את המלאי המקומי Ansible ואת תצורות ה- SSH. זה מגלה את המקרים באמצעות API של AWS על ידי תגיות, מעביר את המלאי וקבצי התצורה מתבניות ERB ומוסיף את ה- IP החדש ל- SSH ידוע_הסטים.

ExpressVPN ממלא אחר תקני האבטחה הגבוהים ביותר

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

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

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

כיצד ExpressVPN שומר על שרתי האינטרנט שלה טלאים ומאובטחים
admin Author
Sorry! The Author has not filled his profile.