آموزش کوبرنتیز؛ شب نهم: 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، فایلهای موقت را حذف کنید:
برای 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
فایلهای موقت را بلافاصله پاک کنید:
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 را داشته باشند.