Mengaktifkan enkripsi in-transit pada Cluster Redis menggunakan Python - Amazon ElastiCache untuk Redis

Terjemahan disediakan oleh mesin penerjemah. Jika konten terjemahan yang diberikan bertentangan dengan versi bahasa Inggris aslinya, utamakan versi bahasa Inggris.

Mengaktifkan enkripsi in-transit pada Cluster Redis menggunakan Python

Panduan berikut akan menunjukkan cara mengaktifkan enkripsi in-transit pada klaster Redis 7.0 yang awalnya dibuat dengan enkripsi in-transit dinonaktifkan. Klien TCP dan TLS akan terus berkomunikasi dengan klaster selama proses ini tanpa downtime.

Boto3 akan mendapatkan kredensyal yang dibutuhkannya (aws_access_key_id,aws_secret_access_key, danaws_session_token) dari variabel lingkungan. Kredensyal tersebut akan disisipkan terlebih dahulu di terminal bash yang sama di mana kita akan menjalankan python3 untuk memproses kode Python yang ditunjukkan dalam panduan ini. Kode dalam contoh di bawah ini adalah proses dari instans EC2 yang diluncurkan di VPC yang sama yang akan digunakan untuk membuat Cluster ElastiCache Redis di dalamnya.

catatan
  • Contoh berikut menggunakan boto3 SDK untuk operasi ElastiCache manajemen (cluster atau pembuatan pengguna) dan redis-py-cluster redis-py/ untuk penanganan data.

  • Anda harus menggunakan setidaknya versi boto3 (=~) 1.26.39 untuk menggunakan migrasi TLS online dengan API modifikasi klaster.

  • ElastiCachemendukung migrasi TLS online hanya untuk Redis Cluster dengan versi 7.0 atau lebih tinggi. Jadi jika Anda memiliki klaster yang menjalankan versi Redis lebih awal dari 7.0, Anda harus meng-upgrade versi Redis dari klaster Anda. Untuk informasi selengkapnya tentang perbedaan versi, lihatPerilaku versi utama dan perbedaan kompatibilitas.

Tentukan konstanta string yang akan meluncurkan Cluster ElastiCache Redis

Pertama, mari kita mendefinisikan beberapa sederhana Python string konstanta yang akan memegang nama-nama AWS entitas yang diperlukan untuk membuat ElastiCache cluster sepertisecurity-group,, Cache Subnet group dan. default parameter group Semua AWS entitas ini harus dibuat terlebih dahulu di AWS akun Anda di Wilayah yang ingin Anda gunakan.

#Constants definitions SECURITY_GROUP = "sg-0492aa0a29c558427" CLUSTER_DESCRIPTION = "This cluster has been launched as part of the online TLS migration user guide" EC_SUBNET_GROUP = "client-testing" DEFAULT_PARAMETER_GROUP_REDIS_7_CLUSTER_MODE_ENABLED = "default.redis7.cluster.on"

Mendefinisikan kelas untuk konfigurasi cluster

Sekarang, mari kita mendefinisikan beberapa kelas Python sederhana yang akan mewakili konfigurasi cluster, yang akan menyimpan metadata tentang cluster seperti versi Redis, jenis instance, dan apakah in-transit encryption (TLS) diaktifkan atau dinonaktifkan.

#Class definitions class Config: def __init__( self, instance_type: str = "cache.t4g.small", version: str = "7.0", multi_az: bool = True, TLS: bool = True, name: str = None, ): self.instance_type = instance_type self.version = version self.multi_az = multi_az self.TLS = TLS self.name = name or f"alonare-test" def create_base_launch_request(self): return { "ReplicationGroupId": self.name, "TransitEncryptionEnabled": self.TLS, "MultiAZEnabled": self.multi_az, "CacheNodeType": self.instance_type, "Engine": "redis", "EngineVersion": self.version, "CacheSubnetGroupName": EC_SUBNET_GROUP , "CacheParameterGroupName": DEFAULT_PARAMETER_GROUP_REDIS_7_CLUSTER_MODE_ENABLED , "ReplicationGroupDescription": CLUSTER_DESCRIPTION, "SecurityGroupIds": [SECURITY_GROUP], } class ConfigCME(Config): def __init__( self, instance_type: str = "cache.t4g.small", version: str = "7.0", multi_az: bool = True, TLS: bool = True, name: str = None, num_shards: int = 2, num_replicas_per_shard: int = 1, ): super().__init__(instance_type, version, multi_az, TLS, name) self.num_shards = num_shards self.num_replicas_per_shard = num_replicas_per_shard def create_launch_request(self) -> dict: launch_request = self.create_base_launch_request() launch_request["NumNodeGroups"] = self.num_shards launch_request["ReplicasPerNodeGroup"] = self.num_replicas_per_shard return launch_request

Mendefinisikan kelas yang akan mewakili cluster itu sendiri

Sekarang, mari kita mendefinisikan beberapa kelas Python sederhana yang akan mewakili ElastiCache Redis Cluster itu sendiri. Kelas ini akan memiliki bidang klien yang akan menampung klien boto3 untuk operasi ElastiCache manajemen seperti membuat cluster dan query API. ElastiCache

import botocore.config import boto3 # Create boto3 client def init_client(region: str = "us-east-1"): config = botocore.config.Config(retries={"max_attempts": 10, "mode": "standard"}) init_request = dict() init_request["config"] = config init_request["service_name"] = "elasticache" init_request["region_name"] = region return boto3.client(**init_request) class ElastiCacheClusterBase: def __init__(self, name: str): self.name = name self.elasticache_client = init_client() def get_first_replication_group(self): return self.elasticache_client.describe_replication_groups( ReplicationGroupId=self.name )["ReplicationGroups"][0] def get_status(self) -> str: return self.get_first_replication_group()["Status"] def get_transit_encryption_enabled(self) -> bool: return self.get_first_replication_group()["TransitEncryptionEnabled"] def is_available(self) -> bool: return self.get_status() == "available" def is_modifying(self) -> bool: return self.get_status() == "modifying" def wait_for_available(self): while True: if self.is_available(): break else: time.sleep(5) def wait_for_modifying(self): while True: if self.is_modifying(): break else: time.sleep(5) def delete_cluster(self) -> bool: self.elasticache_client.delete_replication_group( ReplicationGroupId=self.name, RetainPrimaryCluster=False ) def modify_transit_encryption_mode(self, new_transit_encryption_mode: str): # generate api call to migrate the cluster to TLS preffered or to TLS required self.elasticache_client.modify_replication_group( ReplicationGroupId=self.name, TransitEncryptionMode=new_transit_encryption_mode, TransitEncryptionEnabled=True, ApplyImmediately=True, ) self.wait_for_modifying() class ElastiCacheClusterCME(ElastiCacheClusterBase): def __init__(self, name: str): super().__init__(name) @classmethod def launch(cls, config: ConfigCME = None) -> ElastiCacheClusterCME: config = config or ConfigCME() print(config) new_cluster = ElastiCacheClusterCME(config.name) launch_request = config.create_launch_request() new_cluster.elasticache_client.create_replication_group(**launch_request) new_cluster.wait_for_available() return new_cluster def get_configutration_endpoint(self) -> str: return self.get_first_replication_group()["ConfigurationEndpoint"]["Address"] #Since the code can throw exceptions, we define this class to make the code more readable and #so we won't forget to delete the cluster class ElastiCacheCMEManager: def __init__(self, config: ConfigCME = None): self.config = config or ConfigCME() def __enter__(self) -> ElastiCacheClusterCME: self.cluster = ElastiCacheClusterCME.launch(self.config) return self.cluster def __exit__(self, exc_type, exc_val, exc_tb): self.cluster.delete_cluster()

(Opsional) Buat kelas wrapper untuk demo koneksi klien ke Redis cluster

Sekarang, mari kita membuat kelas wrapper untuk redis-py-cluster klien. Kelas wrapper ini akan mendukung pra-mengisi cluster dengan beberapa kunci dan kemudian melakukan perintah berulang acak. get

catatan

Ini adalah langkah opsional tetapi menyederhanakan kode fungsi utama yang datang pada langkah selanjutnya.

import redis improt random from time import perf_counter_ns, time class DowntimeTestClient: def __init__(self, client): self.client = client # num of keys prefilled self.prefilled = 0 # percent of get above prefilled self.percent_get_above_prefilled = 10 # nil result expected when get hit above prefilled # total downtime in nano seconds self.downtime_ns = 0 # num of success and fail operations self.success_ops = 0 self.fail_ops = 0 self.connection_errors = 0 self.timeout_errors = 0 def replace_client(self, client): self.client = client def prefill_data(self, timelimit_sec=60): end_time = time() + timelimit_sec while time() < end_time: self.client.set(self.prefilled, self.prefilled) self.prefilled += 1 # unsuccesful operations throw exceptions def _exec(self, func): try: start_ns = perf_counter_ns() func() self.success_ops += 1 elapsed_ms = (perf_counter_ns() - start_ns) // 10 ** 6 # upon succesful execution of func # reset random_key to None so that the next command # will use a new random key self.random_key = None except Exception as e: elapsed_ns = perf_counter_ns() - start_ns self.downtime_ns += elapsed_ns # in case of failure- increment the relevant counters so that we will keep track # of how many connection issues we had while trying to communicate with # the cluster. self.fail_ops += 1 if e.__class__ is redis.exceptions.ConnectionError: self.connection_errors += 1 if e.__class__ is redis.exceptions.TimeoutError: self.timeout_errors += 1 def _repeat_exec(self, func, seconds): end_time = time() + seconds while time() < end_time: self._exec(func) def _new_random_key_if_needed(self, percent_above_prefilled): if self.random_key is None: max = int((self.prefilled * (100 + percent_above_prefilled)) / 100) return random.randint(0, max) return self.random_key def _random_get(self): key = self._new_random_key_if_needed(self.percent_get_above_prefilled) result = self.client.get(key) # we know the key was set for sure only in the case key < self.prefilled if key < self.prefilled: assert result.decode("UTF-8") == str(key) def repeat_get(self, seconds=60): self._repeat_exec(self._random_get, seconds) def get_downtime_ms(self) -> int: return self.downtime_ns // 10 ** 6 def do_get_until(self, cond_check): while not cond_check(): self.repeat_get() # do one more get cycle once condition is met self.repeat_get()

Buat fungsi utama yang mendemo proses mengubah konfigurasi enkripsi in-transit

Sekarang, mari kita mendefinisikan fungsi utama, yang akan melakukan hal berikut:

  1. Buat cluster menggunakan klien boto3ElastiCache.

  2. Inisialisasi redis-py-cluster klien yang akan terhubung ke cluster dengan koneksi TCP yang jelas tanpa TLS.

  3. redis-py-clusterKlien mengisi ulang cluster dengan beberapa data.

  4. Klien boto3 akan memicu migrasi TLS dari No-TLS ke TLS yang disukai.

  5. Saat klaster sedang dimigrasi ke TLSPreferred, klien redis-py-cluster TCP akan mengirim get operasi berulang ke klaster sampai migrasi selesai.

  6. Setelah migrasi ke TLS Preferred selesai, kami akan menegaskan bahwa cluster mendukung enkripsi in-transit. Setelah itu, kita akan membuat redis-py-cluster klien yang akan terhubung ke cluster dengan TLS.

  7. Kami akan mengirim beberapa get perintah menggunakan klien TLS baru dan klien TCP lama.

  8. Klien boto3 akan memicu migrasi TLS dari TLS ke TLS Preferred yang diperlukan.

  9. Sementara klaster sedang dimigrasi ke TLS diperlukan, klien redis-py-cluster TLS akan mengirim get operasi berulang ke klaster sampai migrasi selesai.

import redis def init_cluster_client( cluster: ElastiCacheClusterCME, prefill_data: bool, TLS: bool = True) -> DowntimeTestClient: # we must use for the host name the cluster configuration endpoint. redis_client = redis.RedisCluster( host=cluster.get_configutration_endpoint(), ssl=TLS, socket_timeout=0.25, socket_connect_timeout=0.1 ) test_client = DowntimeTestClient(redis_client) if prefill_data: test_client.prefill_data() return test_client if __name__ == '__main__': config = ConfigCME(TLS=False, instance_type="cache.m5.large") with ElastiCacheCMEManager(config) as cluster: # create a client that will connect to the cluster with clear tcp connection test_client_tcp = init_cluster_client(cluster, prefill_data=True, TLS=False) # migrate the cluster to TLS Preferred cluster.modify_transit_encryption_mode(new_transit_encryption_mode="preferred") # do repeated get commands until the cluster finishes the migration to TLS Preferred test_client_tcp.do_get_until(cluster.is_available) # verify that in transit encryption is enabled so that clients will be able to connect to the cluster with TLS assert cluster.get_transit_encryption_enabled() == True # create a client that will connect to the cluster with TLS connection. # we must first make sure that the cluster indeed supports TLS test_client_tls = init_cluster_client(cluster, prefill_data=True, TLS=True) # by doing get commands with the tcp client for 60 more seconds # we can verify that the existing tcp connection to the cluster still works test_client_tcp.repeat_get(seconds=60) # do get commands with the new TLS client for 60 more seconds test_client_tcp.repeat_get(seconds=60) # migrate the cluster to TLS required cluster.modify_transit_encryption_mode(new_transit_encryption_mode="required") # from this point the tcp clients will be disconnected and we must not use them anymore. # do get commands with the TLS client until the cluster finishes migartion to TLS required mode. test_client_tls.do_get_until(cluster.is_available)

Praktik terbaik untuk membuat koneksi sebelum, selama, dan setelah mengaktifkan enkripsi in-transit

Sebelum mengaktifkan enkripsi dalam transit: pastikan Anda memiliki penanganan data DNS yang tepat

catatan

Kami mengubah dan menghapus endpoint lama selama proses ini. Penggunaan endpoint yang salah dapat mengakibatkan klien Redis menggunakan endpoint lama dan dihapus yang akan mencegahnya terhubung ke klaster.

Sementara cluster sedang dimigrasi dari No-TLS ke TLS-preferen, catatan DNS per-node lama disimpan dan catatan DNS per-node baru sedang dihasilkan dalam format yang berbeda. Cluster berkemampuan TLS menggunakan format data DNS yang berbeda dari kluster non-TLS. ElastiCacheakan menyimpan kedua catatan DNS ketika cluster dikonfigurasi dalam mode enkripsi: Disukai sehingga Aplikasi dan Klien Redis lainnya dapat beralih di antara mereka. Perubahan berikut dalam data DNS terjadi selama proses migrasi TLS:

Deskripsi perubahan data DNS yang terjadi saat mengaktifkan enkripsi dalam transit

Untuk cluster CME

Ketika cluster diatur ke 'mode enkripsi transit: preferred':

  • Endpoint klaster asli untuk klaster non-TLS diaktifkan akan tetap aktif. Tidak akan ada downtime ketika klaster dikonfigurasi ulang bentuk modus enkripsi TLS 'none' untuk 'preferred'.

  • Titik akhir TLS Redis baru akan dihasilkan saat klaster diatur ke mode pilihan TLS. Endpoint baru ini akan menyelesaikan IP yang sama dengan yang lama (non-TLS).

  • Endpoint konfigurasi TLS Redis yang baru akan diekspos di ElastiCache Console dan sebagai respons terhadap API. describe-replication-group

Ketika cluster diatur ke 'mode enkripsi transit: required':

  • Endpoint non-TLS lama yang diaktifkan akan dihapus. Tidak akan ada downtime dari endpoint klaster TLS.

  • Anda dapat mengambil yang baru cluster-configuration-endpoint dari ElastiCache Console atau dari describe-replication-group API.

Untuk cluster CMD dengan Automatic Failover diaktifkan atau Automatic Failover dinonaktifkan

Ketika grup replikasi diatur ke 'mode enkripsi transit: preferred':

  • Endpoint utama dan endpoint pembaca asli untuk klaster None-TLS diaktifkan akan tetap aktif.

  • Titik akhir primer dan pembaca TLS baru akan dihasilkan saat klaster diatur ke mode TLS. Preferred Endpoint baru ini akan menyelesaikan IP yang sama dengan yang lama (non-TLS).

  • Endpoint utama dan endpoint pembaca baru akan diekspos di ElastiCache Console dan sebagai respons terhadap API. describe-replication-group

Ketika grup replikasi diatur ke 'mode enkripsi transit: diperlukan':

  • Endpoint utama dan endpoint pembaca asli untuk klaster non-TLS diaktifkan akan tetap aktif.

  • Titik akhir primer dan pembaca non-TLS lama akan dihapus. Tidak akan ada downtime dari endpoint klaster TLS.

  • Anda dapat mengambil titik akhir primer dan pembaca baru dari ElastiCache Console atau dari API. describe-replication-group

Penggunaan catatan DNS yang disarankan

Untuk cluster CME

  • Gunakan endpoint konfigurasi klaster alih-alih data DNS per-node dalam kode aplikasi Anda. Menggunakan nama DNS per node secara langsung tidak disarankan karena mereka mungkin berubah saat menambahkan atau menghapus pecahan.

  • Jangan hardcode konfigurasi cluster endpoint dalam aplikasi Anda karena akan berubah selama proses ini.

  • Memiliki endpoint konfigurasi cluster hardcoded dalam aplikasi Anda adalah praktik yang buruk karena dapat diubah selama proses ini. Setelah enkripsi in-transit selesai, kueri endpoint konfigurasi cluster dengan describe-replication-group API (seperti yang ditunjukkan di atas (dalam huruf tebal)) dan gunakan DNS yang Anda dapatkan sebagai respons dari titik ini.

Untuk cluster CMD dengan Automatic Failover diaktifkan

  • Gunakan endpoint utama dan endpoint pembaca alih-alih nama DNS per-node dalam kode aplikasi Anda karena nama DNS per-node lama dihapus dan yang baru dihasilkan saat memigrasi klaster dari No-TLS ke TLS yang disukai. Menggunakan nama DNS per node secara langsung tidak disarankan karena Anda dapat menambahkan replika ke klaster Anda di masa mendatang. Selain itu, ketika Failover Otomatis diaktifkan, peran klaster utama dan replika diubah secara otomatis oleh ElastiCache layanan, menggunakan endpoint utama dan endpoint pembaca disarankan untuk membantu Anda melacak perubahan tersebut. Terakhir, menggunakan endpoint pembaca akan membantu Anda mendistribusikan bacaan Anda dari replika secara merata di antara replika di klaster.

  • Memiliki endpoint utama dan endpoint pembaca hardcode dalam aplikasi Anda adalah praktik yang buruk karena dapat diubah selama proses migrasi TLS. Setelah perubahan migrasi ke TLS-Preferred selesai, kueri endpoint utama dan endpoint endpoint pembaca dengan describe-replication-group API dan gunakan DNS yang Anda dapatkan sebagai respons dari titik ini. Dengan cara ini Anda akan dapat melacak perubahan titik akhir dengan cara yang dinamis.

Untuk cluster CMD dengan Automatic Failover dinonaktifkan

  • Gunakan endpoint utama dan endpoint pembaca, bukan nama DNS per-node dalam kode aplikasi Anda. Ketika Failover Otomatis dinonaktifkan, penskalaan, patching, failover, dan prosedur lain yang dikelola secara otomatis oleh ElastiCache layanan saat Failover Otomatis diaktifkan dilakukan oleh Anda sebagai gantinya. Ini memudahkan Anda untuk melacak titik akhir yang berbeda secara manual. Karena nama DNS per-node lama dihapus dan yang baru dihasilkan saat memigrasi cluster dari No-TLS ke TLS-preferen, jangan gunakan nama DNS per-node secara langsung. Ini wajib agar klien dapat terhubung ke klaster selama migrasi TLS. Selain itu, Anda akan mendapat manfaat dari menyebarkan bacaan secara merata di antara replika saat menggunakan endpoint pembaca dan melacak rekaman DNS saat menambahkan atau menghapus replika dari cluster.

  • Memiliki endpoint konfigurasi cluster hardcoded dalam aplikasi Anda adalah praktik yang buruk karena dapat diubah selama proses migrasi TLS.

Selama enkripsi dalam transit: perhatikan saat proses migrasi selesai

Perubahan modus enkripsi transit tidak langsung dan dapat mengambil beberapa waktu. Hal ini terutama berlaku untuk cluster besar. Hanya ketika klaster menyelesaikan migrasi ke TLS-Preferred, ia dapat menerima dan melayani koneksi TCP dan TLS. Oleh karena itu, Anda tidak boleh membuat klien yang akan mencoba membuat koneksi TLS ke cluster sampai enkripsi in-transit selesai.

Ada beberapa cara untuk mendapatkan pemberitahuan ketika enkripsi in-transit berhasil diselesaikan atau gagal: (Tidak ditampilkan dalam contoh kode di atas):

  • Menggunakan layanan SNS untuk mendapatkan pemberitahuan saat enkripsi selesai

  • Menggunakan describe-events API yang akan memancarkan peristiwa ketika enkripsi selesai

  • Melihat pesan di ElastiCache Konsol bahwa enkripsi selesai

Anda juga dapat menerapkan logika dalam aplikasi Anda untuk mengetahui apakah enkripsi selesai. Pada contoh di atas, kita melihat beberapa cara untuk memastikan klaster menyelesaikan migrasi:

  • Menunggu hingga proses migrasi dimulai (status klaster berubah menjadi “modifikasi “), dan menunggu sampai modifikasi selesai (status klaster berubah kembali ke “available “)

  • Menegaskan bahwa cluster telah transit_encryption_enabled diatur ke True dengan query API. describe-replication-group

Setelah mengaktifkan enkripsi dalam transit: pastikan klien yang Anda gunakan dikonfigurasi dengan benar

Saat klaster berada dalam mode pilihan TLS, aplikasi Anda harus membuka koneksi TLS ke klaster dan hanya menggunakan koneksi tersebut. Dengan cara ini aplikasi Anda tidak akan mengalami downtime saat mengaktifkan enkripsi in-transit. Anda dapat memastikan bahwa tidak ada koneksi TCP yang lebih jelas ke mesin Redis menggunakan perintah info Redis di bawah bagian SSL.

# SSL ssl_enabled:yes ssl_current_certificate_not_before_date:Mar 20 23:27:07 2017 GMT ssl_current_certificate_not_after_date:Feb 24 23:27:07 2117 GMT ssl_current_certificate_serial:D8C7DEA91E684163 tls_mode_connected_tcp_clients:0 (should be zero) tls_mode_connected_tls_clients:100