Lewati ke konten utama

Admission Webhook for K8s

1. Ikhtisar & Dokumen untuk Casbin K8s-Gatekeeper

Casbin K8s-GateKeeper adalah webhook adimisi Kubernetes yang mengintegrasikan Casbin sebagai alat Kontrol Akses. Dengan menggunakan Casbin K8s-GateKeeper, Anda dapat menetapkan aturan yang fleksibel untuk mengotorisasi atau memblokir operasi apapun pada sumber daya K8s, TANPA menulis satu baris kode pun, tetapi hanya beberapa baris konfigurasi deklaratif dari model dan kebijakan Casbin, yang merupakan bagian dari bahasa ACL (Access Control List) Casbin.

Casbin K8s-GateKeeper dikembangkan dan dipelihara oleh komunitas Casbin. Repositori proyek ini tersedia di sini: https://github.com/casbin/k8s-gatekeeper

0.1 Contoh Sederhana

Misalnya, Anda tidak perlu menulis kode apa pun, tetapi gunakan beberapa baris konfigurasi berikut untuk mencapai fungsi ini: "Larang penggunaan gambar dengan beberapa tag tertentu dalam penyebaran apa pun":

Model:

[request_definition]
r = obj

[policy_definition]
p = obj,eft

[policy_effect]
e = !some(where (p.eft == deny))

[matchers]
m = r.obj.Request.Namespace == "default" && r.obj.Request.Resource.Resource =="deployments" && \
contain(split(accessWithWildcard(${OBJECT}.Spec.Template.Spec.Containers , "*", "Image"),":",1) , p.obj)

And Policy:

p, "1.14.1",deny

Ini adalah dalam bahasa Casbin ACL biasa. Misalkan Anda sudah membaca bab tentang mereka, akan sangat mudah untuk dipahami.

Casbin K8s-GateKeeper memiliki keunggulan sebagai berikut:

  • Mudah digunakan. Menulis beberapa baris ACL jauh lebih baik daripada menulis banyak kode.
  • Ini memungkinkan pembaruan konfigurasi secara langsung. Anda tidak perlu mematikan seluruh plugin untuk mengubah konfigurasi.
  • Ini fleksibel. Aturan sewenang-wenang dapat dibuat pada sumber daya K8s apa pun, yang dapat dieksplorasi dengan kubectl gatekeeper.
  • Ini menyederhanakan implementasi webhook masuk K8s, yang sangat rumit. Anda tidak perlu tahu apa itu webhook masuk K8s atau bagaimana menulis kode untuk itu. Yang perlu Anda lakukan hanyalah mengetahui sumber daya yang ingin Anda batasi dan kemudian menulis ACL Casbin. Semua orang tahu bahwa K8s itu kompleks, tetapi dengan menggunakan Casbin K8s-Gatekeeper, waktu Anda dapat dihemat.
  • Ini dikelola oleh komunitas Casbin. Jangan ragu untuk menghubungi kami jika ada hal tentang plugin ini yang membuat Anda bingung atau jika Anda menemui masalah saat mencobanya.

1.1 Bagaimana Cara Kerja Casbin K8s-Gatekeeper?

K8s-Gatekeeper adalah webhook keberatan untuk K8s yang menggunakan Casbin untuk menerapkan aturan kontrol akses yang ditentukan pengguna secara arbitrer guna mencegah operasi apa pun pada K8s yang tidak diinginkan oleh administrator.

Casbin adalah pustaka kontrol akses sumber terbuka yang kuat dan efisien. Pustaka ini menyediakan dukungan untuk menegakkan otorisasi berdasarkan berbagai model kontrol akses. Untuk informasi lebih lanjut tentang Casbin, lihat Overview.

Webhook keberatan dalam K8s adalah panggilan balik HTTP yang menerima 'permintaan keberatan' dan melakukan sesuatu dengan permintaan tersebut. Secara khusus, K8s-Gatekeeper adalah jenis webhook keberatan khusus: 'ValidatingAdmissionWebhook', yang dapat memutuskan apakah akan menerima atau menolak permintaan keberatan ini atau tidak. Mengenai permintaan keberatan, permintaan tersebut adalah permintaan HTTP yang menjelaskan operasi pada sumber daya yang ditentukan dari K8s (misalnya, membuat/menghapus sebuah deployment). Untuk informasi lebih lanjut tentang webhook keberatan, lihat dokumentasi resmi K8s.

1.2 Contoh Menggambarkan Cara Kerjanya

Misalnya, ketika seseorang ingin membuat penyebaran yang berisi pod menjalankan nginx (menggunakan kubectl atau klien K8s), K8s akan menghasilkan permintaan masuk, yang (jika diterjemahkan ke dalam format YAML) bisa menjadi seperti ini:

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 1
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.1
ports:
- containerPort: 80

Permintaan ini akan melalui proses semua middleware yang ditunjukkan dalam gambar, termasuk K8s-Gatekeeper kami. K8s-Gatekeeper dapat mendeteksi semua enforcer Casbin yang disimpan di etcd K8s, yang dibuat dan dipelihara oleh pengguna (melalui kubectl atau klien Go yang kami sediakan). Setiap enforcer berisi model Casbin dan kebijakan Casbin. Permintaan masuk akan diproses oleh setiap enforcer, satu per satu, dan hanya dengan lulus semua enforcer, sebuah permintaan dapat diterima oleh K8s-Gatekeeper ini.

(Jika Anda tidak mengerti apa itu enforcer Casbin, model, atau kebijakan, lihat dokumen ini: Mulai).

Misalnya, karena suatu alasan, administrator ingin melarang kemunculan gambar 'nginx:1.14.1' sementara mengizinkan 'nginx:1.3.1'. Sebuah enforcer yang berisi aturan dan kebijakan berikut dapat dibuat (Kami akan menjelaskan cara membuat enforcer, apa model dan kebijakan ini, dan bagaimana menulisnya di bab-bab berikutnya).

Model:

[request_definition]
r = obj

[policy_definition]
p = obj,eft

[policy_effect]
e = !some(where (p.eft == deny))

[matchers]
m = r.obj.Request.Namespace == "default" && r.obj.Request.Resource.Resource =="deployments" && \
access(r.obj.Request.Object.Object.Spec.Template.Spec.Containers , 0, "Image") == p.obj

Policy:

p, "nginx:1.13.1",allow
p, "nginx:1.14.1",deny

Dengan membuat enforcer yang mengandung model dan kebijakan di atas, permintaan masuk sebelumnya akan ditolak oleh enforcer ini, yang berarti K8s tidak akan membuat penyebaran ini.

2 Instal K8s-gatekeeper

Ada tiga metode yang tersedia untuk menginstal K8s-gatekeeper: Webhook eksternal, webhook internal, dan Helm.

catatan

Catatan: Metode ini hanya dimaksudkan untuk pengguna mencoba K8s-gatekeeper dan tidak aman. Jika Anda ingin menggunakannya dalam lingkungan produktif, pastikan Anda membaca Bab 5. Pengaturan lanjutan dan melakukan modifikasi yang diperlukan sebelum instalasi.

2.1 Webhook internal

2.1.1 Langkah 1: Bangun gambar

Untuk metode webhook internal, webhook itu sendiri akan diimplementasikan sebagai layanan dalam Kubernetes. Untuk membuat layanan dan penyebaran yang diperlukan, Anda perlu membangun sebuah gambar dari K8s-gatekeeper. Anda dapat membangun gambar Anda sendiri dengan menjalankan perintah berikut:

docker build --target webhook -t k8s-gatekeeper .

Perintah ini akan membuat sebuah gambar lokal bernama 'k8s-gatekeeper:latest'.

catatan

Catatan: Jika Anda menggunakan minikube, silakan jalankan eval $(minikube -p minikube docker-env) sebelum menjalankan 'docker build'.

2.1.2 Langkah 2: Menyiapkan layanan dan penyebaran untuk K8s-gatekeeper

Jalankan perintah berikut:

kubectl apply -f config/rbac.yaml
kubectl apply -f config/webhook_deployment.yaml
kubectl apply -f config/webhook_internal.yaml

Ini akan memulai K8s-gatekeeper, dan Anda dapat mengonfirmasi ini dengan menjalankan kubectl get pods.

2.1.3 Langkah 3: Menginstal Sumber Daya CRD untuk K8s-gatekeeper

Jalankan perintah berikut:

kubectl apply -f config/auth.casbin.org_casbinmodels.yaml 
kubectl apply -f config/auth.casbin.org_casbinpolicies.yaml

2.2 Webhook Eksternal

Untuk metode webhook eksternal, K8s-gatekeeper akan berjalan di luar Kubernetes, dan Kubernetes akan mengakses K8s-gatekeeper seperti mengakses situs web biasa. Kubernetes memiliki persyaratan wajib bahwa webhook admission harus menggunakan HTTPS. Untuk tujuan mencoba K8s-gatekeeper, kami telah menyediakan seperangkat sertifikat dan kunci pribadi (meskipun ini tidak aman). Jika Anda lebih suka menggunakan sertifikat Anda sendiri, silakan merujuk ke Bab 5. Pengaturan Lanjutan untuk instruksi mengenai penyesuaian sertifikat dan kunci pribadi.

Sertifikat yang kami sediakan dikeluarkan untuk 'webhook.domain.local'. Jadi, modifikasi host (misalnya, /etc/hosts) dan arahkan 'webhook.domain.local' ke alamat IP di mana K8s-gatekeeper berjalan.

Kemudian jalankan perintah berikut:

go mod tidy
go mod vendor
go run cmd/webhook/main.go
kubectl apply -f config/auth.casbin.org_casbinmodels.yaml
kubectl apply -f config/auth.casbin.org_casbinpolicies.yaml
kubectl apply -f config/webhook_external.yaml

2.3 Instal K8s-gatekeeper melalui Helm

2.3.1 Langkah 1: Bangun image

Silakan merujuk ke Bab 2.1.1.

2.3.2 Instalasi Helm

Jalankan perintah helm install k8sgatekeeper ./k8sgatekeeper.

3. Coba K8s-gatekeeper

3.1 Membuat Model dan Kebijakan Casbin

Anda memiliki dua metode untuk membuat model dan kebijakan: melalui kubectl atau melalui go-client yang kami sediakan.

3.1.1 Membuat/Memperbarui Model dan Kebijakan Casbin melalui kubectl

Di K8s-gatekeeper, model Casbin disimpan dalam sumber daya CRD yang disebut 'CasbinModel'. Definisinya terletak di config/auth.casbin.org_casbinmodels.yaml.

Terdapat contoh di example/allowed_repo/model.yaml. Perhatikan bidang-bidang berikut:

  • metadata.name: nama model. Nama ini HARUS sama dengan nama objek CasbinPolicy yang terkait dengan model ini, sehingga K8s-gatekeeper dapat memasangkannya dan membuat enforcer.
  • spec.enable: jika bidang ini diatur ke "false", model ini (serta objek CasbinPolicy yang terkait dengan model ini) akan diabaikan.
  • spec.modelText: sebuah string yang berisi teks model dari model Casbin.

Kebijakan Casbin disimpan dalam sumber daya CRD lain yang disebut 'CasbinPolicy', yang definisinya dapat ditemukan di config/auth.casbin.org_casbinpolicies.yaml.

Terdapat contoh di example/allowed_repo/policy.yaml. Perhatikan bidang-bidang berikut:

  • metadata.name: nama kebijakan. Nama ini HARUS sama dengan nama objek CasbinModel yang terkait dengan kebijakan ini, agar K8s-gatekeeper dapat memasangkannya dan membuat pelaksana.
  • spec.policyItem: sebuah string yang berisi teks kebijakan dari model Casbin.

Setelah membuat file CasbinModel dan CasbinPolicy Anda sendiri, gunakan perintah berikut untuk menerapkannya:

kubectl apply -f <filename>

Setelah sepasang CasbinModel dan CasbinPolicy dibuat, K8s-gatekeeper akan dapat mendeteksinya dalam waktu 5 detik.

3.1.2 Membuat/Memperbarui Model dan Kebijakan Casbin melalui go-client yang kami sediakan

Kami mengerti bahwa mungkin ada situasi di mana tidak nyaman menggunakan shell untuk mengeksekusi perintah secara langsung pada sebuah node dari klaster K8s, seperti ketika Anda membangun platform cloud otomatis untuk perusahaan Anda. Oleh karena itu, kami telah mengembangkan go-client untuk membuat dan memelihara CasbinModel dan CasbinPolicy.

Pustaka go-client terletak di pkg/client.

Di client.go, kami menyediakan fungsi untuk membuat klien.

func NewK8sGateKeeperClient(externalClient bool) (*K8sGateKeeperClient, error) 

Parameter externalClient menentukan apakah K8s-gatekeeper berjalan di dalam klaster K8s atau tidak.

Di model.go, kami menyediakan berbagai fungsi untuk membuat, menghapus, dan memodifikasi CasbinModel. Anda dapat mengetahui cara menggunakan antarmuka ini di model_test.go.

Di policy.go, kami menyediakan berbagai fungsi untuk membuat, menghapus, dan memodifikasi CasbiPolicy. Anda dapat mengetahui cara menggunakan antarmuka ini di policy_test.go.

3.1.2 Coba Apakah K8s-gatekeeper Bekerja

Misalkan Anda telah membuat model dan kebijakan yang tepat di example/allowed_repo. Sekarang, coba perintah berikut:

kubectl apply -f example/allowed_repo/testcase/reject_1.yaml

Anda akan menemukan bahwa K8s akan menolak permintaan ini dan menyebutkan bahwa webhook adalah alasan mengapa permintaan ini ditolak. Namun, ketika Anda mencoba menerapkan example/allowed_repo/testcase/approve_2.yaml, permintaan tersebut akan diterima.

4. Cara Menulis Model dan Kebijakan dengan K8s-gatekeeper

Pertama-tama, pastikan Anda familiar dengan dasar-dasar tata bahasa Model dan Kebijakan Casbin. Jika belum, silakan baca bagian Memulai terlebih dahulu. Dalam bab ini, kami menganggap Anda sudah memahami apa itu Model dan Kebijakan Casbin.

4.1 Definisi Permintaan Model

Ketika K8s-gatekeeper mengotorisasi permintaan, inputnya selalu berupa objek: objek Go dari Permintaan Penyetujuan. Ini berarti enforcer akan selalu digunakan seperti ini:

ok, err := enforcer.Enforce(admission)

di mana admission adalah objek AdmissionReview yang didefinisikan oleh API resmi Go K8s "k8s.io/api/admission/v1". Anda dapat menemukan definisi struct ini di repositori ini: https://github.com/kubernetes/api/blob/master/admission/v1/types.go. Untuk informasi lebih lanjut, Anda juga dapat merujuk ke https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#webhook-request-and-response.

Oleh karena itu, untuk setiap model yang digunakan oleh K8s-gatekeeper, definisi request_definition harus selalu seperti ini:

    [request_definition]
r = obj

Nama 'obj' tidak wajib, selama nama tersebut konsisten dengan nama yang digunakan di bagian [matchers].

4.2 Matchers dari Model

Anda seharusnya menggunakan fitur ABAC dari Casbin untuk menulis aturan Anda. Namun, evaluator ekspresi yang terintegrasi dalam Casbin tidak mendukung pengindeksan dalam peta atau array (slices), juga tidak mendukung ekspansi array. Oleh karena itu, K8s-gatekeeper menyediakan berbagai 'fungsi Casbin' sebagai ekstensi untuk mengimplementasikan fitur-fitur ini. Jika Anda masih menemukan bahwa permintaan Anda tidak dapat dipenuhi oleh ekstensi ini, jangan ragu untuk memulai sebuah masalah, atau membuat permintaan tarik.

Jika Anda tidak familiar dengan fungsi Casbin, Anda dapat merujuk ke Function untuk informasi lebih lanjut.

Berikut adalah fungsi ekstensi:

4.2.1 Fungsi Ekstensi

4.2.1.1 akses

Akses digunakan untuk menyelesaikan masalah bahwa Casbin tidak mendukung pengindeksan dalam peta atau array. Contoh example/allowed_repo/model.yaml menunjukkan penggunaan fungsi ini:

[matchers]
m = r.obj.Request.Namespace == "default" && r.obj.Request.Resource.Resource =="deployments" && \
access(r.obj.Request.Object.Object.Spec.Template.Spec.Containers , 0, "Image") == p.obj

Dalam matcher ini, access(r.obj.Request.Object.Object.Spec.Template.Spec.Containers , 0, "Image") setara dengan r.obj.Request.Object.Object.Spec.Template.Spec.Containers[0].Image, di mana r.obj.Request.Object.Object.Spec.Template.Spec.Containers adalah sebuah slice.

Akses juga dapat memanggil fungsi sederhana yang tidak memiliki parameter dan mengembalikan satu nilai. Contoh example/container_resource_limit/model.yaml menunjukkan hal ini:

[matchers]
m = r.obj.Request.Namespace == "default" && r.obj.Request.Resource.Resource =="deployments" && \
parseFloat(access(r.obj.Request.Object.Object.Spec.Template.Spec.Containers , 0, "Resources","Limits","cpu","Value")) >= parseFloat(p.cpu) && \
parseFloat(access(r.obj.Request.Object.Object.Spec.Template.Spec.Containers , 0, "Resources","Limits","memory","Value")) >= parseFloat(p.memory)

Dalam matcher ini, access(r.obj.Request.Object.Object.Spec.Template.Spec.Containers , 0, "Resources","Limits","cpu","Value") setara dengan r.obj.Request.Object.Object.Spec.Template.Spec.Containers[0].Resources.Limits["cpu"].Value(), di mana r.obj.Request.Object.Object.Spec.Template.Spec.Containers[0].Resources.Limits adalah sebuah peta, dan Value() adalah fungsi sederhana yang tidak memiliki parameter dan mengembalikan satu nilai.

4.2.1.2 aksesDenganWildcard

Terkadang, Anda mungkin memiliki permintaan seperti ini: semua elemen dalam array harus memiliki awalan "aaa". Namun, Casbin tidak mendukung loop for. Dengan accessWithWildcard dan fitur "ekspansi peta/slice", Anda dapat dengan mudah mengimplementasikan permintaan seperti itu.

Misalnya, anggap a.b.c adalah array [aaa, bbb, ccc, ddd, eee], maka hasil dari accessWithWildcard(a, "b", "c", "*") akan menjadi slice [aaa, bbb, ccc, ddd, eee]. Dengan menggunakan wildcard *, slice akan di-expand.

Demikian pula, wildcard dapat digunakan lebih dari sekali. Misalnya, hasil dari accessWithWildcard(a, "b", "c", "*", "*") akan menjadi [a.b.c[0][0], a.b.c[0][1], ..., a.b.c[1][0], a.b.c[1][1], ...].

4.2.1.3 Fungsi yang Mendukung Argumen Panjang Variabel

Dalam evaluator ekspresi Casbin, ketika sebuah parameter adalah array, itu akan secara otomatis di-expand sebagai argumen panjang variabel. Menggunakan fitur ini untuk mendukung ekspansi array/slice/peta, kami juga telah mengintegrasikan beberapa fungsi yang menerima array/slice sebagai parameter:

  • contain(): menerima beberapa parameter dan mengembalikan apakah ada parameter (kecuali parameter terakhir) yang sama dengan parameter terakhir.
  • split(a, b, c..., sep, index): mengembalikan slice yang berisi [splits(a, sep)[index], splits(b, sep)[index], splits(a, sep)[index], ...].
  • len(): mengembalikan panjang dari argumen berukuran variabel.
  • matchRegex(a, b, c..., regex): mengembalikan apakah semua parameter yang diberikan (a, b, c, ...) cocok dengan regex yang diberikan.

Berikut adalah contoh dalam example/disallowed_tag/model.yaml:

    [matchers]
m = r.obj.Request.Namespace == "default" && r.obj.Request.Resource.Resource =="deployments" && \
contain(split(accessWithWildcard(r.obj.Request.Object.Object.Spec.Template.Spec.Containers , "*", "Image"),":",1) , p.obj)

Dengan asumsi bahwa accessWithWildcard(r.obj.Request.Object.Object.Spec.Template.Spec.Containers , "*", "Image") mengembalikan ["a:b", "c:d", "e:f", "g:h"], karena splits mendukung argumen berukuran variabel dan melakukan operasi split pada setiap elemen, elemen pada indeks 1 akan dipilih dan dikembalikan. Oleh karena itu, split(accessWithWildcard(r.obj.Request.Object.Object.Spec.Template.Spec.Containers , "*", "Image"),":",1) mengembalikan ["b","d","f","h"]. Dan contain(split(accessWithWildcard(r.obj.Request.Object.Object.Spec.Template.Spec.Containers , "*", "Image"),":",1) , p.obj) mengembalikan apakah p.obj terdapat dalam ["b","d","f","h"].

4.2.1.2 Fungsi Konversi Tipe

  • ParseFloat(): Mengurai sebuah integer menjadi float (ini diperlukan karena setiap angka yang digunakan dalam perbandingan harus diubah menjadi float).
  • ToString(): Mengonversi sebuah objek menjadi string. Objek ini harus memiliki tipe dasar string (misalnya, objek dari tipe XXX ketika ada pernyataan type XXX string).
  • IsNil(): Mengembalikan apakah parameter tersebut nil.

5. Pengaturan Lanjutan

5.1 Tentang Sertifikat

Di Kubernetes (k8s), wajib bagi webhook untuk menggunakan HTTPS. Ada dua pendekatan untuk mencapai ini:

  • Gunakan sertifikat yang ditandatangani sendiri (contoh dalam repositori ini menggunakan metode ini)
  • Gunakan sertifikat normal

5.1.1 Sertifikat yang Ditandatangani Sendiri

Menggunakan sertifikat yang ditandatangani sendiri berarti bahwa Otoritas Sertifikat (CA) yang mengeluarkan sertifikat tersebut bukan salah satu CA yang terkenal. Oleh karena itu, Anda harus memberi tahu k8s tentang CA ini.

Saat ini, contoh dalam repositori ini menggunakan CA buatan sendiri, yang kunci pribadi dan sertifikatnya disimpan masing-masing di config/certificate/ca.crt dan config/certificate/ca.key. Sertifikat untuk webhook adalah config/certificate/server.crt, yang dikeluarkan oleh CA buatan sendiri. Domain dari sertifikat ini adalah "webhook.domain.local" (untuk webhook eksternal) dan "casbin-webhook-svc.default.svc" (untuk webhook internal).

Informasi tentang CA diteruskan ke k8s melalui file konfigurasi webhook. Baik config/webhook_external.yaml maupun config/webhook_internal.yaml memiliki bidang yang disebut "CABundle", yang berisi string terenkripsi base64 dari sertifikat CA.

Jika Anda perlu mengubah sertifikat/domain (misalnya, jika Anda ingin menempatkan webhook ini ke namespace lain di k8s sambil menggunakan webhook internal, atau jika Anda ingin mengubah domain sambil menggunakan webhook eksternal), prosedur berikut harus diikuti:

  1. Buat CA baru:

    • Hasilkan kunci pribadi untuk CA palsu:

      openssl genrsa -des3 -out ca.key 2048
    • Hapus perlindungan kata sandi dari kunci pribadi:

      openssl rsa -in ca.key -out ca.key
  2. Hasilkan kunci pribadi untuk server webhook:

    openssl genrsa -des3 -out server.key 2048
    openssl rsa -in server.key -out server.key
  3. Gunakan CA yang dihasilkan sendiri untuk menandatangani sertifikat untuk webhook:

    • Salin file konfigurasi openssl sistem Anda untuk penggunaan sementara. Anda dapat menemukan lokasi file konfigurasi dengan menjalankan openssl version -a, biasanya disebut openssl.cnf.

    • Dalam file konfigurasi:

      • Temukan paragraf [req] dan tambahkan baris berikut: req_extensions = v3_req

      • Temukan paragraf [v3_req] dan tambahkan baris berikut: subjectAltName = @alt_names

      • Tambahkan baris berikut ke file:

        [alt_names]
        DNS.2=<The domain you want>

        Catatan: Ganti 'casbin-webhook-svc.default.svc' dengan nama layanan sebenarnya Anda sendiri jika Anda memutuskan untuk mengubah nama layanan.

    • Gunakan file konfigurasi yang dimodifikasi untuk menghasilkan file permintaan sertifikat:

      openssl req -new -nodes -keyout server.key -out server.csr -config openssl.cnf
    • Gunakan CA buatan sendiri untuk merespon permintaan dan menandatangani sertifikat:

      openssl x509 -req -days 3650 -in server.csr -out server.crt -CA ca.crt -CAkey ca.key -CAcreateserial -extensions v3_req -extensions SAN -extfile openssl.cnf
  4. Ganti field 'CABundle': Perbarui field ini dengan sertifikat baru.

  5. Jika Anda menggunakan helm, perubahan serupa perlu diterapkan pada bagan helm.

Jika Anda menggunakan sertifikat legal, Anda tidak perlu melalui semua prosedur ini. Hapus field "CABundle" di config/webhook_external.yaml dan config/webhook_internal.yaml, dan ubah domain dalam file-file ini menjadi domain yang Anda miliki.