--- title: Secret content_template: templates/concept feature: title: Secret dan manajemen konfigurasi description: > Menerapkan serta mengubah secret serta konfigurasi aplikasi tanpa melakukan perubahan pada image kamu serta mencegah tereksposnya secret yang kamu miliki pada konfigurasi. weight: 50 --- {{% capture overview %}} Objek `secret` pada Kubernetes mengizinkan kamu menyimpan dan mengatur informasi yang sifatnya sensitif, seperti _password_, token OAuth, dan ssh _keys_. Menyimpan informasi yang sifatnya sensitif ini ke dalam `secret` cenderung lebih aman dan fleksible jika dibandingkan dengan menyimpan informasi tersebut secara apa adanya pada definisi {{< glossary_tooltip term_id="pod" >}} atau di dalam {{< glossary_tooltip text="container image" term_id="image" >}}. Silahkan lihat [Dokumen desain Secret](https://git.k8s.io/community/contributors/design-proposals/auth/secrets.md) untuk informasi yang sifatnya mendetail. {{% /capture %}} {{% capture body %}} ## Ikhtisar Secret Sebuah Secret merupakan sebuah objek yang mengandung informasi yang sifatnya sensitif, seperti _password_, token, atau _key_. Informasi tersebut sebenarnya bisa saja disimpan di dalam spesifikasi Pod atau _image_; meskipun demikian, melakukan penyimpanan di dalam objek Secret mengizinkan pengguna untuk memiliki kontrol lebih lanjut mengenai bagaimana Secret ini disimpan, serta mencegah tereksposnya informasi sensitif secara tidak disengaja. Baik pengguna dan sistem memiliki kemampuan untuk membuat objek Secret. Untuk menggunakan Secret, sebuah Pod haruslah merujuk pada Secret tersebut. Sebuah Secret dapat digunakan di dalam sebuah Pod melalui dua cara: sebagai _file_ yang ada di dalam _volume_ {{< glossary_tooltip text="volume" term_id="volume" >}} yang di-_mount_ pada salah satu container Pod, atau digunakan oleh kubelet ketika menarik _image_ yang digunakan di dalam Pod. ### Secret _Built-in_ #### Sebuah _Service Account_ akan Secara Otomatis Dibuat dan Meng-_attach_ Secret dengan Kredensial API Kubernetes secara otomatis membuat secret yang mengandung kredensial yang digunakan untuk mengakses API, serta secara otomatis memerintahkan Pod untuk menggunakan Secret ini. Mekanisme otomatisasi pembuatan secret dan penggunaan kredensial API dapat di nonaktifkan atau di-_override_ jika kamu menginginkannya. Meskipun begitu, jika apa yang kamu butuhkan hanyalah mengakses apiserver secara aman, maka mekanisme _default_ inilah yang disarankan. Baca lebih lanjut dokumentasi [_Service Account_](/docs/tasks/configure-pod-container/configure-service-account/) untuk informasi lebih lanjut mengenai bagaimana cara kerja _Service Account_. ### Membuat Objek Secret Kamu Sendiri #### Membuat Secret dengan Menggunakan kubectl Misalnya saja, beberapa Pod memerlukan akses ke sebuah basis data. Kemudian _username_ dan _password_ yang harus digunakan oleh Pod-Pod tersebut berada pada mesin lokal kamu dalam bentuk _file-file_ `./username.txt` dan `./password.txt`. ```shell # Buatlah file yang selanjutnya akan digunakan pada contoh-contoh selanjutnya echo -n 'admin' > ./username.txt echo -n '1f2d1e2e67df' > ./password.txt ``` Perintah `kubectl create secret` akan mengemas _file-file_ ini menjadi Secret dan membuat sebuah objek pada Apiserver. ```shell kubectl create secret generic db-user-pass --from-file=./username.txt --from-file=./password.txt ``` ``` secret "db-user-pass" created ``` {{< note >}} Karakter spesial seperti `$`, `\*`, and `!` membutuhkan mekanisme _escaping_. Jika _password_ yang kamu gunakan mengandung karakter spesial, kamu perlu melakukan _escape_ karakter dengan menggunakan karakter `\\`. Contohnya, apabila _password_ yang kamu miliki adalah `S!B\*d$zDsb`, maka kamu harus memanggil perintah kubectl dengan cara berikut: kubectl create secret generic dev-db-secret --from-literal=username=devuser --from-literal=password=S\\!B\\\\*d\\$zDsb Perhatikan bahwa kamu tidak perlu melakukan _escape_ karakter apabila massukan yang kamu berikan merupakan _file_ (`--from-file`). {{< /note >}} Kamu dapat memastikan apakah suatu Secret sudah dibuat atau belum dengan menggunakan perintah: ```shell kubectl get secrets ``` ``` NAME TYPE DATA AGE db-user-pass Opaque 2 51s ``` ```shell kubectl describe secrets/db-user-pass ``` ``` Name: db-user-pass Namespace: default Labels: Annotations: Type: Opaque Data ==== password.txt: 12 bytes username.txt: 5 bytes ``` {{< note >}} Perintah-perintah `kubectl get` dan `kubectl describe` secara _default_ akan mencegah ditampilkannya informasi yang ada di dalam Secret. Hal ini dilakukan untuk melindungi agar Secret tidak terekspos secara tidak disengaja oleh orang lain, atau tersimpan di dalam _log_ _terminal_. {{< /note >}} Kamu dapat membaca [bagaimana cara melakukan _decode_ sebuah secret](#decoding-a-secret) untuk mengetahui bagaimana cara melihat isi dari Secret. #### Membuat Secret Secara Manual Kamu dapat membuat sebuah Secret dengan terlebih dahulu membuat _file_ yang berisikan informasi yang ingin kamu jadikan Secret dalam bentuk yaml atau json dan kemudian membuat objek dengan menggunakan _file_ tersebut. [Secret](/docs/reference/generated/kubernetes-api/v1.12/#secret-v1-core) mengandung dua buah _map_: _data_ dan _stringData_. _Field_ _data_ digunakan untuk menyimpan sembarang data, yang di-_encode_ menggunakan base64. Sementara itu _stringData_ disediakan untuk memudahkan kamu untuk menyimpan informasi sensitif dalam format yang tidak di-_encode_. Sebagai contoh, untuk menyimpan dua buah string di dalam Secret dengan menggunakan _field_ data, ubahlah informasi tersebut ke dalam base64 dengan menggunakan mekanisme sebagai berikut: ```shell echo -n 'admin' | base64 YWRtaW4= echo -n '1f2d1e2e67df' | base64 MWYyZDFlMmU2N2Rm ``` Buatlah sebuah Secret yang memiliki bentuk sebagai berikut: ```yaml apiVersion: v1 kind: Secret metadata: name: mysecret type: Opaque data: username: YWRtaW4= password: MWYyZDFlMmU2N2Rm ``` Kemudian buatlah Secret menggunakan perintah [`kubectl apply`](/docs/reference/generated/kubectl/kubectl-commands#apply): ```shell kubectl apply -f ./secret.yaml ``` ``` secret "mysecret" created ``` Untuk beberapa skenario, kamu bisa saja ingin menggunakan opsi _field_ stringData. _Field_ ini mengizinkan kamu untuk memberikan masukan berupa informasi yang belum di-_encode_ secara langsung pada sebuah Secret, informasi dalam bentuk string ini kemudian akan di-_encode_ ketika Secret dibuat maupun diubah. Contoh praktikal dari hal ini adalah ketika kamu melakukan proses _deploy_ aplikasi yang menggunakan Secret sebagai penyimpanan _file_ konfigurasi, dan kamu ingin mengisi bagian dari konfigurasi _file_ tersebut ketika aplikasi di_deploy_. Jika kamu ingin aplikasi kamu menggunakan _file_ konfigurasi berikut: ```yaml apiUrl: "https://my.api.com/api/v1" username: "user" password: "password" ``` Kamu dapat menyimpan Secret ini dengan menggunakan cara berikut: ```yaml apiVersion: v1 kind: Secret metadata: name: mysecret type: Opaque stringData: config.yaml: |- apiUrl: "https://my.api.com/api/v1" username: {{username}} password: {{password}} ``` Alat _deployment_ yang kamu gunakan kemudian akan mengubah templat variabel `{{username}}` dan `{{password}}` sebelum menjalankan perintah `kubectl apply`. stringData merupakan _field_ yang sifatnya _write-only_ untuk alasan kenyamanan pengguna. _Field_ ini tidak pernah ditampilkan ketika Secret dibaca. Sebagai contoh, misalkan saja kamu menjalankan perintah sebagai berikut: ```shell kubectl get secret mysecret -o yaml ``` Keluaran yang diberikan kurang lebih akan ditampilkan sebagai berikut: ```yaml apiVersion: v1 kind: Secret metadata: creationTimestamp: 2018-11-15T20:40:59Z name: mysecret namespace: default resourceVersion: "7225" selfLink: /api/v1/namespaces/default/secrets/mysecret uid: c280ad2e-e916-11e8-98f2-025000000001 type: Opaque data: config.yaml: YXBpVXJsOiAiaHR0cHM6Ly9teS5hcGkuY29tL2FwaS92MSIKdXNlcm5hbWU6IHt7dXNlcm5hbWV9fQpwYXNzd29yZDoge3twYXNzd29yZH19 ``` Jika sebuah _field_ dispesifikasikan dalam bentuk data maupun stringData, maka nilai dari stringData-lah yang akan digunakan. Sebagai contoh, misalkan saja terdapat definisi Secret sebagai berikut: ```yaml apiVersion: v1 kind: Secret metadata: name: mysecret type: Opaque data: username: YWRtaW4= stringData: username: administrator ``` Akan menghasilkan Secret sebagai berikut: ```yaml apiVersion: v1 kind: Secret metadata: creationTimestamp: 2018-11-15T20:46:46Z name: mysecret namespace: default resourceVersion: "7579" selfLink: /api/v1/namespaces/default/secrets/mysecret uid: 91460ecb-e917-11e8-98f2-025000000001 type: Opaque data: username: YWRtaW5pc3RyYXRvcg== ``` Dimana string `YWRtaW5pc3RyYXRvcg==` akan di-_decode_ sebagai `administrator`. _Key_ dari data dan stringData yang boleh tersusun atas karakter alfanumerik, '-', '_' atau '.'. **Catatan _Encoding_:** _Value_ dari JSON dan YAML yang sudah diseriakisasi dari data Secret akan di-_encode_ ke dalam string base64. _Newline_ dianggap tidak valid pada string ini dan harus dihilangkan. Ketika pengguna Darwin/macOS menggunakan alat `base64`, maka pengguna tersebut harus menghindari opsi `-b` yang digunakan untuk memecah baris yang terlalu panjang. Sebaliknya pengguna Linux _harus_ menambahkan opsi `-w 0` pada perintah `base64` atau melakukan mekanisme _pipeline_ `base64 | tr -d '\n'` jika tidak terdapat opsi `-w`. #### Membuat Secret dengan Menggunakan _Generator_ Kubectl mendukung [mekanisme manajemen objek dengan menggunakan Kustomize](/docs/tasks/manage-kubernetes-objects/kustomization/) sejak versi 1.14. Dengan fitur baru ini, kamu juga dapat membuat sebuah Secret dari sebuah _generator_ dan kemudian mengaplikasikannya untuk membuat sebuah objek pada Apiserver. _Generator_ yang digunakan haruslah dispesifikasikan di dalam sebuah _file_ `kustomization.yaml` di dalam sebuah direktori. Sebagai contoh, untuk menghasilan sebuah Secret dari _file-file_ `./username.txt` dan `./password.txt` ```shell # Membuat sebuah file kustomization.yaml dengan SecretGenerator cat <./kustomization.yaml secretGenerator: - name: db-user-pass files: - username.txt - password.txt EOF ``` Gunakan direktori _kustomization_ untuk membuat objek Secret yang diinginkan. ```shell $ kubectl apply -k . secret/db-user-pass-96mffmfh4k created ``` Kamu dapat memastikan Secret tersebut sudah dibuat dengan menggunakan perintah berikut: ```shell $ kubectl get secrets NAME TYPE DATA AGE db-user-pass-96mffmfh4k Opaque 2 51s $ kubectl describe secrets/db-user-pass-96mffmfh4k Name: db-user-pass Namespace: default Labels: Annotations: Type: Opaque Data ==== password.txt: 12 bytes username.txt: 5 bytes ``` Sebagai contoh, untuk membuat sebuah Secret dari literal `username=admin` dan `password=secret`, kamu dapat menspesifikasikan _generator_ Secret pada _file_ `kustomization.yaml` sebagai ```shell # Membuat sebuah file kustomization.yaml dengan menggunakan SecretGenerator $ cat <./kustomization.yaml secretGenerator: - name: db-user-pass literals: - username=admin - password=secret EOF ``` Aplikasikan direktori _kustomization_ untuk membuat objek Secret. ```shell $ kubectl apply -k . secret/db-user-pass-dddghtt9b5 created ``` {{< note >}} Secret yang dihasilkan nantinya akan memiliki tambahan sufix dengan cara melakukan teknik _hashing_ pada isi Secret tersebut. Hal ini dilakukan untuk menjamin dibuatnya sebuah Secret baru setiap kali terjadi perubahan isi dari Secret tersebut. {{< /note >}} #### Melakukan Proses _Decode_ pada Secret Secret dapat dibaca dengan menggunakan perintah `kubectl get secret`. Misalnya saja, untuk membaca Secret yang dibuat pada bagian sebelumya: ```shell kubectl get secret mysecret -o yaml ``` ``` apiVersion: v1 kind: Secret metadata: creationTimestamp: 2016-01-22T18:41:56Z name: mysecret namespace: default resourceVersion: "164619" selfLink: /api/v1/namespaces/default/secrets/mysecret uid: cfee02d6-c137-11e5-8d73-42010af00002 type: Opaque data: username: YWRtaW4= password: MWYyZDFlMmU2N2Rm ``` Kemudian lakukan mekanisme _decode_ _field_ _password_: ```shell echo 'MWYyZDFlMmU2N2Rm' | base64 --decode ``` ``` 1f2d1e2e67df ``` ## Menggunakan Secret Secret dapat di-_mount_ sebagai _volume_ data atau dapat diekspos sebagai {{< glossary_tooltip text="variabel-variabel environment" term_id="container-env-variables" >}} dapat digunakan di dalam Pod. Secret ini juga dapat digunakan secara langsug oleh bagian lain dari sistem, tanpa secara langsung berkaitan dengan Pod. Sebagai contoh, Secret dapat berisikan kredensial bagian suatu sistem lain yang digunakan untuk berinteraksi dengan sistem eksternal yang kamu butuhkan. ### Menggunakan Secret sebagai _File_ melalui Pod Berikut adalah langkah yang harus kamu penuhi agar kamu dapat menggunakan Secret di dalam _volume_ dalam sebuah Pod: 1. Buatlah sebuah Secret, atau gunakan sebuah Secret yang sudah kamu buat sebelumnya. Beberapa Pod dapat merujuk pada sebuah Secret yang sama. 1. Modifikasi definisi Pod kamu dengan cara menambahkan sebuah _volume_ di bawah `.spec.volumes[]`. Berilah _volume_ tersebut nama, dan pastikan _field_ `.spec.volumes[].secret.secretName` merujuk pada nama yang sama dengan objek secret. 1. Tambahkan _field_ `.spec.containers[].volumeMounts[]` pada setiap container yang membutuhkan Secret. Berikan spesifikasi `.spec.containers[].volumeMounts[].readOnly = true` dan `.spec.containers[].volumeMounts[].mountPath` pada direktori dimana Secret tersebut diletakkan. 1. Modifikasi image dan/atau _command line_ kamu agar program yang kamu miliki merujuk pada _file_ di dalam direktori tersebut. Setiap _key_ pada map `data` Secret akan menjadi nama dari sebuah _file_ pada `mountPath`. Berikut merupakan salah satu contoh dimana sebuah Pod melakukan proses _mount_ Secret pada sebuah _volume_: ```yaml apiVersion: v1 kind: Pod metadata: name: mypod spec: containers: - name: mypod image: redis volumeMounts: - name: foo mountPath: "/etc/foo" readOnly: true volumes: - name: foo secret: secretName: mysecret ``` Setiap Secret yang ingin kamu gunakan harus dirujuk pada _field_ `.spec.volumes`. Jika terdapat lebih dari satu container di dalam Pod, maka setiap container akan membutuhkan blok `volumeMounts`-nya masing-masing, meskipun demikian hanya sebuah _field_ `.spec.volumes` yang dibutuhkan untuk setiap Secret. Kamu dapat menyimpan banyak _file_ ke dalam satu Secret, atau menggunakan banyak Secret, hal ini tentunya bergantung pada preferensi pengguna. **Proyeksi _key_ Secret pada Suatu _Path_ Spesifik** Kita juga dapat mengontrol _path_ di dalam _volume_ di mana sebuah Secret diproyeksikan. Kamu dapat menggunakan _field_ `.spec.volumes[].secret.items` untuk mengubah _path_ target dari setiap _key_: ```yaml apiVersion: v1 kind: Pod metadata: name: mypod spec: containers: - name: mypod image: redis volumeMounts: - name: foo mountPath: "/etc/foo" readOnly: true volumes: - name: foo secret: secretName: mysecret items: - key: username path: my-group/my-username ``` Apa yang akan terjadi jika kita menggunakan definisi di atas: * Secret `username` akan disimpan pada _file_ `/etc/foo/my-group/my-username` dan bukan `/etc/foo/username`. * Secret `password` tidak akan diproyeksikan. Jika _field_ `.spec.volumes[].secret.items` digunakan, hanya _key-key_ yang dispesifikan di dalam `items` yang diproyeksikan. Untuk mengonsumsi semua _key-key_ yang ada dari Secret, semua _key_ yang ada harus didaftarkan pada _field_ `items`. Semua _key_ yang didaftarkan juga harus ada di dalam Secret tadi. Jika tidak, _volume_ yang didefinisikan tidak akan dibuat. **_Permission_ _File-File_ Secret** Kamu juga dapat menspesifikasikan mode _permission_ dari _file_ Secret yang kamu inginkan. Jika kamu tidak menspesifikasikan hal tersebut, maka nilai _default_ yang akan diberikan adalah `0644` is used by default. Kamu dapat memberikan mode _default_ untuk semua Secret yang ada serta melakukan mekanisme _override_ _permission_ pada setiap _key_ jika memang diperlukan. Sebagai contoh, kamu dapat memberikan spesifikasi mode _default_ sebagai berikut: ```yaml apiVersion: v1 kind: Pod metadata: name: mypod spec: containers: - name: mypod image: redis volumeMounts: - name: foo mountPath: "/etc/foo" volumes: - name: foo secret: secretName: mysecret defaultMode: 256 ``` Kemudian, sebuah Secret akan di-_mount_ pada `/etc/foo`, selanjutnya semua _file_ yang dibuat pada _volume_ secret tersebut akan memiliki _permission_ `0400`. Perhatikan bahwa spesifikasi JSON tidak mendukung notasi _octal_, dengan demikian gunakanlah _value_ 256 untuk _permission_ 0400. Jika kamu menggunakan format YAML untuk spesifikasi Pod, kamu dapat menggunakan notasi _octal_ untuk memberikan spesifikasi _permission_ dengan cara yang lebih natural. Kamu juga dapat melakukan mekanisme pemetaan, seperti yang sudah dilakukan pada contoh sebelumnya, dan kemudian memberikan spesifikasi _permission_ yang berbeda untuk _file_ yang berbeda. ```yaml apiVersion: v1 kind: Pod metadata: name: mypod spec: containers: - name: mypod image: redis volumeMounts: - name: foo mountPath: "/etc/foo" volumes: - name: foo secret: secretName: mysecret items: - key: username path: my-group/my-username mode: 511 ``` Pada kasus tersebut, _file_ yang dihasilkan pada `/etc/foo/my-group/my-username` akan memiliki _permission_ `0777`. Karena terdapat batasan pada representasi JSON, maka kamu harus memberikan spesifikasi mode _permission_ dalam bentuk notasi desimal. Perhatikan bahwa _permission_ ini bida saja ditampilkan dalam bentuk notasi desimal, hal ini akan ditampilkan pada bagian selanjutnya. **Mengonsumsi _Value_ dari Secret melalui Volume** Di dalam sebuah container dimana _volume_ secret di-_mount_, _key_ dari Secret akan ditampilkan sebagai _file_ dan _value_ dari Secret yang berada dalam bentuk base64 ini akan di-_decode_ dam disimpan pada _file-file_ tadi. Berikut merupakan hasil dari eksekusi perintah di dalam container berdasarkan contoh yang telah dipaparkan di atas: ```shell ls /etc/foo/ ``` ``` username password ``` ```shell cat /etc/foo/username ``` ``` admin ``` ```shell cat /etc/foo/password ``` ``` 1f2d1e2e67df ``` Program di dalam container bertanggung jawab untuk membaca Secret dari _file-file_ yang ada. **Secret yang di-_mount_ Akan Diubah Secara Otomatis** Ketika sebuah Secret yang sedang digunakan di dalam _volume_ diubah, maka _key_ yang ada juga akan diubah. Kubelet akan melakukan mekanisme pengecekan secara periodik apakah terdapat perubahan pada Secret yang telah di-_mount_. Meskipun demikian, proses pengecekan ini dilakukan dengan menggunakan _cache_ lokal untuk mendapatkan _value_ saat ini dari sebuah Secret. Tipe _cache_ yang ada dapat diatur dengan menggunakan (_field_ `ConfigMapAndSecretChangeDetectionStrategy` pada [_struct_ KubeletConfiguration](https://github.com/kubernetes/kubernetes/blob/{{< param "docsbranch" >}}/staging/src/k8s.io/kubelet/config/v1beta1/types.go)). Mekanisme ini kemudian dapat diteruskan dengan mekanisme _watch_(_default_), ttl, atau melakukan pengalihan semua _request_ secara langsung pada kube-apiserver. Sebagai hasilnya, _delay_ total dari pertama kali Secret diubah hingga dilakukannya mekanisme proyeksi _key_ yang baru pada Pod berlangsung dalam jangka waktu sinkronisasi periodik kubelet + _delay_ propagasi _cache_, dimana _delay_ propagasi _cache_ bergantung pada jenis _cache_ yang digunakan (ini sama dengan _delay_ propagasi _watch_, ttl dari _cache_, atau nol). {{< note >}} Sebuah container menggunakan Secret sebagai [subPath](/docs/concepts/storage/volumes#using-subpath) dari _volume_ yang di-_mount_ tidak akan menerima perubahan Secret. {{< /note >}} ### Menggunakan Secret sebagai Variabel _Environment_ Berikut merupakan langkah-langkah yang harus kamu terapkan, untuk menggunakan secret sebagai {{< glossary_tooltip text="variabel _environment_" term_id="container-env-variables" >}} pada sebuah Pod: 1. Buatlah sebuah Secret, atau gunakan sebuah Secret yang sudah kamu buat sebelumnya. Beberapa Pod dapat merujuk pada sebuah Secret yang sama. 1. Modifikasi definisi Pod pada setiap container dimana kamu menginginkan container tersebut dapat mengonsumsi your Pod definition in each container that you wish to consume the value of a secret key to add an environment variabele for each secret key you wish to consume. The environment variabele that consumes the secret key should populate the secret's name and key in `env[].valueFrom.secretKeyRef`. 1. Modifikasi _image_ dan/atau _command line_ kamu agar program yang kamu miliki merujuk pada _value_ yang sudah didefinisikan pada variabel _environment_. Berikut merupakan contoh dimana sebuah Pod menggunakan Secret sebagai variabel _environment_: ```yaml apiVersion: v1 kind: Pod metadata: name: secret-env-pod spec: containers: - name: mycontainer image: redis env: - name: SECRET_USERNAME valueFrom: secretKeyRef: name: mysecret key: username - name: SECRET_PASSWORD valueFrom: secretKeyRef: name: mysecret key: password restartPolicy: Never ``` **Menggunakan Secret dari Variabel _Environment_** Di dalam sebuah container yang mengkonsumsi Secret pada sebuah variabel _environment_, _key_ dari sebuah secret akan ditampilkan sebagai variabel _environment_ pada umumnya dengan _value_ berupa informasi yang telah di-_decode_ ke dalam base64. Berikut merupakan hasil yang didapatkan apabila perintah-perintah di bawah ini dijalankan dari dalam container yang didefinisikan di atas: ```shell echo $SECRET_USERNAME ``` ``` admin ``` ```shell echo $SECRET_PASSWORD ``` ``` 1f2d1e2e67df ``` ### Menggunakan imagePullSecrets Sebuah `imagePullSecret` merupakan salah satu cara yang dapat digunakan untuk menempatkan secret yang mengandung _password_ dari registri Docker (atau registri _image_ lainnya) pada Kubelet, sehingga Kubelet dapat mengunduh _image_ dan menempatkannya pada Pod. **Memberikan spesifikasi manual dari sebuah imagePullSecret** Penggunaan imagePullSecrets dideskripsikan di dalam [dokumentasi _image_](/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod) ### Mekanisme yang Dapat Diterapkan agar imagePullSecrets dapat Secara Otomatis Digunakan Kamu dapat secara manual membuat sebuah imagePullSecret, serta merujuk imagePullSecret yang sudah kamu buat dari sebuah serviceAccount. Semua Pod yang dibuat dengan menggunakan serviceAccount tadi atau serviceAccount _default_ akan menerima _field_ imagePullSecret dari serviceAccount yang digunakan. Bacalah [Cara menambahkan ImagePullSecrets pada sebuah _service account_](/docs/tasks/configure-pod-container/configure-service-account/#add-imagepullsecrets-to-a-service-account) untuk informasi lebih detail soal proses yang dijalankan. ### Mekanisme _Mounting_ Otomatis dari Secret yang Sudah Dibuat Secret yang dibuat secara manual (misalnya, secret yang mengandung token yang dapat digunakan untuk mengakses akun GitHub) dapat di-_mount_ secara otomatis pada sebuah Pod berdasarkan _service account_ yang digunakan oleh Pod tadi. Baca [Bagaimana Penggunaan PodPreset untuk Memasukkan Informasi ke Dalam Pod](/docs/tasks/inject-data-application/podpreset/) untuk informasi lebih lanjut. ## Detail ### Batasan-Batasan Sumber dari _secret volume_ akan divalidasi untuk menjamin rujukan pada objek yang dispesifikasikan mengarah pada objek dengan _type_ `Secret`. Oleh karenanya, sebuah _secret_ harus dibuat sebelum Pod yang merujuk pada _secret_ tersebut dibuat. Sebuah objek API Secret berada di dalam sebuah {{< glossary_tooltip text="namespace" term_id="namespace" >}}. Objek-objek ini hanya dapat dirujuk oleh Pod-Pod yang ada pada namespace yang sama. Secret memiliki batasi dalam hal ukuran maksimalnya yaitu hanya sampai 1MiB per objek. Oleh karena itulah, pembuatan secret dalam ukuran yang sangat besar tidak dianjurkan karena dapat menghabiskan sumber daya apiserver dan memori kubelet. Meskipun demikian, pembuatan banyak secret dengan ukuran kecil juga dapat menhabiskan memori. Pembatasan sumber daya yang diizinkan untuk pembuatan secret merupakan salah satu fitur tambahan yang direncanakan kedepannya. Kubelet hanya mendukung penggunaan secret oleh Pod apabila Pod tersebut didapatkan melalui apiserver. Hal ini termasuk Pod yang dibuat dengan menggunakan kubectl, atau secara tak langsung melalui _replication controller_. Hal ini tidak termasuk Pod yang dibuat melalui _flag_ `--manifest-url` yang ada pada kubelet, maupun REST API yang disediakan (hal ini bukanlah merupakan mekanisme umum yang dilakukan untuk membuat sebuah Pod). Secret harus dibuat sebelum digunakan oleh Pod sebagai variabel _environment_, kecuali apabila variabel _environment_ ini dianggap opsional. Rujukan pada Secret yang tidak dapat dipenuhi akan menyebabkan Pod gagal _start_. Rujukan melalui `secretKeyRef` pada _key_ yang tidak ada pada _named_ Secret akan akan menyebabkan Pod gagal _start_. Secret yang digunakan untuk memenuhi variabel _environment_ melalui `envFrom` yang memiliki _key_ yang dianggap memiliki penamaan yang tidak valid akan diabaikan. Hal ini akan akan menyebabkan Pod gagal _start_. Selanjutnya akan terdapat _event_ dengan alasan `InvalidvariabeleNames` dan pesan yang berisikan _list_ dari _key_ yang diabaikan akibat penamaan yang tidak valid. Contoh yang ada akan menunjukkan sebuah pod yang merujuk pada secret `default/mysecret` yang mengandung dua buah _key_ yang tidak valid, yaitu 1badkey dan 2alsobad. ```shell kubectl get events ``` ``` LASTSEEN FIRSTSEEN COUNT NAME KIND SUBOBJECT TYPE REASON 0s 0s 1 dapi-test-pod Pod Warning InvalidEnvironmentvariabeleNames kubelet, 127.0.0.1 Keys [1badkey, 2alsobad] from the EnvFrom secret default/mysecret were skipped since they are considered invalid environment variabele names. ``` ### Interaksi Secret dan Pod Lifetime Ketika sebuah pod dibuat melalui API, tidak terdapat mekanisme pengecekan yang digunakan untuk mengetahui apakah sebuah Secret yang dirujuk sudah dibuat atau belum. Ketika sebuah Pod di-_schedule_, kubelet akan mencoba mengambil informasi mengenai _value_ dari secret tadi. Jika secret tidak dapat diambil _value_-nya dengan alasan temporer karena hilangnya koneksi ke API server atau secret yang dirujuk tidak ada, kubelet akan melakukan mekanisme _retry_ secara periodik. Kubelet juga akan memberikan laporan mengenai _event_ yang terjadi pada Pod serta alasan kenapa Pod tersebut belum di-_start_. Apabila Secret berhasil didapatkan, kubelet akan membuat dan me-_mount_ volume yang mengandung secret tersebut. Tidak akan ada container dalam pod yang akan di-_start_ hingga semua volume pod berhasil di-_mount_. ## Contoh-Contoh Penggunaan ### Contoh Penggunaan: Pod dengan _ssh key_ Buatlah sebuah kustomization.yaml dengan SecretGenerator yang mengandung beberapa _ssh key_: ```shell kubectl create secret generic ssh-key-secret --from-file=ssh-privatekey=/path/to/.ssh/id_rsa --from-file=ssh-publickey=/path/to/.ssh/id_rsa.pub ``` ``` secret "ssh-key-secret" created ``` {{< caution >}} Pikirkanlah terlebih dahulu sebelum kamu menggunakan _ssh key_ milikmu sendiri: pengguna lain pada kluster tersebut bisa saja memiliki akses pada secret yang kamu definisikan. Gunakanlah service account untuk membagi informasi yang kamu inginkan di dalam kluster tersebut, dengan demikian kamu dapat membatalkan service account tersebut apabila secret tersebut disalahgunakan. {{< /caution >}} Sekarang, kita dapat membuat sebuah pod yang merujuk pada secret dengan _ssh key_ yang sudah dibuat tadi serta menggunakannya melalui sebuah volume yang di-_mount_: ```yaml apiVersion: v1 kind: Pod metadata: name: secret-test-pod labels: name: secret-test spec: volumes: - name: secret-volume secret: secretName: ssh-key-secret containers: - name: ssh-test-container image: mySshImage volumeMounts: - name: secret-volume readOnly: true mountPath: "/etc/secret-volume" ``` Ketika sebuah perintah dijalankan di dalam container, bagian dari _key_ tadi akan terdapat pada: ```shell /etc/secret-volume/ssh-publickey /etc/secret-volume/ssh-privatekey ``` container kemudian dapat menggunakan secret secara bebas untuk membuat koneksi ssh. ### Contoh Penggunaan: Pod dengan kredensial prod / test Contoh ini memberikan ilustrasi pod yang mengonsumsi secret yang mengandung kredensial dari _environment_ _production_ atau _environment_ _test_. Buatlah suatu kustomization.yaml dengan SecretGenerator ```shell kubectl create secret generic prod-db-secret --from-literal=username=produser --from-literal=password=Y4nys7f11 ``` ``` secret "prod-db-secret" created ``` ```shell kubectl create secret generic test-db-secret --from-literal=username=testuser --from-literal=password=iluvtests ``` ``` secret "test-db-secret" created ``` {{< note >}} Karakter spesial seperti `$`, `\*`, dan `!` membutuhkan mekanisme _escaping_. Jika password yang kamu gunakan memiliki karakter spesial, kamu dapat melakukan mekanisme _escape_ dengan karakter `\\` character. Sebagai contohnya, jika _password_ kamu yang sebenarnya adalah `S!B\*d$zDsb`, maka kamu harus memanggil perintah eksekusi dengan cara sebagai berikut: ```shell kubectl create secret generic dev-db-secret --from-literal=username=devuser --from-literal=password=S\\!B\\\*d\\$zDsb ``` Kamu tidak perlu melakukan mekanisme _escape_ karakter apabila menggunakan opsi melalui _file_ (`--from-file`). {{< /note >}} Kemudian buatlah Pod-Pod yang dibutuhkan: ```shell $ cat < pod.yaml apiVersion: v1 kind: List items: - kind: Pod apiVersion: v1 metadata: name: prod-db-client-pod labels: name: prod-db-client spec: volumes: - name: secret-volume secret: secretName: prod-db-secret containers: - name: db-client-container image: myClientImage volumeMounts: - name: secret-volume readOnly: true mountPath: "/etc/secret-volume" - kind: Pod apiVersion: v1 metadata: name: test-db-client-pod labels: name: test-db-client spec: volumes: - name: secret-volume secret: secretName: test-db-secret containers: - name: db-client-container image: myClientImage volumeMounts: - name: secret-volume readOnly: true mountPath: "/etc/secret-volume" EOF ``` Tambahkan Pod-Pod terkait pada _file_ kustomization.yaml yang sama ```shell $ cat <> kustomization.yaml resources: - pod.yaml EOF ``` Terapkan semua perubahan pada objek-objek tadi ke Apiserver dengan menggunakan ```shell kubectl apply --k . ``` Kedua container kemudian akan memiliki _file-file_ berikut ini di dalam _filesystem_ keduanya dengan _value_ sebagai berikut untuk masing-masing _environment_: ```shell /etc/secret-volume/username /etc/secret-volume/password ``` Perhatikan bahwa _specs_ untuk kedua pod berbeda hanya pada satu _field_ saja; hal ini bertujuan untuk memfasilitasi adanya kapabilitas yang berbeda dari templat konfigurasi umum yang tersedia. Kamu dapat mensimplifikasi spesifikasi dasar Pod dengan menggunakan dua buah _service account_ yang berbeda: misalnya saja salah satunya disebut sebagai `prod-user` dengan `prod-db-secret`, dan satunya lagi disebut `test-user` dengan `test-db-secret`. Kemudian spesifikasi Pod tadi dapat diringkas menjadi: ```yaml apiVersion: v1 kind: Pod metadata: name: prod-db-client-pod labels: name: prod-db-client spec: serviceAccount: prod-db-client containers: - name: db-client-container image: myClientImage ``` ### Contoh Penggunaan: _Dotfiles_ pada volume secret Dengan tujuan membuat data yang ada 'tersembunyi' (misalnya, di dalam sebuah _file_ dengan nama yang dimulai dengan karakter titik), kamu dapat melakukannya dengan cara yang cukup sederhana, yaitu cukup dengan membuat karakter awal _key_ yang kamu inginkan dengan titik. Contohnya, ketika sebuah secret di bawah ini di-_mount_ pada sebuah volume: ```yaml apiVersion: v1 kind: Secret metadata: name: dotfile-secret data: .secret-file: dmFsdWUtMg0KDQo= --- apiVersion: v1 kind: Pod metadata: name: secret-dotfiles-pod spec: volumes: - name: secret-volume secret: secretName: dotfile-secret containers: - name: dotfile-test-container image: k8s.gcr.io/busybox command: - ls - "-l" - "/etc/secret-volume" volumeMounts: - name: secret-volume readOnly: true mountPath: "/etc/secret-volume" ``` Volume `secret-volume` akan mengandung sebuah _file_, yang disebut sebagai `.secret-file`, serta container `dotfile-test-container` akan memiliki _file_ konfigurasinya pada _path_ `/etc/secret-volume/.secret-file`. {{< note >}} _File-file_ yang diawali dengan karakter titik akan "tersembunyi" dari keluaran perintah `ls -l`; kamu harus menggunakan perintah `ls -la` untuk melihat _file-file_ tadi dari sebuah direktori. {{< /note >}} ### Contoh Penggunaan: Secret yang dapat diakses hanya pada salah satu container di dalam pod Misalkan terdapat sebuah program yang memiliki kebutuhan untuk menangani _request_ HTTP, melakukan logika bisnis yang kompleks, serta kemudian menandai beberapa _message_ yang ada dengan menggunakan HMAC. Karena program ini memiliki logika aplikasi yang cukup kompleks, maka bisa jadi terdapat beberapa celah terjadinya eksploitasi _remote_ _file_ pada server, yang nantinya bisa saja mengekspos _private key_ yang ada pada _attacker_. Hal ini dapat dipisah menjadi dua buah proses yang berbeda di dalam dua container: sebuah container _frontend_ yang menangani interaksi pengguna dan logika bisnis, tetapi tidak memiliki kapabilitas untuk melihat _private key_; container lain memiliki kapabilitas melihat _private key_ yang ada dan memiliki fungsi untuk menandai _request_ yang berasal dari _frontend_ (melalui jaringan _localhost_). Dengan strategi ini, seorang _attacker_ harus melakukan teknik tambahan untuk memaksa aplikasi melakukan hal yang acak, yang kemudian menyebabkan mekanisme pembacaan _file_ menjadi lebih susah. ## _Best practices_ ### Klien yang menggunakan API secret Ketika men-_deploy_ aplikasi yang berinteraksi dengan API secret, akses yang dilakukan haruslah dibatasi menggunakan [_policy_ autorisasi]( /docs/reference/access-authn-authz/authorization/) seperti [RBAC]( /docs/reference/access-authn-authz/rbac/). Secret seringkali menyimpan _value_ yang memiliki jangkauan spektrum kepentingan, yang mungkin saja dapat menyebabkan terjadinya eskalasi baik di dalam Kubernetes (misalnya saja token dari sebuah _service account_) maupun sistem eksternal. Bahkan apabila setiap aplikasi secara individual memiliki kapabilitas untuk memahami tingkatan yang dimilikinya untuk berinteraksi dengan secret tertentu, aplikasi lain dalam namespace itu bisa saja menyebabkan asumsi tersebut menjadi tidak valid. Karena alasan-alasan yang sudah disebutkan tadi _request_ `watch` dan `list` untuk sebuah secret di dalam suatu namespace merupakan kapabilitas yang sebisa mungkin harus dihindari, karena menampilkan semua secret yang ada berimplikasi pada akses untuk melihat isi yang ada pada secret yang ada. Kapabilitas untuk melakukan _request_ `watch` dan `list` pada semua secret di kluster hanya boleh dimiliki oleh komponen pada sistem level yang paling _previleged_. Aplikasi yang membutuhkan akses ke API secret harus melakukan _request_ `get` pada secret yang dibutuhkan. Hal ini memungkinkan administrator untuk membatasi akses pada semua secret dengan tetap memberikan [akses pada instans secret tertentu](/docs/reference/access-authn-authz/rbac/#referring-to-resources) yang dibutuhkan aplikasi. Untuk meningkatkan performa dengan menggunakan iterasi `get`, klien dapat mendesain sumber daya yang merujuk pada suatu secret dan kemudian melakukan `watch` pada secret tersebut, serta melakukan _request_ secret ketika terjadi perubahan pada rujukan tadi. Sebagai tambahan, [API "bulk watch"]( https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/bulk_watch.md) yang dapat memberikan kapabilitas `watch` individual pada sumber daya melalui klien juga sudah direncanakan, dan kemungkinan akan diimplementasikan dirilis Kubernetes selanjutnya. ## Properti Keamanan ### Proteksi Karena objek `secret` dapat dibuat secara independen dengan `pod` yang menggunakannya, risiko tereksposnya secret di dalam workflow pembuatan, pemantauan, serta pengubahan pod. Sistem yang ada juga dapat memberikan tindakan pencegahan ketika berinteraksi dengan `secret`, misalnya saja tidak melakukan penulisan isi `secret` ke dalam disk apabila hal tersebut memungkinkan. Sebuah secret hanya diberikan pada node apabila pod yang ada di dalam node membutuhkan secret tersebut. Kubelet menyimpan secret yang ada pada `tmpfs` sehingga secret tidak ditulis pada disk. Setelah pod yang bergantung pada secret tersebut dihapus, maka kubelet juga akan menghapus salinan lokal data secret. Di dalam sebuah node bisa saja terdapat beberapa secret yang dibutuhkan oleh pod yang ada di dalamnya. Meskipun demikian, hanya secret yang di-_request_ oleh sebuah pod saja yang dapat dilihat oleh container yang ada di dalamnya. Dengan demikian, sebuah Pod tidak memiliki akses untuk melihat secret yang ada pada pod yang lain. Di dalam sebuah pod bisa jadi terdapat beberapa container. Meskipun demikian, agar sebuah container bisa mengakses _volume secret_, container tersebut haruslah mengirimkan _request_ `volumeMounts` yang ada dapat diakses dari container tersebut. Pengetahuan ini dapat digunakan untuk membentuk [partisi security pada level pod](#contoh-penggunaan-secret-yang-dapat-diakses-hanya-pada-salah-satu-container-di-dalam-pod). Pada sebagian besar distribusi yang dipelihara projek Kubernetes, komunikasi antara pengguna dan apiserver serta apisserver dan kubelet dilindungi dengan menggunakan SSL/TLS. Dengan demikian, secret dalam keadaan dilindungi ketika ditransmisi. {{< feature-state for_k8s_version="v1.13" state="beta" >}} Kamu dapat mengaktifkan [enkripsi pada rest](/docs/tasks/administer-cluster/encrypt-data/) untuk data secret, sehingga secret yang ada tidak akan ditulis ke dalam {{< glossary_tooltip term_id="etcd" >}} dalam keadaan tidak terenkripsi. ### Resiko - Pada API server, data secret disimpan di dalam {{< glossary_tooltip term_id="etcd" >}}; dengan demikian: - Administrator harus mengaktifkan enkripsi pada rest untuk data kluster (membutuhkan versi v1.13 atau lebih) - Administrator harus membatasi akses etcd pada pengguna dengan kapabilitas admin - Administrator bisa saja menghapus data disk yang sudah tidak lagi digunakan oleh etcd - Jika etcd dijalankan di dalam kluster, administrator harus memastikan SSL/TLS digunakan pada proses komunikasi peer-to-peer etcd. - Jika kamu melakukan konfigurasi melalui sebuah _file_ manifest (JSON or YAML) yang menyimpan data secret dalam bentuk base64, membagi atau menyimpan secret ini dalam repositori kode sumber sama artinya dengan memberikan informasi mengenai data secret. Mekanisme _encoding_ base64 bukanlah merupakan teknik enkripsi dan nilainya dianggap sama saja dengan _plain text_. - Aplikasi masih harus melindungi _value_ dari secret setelah membaca nilainya dari suatu volume dengan demikian risiko terjadinya _logging_ secret secara tidak engaja dapat dihindari. - Seorang pengguna yang dapat membuat suatu pod yang menggunakan secret, juga dapat melihat _value_ secret. Bahkan apabila _policy_ apiserver tidak memberikan kapabilitas untuk membaca objek secret, pengguna dapat menjalankan pod yang mengekspos secret. - Saat ini, semua orang dengan akses _root_ pada node dapat membaca secret _apapun_ dari apiserver, dengan cara meniru kubelet. Meskipun begitu, terdapat fitur yang direncanakan pada rilis selanjutnya yang memungkinkan pengiriman secret hanya dapat mengirimkan secret pada node yang membutuhkan secret tersebut untuk membatasi adanya eksploitasi akses _root_ pada node ini. {{% capture whatsnext %}} {{% /capture %}}