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

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

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:

  1. Buatlah sebuah Secret, atau gunakan sebuah Secret yang sudah kamu buat sebelumnya. Beberapa Pod dapat merujuk pada sebuah Secret yang sama.
  2. 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.
  3. 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.
  4. 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:

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 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:

  1. Buatlah sebuah Secret, atau gunakan sebuah Secret yang sudah kamu buat sebelumnya. Beberapa Pod dapat merujuk pada sebuah Secret yang sama.
  2. 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.
  3. 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

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: registry.k8s.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.

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.

FEATURE STATE: 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.

Selanjutnya

Last modified December 15, 2024 at 6:24 PM PST: Merge pull request #49087 from Arhell/es-link (2c4497f)