Pelajari cara menggunakan pemicu Cloud Functions untuk menindaklanjuti peristiwa Shielded VM integrity monitoring.
Ringkasan
Pemantauan integritas mengumpulkan pengukuran dari instance Shielded VM dan menampilkannya di Cloud Logging. Jika pengukuran integritas berubah di seluruh booting instance Shielded VM, validasi integritas akan gagal. Kegagalan ini dicatat sebagai peristiwa yang dicatat ke dalam log, dan juga dilaporkan di Cloud Monitoring.
Terkadang, pengukuran integritas Shielded VM berubah karena alasan yang sah. Misalnya, update sistem dapat menyebabkan perubahan yang diharapkan pada kernel sistem operasi. Karena itu, pemantauan integritas memungkinkan Anda meminta instance Shielded VM untuk mempelajari dasar pengukuran kebijakan integritas baru jika terjadi kegagalan validasi integritas yang diharapkan.
Dalam tutorial ini, pertama-tama Anda akan membuat sistem otomatis sederhana yang menonaktifkan instance VM yang Terlindungi yang gagal dalam validasi integritas:
- Ekspor semua peristiwa pemantauan integritas ke topik Pub/Sub.
- Buat pemicu Cloud Functions yang menggunakan peristiwa dalam topik tersebut untuk mengidentifikasi dan menonaktifkan instance Shielded VM yang gagal dalam validasi integritas.
Selanjutnya, Anda dapat memperluas sistem secara opsional agar meminta instance Shielded VM yang gagal dalam validasi integritas untuk mempelajari dasar pengukuran baru jika cocok dengan pengukuran bagus yang diketahui, atau untuk dimatikan jika tidak:
- Buat database Firestore untuk mempertahankan sekumpulan pengukuran dasar integritas yang baik dan diketahui.
- Update pemicu Cloud Functions sehingga memicu instance Shielded VM yang gagal dalam validasi integritas untuk mempelajari baseline baru jika ada di dalam database, atau jika ingin dimatikan.
Jika Anda memilih untuk menerapkan solusi yang diperluas, gunakan dengan cara berikut:
- Setiap kali ada update yang diperkirakan akan menyebabkan kegagalan validasi karena alasan yang sah, jalankan update tersebut pada satu instance VM yang Disembunyikan dalam grup instance.
- Dengan menggunakan peristiwa booting terlambat dari instance VM yang diupdate sebagai sumber, tambahkan pengukuran dasar pengukuran kebijakan baru ke database dengan membuat dokumen baru di koleksi known_good_measurements. Lihat Membuat database pengukuran dasar pengukuran yang diketahui berkualitas untuk informasi selengkapnya.
- Update instance Shielded VM yang tersisa. Pemicu meminta instance yang tersisa untuk mempelajari baseline baru karena dapat diverifikasi sebagai hasilnya yang diketahui. Lihat Memperbarui pemicu Cloud Functions untuk mempelajari dasar pengukuran bagus yang diketahui untuk mengetahui informasi selengkapnya.
Prasyarat
- Gunakan project yang memiliki Firestore dalam mode Native yang dipilih sebagai layanan database. Anda membuat pilihan ini saat membuat project, dan tidak dapat diubah. Jika project Anda tidak menggunakan Firestore dalam mode Native, Anda akan melihat pesan "Project ini menggunakan layanan database lain" saat membuka konsol Firestore.
- Memiliki instance VM Compute Engine Shielded dalam project tersebut untuk berfungsi sebagai sumber pengukuran dasar pengukuran integritas. Instance Shielded VM harus sudah dimulai ulang setidaknya sekali.
- Sudah menginstal alat command line
gcloud
. Aktifkan Cloud Logging dan Cloud Functions API dengan mengikuti langkah-langkah berikut:
Di konsol Google Cloud, buka halaman APIs & Services.
Lihat apakah Cloud Functions API dan Stackdriver Logging API muncul di daftar Enabled APIs and services.
Jika salah satu API tidak muncul, klik Add APIs and Services.
Telusuri dan aktifkan API, sesuai kebutuhan.
Mengekspor entri log pemantauan integritas ke topik Pub/Sub
Gunakan Logging untuk mengekspor semua entri log pemantauan integritas yang dihasilkan oleh instance Shielded VM ke topik Pub/Sub. Anda menggunakan topik ini sebagai sumber data untuk pemicu Cloud Functions guna mengotomatiskan respons terhadap peristiwa pemantauan integritas.
Logs Explorer
Di Konsol Google Cloud, buka halaman Logs Explorer.
Di Query Builder, masukkan nilai berikut.
resource.type="gce_instance" AND logName: "projects/YOUR_PROJECT_ID/logs/compute.googleapis.com%2Fshielded_vm_integrity"
Klik Run Filter.
Klik
More actions, kemudian pilih Create sink.Di halaman Create logs routing sink:
- Di Sink details, untuk Sink Name, masukkan
integrity-monitoring
, lalu klik Next. - Di Sink destination, luaskan Sink Service, lalu pilih Cloud Pub/Sub.
- Luaskan Select a Cloud Pub/Sub topic, lalu pilih Create a topic.
- Dalam dialog Create a topic, untuk Topic ID, masukkan
integrity-monitoring
, lalu klik Create topic. - Klik Next, lalu klik Create sink.
- Di Sink details, untuk Sink Name, masukkan
Logs Explorer
Di Konsol Google Cloud, buka halaman Logs Explorer.
Klik Options, lalu pilih Go back to Legacy Logs Explorer.
Luaskan Filter menurut label atau penelusuran teks, lalu klik Convert to advanced filter.
Masukkan filter lanjutan berikut:
resource.type="gce_instance" AND logName: "projects/YOUR_PROJECT_ID/logs/compute.googleapis.com%2Fshielded_vm_integrity"
Perhatikan bahwa ada dua spasi setelahlogName:
.Klik Kirim Filter.
Klik Buat Ekspor.
Untuk Sink Name, masukkan
integrity-monitoring
.Untuk Sink Service, pilih Cloud Pub/Sub.
Luaskan Sink Destination, lalu klik Create new Cloud Pub/Sub topic.
Untuk Name, masukkan
integrity-monitoring
lalu klik Create.Klik Create Sink.
Membuat pemicu Cloud Functions untuk merespons kegagalan integritas
Buat pemicu Cloud Functions yang membaca data dalam topik Pub/Sub dan yang menghentikan instance Shielded VM yang gagal dalam validasi integritas.
Kode berikut menentukan pemicu Cloud Functions. Salin ke dalam file bernama
main.py
.import base64 import json import googleapiclient.discovery def shutdown_vm(data, context): """A Cloud Function that shuts down a VM on failed integrity check.""" log_entry = json.loads(base64.b64decode(data['data']).decode('utf-8')) payload = log_entry.get('jsonPayload', {}) entry_type = payload.get('@type') if entry_type != 'type.googleapis.com/cloud_integrity.IntegrityEvent': raise TypeError("Unexpected log entry type: %s" % entry_type) report_event = (payload.get('earlyBootReportEvent') or payload.get('lateBootReportEvent')) if report_event is None: # We received a different event type, ignore. return policy_passed = report_event['policyEvaluationPassed'] if not policy_passed: print('Integrity evaluation failed: %s' % report_event) print('Shutting down the VM') instance_id = log_entry['resource']['labels']['instance_id'] project_id = log_entry['resource']['labels']['project_id'] zone = log_entry['resource']['labels']['zone'] # Shut down the instance. compute = googleapiclient.discovery.build( 'compute', 'v1', cache_discovery=False) # Get the instance name from instance id. list_result = compute.instances().list( project=project_id, zone=zone, filter='id eq %s' % instance_id).execute() if len(list_result['items']) != 1: raise KeyError('unexpected number of items: %d' % len(list_result['items'])) instance_name = list_result['items'][0]['name'] result = compute.instances().stop(project=project_id, zone=zone, instance=instance_name).execute() print('Instance %s in project %s has been scheduled for shut down.' % (instance_name, project_id))
Di lokasi yang sama dengan
main.py
, buat file bernamarequirements.txt
lalu salin dalam dependensi berikut:google-api-python-client==1.6.6 google-auth==1.4.1 google-auth-httplib2==0.0.3
Buka jendela terminal dan buka direktori yang berisi
main.py
danrequirements.txt
.Jalankan perintah
gcloud beta functions deploy
untuk men-deploy pemicu:gcloud beta functions deploy shutdown_vm --project PROJECT_ID \ --runtime python37 --trigger-resource integrity-monitoring \ --trigger-event google.pubsub.topic.publish
Membuat database untuk pengukuran dasar pengukuran yang diketahui
Buat database Firestore untuk menyediakan sumber pengukuran dasar pengukuran kebijakan integritas yang baik dan diketahui. Anda harus menambahkan pengukuran dasar pengukuran secara manual agar database ini selalu terbaru.
Di konsol Google Cloud, buka halaman VM instances.
Klik ID instance Shielded VM untuk membuka halaman VM instance details.
Di bagian Logs, klik Stackdriver Logging.
Cari entri log
lateBootReportEvent
terbaru.Luaskan entri log >
jsonPayload
>lateBootReportEvent
>policyMeasurements
.Perhatikan nilai untuk elemen yang terdapat dalam
lateBootReportEvent
>policyMeasurements
.Di Konsol Google Cloud, buka halaman Firestore.
Pilih Start collection.
Untuk Collection ID, ketikkan known_good_measurements.
Untuk Document ID, ketik baseline1.
Untuk Field name, ketik nilai kolom pcrNum dari elemen
0
dilateBootReportEvent
>policyMeasurements
.Untuk Field type, pilih map.
Tambahkan tiga kolom string ke kolom peta, yang bernama hashAlgo, pcrNum, dan value. Jadikan nilainya sebagai nilai kolom elemen
0
dilateBootReportEvent
>policyMeasurements
.Buat lebih banyak kolom peta, satu untuk setiap elemen tambahan di
lateBootReportEvent
>policyMeasurements
. Beri mereka subkolom yang sama seperti kolom peta pertama. Nilai untuk subkolom tersebut harus dipetakan ke nilai di setiap elemen tambahan.Misalnya, jika Anda menggunakan VM Linux, koleksinya akan terlihat mirip seperti berikut ini setelah Anda selesai:
Jika menggunakan VM Windows, Anda akan melihat lebih banyak pengukuran sehingga koleksi akan terlihat seperti berikut:
Memperbarui pemicu Cloud Functions untuk mempelajari baseline yang sudah dikenali dengan baik
Kode berikut membuat pemicu Cloud Functions yang menyebabkan instance Shielded VM yang gagal melakukan validasi integritas dapat mempelajari baseline baru jika berada dalam database pengukuran baik yang dikenali, jika tidak, instance akan dinonaktifkan. Salin kode ini dan gunakan untuk menimpa kode yang sudah ada di
main.py
.import base64 import json import googleapiclient.discovery import firebase_admin from firebase_admin import credentials from firebase_admin import firestore PROJECT_ID = 'PROJECT_ID' firebase_admin.initialize_app(credentials.ApplicationDefault(), { 'projectId': PROJECT_ID, }) def pcr_values_to_dict(pcr_values): """Converts a list of PCR values to a dict, keyed by PCR num""" result = {} for value in pcr_values: result[value['pcrNum']] = value return result def instance_id_to_instance_name(compute, zone, project_id, instance_id): list_result = compute.instances().list( project=project_id, zone=zone, filter='id eq %s' % instance_id).execute() if len(list_result['items']) != 1: raise KeyError('unexpected number of items: %d' % len(list_result['items'])) return list_result['items'][0]['name'] def relearn_if_known_good(data, context): """A Cloud Function that shuts down a VM on failed integrity check. """ log_entry = json.loads(base64.b64decode(data['data']).decode('utf-8')) payload = log_entry.get('jsonPayload', {}) entry_type = payload.get('@type') if entry_type != 'type.googleapis.com/cloud_integrity.IntegrityEvent': raise TypeError("Unexpected log entry type: %s" % entry_type) # We only send relearn signal upon receiving late boot report event: if # early boot measurements are in a known good database, but late boot # measurements aren't, and we send relearn signal upon receiving early boot # report event, the VM will also relearn late boot policy baseline, which we # don't want, because they aren't known good. report_event = payload.get('lateBootReportEvent') if report_event is None: return evaluation_passed = report_event['policyEvaluationPassed'] if evaluation_passed: # Policy evaluation passed, nothing to do. return # See if the new measurement is known good, and if it is, relearn. measurements = pcr_values_to_dict(report_event['actualMeasurements']) db = firestore.Client() kg_ref = db.collection('known_good_measurements') # Check current measurements against known good database. relearn = False for kg in kg_ref.get(): kg_map = kg.to_dict() # Check PCR values for lateBootReportEvent measurements against the known good # measurements stored in the Firestore table if ('PCR_0' in kg_map and kg_map['PCR_0'] == measurements['PCR_0'] and 'PCR_4' in kg_map and kg_map['PCR_4'] == measurements['PCR_4'] and 'PCR_7' in kg_map and kg_map['PCR_7'] == measurements['PCR_7']): # Linux VM (3 measurements), only need to check above 3 measurements if len(kg_map) == 3: relearn = True # Windows VM (6 measurements), need to check 3 additional measurements elif len(kg_map) == 6: if ('PCR_11' in kg_map and kg_map['PCR_11'] == measurements['PCR_11'] and 'PCR_13' in kg_map and kg_map['PCR_13'] == measurements['PCR_13'] and 'PCR_14' in kg_map and kg_map['PCR_14'] == measurements['PCR_14']): relearn = True compute = googleapiclient.discovery.build('compute', 'beta', cache_discovery=False) instance_id = log_entry['resource']['labels']['instance_id'] project_id = log_entry['resource']['labels']['project_id'] zone = log_entry['resource']['labels']['zone'] instance_name = instance_id_to_instance_name(compute, zone, project_id, instance_id) if not relearn: # Issue shutdown API call. print('New measurement is not known good. Shutting down a VM.') result = compute.instances().stop(project=project_id, zone=zone, instance=instance_name).execute() print('Instance %s in project %s has been scheduled for shut down.' % (instance_name, project_id)) else: # Issue relearn API call. print('New measurement is known good. Relearning...') result = compute.instances().setShieldedInstanceIntegrityPolicy( project=project_id, zone=zone, instance=instance_name, body={'updateAutoLearnPolicy':True}).execute() print('Instance %s in project %s has been scheduled for relearning.' % (instance_name, project_id))
Salin dependensi berikut, lalu gunakan untuk menimpa kode yang sudah ada di
requirements.txt
:google-api-python-client==1.6.6 google-auth==1.4.1 google-auth-httplib2==0.0.3 google-cloud-firestore==0.29.0 firebase-admin==2.13.0
Buka jendela terminal dan buka direktori yang berisi
main.py
danrequirements.txt
.Jalankan perintah
gcloud beta functions deploy
untuk men-deploy pemicu:gcloud beta functions deploy relearn_if_known_good --project PROJECT_ID \ --runtime python37 --trigger-resource integrity-monitoring \ --trigger-event google.pubsub.topic.publish
Hapus fungsi
shutdown_vm
sebelumnya secara manual di cloud function console.Di konsol Google Cloud, buka halaman Cloud Functions.
Pilih fungsi shutdown_vm, lalu klik hapus.
Memverifikasi respons otomatis terhadap kegagalan validasi integritas
- Pertama, periksa apakah Anda memiliki instance yang berjalan dengan mengaktifkan Booting Aman sebagai opsi Shielded VM. Jika tidak, Anda dapat membuat instance baru dengan Shielded VM image (Ubuntu 18.04LTS) dan mengaktifkan opsi Secure Boot. Anda mungkin akan dikenai biaya beberapa sen untuk instance ini (langkah ini dapat selesai dalam waktu satu jam).
- Sekarang, asumsikan untuk beberapa alasan, Anda ingin mengupgrade kernel secara manual.
Terapkan SSH ke instance, dan gunakan perintah berikut untuk memeriksa kernel saat ini.
uname -sr
Anda akan melihat yang seperti
Linux 4.15.0-1028-gcp
.Download kernel generik dari https://kernel.ubuntu.com/~kernel-ppa/mainline/
Gunakan perintah untuk menginstal.
sudo dpkg -i *.deb
Mulai ulang VM.
Anda akan melihat VM tidak booting (tidak dapat menjalankan SSH ke dalam mesin). Inilah yang kami harapkan, karena tanda tangan kernel baru tidak ada dalam daftar yang diizinkan Booting Aman. Contoh ini juga menunjukkan cara Booting Aman dapat mencegah modifikasi kernel yang tidak sah/berbahaya.
Namun, karena saat ini upgrade kernel tidak berbahaya dan memang dilakukan sendiri, kita dapat menonaktifkan Secure Boot untuk mem-booting kernel baru.
Matikan VM dan hapus centang pada opsi Booting Aman, lalu mulai ulang VM.
Booting mesin semestinya akan gagal lagi. Namun, kali ini fungsi tersebut dimatikan secara otomatis oleh cloud function yang kita buat sebagai opsi Secure Boot telah diubah (juga karena image kernel yang baru), dan keduanya menyebabkan pengukuran berbeda dari garis dasar. (Kita dapat memeriksa hal tersebut di log Stackdriver pada fungsi cloud.)
Karena kita tahu ini bukan modifikasi berbahaya dan akar masalahnya, kita dapat menambahkan pengukuran saat ini di
lateBootReportEvent
ke tabel Firebase pengukuran baik yang diketahui. (Ingat ada dua hal yang diubah: 1. Opsi Booting Aman 2. Image Kernel.)Ikuti langkah sebelumnya Membuat database pengukuran dasar pengukuran yang diketahui bernilai baik untuk menambahkan dasar pengukuran baru ke database Firestore menggunakan pengukuran sebenarnya di
lateBootReportEvent
terbaru.Sekarang reboot komputernya. Saat memeriksa log Stackdriver, Anda akan melihat
lateBootReportEvent
masih menampilkan nilai salah, tetapi sekarang mesin akan berhasil di-booting, karena cloud function mempercayai dan mempelajari ulang ukuran yang baru. Kita dapat memverifikasinya dengan memeriksa Stackdriver dari fungsi cloud.Dengan menonaktifkan Secure Boot, sekarang kita dapat melakukan booting ke kernel. Terapkan SSH ke komputer dan periksa kernel lagi, Anda akan melihat versi kernel baru.
uname -sr
Terakhir, mari bersihkan sumber daya dan data yang digunakan pada langkah ini.
Matikan VM jika Anda membuat VM untuk langkah ini guna menghindari biaya tambahan.
Di konsol Google Cloud, buka halaman VM instances.
Hapus pengukuran baik yang diketahui yang Anda tambahkan pada langkah ini.
Di Konsol Google Cloud, buka halaman Firestore.