پرش به محتویات

آموزش کوبرنتیز؛ شب نهم: Secretها

امشب یک Secret می‌سازیم و Mount می‌کنیم، بدون آن‌که مقدارهایش را چاپ کنیم، متن ساده را در Manifest بگذاریم یا Credentialها را در Git ثبت کنیم.

Secret چه کاری می‌کند و چه کاری نمی‌کند؟

Secret یک Object در API کوبرنتیز برای نگه‌داری Byteهای حساس است. مقدارها در نمایش عادی YAML با Base64 کدگذاری می‌شوند. Base64 یک Encoding برگشت‌پذیر است، نه Encryption. محرمانگی به RBAC، رمزنگاری Etcd در حالت سکون، انتقال امن، امنیت Node، انضباط Logging و Rotation وابسته است.

هرکس بتواند یک Secret را از API با get بخواند، معمولاً می‌تواند مقدار آن را بازیابی کند. کسی که اجازهٔ ساخت Pod و Mount کردن آن Secret را دارد نیز ممکن است بتواند داده را بخواند. اصل حداقل دسترسی باید هر دو مسیر را پوشش دهد.

Manifest خوانندهٔ Secret

فایل app.yaml فقط وجود فایل‌های Credential را بررسی می‌کند و مقدارشان را نمایش نمی‌دهد:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: secret-reader
  namespace: course-night-9
spec:
  replicas: 1
  selector:
    matchLabels:
      app: secret-reader
  template:
    metadata:
      labels:
        app: secret-reader
    spec:
      automountServiceAccountToken: false
      containers:
        - name: reader
          image: busybox:1.36
          command: ["/bin/sh", "-c"]
          args:
            - |
              test -s /var/run/course/username
              test -s /var/run/course/password
              echo "Secret files exist; values were not printed"
              sleep 3600
          volumeMounts:
            - name: credentials
              mountPath: /var/run/course
              readOnly: true
      volumes:
        - name: credentials
          secret:
            secretName: app-credentials
            defaultMode: 0400

ساخت بدون قرار دادن مقدارها در تاریخچهٔ Shell

ابتدا یک دایرکتوری موقت محلی بسازید و مقدارها را تعاملی دریافت کنید:

kubectl create namespace course-night-9
tmp_dir="$(mktemp -d)"
read -r -p 'Username: ' secret_user
read -r -s -p 'Password: ' secret_password; printf '\n'
printf '%s' "$secret_user" > "$tmp_dir/username"
printf '%s' "$secret_password" > "$tmp_dir/password"
unset secret_user secret_password

kubectl create secret generic app-credentials \
  --namespace course-night-9 \
  --from-file=username="$tmp_dir/username" \
  --from-file=password="$tmp_dir/password"

پس از ساخت Secret، فایل‌های موقت را حذف کنید:

rm -f "$tmp_dir/username" "$tmp_dir/password"
rmdir "$tmp_dir"
unset tmp_dir

برای Credential واقعی از --from-literal=password=... استفاده نکنید؛ مقدار ممکن است در تاریخچهٔ Shell و فهرست Processها افشا شود.

Mount و بررسی امن

kubectl apply -f app.yaml
kubectl rollout status deployment/secret-reader --namespace course-night-9
kubectl logs deployment/secret-reader --namespace course-night-9
kubectl exec --namespace course-night-9 deployment/secret-reader -- \
  sh -c 'stat -c "%a %n" /var/run/course/*; test -s /var/run/course/username; test -s /var/run/course/password'

این بررسی‌ها بدون نمایش محتوا ثابت می‌کنند که فایل‌ها وجود دارند و خالی نیستند. Volume فقط خواندنی است و Permission فایل 0400 درخواست شده است.

Metadata را بررسی کنید، نه داده را:

kubectl get secret app-credentials --namespace course-night-9
kubectl describe secret app-credentials --namespace course-night-9
kubectl auth can-i get secrets --namespace course-night-9

describe نام کلیدها و تعداد Byteها را بدون Decode کردن مقدارها گزارش می‌کند.

Rotation

با همان الگوی ورودی امن، فایل‌های موقت تازه‌ای بسازید:

tmp_dir="$(mktemp -d)"
read -r -p 'Username: ' secret_user
read -r -s -p 'New password: ' secret_password; printf '\n'
printf '%s' "$secret_user" > "$tmp_dir/username"
printf '%s' "$secret_password" > "$tmp_dir/password"
unset secret_user secret_password

kubectl create secret generic app-credentials \
  --namespace course-night-9 \
  --from-file=username="$tmp_dir/username" \
  --from-file=password="$tmp_dir/password" \
  --dry-run=client -o yaml |
kubectl apply -f -

Secret Volumeهای Mountشده سرانجام Refresh می‌شوند، اما برنامه ممکن است Credential قدیمی را Cache کرده باشد. در این آزمایش، Restart کنترل‌شده ساده‌ترین راه است:

kubectl rollout restart deployment/secret-reader --namespace course-night-9
kubectl rollout status deployment/secret-reader --namespace course-night-9
kubectl logs deployment/secret-reader --namespace course-night-9

فایل‌های موقت را بلافاصله پاک کنید:

rm -f "$tmp_dir/username" "$tmp_dir/password"
rmdir "$tmp_dir"
unset tmp_dir

Secret Managerهای محیط تولید می‌توانند Credential صادر، Rotate و همگام کنند. Git باید فقط Reference یا شکل رمزنگاری‌شده را نگه دارد، هرگز متن ساده را نه.

تمرین خرابی: نام اشتباه Secret

موقتاً secretName را در app.yaml به missing-credentials تغییر دهید:

kubectl apply -f app.yaml
kubectl get pods --namespace course-night-9
kubectl describe pod <new-pod-name> --namespace course-night-9

Pod در مرحلهٔ ساخت Container منتظر می‌ماند، زیرا Volume اجباری قابل Mount نیست. نام را تعمیر و دوباره Apply کنید.

چک‌لیست پایان

  • Credentialها را بدون قرار دادن مقدار در Git یا Argument فرمان ساخته‌ام.
  • Mount را بدون چاپ مقدارها بررسی کرده‌ام.
  • توضیح می‌دهم چرا Base64 رمزنگاری نیست.
  • خرابی Volume ناشی از Secret گم‌شده را تعمیر کرده‌ام.
  • مستند کرده‌ام چه کسانی باید اجازهٔ خواندن یا Mount کردن این Secret را داشته باشند.
kubectl delete namespace course-night-9