
سلام به همه رفقای فعال در حوزه زیرساخت و بچه های دواپس! امروز میخوام داستان یه ماجراجویی واقعی رو براتون تعریف کنم. داستانی که توش با غولهای هفتسر کوبرنتیز جنگیدیم، تو باتلاقهای DNS گیر کردیم و آخرش تونستیم پرچم پیروزی رو روی قلهی یک سیستم SSO (Single Sign-On) کامل بکوبیم. اگه شما هم تو کلاستر کوبرنتیزتون با مدیریت کاربرها و دسترسیها سر و کله میزنید و از کپی کردن فایل admin.conf برای هر نفر خسته شدید، این مقاله خوراک خودتونه.
هدف ما خیلی ساده و مشخص بود: میخواستیم یه سیستم بسازیم که هر کاربری، چه ادمین باشه چه توسعهدهنده، با یه یوزرنیم و پسورد واحد بتونه هم با kubectl به کلاستر وصل بشه و هم وارد داشبورد Argo CD بشه، اونم دقیقاً با سطح دسترسیای که براش تعریف شده.
ابزارهای ما برای این نبرد:
کوبرنتیز (Kubernetes): زمین اصلی بازی.
Keycloak: قلعهی فرماندهی ما برای مدیریت هویت و کاربران (OIDC).
Argo CD: ماشین جنگی ما برای پیادهسازی GitOps.
Traefik: دروازهبان کلاستر ما برای مدیریت ترافیک ورودی (Ingress).
Cert-Manager: جادوگر ما برای صدور خودکار گواهینامههای TLS.
نتیجه نهایی: ورود موفق کاربر admin@gmail.com با گروه admins به داشبورد Argo CD
اولین قدم، راهاندازی یه نمونه سالم از Keycloak بود. ما این کار رو با چارت Helm انجام دادیم. اما اینجا بود که اولین غول از خواب بیدار شد...
بعد از نصب، به جای دیدن صفحه زیبای Keycloak، با خطای ترسناک net::ERR_CERT_AUTHORITY_INVALID تو مرورگر مواجه شدیم. این یعنی Traefik داشت از گواهینامه پیشفرض خودش استفاده میکرد.
مسیر عیبیابی: ما مثل کارآگاهها، زنجیره رو دنبال کردیم:
kubectl describe certificate ...: دیدیم که وضعیتش Issuing مونده.
kubectl describe order ...: دیدیم که یه Order ساخته شده ولی جلوتر نرفته.
kubectl describe challenge ...: اینجا بود که به سرنخ اصلی رسیدیم. چالش DNS-01 در وضعیت pending گیر کرده بود و لاگهای cert-manager میگفت propagation check failed.
راهحل: بعد از کلی بررسی، دو تا مشکل پیدا شد: اول اینکه دسترسی توکن API ما تو Cloudflare اشتباه بود (Zone:Read کافی نبود و باید Zone:Zone:Read میبود). دوم اینکه DNS سرور داخلی کلاستر ما نمیتونست رکوردهای اعتبارسنجی Let's Encrypt رو ببینه. بعد از اصلاح این دو مورد، بالاخره قفل سبز 🔒 رو دیدیم!
فکر کردیم سختترین قسمت تموم شده، اما پاد Keycloak شروع کرد به CrashLoopBackOff. با دستور kubectl logs keycloak-0 -n keycloak --previous به لاگها نگاه کردیم و با این صحنه مواجه شدیم: FATAL: password authentication failed.
راهحل: مشکل این بود که helm uninstall به صورت پیشفرض Persistent Volume Claim (PVC) رو پاک نمیکنه. دیتابیس ما با یه رمز عبور تصادفی از نصب قبلی بالا میاومد. راهحل نهایی، حذف کامل Helm release، بعد حذف دستی PVC (kubectl delete pvc data-keycloak-postgresql-0 -n keycloak) و در نهایت نصب مجدد با رمزهای عبور صحیح بود که این بار به صورت امن از طریق Kubernetes Secret مدیریتشون کردیم.
بالاخره Keycloak بالا اومد، اما کنسول ادمینش با خطای somethingWentWrong باز نمیشد. با باز کردن کنسول مرورگر، متوجه شدیم که صفحه https داره سعی میکنه فایلهای http رو لود کنه.
راهحل: بعد از کلی آزمون و خطا با متغیرهای محیطی مختلف، راهحل نهایی استفاده از متغیر KC_PROXY_HEADERS=xforwarded بود. این متغیر به Keycloak میگه که به هدرهای X-Forwarded-* که از Traefik میاد اعتماد کنه و بفهمه که پشت یه پراکسی امن قرار داره.
فایل keycloak-values.yaml نهایی ما این شکلی شد:
# keycloak-values.yaml auth: adminUser: "admin" adminPassword: "SomePassword123!" extraEnvVars: - name: KC_PROXY_HEADERS value: "xforwarded" - name: KC_HOSTNAME value: "auth.company.com" - name: KC_HOSTNAME_ADMIN_URL value: "https://auth.company.com" postgresql: enabled: true auth: existingSecret: "keycloak-db-credentials" # ... و بقیه تنظیمات
حالا که قلعهمون آماده بود، وقتش بود که سربازها (کاربرها) رو برای ورود به کلاستر مجهز کنیم.
در Keycloak، یک کلاینت جدید به نام kubernetes ساختیم. مهمترین قسمت، ساختن یک Client Scope سفارشی به نام groups-scope و اضافه کردن Group Membership Mapper به اون بود. این کار باعث میشد که Keycloak اسم گروههای کاربر رو توی توکن هویتی قرار بده.
ب) تعریف قوانین دسترسی در کوبرنتیز (RBAC)
با دو تا فایل YAML ساده، قوانین بازی رو مشخص کردیم:
# admins-binding.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: oidc-cluster-admins subjects: - kind: Group name: "admins" apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: cluster-admin apiGroup: rbac.authorization.k8s.io
این فایل گروه admins رو به ادمین کل کلاستر تبدیل میکرد. یه فایل مشابه هم برای گروه developers ساختیم که اونها رو به یه ClusterRole فقط-خواندنی متصل میکرد.
این حساسترین مرحله بود. ما باید به kube-apiserver میگفتیم که به Keycloak اعتماد کنه. برای سرعت بیشتر، ما مستقیماً فایل مانیفست پاد استاتیک رو ویرایش کردیم:
vim /etc/kubernetes/manifests/kube-apiserver.yaml
و این فلگها رو بهش اضافه کردیم:
- --oidc-issuer-url=https://auth.company.com/realms/kubernetes - --oidc-client-id=kubernetes - --oidc-username-claim=email - --oidc-groups-claim=groups - --oidc-ca-file=/etc/kubernetes/ssl/keycloak-ca.crt
چالش: بعد از این تغییر، API Server کلاً از کار افتاد! با دستور crictl logs <container-id> فهمیدیم که مشکل از خط آخر (oidc-ca-file) بود: no such file or directory. فایل CA ما در مسیری بود که داخل کانتینر mount نشده بود. با انتقال فایل به مسیر /etc/kubernetes/ssl/ و اصلاح مسیر در مانیفست، این مشکل هم حل شد.
برای کاربرهامون یه فایل kubeconfig جدید ساختیم که به جای سرتیفیکیت، حاوی یه بلوک exec بود. این بلوک به kubectl میگفت که برای لاگین، ابزار kubelogin رو اجرا کنه.
# kubeconfig-sample-user.yaml users: - name: a.admin user: exec: apiVersion: client.authentication.k8s.io/v1beta1 command: kubelogin args: - get-token - --oidc-issuer-url=https://auth.company.com/realms/kubernetes - --oidc-client-id=kubernetes - --oidc-client-secret=...
بعد از کلی کلنجار رفتن با نسخههای قدیمی kubelogin و مشکلات PATH در سمت کلاینت، بالاخره اولین لاگین موفق انجام شد و دستور kubectl get nodes جواب داد!
حالا که kubectl رام شده بود، نوبت به Argo CD رسید.
فایل argocd-values.yaml نهایی ما این شکلی شد:
# argocd-values.yaml (بخشهای کلیدی) server: ingress: enabled: true hostname: gitops.company.com # ... configs: cm: url: https://gitops.company.com oidc.config: | name: Login with Keycloak issuer: https://auth.company.com/realms/kubernetes clientID: argocd clientSecret: $oidc.argocd.clientSecret requestedScopes: - openid - profile - email - groups-scope # <-- نکته کلیدی! rbac: policy.csv: | g, admins, role:admin g, developers, role:readonly secret: extra: oidc.argocd.clientSecret: <Your-ArgoCD-Client-Secret>
آخرین غول: DNS داخلی کلاستر
بعد از نصب، با کلیک روی دکمه لاگین، با خطای no such host مواجه شدیم. پاد argocd-server نمیتونست آدرس عمومی Keycloak رو پیدا کنه.
راهحل نهایی و قطعی: ما مشکل رو به صورت ریشهای در CoreDNS حل کردیم. با kubectl edit configmap coredns -n kube-system، یک بلوک hosts به Corefile اضافه کردیم:
hosts { 10.233.31.174 auth.company.com # IP سرویس Traefik fallthrough }
این کار به تمام کلاستر یاد داد که چطور آدرس عمومی Keycloak رو پیدا کنه.
این سفر پرماجرا، نمونهای واقعی از چالشهای پیادهسازی یک زیرساخت مدرن بود. نتیجه نهایی، یک سیستم مدیریت هویت یکپارچه و یک زیرساخت GitOps قابل اتکا و آماده برای محیط پروداکشن است. امیدوارم این داستان و جزئیات فنی اون، به شما در ماجراجوییهای خودتون در دنیای کوبرنتیز کمک کنه.