Hur ExpressVPN håller sina webbservrar patchade och säkra

[ware_item id=33][/ware_item]

ExpressVPN-server stiger upp från asken.


Den här artikeln förklarar ExpressVPN: s inställning till hantering av säkerhetspapper för infrastrukturen som kör ExpressVPN-webbplatsen (inte VPN-servrarna). I allmänhet är vår strategi för säkerhet:

  1. Gör system mycket svårt att hacka.
  2. Minimera den potentiella skadan om ett system hypotetiskt blir hackat och erkänner det faktum att vissa system inte kan göras helt säkra. Vanligtvis börjar detta i den arkitektoniska designfasen, där vi minimerar en applikations åtkomst.
  3. Minimera tiden att ett system kan förbli äventyrat.
  4. Bekräfta dessa punkter med regelbundna pentests, både interna och externa.

Säkerhet ingår i vår kultur och är den främsta frågan som styr allt vårt arbete. Det finns många andra ämnen som våra utvecklingsmetoder för säkerhetsprogramvara, applikationssäkerhet, anställdsprocesser och utbildning, etc., men de är utanför ramen för detta inlägg.

Här förklarar vi hur vi uppnår följande:

  1. Se till att alla servrar är fullständigt korrigerade och aldrig mer än 24 timmar efter publicering av CVE: er.
  2. Se till att ingen server någonsin används under mer än 24 timmar, vilket sätter en övre gräns för den tid som en angripare kan ha uthållighet.

Vi uppnår båda målen genom en automatiserat system som bygger om servrar, börjar med operativsystemet och alla senaste korrigeringar och förstör dem minst en gång var 24 timmar.

Vår avsikt med den här artikeln är att vara användbar för andra utvecklare som möter liknande utmaningar och att ge transparens i ExpressVPN: s verksamhet till våra kunder och media.

Hur vi använder Ansible playbooks och Cloudformation

ExpressVPNs webbinfrastruktur är värd på AWS (i motsats till våra VPN-servrar som körs på dedicerad hårdvara) och vi använder kraftigt dess funktioner för att möjliggöra ombyggnad.

Hela vår webbinfrastruktur är försedd med Cloudformation, och vi försöker automatisera så många processer som vi kan. Vi tycker dock att det är ganska obehagligt att arbeta med råa Cloudformation-mallar på grund av behovet av upprepning, generellt dålig läsbarhet och begränsningarna i JSON- eller YAML-syntax.

För att mildra detta använder vi en DSL som heter cloudformation-ruby-dsl som gör det möjligt för oss att skriva malldefinitioner i Ruby och exportera Cloudformation-mallar i JSON.

I synnerhet tillåter DSL oss att skriva användardataskript som vanliga skript som konverteras till JSON automatiskt (och inte går igenom den smärtsamma processen att göra varje rad i skriptet till en giltig JSON-sträng).

En generisk Ansible-roll som kallas cloudformation-infrastruktur tar hand om att återge den faktiska mallen till en tillfällig fil, som sedan används av cloudformationen Ansible-modulen:

- namn: 'render {{component}} stack cloudformation json'
skal: 'rubin "{{mall_namn | standard (komponent)}}. rb" expand - stack-name {{stack}} - region {{aws_region}} > {{tempfile_path}} '
args:
chdir: ../cloudformation/templates
ändrad_ när: falsk

- namn: 'skapa / uppdatera {{component}} stack'
cloudformation:
stack_name: '{{stack}} - {{xv_env_name}} - {{component}}'
tillstånd: närvarande
region: '{{aws_region}}'
mall: '{{tempfile_path}}'
template_parameters: '{{template_parameters | standard({}) }}'
stack_policy: '{{stack_policy}}'
register: cf_result

I spelboken kallar vi molnformationsinfrastrukturrollen flera gånger med olika komponentvariabler för att skapa flera Cloudformation-staplar. Vi har till exempel en nätverksstack som definierar VPC och relaterade resurser och en appstack som definierar autoskalningsgruppen, startkonfiguration, livscykelkrokar osv..

Sedan använder vi ett något fult men användbart trick för att förvandla outputen från molnformationsmodulen till Ansvängliga variabler för efterföljande roller. Vi måste använda den här metoden eftersom Ansible inte tillåter skapandet av variabler med dynamiska namn:

- inkluderar: _tempfile.yml
- kopia:
innehåll: '{{komponent | regex_replace ("-", "_")}} _ stack: {{cf_result.stack_outputs | to_json}} '
dest: '{{tempfile_path}}. json'
no_log: sant
ändrad_ när: falsk

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

Uppdatering av EC2 Auto Scaling-gruppen

ExpressVPN-webbplatsen är värd på flera EC2-instanser i en autoskalningsgrupp bakom en applikationsbelastningsbalansör som gör det möjligt för oss att förstöra servrar utan driftstopp eftersom lastbalansern kan tömma befintliga anslutningar innan en instans upphör..

Cloudformation orkesterar hela ombyggnaden, och vi utlöser den ansvarsfulla spellistan som beskrivs ovan var 24: e timme för att bygga om alla instanser, med hjälp av AutoScalingRollingUpdate UpdatePolicy-attributet för AWS :: AutoScaling :: AutoScalingGroup resurs.

När du helt enkelt utlöses upprepade gånger utan några ändringar används inte attributet UpdatePolicy - det åberopas endast under speciella omständigheter som beskrivs i dokumentationen. En av dessa omständigheter är en uppdatering av startskonfigurationen för automatisk skalning - en mall som en grupp för automatisk skalning använder för att starta EC2-instanser - som inkluderar EC2-användardataskriptet som körs för att skapa en ny instans:

resurs 'AppLaunchConfiguration', typ: 'AWS :: AutoScaling :: LaunchConfiguration',
Egenskaper: {
Nyckelnamn: param ('AppServerKey'),
ImageId: param ('AppServerAMI'),
InstanceType: param ('AppServerInstanceType'),
Säkerhetsgrupper: [
param ( 'SecurityGroupApp'),
],
IamInstanceProfile: param ('RebuildIamInstanceProfile'),
InstanceMonitoring: true,
BlockDeviceMappings: [
{
Enhetsnamn: '/ dev / sda1', # rotvolym
Ebs: {
VolumeSize: param ('AppServerStorageSize'),
VolumeType: param ('AppServerStorageType'),
DeleteOnTermination: true,
},
},
],
UserData: base64 (interpolera (fil ('skript / app_user_data.sh'))),
}

Om vi ​​gör någon uppdatering av användardataskriptet, till och med en kommentar, kommer startskonfigurationen att betraktas som ändrad, och Cloudformation kommer att uppdatera alla instanser i Auto Scaling-gruppen för att följa den nya lanseringskonfigurationen.

Tack vare cloudformation-ruby-dsl och dess interpolatverktygsfunktion kan vi använda Cloudformation-referenser i skriptet app_user_data.sh:

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

Den här proceduren säkerställer att vår startkonfiguration är ny varje gång ombyggnaden utlöses.

Livscykelkrokar

Vi använder autoskalningens livscykelkrokar för att se till att våra instanser är helt tillhandahållna och klarar de nödvändiga hälsokontrollerna innan de går live.

Genom att använda livscykelkrokar kan vi ha samma instanslivscykel både när vi utlöser uppdateringen med Cloudformation och när en automatisk skalningshändelse inträffar (till exempel när en instans misslyckas med en EC2-hälsokontroll och blir avslutad). Vi använder inte cfn-signal och uppdateringspolicyn för WaitOnResourceSignals automatisk skalning eftersom de endast tillämpas när Cloudformation utlöser en uppdatering.

När en automatisk skalningsgrupp skapar en ny instans utlöses EC2_INSTANCE_LAUNCHING livscykelkroken, och den sätter automatiskt instansen i ett väntande: vänta-tillstånd.

När instansen är helt konfigurerad, börjar den träffa sina egna endpoints för hälsokontroll med lock från användardataskriptet. När hälsokontrollerna rapporterar att ansökan är frisk, ger vi ut en åtgärd FORTSÄTT för denna livscykelkrok, så att instansen fästs vid lastbalansen och börjar betjäna trafiken.

Om hälsokontrollerna misslyckas utfärdar vi en ABANDON-åtgärd som avslutar den felaktiga instansen, och den automatiska skalningsgruppen startar en annan.

Förutom att inte klara hälsokontroller kan vårt användardataskript misslyckas vid andra punkter - till exempel om tillfälliga anslutningsfrågor förhindrar installation av programvara.

Vi vill att skapandet av en ny instans ska misslyckas så snart vi inser att den aldrig kommer att bli frisk. För att uppnå detta ställer vi in ​​en ERR-fälla i användardataskriptet tillsammans med set -o errtrace för att ringa en funktion som skickar en ABANDON-livscykelåtgärd så att en felaktig instans kan avslutas så snart som möjligt.

Användardataskript

Användardataskriptet ansvarar för att installera all nödvändig programvara på instansen. Vi har framgångsrikt använt Ansible to provision-instanser och Capistrano för att distribuera applikationer under lång tid, så vi använder dem också här, vilket möjliggör en minimal skillnad mellan regelbundna distributionsplatser och ombyggnader.

Användardataskriptet kontrollerar vårt applikationsförråd från Github, som innehåller skript för ansvarsfullt tillhandahållande, kör sedan Ansible och Capistrano pekade på localhost.

När vi checkar ut koden måste vi vara säkra på att den aktuella versionen av applikationen distribueras under ombyggnaden. Capistrano-implementeringsskriptet innehåller en uppgift som uppdaterar en fil i S3 som lagrar det för närvarande utplacerade commit SHA. När ombyggnaden händer, tar systemet upp åtagandet som är tänkt att distribueras från den filen.

Programuppdateringar tillämpas genom att köra obevakad uppgradering i förgrunden med kommandot obevakad uppgradering -d. När det är klart startar instansen om och startar hälsokontrollerna.

Att hantera hemligheter

Servern behöver tillfällig åtkomst till hemligheter (t.ex. lösenordet Ansible vault) som hämtas från EC2 Parameter Store. Servern kan bara komma åt hemligheter under en kort tid under ombyggnaden. När de har hämtats ersätter vi omedelbart den ursprungliga instansprofilen mot en annan som bara har tillgång till resurser som krävs för att applikationen ska kunna köras.

Vi vill undvika att lagra några hemligheter i instansens ihållande minne. Den enda hemligheten vi sparar på disken är Github SSH-nyckeln, men inte dess lösenfras. Vi sparar inte heller lösenordet för Ansible vault.

Men vi måste skicka dessa lösenfraser till SSH respektive Ansible, och det är bara möjligt i interaktivt läge (dvs. verktyget uppmanar användaren att mata in lösenfraser manuellt) av goda skäl - om en lösenfras är en del av ett kommando är det sparas i skalhistoriken och kan vara synliga för alla användare i systemet om de kör ps. Vi använder förväntningsverktyget för att automatisera interaktion med dessa verktyg:

förvänta << EOF
cd $ {repo_dir}
spawn make ansible_local env = $ {deploy_env} stack = $ {stack} hostname = $ {server_hostname}
ställa in timeout 2
förvänta sig "Vault password"
skicka "$ {Vault_password} \ r"
ställa in timeout 900
förvänta sig {
"ouppnåelig = 0 misslyckades = 0" {
avsluta 0
}
eof {
avsluta 1
}
Paus {
avsluta 1
}
}
EOF

Aktivera ombyggnaden

Eftersom vi utlöser ombyggnaden genom att köra samma Cloudformation-skript som används för att skapa / uppdatera vår infrastruktur, måste vi se till att vi av misstag inte uppdaterar en del av infrastrukturen som inte ska uppdateras under ombyggnaden.

Vi uppnår detta genom att fastställa en restriktiv stackpolicy för våra Cloudformation-stackar så att bara de resurser som krävs för ombyggnaden uppdateras:

{
"Påstående" : [
{
"Effekt" : "Tillåta",
"Verkan" : "Uppdatering: Ändra",
"Rektor": "*",
"Resurs" : [
"LogicalResourceId / * AutoScalingGroup"
]
},
{
"Effekt" : "Tillåta",
"Verkan" : "Uppdatering: Ersätt",
"Rektor": "*",
"Resurs" : [
"LogicalResourceId / * LaunchConfiguration"
]
}
]
}

När vi behöver göra faktiska infrastrukturuppdateringar måste vi uppdatera stapelpolicyn manuellt för att tillåta uppdateringar av dessa resurser uttryckligen.

Eftersom våra servernamn och IP: er ändras varje dag, har vi ett skript som uppdaterar våra lokala Ansible-inventarier och SSH-konfigurationer. Den upptäcker förekomsten via AWS API med taggar, återger inventeringen och konfigurerar filer från ERB-mallar och lägger till de nya IP: erna till SSH known_hosts.

ExpressVPN följer de högsta säkerhetsstandarderna

Ombyggnad av servrar skyddar oss mot ett specifikt hot: angripare får tillgång till våra servrar via en kärna- / programvaresårbarhet.

Detta är dock bara ett av de många sätten vi håller vår infrastruktur säker, inklusive men inte begränsat till att genomgå regelbundna säkerhetsrevisioner och göra kritiska system otillgängliga från internet.

Dessutom ser vi till att alla våra kod och interna processer följer de högsta säkerhetsstandarderna.

Hur ExpressVPN håller sina webbservrar patchade och säkra
admin Author
Sorry! The Author has not filled his profile.