Secret
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 Pod atau di dalam container image.
Silahkan lihat Dokumen desain Secret untuk informasi yang sifatnya mendetail.
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 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 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
.
# Buatlah berkas 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.
kubectl create secret generic db-user-pass --from-file=./username.txt --from-file=./password.txt
secret "db-user-pass" created
$
, \*
, 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
).
Kamu dapat memastikan apakah suatu Secret sudah dibuat atau belum dengan menggunakan perintah:
kubectl get secrets
NAME TYPE DATA AGE
db-user-pass Opaque 2 51s
kubectl describe secrets/db-user-pass
Name: db-user-pass
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
password.txt: 12 bytes
username.txt: 5 bytes
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.
Kamu dapat membaca bagaimana cara melakukan decode sebuah 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 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:
echo -n 'admin' | base64
YWRtaW4=
echo -n '1f2d1e2e67df' | base64
MWYyZDFlMmU2N2Rm
Buatlah sebuah Secret yang memiliki bentuk sebagai berikut:
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: YWRtaW4=
password: MWYyZDFlMmU2N2Rm
Kemudian buatlah Secret menggunakan perintah kubectl apply
:
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:
apiUrl: "https://my.api.com/api/v1"
username: "user"
password: "password"
Kamu dapat menyimpan Secret ini dengan menggunakan cara berikut:
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:
kubectl get secret mysecret -o yaml
Keluaran yang diberikan kurang lebih akan ditampilkan sebagai berikut:
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:
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: YWRtaW4=
stringData:
username: administrator
Akan menghasilkan Secret sebagai berikut:
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
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
# Membuat sebuah berkas kustomization.yaml dengan SecretGenerator
cat <<EOF >./kustomization.yaml
secretGenerator:
- name: db-user-pass
files:
- username.txt
- password.txt
EOF
Gunakan direktori kustomization untuk membuat objek Secret yang diinginkan.
$ kubectl apply -k .
secret/db-user-pass-96mffmfh4k created
Kamu dapat memastikan Secret tersebut sudah dibuat dengan menggunakan perintah berikut:
$ 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: <none>
Annotations: <none>
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
# Membuat sebuah berkas kustomization.yaml dengan menggunakan SecretGenerator
$ cat <<EOF >./kustomization.yaml
secretGenerator:
- name: db-user-pass
literals:
- username=admin
- password=secret
EOF
Aplikasikan direktori kustomization untuk membuat objek Secret.
$ kubectl apply -k .
secret/db-user-pass-dddghtt9b5 created
Melakukan Proses Decode pada Secret
Secret dapat dibaca dengan menggunakan perintah kubectl get secret
.
Misalnya saja, untuk membaca Secret yang dibuat pada bagian sebelumya:
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:
echo 'MWYyZDFlMmU2N2Rm' | base64 --decode
1f2d1e2e67df
Menggunakan Secret
Secret dapat di-mount sebagai volume data atau dapat diekspos sebagai variabel-variabel environment 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:
- Buatlah sebuah Secret, atau gunakan sebuah Secret yang sudah kamu buat sebelumnya. Beberapa Pod dapat merujuk pada sebuah Secret yang sama.
- 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. - 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. - 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 padamountPath
.
Berikut merupakan salah satu contoh dimana sebuah Pod melakukan proses mount Secret pada sebuah volume:
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:
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:
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.
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:
ls /etc/foo/
username
password
cat /etc/foo/username
admin
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).
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).
Menggunakan Secret sebagai Variabel Environment
Berikut merupakan langkah-langkah yang harus kamu terapkan, untuk menggunakan secret sebagai variabel _environment_ pada sebuah Pod:
- Buatlah sebuah Secret, atau gunakan sebuah Secret yang sudah kamu buat sebelumnya. Beberapa Pod dapat merujuk pada sebuah Secret yang sama.
- 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
. - 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:
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:
echo $SECRET_USERNAME
admin
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
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 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 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 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.
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:
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
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:
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:
/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
kubectl create secret generic prod-db-secret --from-literal=username=produser --from-literal=password=Y4nys7f11
secret "prod-db-secret" created
kubectl create secret generic test-db-secret --from-literal=username=testuser --from-literal=password=iluvtests
secret "test-db-secret" created
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:
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
).
Kemudian buatlah Pod-Pod yang dibutuhkan:
$ cat <<EOF > 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
$ cat <<EOF >> kustomization.yaml
resources:
- pod.yaml
EOF
Terapkan semua perubahan pada objek-objek tadi ke Apiserver dengan menggunakan
kubectl apply --k .
Kedua container kemudian akan memiliki file-file berikut ini di dalam filesystem keduanya dengan value sebagai berikut untuk masing-masing environment:
/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:
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:
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
.
ls -l
;
kamu harus menggunakan perintah ls -la
untuk melihat file-file tadi dari sebuah direktori.
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 seperti 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
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"
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.
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.
Kubernetes v1.13 [beta]
Kamu dapat mengaktifkan enkripsi pada rest untuk data secret, sehingga secret yang ada tidak akan ditulis ke dalam etcd dalam keadaan tidak terenkripsi.
Resiko
- Pada API server, data secret disimpan di dalam 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.