Chuyển tới nội dung chính

Đăng nhập cluster bằng SSO

Kubernetes không có cơ sở dữ liệu người dùng — không có object User, không có lệnh tạo user. API server chỉ biết cách xác minh danh tính do Identity Provider (IdP) của bạn cung cấp.

Luồng xác thực: bạn (kubectl + kubelogin) đăng nhập → IdP (Keycloak/Azure AD/Okta) trả về ID token → bạn gửi token đó thẳng tới API server của cluster → API server verify chữ ký qua JWKS của IdP → Kubernetes RBAC quyết định bạn được làm gì.

ghi chú

Token đi thẳng từ bạn tới API server — IdP không nằm trong đường đi của request. Nếu IdP tạm ngừng hoạt động, token đang cache vẫn dùng được tới khi hết hạn, cluster vẫn chạy bình thường.

cảnh báo

Đăng nhập thành công không đồng nghĩa với việc có quyền làm bất cứ điều gì. Người dùng đã xác thực nhưng chưa có RoleBinding sẽ nhận 403 Forbidden khi chạy lệnh như kubectl get pods. Đây là hành vi đúng theo thiết kế, không phải lỗi cấu hình.

Ba tầng phải phân biệt​

Để chạy được workload GPU cho team, có 3 tầng riêng biệt — nhầm lẫn giữa các tầng này là nguồn gốc phổ biến nhất gây bối rối:

TầngTrả lời câu hỏiCấu hình qua
Xác thực (Authentication)Bạn là ai?OIDC (Issuer URL, Client ID) — Cluster Admin cấu hình 1 lần
Phân quyền (Authorization)Bạn được làm gì?Kubernetes RBAC (RoleBinding / ClusterRoleBinding) — trang này
Hạn ngạch (Quota)Bạn dùng được bao nhiêu?ResourceQuota / LimitRange (CPU, RAM, và requests.nvidia.com/gpu) — trang này

Một user có thể xác thực thành công nhưng vẫn bị chặn bởi 1 trong 2 tầng còn lại: có quyền trên namespace nhưng hết quota sẽ gặp lỗi scheduling, không phải lỗi phân quyền.

Trước khi bắt đầu​

MụcYêu cầu
ClusterAdmin đã bật xác thực OIDC — xem Configure OIDC authentication for the cluster.
Công cụĐã cài kubectl và kubelogin trên máy bạn.
MạngMáy bạn truy cập được cả API server của cluster và IdP.
HTTPSIdP có chứng chỉ hợp lệ — nếu dùng CA nội bộ, cần nạp CA đó vào máy bạn.
Tài khoảnUsername/password trên IdP, đã được thêm vào nhóm phù hợp.

Bạn cũng cần 4 thông tin từ quản trị viên cluster — không có đủ 4 thứ này thì không tạo được kubeconfig:

Thông tinDùng để
API server URLTrường server trong kubeconfig.
CA certificate của clusterTrường certificate-authority-data.
Issuer URL (IdP)Chính là giá trị Issuer URL admin đã điền lúc cấu hình OIDC trên Portal.
Client IDChính là giá trị Client ID admin đã điền lúc cấu hình OIDC trên Portal.

Cài đặt kubelogin​

Cài krew (trình quản lý plugin của kubectl)​

Xem hướng dẫn cài krew chính thức tại krew.sigs.k8s.io.

Thêm vào PATH​

echo 'export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
# Using zsh: replace ~/.bashrc with ~/.zshrc

# Confirm
kubectl krew version
cảnh báo

PATH khai báo trong ~/.bashrc chỉ áp dụng cho shell tương tác. Lệnh chạy qua ssh host 'kubectl ...' hoặc trong script không nạp file này. Những trường hợp đó phải tự thêm: export PATH="$HOME/.krew/bin:$PATH"; kubectl get nodes.

Cài plugin oidc-login​

kubectl krew install oidc-login

# Upgrade later
kubectl krew upgrade oidc-login

# See installed version
kubectl krew list

Kiểm tra cài đặt​

# 1. Does the binary run?
kubectl oidc-login --version
# kubelogin version v1.36.4

# 2. Does kubectl recognize the plugin?
kubectl plugin list
# /root/.krew/bin/kubectl-oidc_login

# 3. Can you reach the IdP? (replace with your real issuer URL)
curl -sI https://sso.example.com/realms/kubernetes/.well-known/openid-configuration | head -1
# HTTP/2 200
Lỗi khi càiNguyên nhânCách sửa
unknown command "krew"Chưa chạy Bước 1, hoặc bỏ qua Bước 2Thêm ~/.krew/bin vào PATH rồi source ~/.bashrc
unknown command "oidc-login"Plugin chưa cài, hoặc PATH chưa có ~/.krew/binkubectl krew list để kiểm tra, rồi xem lại Bước 2
Chạy tay được, qua SSH thì không~/.bashrc không nạp trong shell phi tương tácThêm export PATH="$HOME/.krew/bin:$PATH" vào đầu lệnh
Phiên bản kubelogin quá cũCài từ lâu chưa cập nhậtkubectl krew upgrade oidc-login

Tạo kubeconfig cho người dùng​

kubeconfig OIDC không chứa mật khẩu hay token. Nó chỉ mô tả cách gọi kubelogin để lấy token. Nhờ vậy một file duy nhất phát được cho cả team — mỗi người đăng nhập bằng tài khoản riêng và nhận đúng quyền của mình.

File mẫu đầy đủ​

apiVersion: v1
kind: Config
current-context: my-gpu-cluster-sso

clusters:
- name: my-gpu-cluster
cluster:
server: https://api.my-gpu-cluster.example.com
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0t...

contexts:
- name: my-gpu-cluster-sso
context:
cluster: my-gpu-cluster
user: sso-user
namespace: ml-training

users:
- name: sso-user
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
command: kubectl
args:
- oidc-login
- get-token
- --oidc-issuer-url=https://sso.example.com/realms/kubernetes
- --oidc-client-id=kubernetes
- --oidc-extra-scope=email
- --oidc-extra-scope=profile
# Machine with no browser: add the 2 lines below
# - --grant-type=device-code
# - --skip-open-browser
interactiveMode: IfAvailable
installHint: |
kubelogin isn't installed. Install it with:
kubectl krew install oidc-login
mẹo

--oidc-issuer-url và --oidc-client-id chính là 2 giá trị Issuer URL và Client ID mà quản trị viên cluster đã cấu hình trên Portal (xem Configure OIDC authentication for the cluster) — xin đúng 2 giá trị này từ quản trị viên cluster của bạn.

Cấu trúc file — ba khối và cách chúng nối với nhau​

clusters ──┐ which cluster (address + CA)
├── contexts ── links a cluster to a user, plus a default namespace
users ──┘ how to log in

current-context chỉ ra context nào đang được dùng. Một file có thể chứa nhiều cụm và nhiều context — chuyển qua lại bằng kubectl config use-context.

Khối clusters:

TrườngÝ nghĩa
serverĐịa chỉ HTTPS của API server. Xin từ quản trị viên.
certificate-authority-dataCA của cụm, dạng base64. Dùng để xác minh chứng chỉ API server — không phải bí mật.
certificate-authorityThay thế cho trường trên, trỏ tới đường dẫn file .crt. Chỉ dùng một trong hai.
insecure-skip-tls-verifyTránh dùng. Bỏ qua kiểm tra chứng chỉ — chỉ dùng trong lab dùng một lần.

Khối contexts:

TrườngÝ nghĩa
clusterTên cụm, phải khớp một mục trong clusters.
userTên người dùng, phải khớp một mục trong users.
namespaceNamespace mặc định khi không truyền -n.

Giải thích từng tham số trong khối exec​

Đây là phần quyết định cách đăng nhập. kubectl chạy lệnh trong command kèm args, rồi đọc token từ đầu ra của nó.

Các trường của exec​

TrườngGiá trịÝ nghĩa
apiVersionclient.authentication.k8s.io/v1beta1Phiên bản giao thức exec credential
commandkubectlLệnh được gọi (vì kubelogin là plugin)
argsdanh sáchTham số truyền cho lệnh
interactiveModeIfAvailableKhi nào được phép chiếm terminal
installHintvăn bảnThông báo hiện ra khi máy chưa cài kubelogin

Hai tham số bắt buộc​

Tham sốÝ nghĩa
oidc-login get-tokenTên plugin + lệnh con, phải đứng đầu và đúng thứ tự
--oidc-issuer-urlĐịa chỉ IdP — phải khớp chính xác từng ký tự với cấu hình trên cụm
--oidc-client-idĐịnh danh ứng dụng đã đăng ký trên IdP, cũng là aud mà API server kiểm tra

Tham số tùy chọn hay dùng​

Tham sốKhi nào cần
--oidc-client-secretChỉ khi client là confidential
--oidc-extra-scopeXin thêm scope, thường email và profile
--grant-typeauto (mặc định), authcode, device-code, password
--skip-open-browserMáy không có trình duyệt
--certificate-authorityCA của IdP (khác CA của cụm)
--force-refreshBỏ qua cache, lấy token mới — hữu ích sau khi đổi nhóm
-v=1 … -v=5Mức log, -v=5 để chẩn đoán lỗi
cảnh báo

Đừng thêm --oidc-extra-scope=groups. Thông tin nhóm đến từ protocol mapper mà quản trị viên IdP cấu hình, không phải từ scope. Xin scope groups khi IdP không định nghĩa sẽ lỗi invalid_scope.

Cơ chế cache token​

Lần đầu chạy, kubelogin lưu token vào ~/.kube/cache/oidc-login. Các lần sau đọc cache, tự refresh, chỉ bắt đăng nhập lại khi refresh token hết hạn.

# Clear the cache when you need to force a re-login (required after a group change)
rm -rf ~/.kube/cache/oidc-login # Linux/macOS
Remove-Item -Recurse -Force "$env:USERPROFILE\.kube\cache\oidc-login" # Windows

Xác nhận đăng nhập thành công​

kubectl auth whoami
ATTRIBUTE VALUE
Username oidc:nguyenvana
Groups [oidc:ml-team system:authenticated]

Thấy đúng tên và đúng nhóm nghĩa là tầng xác thực đã xong. Nếu tiếp theo gặp 403, đó là chuyện của phân quyền, xem Gán quyền Kubernetes.

Máy không có trình duyệt​

Jump host, server, container — nơi không mở được trình duyệt. Dùng kubeconfig mặc định ở đây sẽ treo rồi báo context deadline exceeded.

  • Device code (khuyến nghị): thêm 2 dòng vào args:

    - --grant-type=device-code
    - --skip-open-browser

    Terminal in ra link kèm mã sẵn, mở trên điện thoại/laptop khác để đăng nhập, quay lại terminal chờ vài giây là xong. Mã sống 10 phút.

  • SSH port forwarding: giữ nguyên kubeconfig, chạy ssh -L 8000:localhost:8000 -L 18000:localhost:18000 user@jumphost, rồi chạy kubectl trên jump host bình thường.

  • Authcode keyboard: --grant-type=authcode-keyboard — mở URL, đăng nhập, dán mã trả về vào terminal.

  • Password grant: --grant-type=password --username=... — không dùng được nếu tài khoản bật MFA, đã deprecated, chỉ nên dùng test nhanh.

ghi chú

CI/CD đừng dùng đăng nhập OIDC — dùng ServiceAccount token (xem use case 7 bên dưới).

Gán quyền Kubernetes​

Quy tắc ánh xạ​

Quản trị viên cụm cấu hình một prefix (chính là Username Prefix / Groups Prefix đã điền ở mục Advanced khi cấu hình OIDC trên Portal) được cộng vào mọi danh tính đến từ IdP:

Trên IdPClaim trong tokenTên dùng trong RBAC
User nguyenvanapreferred_usernameoidc:nguyenvana
Group ml-teamgroups[]oidc:ml-team

Không chắc prefix là gì? Chạy kubectl auth whoami — giá trị hiện ra chính là tên phải dùng trong RBAC.

mẹo

Luôn phân quyền theo Group, không theo User. Gán cho kind: User nghĩa là mỗi lần có người vào/rời team phải sửa RBAC trên từng cụm. Gán cho kind: Group thì chỉ cần thêm/bớt thành viên trên IdP. Ngoại lệ duy nhất là tài khoản break-glass.

Bốn ClusterRole dựng sẵn​

ClusterRoleQuyềnDùng cho
cluster-adminToàn quyền, mọi namespaceQuản trị hạ tầng
adminToàn quyền trong 1 namespace, gồm cả tạo RoleBindingTrưởng nhóm MLOps
editSửa tài nguyên, không sửa được RBACData scientist, ML engineer chạy training/inference
viewChỉ đọc, không đọc được SecretNgười review, giám sát

ClusterRoleBinding cấp quyền toàn cụm; RoleBinding giới hạn 1 namespace (có thể tham chiếu tới ClusterRole).

8 use case phổ biến​

Quản trị viên toàn cụm​

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: sso-cluster-admins
subjects:
- kind: Group
name: "oidc:gpu-admins"
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io

Chỉ đọc toàn cụm​

Giống use case 1 nhưng roleRef.name: view. ClusterRole view cố tình không cho đọc Secret.

Team sở hữu namespace riêng​

Trưởng nhóm ML quyền admin, data scientist/ML engineer quyền edit:

# ML team lead
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: ml-training-leads
namespace: ml-training
subjects:
- kind: Group
name: "oidc:ml-team-lead"
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: admin
apiGroup: rbac.authorization.k8s.io
---
# Data scientists / ML engineers
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: ml-training-users
namespace: ml-training
subjects:
- kind: Group
name: "oidc:ml-team"
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: edit
apiGroup: rbac.authorization.k8s.io

Cho phép xem danh sách namespace​

Namespace là tài nguyên cấp cụm, RoleBinding không đủ:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: namespace-lister
rules:
- apiGroups: [""]
resources: ["namespaces"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: all-users-list-namespaces
subjects:
- kind: Group
name: "system:authenticated"
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: namespace-lister
apiGroup: rbac.authorization.k8s.io
cảnh báo

Điều này để lộ tên mọi namespace cho mọi người đã đăng nhập.

Nhiều team dùng chung cụm, có hạn ngạch GPU​

RBAC không giới hạn dùng bao nhiêu — cần thêm ResourceQuota cho GPU:

apiVersion: v1
kind: ResourceQuota
metadata:
name: ml-team-quota
namespace: ml-training
spec:
hard:
requests.cpu: "40"
requests.memory: 160Gi
requests.nvidia.com/gpu: "8"
persistentvolumeclaims: "20"
count/deployments.apps: "50"
cảnh báo

Quota GPU là bắt buộc khi nhiều team dùng chung cluster — không đặt thì 1 team có thể chiếm hết GPU bằng 1 Deployment.

Quyền hẹp trên một loại tài nguyên​

Ví dụ: đội trực ca xem log/restart pod training, không sửa Deployment/đọc Secret:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: oncall-responder
rules:
- apiGroups: [""]
resources: ["pods", "pods/log", "events"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["delete"] # restart = delete the pod, the ReplicaSet recreates it
- apiGroups: ["apps"]
resources: ["deployments", "statefulsets"]
verbs: ["get", "list"]

CI/CD dùng ServiceAccount, không dùng SSO​

Cho pipeline train/deploy model chạy theo lịch:

kubectl -n ml-training create serviceaccount ci-deployer
kubectl -n ml-training create rolebinding ci-deployer \
--clusterrole=edit --serviceaccount=ml-training:ci-deployer
kubectl -n ml-training create token ci-deployer --duration=24h

Từ Kubernetes 1.24, ServiceAccount không tự sinh Secret chứa token vĩnh viễn nữa — token sinh bằng create token có thời hạn, an toàn hơn.

Thu hồi quyền khi nhân sự thay đổi​

Tình huốngThao tácHiệu lực
Chuyển teamĐổi group trên IdPLần làm mới token kế tiếp
Nghỉ việcVô hiệu hóa tài khoản trên IdPTối đa bằng token lifetime
Cần chặn ngay lập tứcVô hiệu hóa tài khoản và xóa sessionTức thì với phiên mới
cảnh báo

Token đã phát ra không thu hồi được — giới hạn cố hữu của bearer token. Cửa sổ rủi ro đúng bằng thời gian sống của token. Với hệ thống nhạy cảm, đề nghị đặt 5–10 phút.

Chẩn đoán lỗi​

Phân biệt 401 và 403 — bước đầu tiên luôn là bước này​

MãNghĩaTầng hỏngAi xử lý
401UnauthorizedXác thực — token không được chấp nhậnQuản trị viên cụm / IdP
403ForbiddenPhân quyền — token hợp lệ, thiếu quyềnQuản trị viên cụm (RBAC)
cảnh báo

Đừng sửa RBAC khi đang gặp 401. 401 nghĩa là API server chưa hề công nhận danh tính của bạn — thêm bao nhiêu RoleBinding cũng vô ích.

Bảng tra lỗi thường gặp​

Triệu chứngNguyên nhânCách sửa
401 dù đăng nhập thành côngIssuer URL trong kubeconfig lệch với cấu hình cụm; hoặc cụm chưa bật OIDCĐối chiếu từng ký tự issuer URL; liên hệ quản trị viên cụm
403, whoami đúng tênChưa có RoleBinding, hoặc tên group trong RBAC không khớpGửi output kubectl auth whoami cho quản trị viên
Groups rỗng trong whoamiIdP chưa cấu hình mapper đẩy nhóm vào tokenLiên hệ quản trị viên IdP
context deadline exceededkubelogin chờ trình duyệt nhưng chạy phi tương tácDùng --grant-type=device-code
invalid_scopeXin scope mà IdP không định nghĩaBỏ --oidc-extra-scope=groups
Whoami ra danh tính lạBiến KUBECONFIG chưa đổiDùng $env:KUBECONFIG, hoặc truyền --kubeconfig
Đổi group mà quyền chưa đổiToken cũ còn trong cacheXóa ~/.kube/cache/oidc-login
invalid_grantSai mật khẩu, hoặc tài khoản bị vô hiệu hóaKiểm tra tài khoản trên IdP
x509: certificate signed by unknown authorityIdP dùng CA nội bộ chưa được tin cậyThêm --certificate-authority=/đường/dẫn/ca.crt
address already in useCổng 8000 đang bị chiếmThêm --listen-address=127.0.0.1:8100, đăng ký redirect URI mới
Missing parameter: code_challenge_methodIdP bắt buộc PKCE trên device endpointĐề nghị quản trị viên gỡ ràng buộc PKCE, hoặc dùng SSH port forwarding
could not open the browser: exit status 3Không phải lỗi — máy không có trình duyệtTìm dòng "Please visit..." bên dưới, mở link đó

Ba lệnh chẩn đoán (theo thứ tự)​

# 1. Who does the API server think I am?
kubectl auth whoami

# 2. Can I do this specific thing?
kubectl auth can-i create deployments -n ml-training

# 3. See everything I can do in one namespace
kubectl auth can-i --list -n ml-training

Xem chi tiết quá trình đăng nhập​

kubectl oidc-login get-token -v=5 \
--oidc-issuer-url=https://sso.example.com/realms/kubernetes \
--oidc-client-id=kubernetes

Lấy được token nghĩa là phía IdP đã ổn, vấn đề nằm ở cụm. Không lấy được thì lỗi nằm ở IdP hoặc đường mạng tới IdP.

Checklist bảo mật​

  • Không chia sẻ kubeconfig kèm token. File kubeconfig OIDC an toàn để phát cho cả team vì không chứa bí mật — nhưng file có sẵn token: hoặc client-key-data: thì tuyệt đối không.
  • Không dùng insecure-skip-tls-verify ngoài môi trường lab dùng một lần.
  • Bật MFA cho tài khoản có quyền quản trị cụm.
  • Xóa cache token khi rời máy dùng chung: rm -rf ~/.kube/cache/oidc-login.
  • Đặt namespace mặc định trong context để tránh thao tác nhầm namespace.
  • Bắt đầu bằng quyền view. Xin nâng quyền khi có nhu cầu cụ thể, đừng xin cluster-admin cho tiện.
  • Phân quyền theo group, không theo user. Ngoại lệ duy nhất là tài khoản break-glass.
  • Luôn đặt ResourceQuota cho requests.nvidia.com/gpu khi nhiều team dùng chung cluster — không đặt thì RBAC không đủ để ngăn 1 team chiếm hết GPU.
  • Giữ kubeconfig break-glass. IdP ngừng hoạt động thì vẫn phải vào được cụm để xử lý. Cất nơi an toàn.
  • Rà soát định kỳ. Mỗi quý đối chiếu thành viên nhóm trên IdP với danh sách nhân sự thực tế.

Tài liệu này áp dụng cho cluster đã được bật xác thực OIDC qua Portal (xem Configure OIDC authentication for the cluster), với kubelogin cài qua krew trên Linux. Việc cấu hình Identity Provider và bật OIDC trên cluster thuộc trách nhiệm quản trị viên cluster — liên hệ họ để lấy 4 thông tin nêu ở mục Trước khi bắt đầu.