별도 설정 없이도 파드 안에는 API 서버 주소와 자격증명이 들어 있다.
echo $KUBERNETES_SERVICE_HOST # 보통 10.96.0.1
echo $KUBERNETES_SERVICE_PORT # 443
ls /var/run/secrets/kubernetes.io/serviceaccount/
# ca.crt namespace token
주소는 https://kubernetes.default.svc 로도 같다. 서비스 어카운트 토큰은 투영 토큰이라 만료 시간이 있고 kubelet 이 주기적으로 파일을 갱신한다. 따라서 토큰 값을 읽어 다른 곳에 복사해 두면 얼마 뒤 만료된다. 쓸 때마다 파일에서 읽는다.
APISERVER="https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT}"
SA=/var/run/secrets/kubernetes.io/serviceaccount
curl --cacert ${SA}/ca.crt \
-H "Authorization: Bearer $(cat ${SA}/token)" \
${APISERVER}/api/v1/namespaces/$(cat ${SA}/namespace)/pods
TCP 연결은 되는데 couldn't get current server API group list 로 끊기는 경우가 있다.
E0321 memcache.go:265] couldn't get current server API group list:
Get "https://192.168.11.1:6443/api?timeout=32s": ...
포트가 열려 있다는 것과 API 호출이 된다는 것은 다르다. TLS 검증과 인증이 그 뒤에 온다. 원인은 대개 셋이다.
kubeconfig 의 서버 주소가 파드에서 닿지 않는다. 관리 노드의 admin.conf 를 그대로 복사해 온 경우 https://127.0.0.1:6443 을 가리키고 있다. 파드 안의 127.0.0.1 은 그 컨테이너 자신이다.
CA 가 다르다. 옛 kubeconfig 를 쓰고 있거나 인증서를 갱신한 뒤 배포하지 않은 경우다.
자격증명이 만료됐다. 클라이언트 인증서는 기본 1년이다.
원인을 가르려면 상세 로그를 본다.
kubectl --v=8 get nodes
grep server $HOME/.kube/config
관리자 kubeconfig 를 파드에 넣는 대신, 그 파드의 서비스 어카운트로 kubeconfig 를 만든다. CA 도 토큰도 파드가 이미 갖고 있으므로 불일치가 생기지 않는다. 토큰은 값을 박지 말고 tokenFile 로 파일을 가리켜 갱신이 반영되게 한다.
#!/bin/sh
set -e
SA=/var/run/secrets/kubernetes.io/serviceaccount
mkdir -p "$HOME/.kube"
cat > "$HOME/.kube/config" <<EOF
apiVersion: v1
kind: Config
clusters:
- name: kubernetes
cluster:
certificate-authority: ${SA}/ca.crt
server: https://kubernetes.default.svc
contexts:
- name: in-cluster
context:
cluster: kubernetes
namespace: $(cat ${SA}/namespace)
user: sa-user
current-context: in-cluster
users:
- name: sa-user
user:
tokenFile: ${SA}/token
EOF
chmod 600 "$HOME/.kube/config"
$HOME 이 비어 있는 컨테이너가 많다. 그때는 KUBECONFIG 환경 변수로 경로를 직접 지정한다.
서비스 어카운트는 기본적으로 아무 권한이 없다. 필요한 권한만 준다.
apiVersion: v1
kind: ServiceAccount
metadata:
name: pod-reader
namespace: staging
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: staging
rules:
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: pod-reader
namespace: staging
subjects:
- kind: ServiceAccount
name: pod-reader
namespace: staging
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
Role 과 RoleBinding 은 네임스페이스 안에서만 유효하고, ClusterRole 과 ClusterRoleBinding 은 클러스터 전체에 적용된다. 여러 네임스페이스를 봐야 한다면 같은 ClusterRole 을 네임스페이스마다 RoleBinding 으로 묶는 방법도 있다.
cluster-admin 을 묶으면 모든 것이 되지만, 그 파드가 탈취되면 클러스터 전체가 넘어간다. 관리 도구를 올릴 때도 실제로 필요한 동사와 리소스만 추린다. 무엇이 필요한지 모르겠으면 넓게 준 뒤 감사 로그를 보고 줄이는 편이 낫다.
권한 확인은 다음으로 한다.
kubectl auth can-i list pods --as=system:serviceaccount:staging:pod-reader -n staging
API 서버에 접근할 필요가 없는 파드에는 토큰을 아예 주지 않는다.
spec:
automountServiceAccountToken: false