k8s 에서 1.24 이전 버전에는 service account secret token이 자동으로 생성 되었지만,
이후 버전 부터는 직접 생성하고 관리해주어야 한다.
특히 토큰에는 수명주기가 있으며 이를 관리해주어야 한다.
임시 기간 토큰 생성 및 영구 생성, Refresh를 통한 토큰 관리가 있다.
1. 임시 기간 토큰 생성
kubectl create token 명령어를 통해 토큰 생성이 가능하며, --duration 옵션을 통해 시간 지정 가능하다.
duration 옵션이 없을 경우 기본적으로 1시간으로 세팅된다.
또한, 해당 명령어로 토큰을 생성할 경우 생성할 때 조회 가능하고 더 이상 생성한 토큰에 대해 조회가 불가능하다.
# 임시 계정 생성
kubectl create sa temp-sa
# create token 명령어를 통해 임시 생성
kubectl create token temp-sa --duration 30m
# result
eyJhbGciOiJSUzI1NiIsImtpZCI6ImU1Mnp2SFpHYS1RN2dXaElkV0tEZC1nemlxeTFhdWQiGE5ZC1hM2E3LTQ4OGYtOTM3OS1iYzc0MGU1ZjUyMDkifX0sIm5iZiI6MTczNDQzOTY4MCwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRlZmF1bHQ6dGVtcC1zYSJ9.Ya83wdlUMifQww_XUmK2rEfxkAMqueDT6QeYiQqwXlWYnUIq4YaCZDbo7XZz8xW3W-_efpx1-7n9dHkUMzk88LvCCS5C0S6J89hGGAI4T_nhyTfIhj4AbC_hhLdZR3b9pSuh7a0xY_CiD
{
"aud": [
"https://kubernetes.default.svc.cluster.local"
],
"exp": 1734441480,
"iat": 1734439680,
"iss": "https://kubernetes.default.svc.cluster.local",
"jti": "360425d7-587d-44ca-8633-67e5e07ac9e5",
"kubernetes.io": {
"namespace": "default",
"serviceaccount": {
"name": "temp-sa",
"uid": "34bd8a9d-a3a7-488f-9379-bc740e5f5209"
}
},
"nbf": 1734439680,
"sub": "system:serviceaccount:default:temp-sa"
}
2. 영구 토큰 생성
영구 토큰은 Service Account 생성 후 Secret을 따로 생성해주면 된다.
하지만, 권장하지는 않는다.
# 계정 생성
kubectl create sa permanent-sa
# secret 생성
vi secret.yaml
apiVersion: v1
kind: Secret
type: kubernetes.io/service-account-token
metadata:
name: permanent-sa-secret
annotations:
kubernetes.io/service-account.name: permanent-sa
# 적용
kubectl apply -f secret.yaml
# 조회
kubectl get secret
NAME TYPE DATA AGE
permanent-sa-secret kubernetes.io/service-account-token 3 4s
# 토큰 조회
kubectl describe secret apiVersion: v1
kind: Secret
type: kubernetes.io/service-account-token
metadata:
name: permanent-sa-secret
annotations:
kubernetes.io/service-account.name: permanent-apiVersion: v1
kind: Secret
type: kubernetes.io/service-account-token
metadata:
name: permanent-sa-secret
annotations:
kubernetes.io/service-account.name: permanent-sa
root@master:~/account# kubectl describe secret permanent-sa
Name: permanent-sa-secret
Namespace: default
Labels: <none>
Annotations: kubernetes.io/service-account.name: permanent-sa
kubernetes.io/service-account.uid: 349d3c79-1180-4cea-9357-5169a7a88d4c
Type: kubernetes.io/service-account-token
Data
====
token: eyJhbGciOiJSUzI1NiIsImtpZCI6ImU1Mnp2SFpHYS1RN2dXXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3V
ca.crt: 1107 bytes
namespace: 7 bytes
3. Service Account Mount를 통한 갱신
3.1 기본적인 Mount 를 통한 갱신
기본적으로 pod yaml 설정시 Service Account를 적용하게되면 1시간마다 토큰이 갱신된다. (확인하려면 1시간 후 pod 에 접속해서 확인 가능, 공식문서에서도 1시간마다 kubelet이 갱신한다고 나와 있음)
# 토큰 생성
kubectl create sa auto-sa
# deploy.yaml 내 serviceAccount 적용
...
spec:
template:
spec:
serviceAccount: auto-sa
containers:
...
...
# 토큰 조회를 위해 pod 접속
kubectl exec -it stdout-node-869cff8b95-cxqf2 -- sh
# 경로
pwd
/var/run/secrets/kubernetes.io/serviceaccount
ls
ca.crt namespace token
# 토큰 조회
# 토큰이 변경되는걸 확인하려면 1시간 후에 해당 pod 접속해서 똑같이 확인하면 됨.
cat token
eyJhbGciOiJSUzI1NiIsImtpZCI6ImU1Mnp2SFpHYS1RN2dXaElkV0tEZC1nemlxeTFMclJubTVTaFhoVkZIY2sifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNzY1OTc2NzIxLCJpYXQiOjE3MzQ0NDA3MjEsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwianRpIjoiNjQ3ZDZmYWMtODc
3.2 Volume Project 를 통한 관리
위 방법 말고 Volume Project를 통한 시간 설정 및 갱신이 가능하다.
automountServiceAccountToken 옵션을 false 로 주어야하므로 yaml로 생성 기본적으로 ServiceAccount 생성시 true 로 생성 됨.
해당 예제는 2시간으로 설정
(2시간뒤 자동 갱신됨)
# automountServiceAccountToken 옵션을 false 로 주어야하므로 yaml로 생성
# 기본적으로 ServiceAccount 생성시 true 로 생성 됨.
vi passive-sa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: passive-sa
namespace: default
automountServiceAccountToken: false
# deploy.yaml 내 serviceAccount volume project 적용
...
spec:
template:
spec:
serviceAccount: passive-sa
volumes:
- name: project-passive-sa
projected:
sources:
- serviceAccountToken:
path: passive-account-file
expirationSeconds: 7200
containers:
volumeMounts:
- mountPath: /tmp/token
name: project-passive-sa
...
# 토큰 조회를 위해 pod 접속
kubectl exec -it stdout-node-7fcc7ff87-tjfjf -- sh
# 경로
# 위에서 /tmp/token 에 설정했음
pwd
/tmp/token
# 위에서 passive-sa-file 로 설정했음
ls
passive-sa-file
# 토큰 조회
# 토큰이 변경되는걸 확인하려면 2시간으로 설정했으므로 2시간 후에 해당 pod 접속해서 똑같이 확인하면 됨.
cat passive-sa-file
eyJhbGciOiJSUzI1NiIsImtpZCI6ImU1Mnp2SFpHY
3.3 TokenRequest API 이용
API 호출용, 일반 Service Account 2개를 만들어, 하나는 Token 갱신을 위해 사용하는 방법으로 사용한다.
Containerd 에서 인증서 에러 발생시 인증서가 있는 경우와 없는 경우 설정하는 방법이다.
참고로 containerd 를 설정했다고 ctr 명령어 까지 같이 적용되는건 아니다. crictl 만 적용된다.
ctr 의 경우 인증서가 있으면 --tlscacert, --tlscrt, --tlskey 3개옵션을 적용하여 사용하면 되며
인증서가 없을 경우 --skip-verify 옵션을 사용하면된다.
1. containerd 설정파일을 수정한다.
$ vi /etc/containerd/config.toml
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = "/etc/containerd/certs.d"
2. certs.d 폴더를 생성한다.
$ cd /etc/containerd/
$ mkdir certs.d
3. 내부에 인증서 연결할 서버 폴더생성
$ cd certs.d
$ mkdir registry.wky.kr
$ cd registry.wky.kr
$ pwd
/etc/containerd/certs.d/registry.wky.kr
4. hosts.toml 파일 생성
인증서가 있는 경우.
$ vi hosts.toml
server = "https://registry.wky.kr"
[host."https://registry.wky.kr"]
capabilities = ["pull", "resolve"]
# pemfile 이 있는 경로 및 파일 입력
ca = pemfile.pem
# crt, key, pem 파일이 있는 경로 및 파일 입력
client = [[crtfile.crt, keyfile.key], [pemfile.pem]]
인증서가 없는 경우
$ vi hosts.toml
server = "https://registry.wky.kr"
[host."https://registry.wky.kr"]
capabilities = ["pull", "resolve"]
skip_verify = true
'ctr image mount imagename:tag 폴더' 명렁어를 통해 확인할 수 있다.
먼저 이미지가 서버 내에 있어야함.
$ crictl images
IMAGE TAG IMAGE ID SIZE
docker.io/library/busybox latest 63cd0d5fb10d7 1.86MB
$ mkdir busybox
$ ctr -n=k8s.io image mount docker.io/library/busybox:latest ./busybox/
sha256:66ccab9b46d8f9daffd6e24c69ab2d6c5a5b44e9d0e39991985a910a2775755e
./busybox/
$ ls busybox/
bin dev etc home lib lib64 root tmp usr var
사설 인증서를 생성하고, 사설인증서이기 때문에 해당 registry를 실제 사용할 서버를 위해 ca파일도 생성한다.
# 사설 인증서 생성
$ openssl req -newkey rsa:4096 -nodes -sha256 -keyout domain.key -subj "/CN=registry.wky.kr" -addext "subjectAltName=DNS:registry.wky.kr" -x509 -days 365 -out domain.crt
# 이미지 push 또는 pull 위한 ca 파일 생성
$ cat domain.crt domain.key > domain.pem