Bagaimana ExpressVPN menyimpan pelayan webnya ditampal dan selamat

Server ExpressVPN meningkat dari abu.

Artikel ini menerangkan pendekatan ExpressVPN kepada pengurusan patch keselamatan untuk infrastruktur yang menjalankan laman web ExpressVPN (bukan pelayan VPN). Secara umum, pendekatan kami terhadap keselamatan adalah:

  1. Buat sistem sangat sukar untuk hack.
  2. Kurangkan kerosakan yang berpotensi jika sistem hipotetis digodam dan mengakui hakikat bahawa sesetengah sistem tidak boleh dijadikan sempurna dengan selamat. Biasanya, ini bermula dalam fasa reka bentuk seni bina, di mana kita meminimumkan akses aplikasi.
  3. Kurangkan jumlah masa bahawa sistem boleh terus dikompromi.
  4. Mengesahkan mata ini dengan pentas biasa, baik dalaman dan luaran.

Keselamatan adalah tertanam dalam budaya kita dan merupakan kebimbangan utama yang membimbing semua kerja kita. Terdapat banyak topik lain seperti amalan pembangunan perisian keselamatan, keselamatan aplikasi, proses pekerja dan latihan, dan lain-lain, tetapi mereka berada di luar skop untuk jawatan ini.

Di sini, kami menerangkan bagaimana kami mencapai perkara berikut:

  1. Pastikan semua pelayan ditampal sepenuhnya dan tidak lebih dari 24 jam di belakang penerbitan CVEs.
  2. Pastikan tiada pelayan yang digunakan selama lebih dari 24 jam, dengan itu meletakkan batas atas jumlah masa yang dapat diserang seorang penyerang.

Kami mencapai kedua-dua matlamat melalui satu sistem automatik yang membina semula pelayan, bermula dengan OS dan semua patch terkini, dan memusnahkannya sekurang-kurangnya sekali setiap 24 jam.

Tujuan kami untuk artikel ini adalah berguna bagi pemaju lain menghadapi cabaran yang sama dan memberikan ketelusan kepada operasi ExpressVPN kepada para pelanggan dan media kami.

Bagaimana kita menggunakan Buku Panduan Ansible dan Cloudformation

Infrastruktur web ExpressVPN dihoskan pada AWS (berbanding dengan pelayan VPN kami yang dijalankan pada perkakasan yang berdedikasi) dan kami memanfaatkan ciri-cirinya untuk membina semula kemungkinan.

Keseluruhan infrastruktur web kami disediakan dengan Cloudformation, dan kami berusaha untuk mengotomatisasi seberapa banyak proses yang dapat kami lakukan. Walau bagaimanapun, kami mendapati kerja dengan template Cloudformation mentah agak tidak menyenangkan kerana keperluan untuk pengulangan, kebolehbacaan keseluruhan yang lemah, dan kekangan sintaks JSON atau YAML.

Untuk mengurangkan ini, kami menggunakan DSL yang dinamakan cloudformation-ruby-dsl yang membolehkan kami menulis definisi template dalam Ruby dan templat Cloudformation eksport di JSON.

Khususnya, DSL membenarkan kita untuk menulis skrip data pengguna sebagai skrip biasa yang ditukarkan kepada JSON secara automatik (dan tidak melalui proses menyakitkan membuat setiap baris skrip ke dalam rentetan JSON yang sah).

Peranan generik yang dinamakan infrastruktur awan-awalan akan menjaga template sebenar untuk fail sementara, yang kemudiannya digunakan oleh modul awan Modul Ansible:

– name: ‘render {{component}} stack cloudformation json’
shell: ‘ruby "{{template_name | lalai (komponen)}} rb" memperluas – stack-name {{stack}} –region {{aws_region}} > {{tempfile_path}} ‘
args:
chdir: ../cloudformation/templates
changed_when: false

– nama: ‘buat / kemasukan stack {{component}}’
awan:
stack_name: ‘{{stack}} – {{xv_env_name}} – {{component}}’
nyatakan: sekarang
rantau: ‘{{aws_region}}’
templat: ‘{{tempfile_path}}’
template_parameters: ‘{{template_parameters | lalai ({})}} ‘
stack_policy: ‘{{stack_policy}}’
daftar: cf_result

Di dalam buku main, kita memanggil peranan infrastruktur cloudformation beberapa kali dengan pembolehubah komponen berbeza untuk membuat beberapa susunan Cloudformation. Contohnya, kita mempunyai timbunan rangkaian yang mentakrifkan VPC dan sumber yang berkaitan dan susunan aplikasi yang mentakrifkan kumpulan Auto Scaling, melancarkan konfigurasi, cangkuk kitar hayat, dan lain-lain.

Kami kemudian menggunakan trik yang agak hodoh tetapi bermanfaat untuk mengubah output modul awan ke pembolehubah yang boleh digunakan untuk peranan seterusnya. Kita perlu menggunakan pendekatan ini sejak Ansible tidak membenarkan penciptaan pembolehubah dengan nama dinamik:

– termasuk: _tempfile.yml
– salinan:
kandungan: ‘{{component | regex_replace ("-", "_")}} _ stack: {{cf_result.stack_outputs | to_json}} ‘
dest: ‘{{tempfile_path}}. json’
no_log: benar
changed_when: false

– include_vars: ‘{{tempfile_path}}. json’

Mengemas kini kumpulan Auto Scaling EC2

Laman web ExpressVPN dihoskan dalam pelbagai keadaan EC2 dalam kumpulan Auto Scaling di belakang Aplikasi Load Balancer yang membolehkan kami memusnahkan pelayan tanpa sebarang downtime sejak pengimbangan beban boleh mengalirkan sambungan yang ada sebelum suatu peristiwa ditamatkan.

Cloudformation menyusun semula keseluruhan membina semula, dan kami mencetuskan playbook Ansible yang diterangkan di atas setiap 24 jam untuk membina semula semua keadaan, menggunakan ciri AutoScalingRollingUpdate UpdatePolicy AWS :: AutoScaling :: AutoScalingGroup resource.

Apabila hanya dicetuskan berulang kali tanpa sebarang perubahan, atribut UpdatePolicy tidak digunakan – ia hanya digunakan dalam keadaan khas seperti yang diterangkan dalam dokumentasi. Salah satu keadaan itu adalah kemas kini kepada konfigurasi pelancaran Auto Scaling-templat yang digunakan oleh kumpulan Auto Scaling untuk melancarkan kejadian EC2-yang merangkumi skrip data pengguna EC2 yang berjalan pada pembuatan contoh baru:

sumber ‘AppLaunchConfiguration’, Taip: ‘AWS :: AutoScaling :: LaunchConfiguration’,
Properties: {
KeyName: param (‘AppServerKey’),
ImageId: param (‘AppServerAMI’),
InstanceType: param (‘AppServerInstanceType’),
KeselamatanGroups: [
param (‘SecurityGroupApp’),
],
IamInstanceProfile: param (‘RebuildIamInstanceProfile’),
InstanceMonitoring: benar,
BlockDeviceMappings: [
{
DeviceName: ‘/ dev / sda1’, # volume root
Ebs: {
VolumeSize: param (‘AppServerStorageSize’),
VolumeType: param (‘AppServerStorageType’),
DeleteOnTermination: benar,
},
},
],
UserData: base64 (interpolate (file (‘scripts / app_user_data.sh’))),
}

Jika kami membuat sebarang kemas kini kepada skrip data pengguna, walaupun ulasan, konfigurasi pelancaran akan dianggap berubah, dan Cloudformation akan mengemas kini semua keadaan dalam kumpulan Auto Scaling untuk mematuhi konfigurasi pelancaran baru.

Terima kasih kepada cloudformation-ruby-dsl dan fungsi utiliti interpolasinya, kita boleh menggunakan rujukan Cloudformation dalam skrip app_user_data.sh:

readonly rebuild_timestamp ="{{param (‘RebuildTimestamp’)}}"

Prosedur ini memastikan konfigurasi pelancaran kami adalah baru setiap kali pembinaan semula dicetuskan.

Cangkuk kitar hayat

Kami menggunakan cangkuk kitar hayat Auto Scaling untuk memastikan keadaan kami sepenuhnya disediakan dan lulus pemeriksaan kesihatan yang diperlukan sebelum mereka pergi secara langsung.

Menggunakan cangkuk kitar hayat membolehkan kita mempunyai kitar hayat contoh yang sama semasa kita mencetuskan kemas kini dengan Cloudformation dan apabila peristiwa auto-penskalaan berlaku (contohnya, apabila suatu contoh gagal mendapat pemeriksaan kesihatan EC2 dan mendapat pengakhiran). Kami tidak menggunakan cfn-signal dan dasar kemas kini auto-scaling WaitOnResourceSignals kerana ia hanya digunakan apabila Cloudformation mencetuskan kemas kini.

Apabila kumpulan auto-penskalaan mewujudkan contoh baru, cangkuk kitar hayat EC2_INSTANCE_LAUNCHING dicetuskan, dan ia secara automatik meletakkan contoh dalam Pending: Wait state.

Setelah contohnya dikonfigurasikan sepenuhnya, ia mula memukul titik akhir pemeriksaan kesihatan dengan curl dari skrip data pengguna. Sekali pemeriksaan kesihatan melaporkan aplikasi menjadi sihat, kami mengeluarkan tindakan CONTINUE untuk cangkuk kitar hayat ini, jadi contohnya akan dilampirkan kepada pengimbang beban dan mula berkhidmat lalu lintas.

Sekiranya pemeriksaan kesihatan gagal, kami mengeluarkan tindakan ABANDON yang menamatkan contoh yang salah, dan kumpulan auto skala melancarkan satu lagi.

Selain gagal untuk lulus pemeriksaan kesihatan, skrip data pengguna kami mungkin gagal di tempat lain-contohnya, jika isu sambungan sementara mencegah pemasangan perisian.

Kami mahu penciptaan contoh baru gagal apabila kami menyedari bahawa ia tidak akan menjadi sihat. Untuk mencapai itu, kami menetapkan perangkap ERR dalam skrip data pengguna bersama-sama dengan set -o errtrace untuk memanggil fungsi yang menghantar tindakan kitaran hayat ABANDON supaya contoh yang salah dapat ditamatkan secepat mungkin.

Skrip data pengguna

Skrip data pengguna bertanggungjawab untuk memasang semua perisian yang diperlukan pada contohnya. Kami telah berjaya menggunakan Ansible untuk keadaan peruntukan dan Capistrano untuk menggunakan aplikasi untuk masa yang lama, jadi kami juga menggunakannya di sini, yang membolehkan perbezaan minimum antara pengaliran biasa dan membina semula.

Skrip data pengguna menyemak repositori aplikasi kami dari Github, yang termasuk skrip provisioning Ansible, kemudian berjalan Ansible dan Capistrano menunjuk kepada localhost.

Semasa menyemak kod, kita perlu memastikan versi aplikasi yang digunakan saat ini digunakan semasa membina semula. Skrip penempatan Capistrano termasuk tugas yang mengemas kini fail dalam S3 yang menyimpan SHA yang buat masa ini digunakan. Apabila pembaikan semula berlaku, sistem mengambil komit yang sepatutnya digunakan dari fail itu.

Kemas kini perisian digunakan dengan menjalankan naik taraf tanpa pengawasan di latar depan dengan perintah-upgrade -dasar tanpa pengawasan. Apabila selesai, contohnya reboot dan memulakan pemeriksaan kesihatan.

Berurusan dengan rahsia

Server memerlukan akses sementara ke rahsia (seperti kata laluan vault Ansible) yang diambil dari Kedai Parameter EC2. Pelayan hanya boleh mengakses rahsia untuk tempoh yang singkat semasa membina semula. Selepas mereka diambil, kami segera menggantikan profil contoh awal dengan yang lain yang hanya mempunyai akses kepada sumber yang diperlukan untuk aplikasi dijalankan.

Kami ingin mengelakkan menyimpan rahsia apa-apa mengenai memori berterusan. Rahsia hanya kami simpan ke cakera adalah kunci Github SSH, tetapi bukan frasa laluannya. Kami tidak menyimpan kata laluan vault Ansible, sama ada.

Walau bagaimanapun, kita perlu lulus kata laluan ini kepada SSH dan Ansible masing-masing, dan hanya boleh dilakukan dalam mod interaktif (iaitu utiliti yang mendorong pengguna untuk memasukkan frasa laluan secara manual) untuk alasan yang baik-jika frasa laluan adalah sebahagian daripada arahan, ia adalah disimpan dalam sejarah shell dan boleh dilihat oleh semua pengguna dalam sistem jika mereka menjalankan ps. Kami menggunakan utiliti yang diharapkan untuk mengautomasikan interaksi dengan alat-alat tersebut:

menjangkakan << EOF
cd $ {repo_dir}
spawn membuat ansible_local env = $ {deploy_env} stack = $ {stack} hostname = $ {server_hostname}
tetapkan masa tamat 2
menjangkakan ‘kata laluan Vault’
hantar "$ {vault_password} \ r"
menetapkan tamat masa 900
mengharapkan {
"unreachable = 0 failed = 0" {
keluar 0
}
eof {
keluar 1
}
masa tamat {
keluar 1
}
}
EOF

Mencetuskan pembinaan semula

Memandangkan kami mencetuskan pembinaan semula dengan menjalankan skrip Cloudformation yang sama yang digunakan untuk membuat / mengemaskini infrastruktur kami, kami perlu memastikan bahawa kami tidak sengaja mengemaskini beberapa bahagian infrastruktur yang tidak sepatutnya dikemas kini semasa membina semula.

Kami mencapai ini dengan menetapkan dasar tumpahan yang ketat pada susunan Cloudformation kami sehingga hanya sumber yang diperlukan untuk membangun kembali diperbarui:

{
"Kenyataan" : [
{
"Kesan" : "Benarkan",
"Tindakan" : "Kemas kini: Ubah suai",
"Pengetua": "*",
"Sumber" : [
"LogicalResourceId / * AutoScalingGroup"
]
},
{
"Kesan" : "Benarkan",
"Tindakan" : "Kemas kini: Gantikan",
"Pengetua": "*",
"Sumber" : [
"LogicalResourceId / * LaunchConfiguration"
]
}
]
}

Apabila kita perlu melakukan kemas kini infrastruktur yang sebenar, kita perlu mengemaskini dasar tindak balas secara manual untuk membenarkan kemas kini kepada sumber-sumber tersebut dengan jelas.

Kerana nama hos pelayan dan IP kami berubah setiap hari, kami mempunyai skrip yang mengemas kini inventori Ansible tempatan dan konfigurasi SSH kami. Ia menemukan contoh-contoh melalui API AWS oleh tag, membuat fail inventori dan konfigurasi dari templat ERB, dan menambah IP baru kepada SSH known_hosts.

ExpressVPN mengikut piawaian keselamatan tertinggi

Pelayan membina semula melindungi kami dari ancaman tertentu: penyerang mendapat akses ke pelayan kami melalui kelemahan kernel / perisian.

Walau bagaimanapun, ini hanyalah satu daripada banyak cara untuk memastikan infrastruktur kami terjamin, termasuk tetapi tidak terhad kepada audit keselamatan biasa dan membuat sistem kritikal yang tidak dapat diakses dari internet.

Di samping itu, kami memastikan bahawa semua kod dan proses dalaman kami mematuhi standard keselamatan tertinggi.