LinuCエヴァンジェリストの鯨井貴博@opensourcetechです。
今回はkubernetesクラスターのapi-serverにREST API(curlなど)でアクセスしてみます。
はじめに
kubernetesクラスターにおけるアクセスの認証では、通常configファイル($HOME/.kube/configなどにある)が使われています。
kubeuser@kubenewmaster1:~$ cat .kube/config apiVersion: v1 clusters: - cluster: certificate-authority-data: SL0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMvakNDQWVhZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJeU1ESXhOREV6TURVeU5Wb1hEVE15TURJeE1qRXpNRFV5TlZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBS0VGCk5HaWtvK2xNcTBadE9zQk1TNFRWZUh6VGFiNjdxYkhKVGMwYktROVRaNnZlZE1jV29vcGNvOFJYSjdzbk5TaksKN0g4WEdKQm1lV1ZnckRYZHprSU9raE50WmZkVnoyaENYWU5LczNCV0RHWXZ3T0ozSDF3ajRmTmlRakkyYXo2Ygozcnd1T1RmaTdQRnZvRlBDQjNqdEFXOHVuWGZ3eVJxaS9xVEpZcDltMUVzRGRCa0dvQzd6MXpTUzBOWERiNEdHCkRKY2ZYK3UvL0d2bCtuVXdRdURiQUtOQ2lRbEllSlJtZHlBWlVRaXYyTDJMYy9rRkdUMTVNbVVvMW8wVzFaTG8KVXFWZW96QVRsZk4xbXpEM2FzV1VpS29WK05waGwzMTNBSWpZYWRkU0wrREkxNVd5YWJxdHBHY2YweUhJcEZmZQp0YmpvUjU2SHIyNS90OWN5QzVVQ0F3RUFBYU5aTUZjd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0hRWURWUjBPQkJZRUZLYzVuL0pKSXBzdy9zQnJzSWR3N0JKZFRScmFNQlVHQTFVZEVRUU8KTUF5Q0NtdDFZbVZ5Ym1WMFpYTXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBRU9HajZTa256R21FNTZ2LzBWcAo2eDlva08yZDM5eEgzODEzM1RZeU5kU0d4cU0xSFdNVWlBaHV3ZG5KSU1PQlZtczNHS2g4WFFiSjdlSTlqdjZUCktMbnp0dFhxWVcwOERrYlZtOEM5SmQyRUoxVnlQVEZZdXRHSitXSVlKdHk2QlBEV2g3cU1VeTZVMm5VKzZQbEYKNVpmSDcwNEF1R0YwS1JIME5sVTJ3SVIvUHI0NnljMFo4WktlRUdOdzBPcHRPbysxMmtiVmkzYVBxQkVLbkVrTwp0N3cvL2k0RFVxbzJ5RGU4NkRHS2xIOSttMWJ3ZWpVUEJZM2xDUnRrMW5LRWExY0YvaXdUUW51cjVkZmxvZzhFCm4zUEw5OXNEMnVUSmhJMUR3L3hwdldKRG9mb1hJeHpiejd0VHBEejNrV1NUOFU4VjNpRmNPa2oreCtuem5XMFEKN0ZrPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== server: https://kubenewmaster1:6443 name: kubernetes contexts: - context: cluster: kubernetes user: kubernetes-admin name: kubernetes-admin@kubernetes current-context: kubernetes-admin@kubernetes kind: Config preferences: {} users: - name: kubernetes-admin user: client-certificate-data: SL0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURJVENDQWdtZ0F3SUJBZ0lJYkZnWFZDVzd4azR3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TWpBeU1UUXhNekExTWpWYUZ3MHlNekF5TVRReE16QTFNekZhTURReApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sa3dGd1lEVlFRREV4QnJkV0psY201bGRHVnpMV0ZrCmJXbHVNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXgxejIvTTI1OGpSRWpkZ2sKejFScitpMU15M2NSZTRGZW1IbGJ4QlhNRUNjNzVzSWRmQ0VUdlI1ZmlFc3pOdkNHeXVnL0kxaUVVakFXd3JtVwpzdTJ5cUNkWkc4OUpRelI3bWk2OTdNc01KVGNHRDNFK0xDZjNNVzhKelBFRnJtbnJHeDNVOFNnM3RrbWEreVVyCmEzMDd6UXBwSTBYTHIrMXVOemI2MFdRSHVraGNnZFY1bFlvMGVvd1p5d3N2dFV6MnBQT2hScTFsVmxrUytKQXcKR0NMeFN5a1I4dnl6cStnQXB6SFpmR0l5RXJmV0lIWE9LY2hlQTRwS1NjTGZOVUhVWHBQMlMwcUN2L2QyWlArOQpPMWE3NDErcWtKSW9WaWdQK2ZORUpXdUF5NGQrMTFRZktETlNzdHlmeGpmVm1nMEJUNXRoZjZabFNCdTJBUnFRCnEyYi94d0lEQVFBQm8xWXdWREFPQmdOVkhROEJBZjhFQkFNQ0JhQXdFd1lEVlIwbEJBd3dDZ1lJS3dZQkJRVUgKQXdJd0RBWURWUjBUQVFIL0JBSXdBREFmQmdOVkhTTUVHREFXZ0JTbk9aL3lTU0tiTVA3QWE3Q0hjT3dTWFUwYQoyakFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBTko3dmE5NDlxYmlseGtHa3pIUzdkZEZPTHR5c0dBMHZaSUUyCmxOMzEvQUlNOEMrWXVpQ2tndFdBcGZPSnRXejYzblNNZlh2amZPdlBKQW1GeDFvQXY0cHpWR2M2bEtRTVBFK0wKZEZKZWpJT1BRa1JRN1BHTzloSk4ramppZmhhanpkZ3RIWnExOURwM0xtTjVGcnJQT3RwM3N4OE1BTUxDU1FVRwpXajd3ek82KzNlZTVhemlmYi9CRUUveFB5dWR1aFQ4ODJsMnpiMTNMdldmczRYZVpUT1ZMdFdIU3hvL1diZE9oCjZ2TVROYUxzRFNveCtpWWdHSmtGYWl6NDdlRzVEVWpFa3F4VzdiSXEwTDNHdWJtRWhNOW5la3JsYlBseDZlODAKd0dGWnZaKy9zak51T3NQVWZjVFJYTWtKUUUvZzNVd3pTSUZGWlIydXVtUGFhRFBrVWc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== client-key-data: SL0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb2dJQkFBS0NBUUVBeDF6Mi9NMjU4alJFamRna3oxUnIraTFNeTNjUmU0RmVtSGxieEJYTUVDYzc1c0lkCmZDRVR2UjVmaUVzek52Q0d5dWcvSTFpRVVqQVd3cm1Xc3UyeXFDZFpHODlKUXpSN21pNjk3TXNNSlRjR0QzRSsKTENmM01XOEp6UEVGcm1uckd4M1U4U2czdGttYSt5VXJhMzA3elFwcEkwWExyKzF1TnpiNjBXUUh1a2hjZ2RWNQpsWW8wZW93Wnl3c3Z0VXoycFBPaFJxMWxWbGtTK0pBd0dDTHhTeWtSOHZ5enErZ0FwekhaZkdJeUVyZldJSFhPCktjaGVBNHBLU2NMZk5VSFVYcFAyUzBxQ3YvZDJaUCs5TzFhNzQxK3FrSklvVmlnUCtmTkVKV3VBeTRkKzExUWYKS0ROU3N0eWZ4amZWbWcwQlQ1dGhmNlpsU0J1MkFScVFxMmIveHdJREFRQUJBb0lCQUNoaXNMWHRodW1GcFEyRwo1NDRJY0FjeC9naUppa1VXbys4SFJvdW1UcnhHOWw5OG16UjJEdVdVclkyU2prRm00Q2RpZk1mUU9wM2JtQURDClQ4RFhYZ1dxVXViTFN2QU9SYXVxSkZjL21xby9SejhCbGJLa05mTVJwMDZZMUtuTVV4QWZMdS9iVWMzZmcwRzAKK2VMQWI4ak5meGJpSUt6MjBBam5Yay9rajV3d2lPUGhjeVNreEhUMmtDVDVTVURsNCswd3l2Z1prN0VkWm1PYQppdlVjU2pseWNFcTBsMnBmM2tWK2hYVk5ZSWxoY0kwM3dGWDFIMEdBREcvT3lnNDNQVWxmb3ZCQWU1TDJLVkcvCmFVUXFqazkvZytPaXY2anRwdWU1b2dEbGthN0hjQ0lDMkdOT0k3S2hCNVhUbFV6eWY1MlNMTlNZeVplVnRWb3YKUm9FbjNBRUNnWUVBMzJXN2RQby9ZYTA3MjJNNU5QK3dCeFRzbHlBamQwblFESHgxcExpbkRzMWNBUmpWRnZxcgpJRHhna2wrQTJDRGJpWkg2dW9SZHRsRm5CYnhZRlVocDNMMjFaaE1BR2wzU092K0libmIzK0daVTJKdTljWURqCnZLRklvQ0RRMnJOQVVveWE4djF2WHRhR1krL245T2pGQlJlVnptenZuU1F6a24rTUo4N29tVWNDZ1lFQTVIVk0KUCtGb0hMem5Qd2dTZnQ3NDNGL2lLdW9PbWJFd3VCbUM3bXF1dWpIM3cyUFJWb1cvMVhBemtGQnRPNVF2UVdYNwpGZG1iVFQ4NW1UN3BldXhIV3VLUldUb25LWFJrWmI1T3dXU1RlVk5IWVZ4S2JrSW15WkFKWnZWRFhQZWhOT2NXCnc1eTNvWGdnK0Y1M0o1RDNocXM0MGgwRXVENTBIVlp0ZGdyaHBZRUNnWUFlLzdqaFpKQkM5NHpreG9IN3JyYzQKWkZqb0o1ZUVTQVBNbDhDaldOUWxvNjF1b1ltQUpNeDJMcXFmNVF5MThPbEZ6N0hoQzlrTklZS1FNekJ0MDV5TQordTRlK2VmN3dLVVpkcmZ4ekNSZ25hS01aQ0FIamdFTC9iMWNLdkdRUjJ0WGlSYy9QSmVscTFMK3J4MmF5R24rCmFPVnF2WWNLWVNtZTNJQVFUZy9NcFFLQmdIWGQzcDBHbWtSWllhVXZjUHRyNWxFc1Z1OTFHbHRKQTYyMzI4bE4KMlIvUEw5anE0dElVNTBnalB6Y3hoMm01cGpmRGVhdG9QYXU0OXVxTmZzQWdyeC9Bek9TUUVDeGZGSDA1bGtCSQp0NTFjemZMNVBwMXNHNzdhUlQrTlFsZndtb2RFd29YaGtRd0pnbGtodzYveUp3S2Z6QXo3VTdnSzRMVlNKZDlFCjllNEJBb0dBRUViZTRzTkhnUDFVS3NBdlJOOFN3MUZZeHdPWFFsWmxIMDFKQXQ1d290UXV2WUo0elQwRkJSdTYKR292VDF5RU9xRzdqbndQczZwZVRlVU56c09nblBRR1FtUitmSUl1QURXekFsUTNxSk55T2VwWEFsSDhJVDlGagp1QzcrMjZUSHJNNUJQTTN4dFVlenlLV2s5T1VvbVh5UWlEMHBWWXRJNTdWNHBabDdYREk9Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg== kubeuser@kubenewmaster1:~$ kubectl get nodes NAME STATUS ROLES AGE VERSION kubenewmaster1 Ready control-plane 369d v1.24.0 kubenewworker1 Ready <none> 324d v1.24.0 kubenewworker2 Ready <none> 324d v1.24.0 kubeuser@kubenewmaster1:~$ kubectl get pods NAME READY STATUS RESTARTS AGE beauty-deploy-7d59f6c75f-hbk8b 1/1 Running 2 (24d ago) 324d beauty-deploy-7d59f6c75f-xp9fd 1/1 Running 2 (24d ago) 324d el-hello-listener-79b696c975-vmshs 1/1 Running 14 (24d ago) 38d food-deploy-54544fc85d-5k5h9 1/1 Running 2 (24d ago) 324d food-deploy-54544fc85d-hhn4r 1/1 Running 2 (24d ago) 324d food-deploy-54544fc85d-smn2l 1/1 Running 2 (24d ago) 324d hello-goodbye-run-4m8xj-goodbye-pod 0/1 Completed 0 38d hello-goodbye-run-4m8xj-hello-pod 0/1 Completed 0 38d hello-goodbye-run-goodbye-pod 0/1 Completed 0 39d hello-goodbye-run-hello-pod 0/1 Completed 0 39d hello-task-run-pod 0/1 Completed 0 39d nfs-subdir-external-provisioner-9c9c5866f-2n2l6 1/1 Running 68 (24d ago) 324d patch-test-6bd9658956-npz4m 1/1 Running 2 (24d ago) 324d patch-test-6bd9658956-qcjhj 1/1 Running 2 (24d ago) 324d pvc-deploy-77f7d456bf-jnp57 1/1 Running 2 (24d ago) 324d
なので同様のデータを使うことで、REST API(curlなど)からもアクセス可能です。
認証データの変換
認証に必要な各データをBASE64ででコードします。
・クラスター認証の鍵
・クライアント認証の証明書
・クライアント認証の鍵
kubeuser@kubenewmaster1:~/curl_access$ export client_certificate_data=$(grep client-cert $HOME/.kube/config |cut -d" " -f 6) kubeuser@kubenewmaster1:~/curl_access$ echo $client_certificate_data SL0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURJVENDQWdtZ0F3SUJBZ0lJYkZnWFZDVzd4azR3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TWpBeU1UUXhNekExTWpWYUZ3MHlNekF5TVRReE16QTFNekZhTURReApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sa3dGd1lEVlFRREV4QnJkV0psY201bGRHVnpMV0ZrCmJXbHVNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXgxejIvTTI1OGpSRWpkZ2sKejFScitpMU15M2NSZTRGZW1IbGJ4QlhNRUNjNzVzSWRmQ0VUdlI1ZmlFc3pOdkNHeXVnL0kxaUVVakFXd3JtVwpzdTJ5cUNkWkc4OUpRelI3bWk2OTdNc01KVGNHRDNFK0xDZjNNVzhKelBFRnJtbnJHeDNVOFNnM3RrbWEreVVyCmEzMDd6UXBwSTBYTHIrMXVOemI2MFdRSHVraGNnZFY1bFlvMGVvd1p5d3N2dFV6MnBQT2hScTFsVmxrUytKQXcKR0NMeFN5a1I4dnl6cStnQXB6SFpmR0l5RXJmV0lIWE9LY2hlQTRwS1NjTGZOVUhVWHBQMlMwcUN2L2QyWlArOQpPMWE3NDErcWtKSW9WaWdQK2ZORUpXdUF5NGQrMTFRZktETlNzdHlmeGpmVm1nMEJUNXRoZjZabFNCdTJBUnFRCnEyYi94d0lEQVFBQm8xWXdWREFPQmdOVkhROEJBZjhFQkFNQ0JhQXdFd1lEVlIwbEJBd3dDZ1lJS3dZQkJRVUgKQXdJd0RBWURWUjBUQVFIL0JBSXdBREFmQmdOVkhTTUVHREFXZ0JTbk9aL3lTU0tiTVA3QWE3Q0hjT3dTWFUwYQoyakFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBTko3dmE5NDlxYmlseGtHa3pIUzdkZEZPTHR5c0dBMHZaSUUyCmxOMzEvQUlNOEMrWXVpQ2tndFdBcGZPSnRXejYzblNNZlh2amZPdlBKQW1GeDFvQXY0cHpWR2M2bEtRTVBFK0wKZEZKZWpJT1BRa1JRN1BHTzloSk4ramppZmhhanpkZ3RIWnExOURwM0xtTjVGcnJQT3RwM3N4OE1BTUxDU1FVRwpXajd3ek82KzNlZTVhemlmYi9CRUUveFB5dWR1aFQ4ODJsMnpiMTNMdldmczRYZVpUT1ZMdFdIU3hvL1diZE9oCjZ2TVROYUxzRFNveCtpWWdHSmtGYWl6NDdlRzVEVWpFa3F4VzdiSXEwTDNHdWJtRWhNOW5la3JsYlBseDZlODAKd0dGWnZaKy9zak51T3NQVWZjVFJYTWtKUUUvZzNVd3pTSUZGWlIydXVtUGFhRFBrVWc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== kubeuser@kubenewmaster1:~/curl_access$ echo $client_certificate_data | base64 -d - > ./client_certificate_data.pem
kubeuser@kubenewmaster1:~/curl_access$ export client_key_data=$(grep client-key-data $HOME/.kube/config |cut -d " " -f 6) kubeuser@kubenewmaster1:~/curl_access$ echo $client_key_data SL0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb2dJQkFBS0NBUUVBeDF6Mi9NMjU4alJFamRna3oxUnIraTFNeTNjUmU0RmVtSGxieEJYTUVDYzc1c0lkCmZDRVR2UjVmaUVzek52Q0d5dWcvSTFpRVVqQVd3cm1Xc3UyeXFDZFpHODlKUXpSN21pNjk3TXNNSlRjR0QzRSsKTENmM01XOEp6UEVGcm1uckd4M1U4U2czdGttYSt5VXJhMzA3elFwcEkwWExyKzF1TnpiNjBXUUh1a2hjZ2RWNQpsWW8wZW93Wnl3c3Z0VXoycFBPaFJxMWxWbGtTK0pBd0dDTHhTeWtSOHZ5enErZ0FwekhaZkdJeUVyZldJSFhPCktjaGVBNHBLU2NMZk5VSFVYcFAyUzBxQ3YvZDJaUCs5TzFhNzQxK3FrSklvVmlnUCtmTkVKV3VBeTRkKzExUWYKS0ROU3N0eWZ4amZWbWcwQlQ1dGhmNlpsU0J1MkFScVFxMmIveHdJREFRQUJBb0lCQUNoaXNMWHRodW1GcFEyRwo1NDRJY0FjeC9naUppa1VXbys4SFJvdW1UcnhHOWw5OG16UjJEdVdVclkyU2prRm00Q2RpZk1mUU9wM2JtQURDClQ4RFhYZ1dxVXViTFN2QU9SYXVxSkZjL21xby9SejhCbGJLa05mTVJwMDZZMUtuTVV4QWZMdS9iVWMzZmcwRzAKK2VMQWI4ak5meGJpSUt6MjBBam5Yay9rajV3d2lPUGhjeVNreEhUMmtDVDVTVURsNCswd3l2Z1prN0VkWm1PYQppdlVjU2pseWNFcTBsMnBmM2tWK2hYVk5ZSWxoY0kwM3dGWDFIMEdBREcvT3lnNDNQVWxmb3ZCQWU1TDJLVkcvCmFVUXFqazkvZytPaXY2anRwdWU1b2dEbGthN0hjQ0lDMkdOT0k3S2hCNVhUbFV6eWY1MlNMTlNZeVplVnRWb3YKUm9FbjNBRUNnWUVBMzJXN2RQby9ZYTA3MjJNNU5QK3dCeFRzbHlBamQwblFESHgxcExpbkRzMWNBUmpWRnZxcgpJRHhna2wrQTJDRGJpWkg2dW9SZHRsRm5CYnhZRlVocDNMMjFaaE1BR2wzU092K0libmIzK0daVTJKdTljWURqCnZLRklvQ0RRMnJOQVVveWE4djF2WHRhR1krL245T2pGQlJlVnptenZuU1F6a24rTUo4N29tVWNDZ1lFQTVIVk0KUCtGb0hMem5Qd2dTZnQ3NDNGL2lLdW9PbWJFd3VCbUM3bXF1dWpIM3cyUFJWb1cvMVhBemtGQnRPNVF2UVdYNwpGZG1iVFQ4NW1UN3BldXhIV3VLUldUb25LWFJrWmI1T3dXU1RlVk5IWVZ4S2JrSW15WkFKWnZWRFhQZWhOT2NXCnc1eTNvWGdnK0Y1M0o1RDNocXM0MGgwRXVENTBIVlp0ZGdyaHBZRUNnWUFlLzdqaFpKQkM5NHpreG9IN3JyYzQKWkZqb0o1ZUVTQVBNbDhDaldOUWxvNjF1b1ltQUpNeDJMcXFmNVF5MThPbEZ6N0hoQzlrTklZS1FNekJ0MDV5TQordTRlK2VmN3dLVVpkcmZ4ekNSZ25hS01aQ0FIamdFTC9iMWNLdkdRUjJ0WGlSYy9QSmVscTFMK3J4MmF5R24rCmFPVnF2WWNLWVNtZTNJQVFUZy9NcFFLQmdIWGQzcDBHbWtSWllhVXZjUHRyNWxFc1Z1OTFHbHRKQTYyMzI4bE4KMlIvUEw5anE0dElVNTBnalB6Y3hoMm01cGpmRGVhdG9QYXU0OXVxTmZzQWdyeC9Bek9TUUVDeGZGSDA1bGtCSQp0NTFjemZMNVBwMXNHNzdhUlQrTlFsZndtb2RFd29YaGtRd0pnbGtodzYveUp3S2Z6QXo3VTdnSzRMVlNKZDlFCjllNEJBb0dBRUViZTRzTkhnUDFVS3NBdlJOOFN3MUZZeHdPWFFsWmxIMDFKQXQ1d290UXV2WUo0elQwRkJSdTYKR292VDF5RU9xRzdqbndQczZwZVRlVU56c09nblBRR1FtUitmSUl1QURXekFsUTNxSk55T2VwWEFsSDhJVDlGagp1QzcrMjZUSHJNNUJQTTN4dFVlenlLV2s5T1VvbVh5UWlEMHBWWXRJNTdWNHBabDdYREk9Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg== kubeuser@kubenewmaster1:~/curl_access$ echo $client_key_data | base64 -d -----BEGIN RSA PRIVATE KEY----- IMIEogIBAAKCAQEAx1z2/M258jREjdgkz1Rr+i1My3cRe4FemHlbxBXMECc75sId fCETvR5fiEszNvCGyug/I1iEUjAWwrmWsu2yqCdZG89JQzR7mi697MsMJTcGD3E+ LCf3MW8JzPEFrmnrGx3U8Sg3tkma+yUra307zQppI0XLr+1uNzb60WQHukhcgdV5 lYo0eowZywsvtUz2pPOhRq1lVlkS+JAwGCLxSykR8vyzq+gApzHZfGIyErfWIHXO KcheA4pKScLfNUHUXpP2S0qCv/d2ZP+9O1a741+qkJIoVigP+fNEJWuAy4d+11Qf KDNSstyfxjfVmg0BT5thf6ZlSBu2ARqQq2b/xwIDAQABAoIBAChisLXthumFpQ2G 544IcAcx/giJikUWo+8HRoumTrxG9l98mzR2DuWUrY2SjkFm4CdifMfQOp3bmADC T8DXXgWqUubLSvAORauqJFc/mqo/Rz8BlbKkNfMRp06Y1KnMUxAfLu/bUc3fg0G0 +eLAb8jNfxbiIKz20AjnXk/kj5wwiOPhcySkxHT2kCT5SUDl4+0wyvgZk7EdZmOa ivUcSjlycEq0l2pf3kV+hXVNYIlhcI03wFX1H0GADG/Oyg43PUlfovBAe5L2KVG/ aUQqjk9/g+Oiv6jtpue5ogDlka7HcCIC2GNOI7KhB5XTlUzyf52SLNSYyZeVtVov RoEn3AECgYEA32W7dPo/Ya0722M5NP+wBxTslyAjd0nQDHx1pLinDs1cARjVFvqr IDxgkl+A2CDbiZH6uoRdtlFnBbxYFUhp3L21ZhMAGl3SOv+Ibnb3+GZU2Ju9cYDj vKFIoCDQ2rNAUoya8v1vXtaGY+/n9OjFBReVzmzvnSQzkn+MJ87omUcCgYEA5HVM P+FoHLznPwgSft743F/iKuoOmbEwuBmC7mquujH3w2PRVoW/1XAzkFBtO5QvQWX7 FdmbTT85mT7peuxHWuKRWTonKXRkZb5OwWSTeVNHYVxKbkImyZAJZvVDXPehNOcW w5y3oXgg+F53J5D3hqs40h0EuD50HVZtdgrhpYECgYAe/7jhZJBC94zkxoH7rrc4 ZFjoJ5eESAPMl8CjWNQlo61uoYmAJMx2Lqqf5Qy18OlFz7HhC9kNIYKQMzBt05yM +u4e+ef7wKUZdrfxzCRgnaKMZCAHjgEL/b1cKvGQR2tXiRc/PJelq1L+rx2ayGn+ aOVqvYcKYSme3IAQTg/MpQKBgHXd3p0GmkRZYaUvcPtr5lEsVu91GltJA62328lN 2R/PL9jq4tIU50gjPzcxh2m5pjfDeatoPau49uqNfsAgrx/AzOSQECxfFH05lkBI t51czfL5Pp1sG77aRT+NQlfwmodEwoXhkQwJglkhw6/yJwKfzAz7U7gK4LVSJd9E 9e4BAoGAEEbe4sNHgP1UKsAvRN8Sw1FYxwOXQlZlH01JAt5wotQuvYJ4zT0FBRu6 GovT1yEOqG7jnwPs6peTeUNzsOgnPQGQmR+fIIuADWzAlQ3qJNyOepXAlH8IT9Fj uC7+26THrM5BPM3xtUezyKWk9OUomXyQiD0pVYtI57V4pZl7XDI= -----END RSA PRIVATE KEY----- kubeuser@kubenewmaster1:~/curl_access$ echo $client_key_data | base64 -d - > ./client_key_data.pem
kubeuser@kubenewmaster1:~/curl_access$ export certificate_authority_data=$(grep certificate-authority-data $HOME/.kube/config |cut -d " " -f 6) kubeuser@kubenewmaster1:~/curl_access$ echo $certificate_authority_data SL0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMvakNDQWVhZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJeU1ESXhOREV6TURVeU5Wb1hEVE15TURJeE1qRXpNRFV5TlZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBS0VGCk5HaWtvK2xNcTBadE9zQk1TNFRWZUh6VGFiNjdxYkhKVGMwYktROVRaNnZlZE1jV29vcGNvOFJYSjdzbk5TaksKN0g4WEdKQm1lV1ZnckRYZHprSU9raE50WmZkVnoyaENYWU5LczNCV0RHWXZ3T0ozSDF3ajRmTmlRakkyYXo2Ygozcnd1T1RmaTdQRnZvRlBDQjNqdEFXOHVuWGZ3eVJxaS9xVEpZcDltMUVzRGRCa0dvQzd6MXpTUzBOWERiNEdHCkRKY2ZYK3UvL0d2bCtuVXdRdURiQUtOQ2lRbEllSlJtZHlBWlVRaXYyTDJMYy9rRkdUMTVNbVVvMW8wVzFaTG8KVXFWZW96QVRsZk4xbXpEM2FzV1VpS29WK05waGwzMTNBSWpZYWRkU0wrREkxNVd5YWJxdHBHY2YweUhJcEZmZQp0YmpvUjU2SHIyNS90OWN5QzVVQ0F3RUFBYU5aTUZjd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0hRWURWUjBPQkJZRUZLYzVuL0pKSXBzdy9zQnJzSWR3N0JKZFRScmFNQlVHQTFVZEVRUU8KTUF5Q0NtdDFZbVZ5Ym1WMFpYTXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBRU9HajZTa256R21FNTZ2LzBWcAo2eDlva08yZDM5eEgzODEzM1RZeU5kU0d4cU0xSFdNVWlBaHV3ZG5KSU1PQlZtczNHS2g4WFFiSjdlSTlqdjZUCktMbnp0dFhxWVcwOERrYlZtOEM5SmQyRUoxVnlQVEZZdXRHSitXSVlKdHk2QlBEV2g3cU1VeTZVMm5VKzZQbEYKNVpmSDcwNEF1R0YwS1JIME5sVTJ3SVIvUHI0NnljMFo4WktlRUdOdzBPcHRPbysxMmtiVmkzYVBxQkVLbkVrTwp0N3cvL2k0RFVxbzJ5RGU4NkRHS2xIOSttMWJ3ZWpVUEJZM2xDUnRrMW5LRWExY0YvaXdUUW51cjVkZmxvZzhFCm4zUEw5OXNEMnVUSmhJMUR3L3hwdldKRG9mb1hJeHpiejd0VHBEejNrV1NUOFU4VjNpRmNPa2oreCtuem5XMFEKN0ZrPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== kubeuser@kubenewmaster1:~/curl_access$ echo $certificate_authority_data | base64 -d -----BEGIN CERTIFICATE----- IMIC/jCCAeagAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwprdWJl cm5ldGVzMB4XDTIyMDIxNDEzMDUyNVoXDTMyMDIxMjEzMDUyNVowFTETMBEGA1UE AxMKa3ViZXJuZXRlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKEF NGiko+lMq0ZtOsBMS4TVeHzTab67qbHJTc0bKQ9TZ6vedMcWoopco8RXJ7snNSjK 7H8XGJBmeWVgrDXdzkIOkhNtZfdVz2hCXYNKs3BWDGYvwOJ3H1wj4fNiQjI2az6b 3rwuOTfi7PFvoFPCB3jtAW8unXfwyRqi/qTJYp9m1EsDdBkGoC7z1zSS0NXDb4GG DJcfX+u//Gvl+nUwQuDbAKNCiQlIeJRmdyAZUQiv2L2Lc/kFGT15MmUo1o0W1ZLo UqVeozATlfN1mzD3asWUiKoV+Nphl313AIjYaddSL+DI15WyabqtpGcf0yHIpFfe tbjoR56Hr25/t9cyC5UCAwEAAaNZMFcwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB /wQFMAMBAf8wHQYDVR0OBBYEFKc5n/JJIpsw/sBrsIdw7BJdTRraMBUGA1UdEQQO MAyCCmt1YmVybmV0ZXMwDQYJKoZIhvcNAQELBQADggEBAEOGj6SknzGmE56v/0Vp 6x9okO2d39xH38133TYyNdSGxqM1HWMUiAhuwdnJIMOBVms3GKh8XQbJ7eI9jv6T KLnzttXqYW08DkbVm8C9Jd2EJ1VyPTFYutGJ+WIYJty6BPDWh7qMUy6U2nU+6PlF 5ZfH704AuGF0KRH0NlU2wIR/Pr46yc0Z8ZKeEGNw0OptOo+12kbVi3aPqBEKnEkO t7w//i4DUqo2yDe86DGKlH9+m1bwejUPBY3lCRtk1nKEa1cF/iwTQnur5dflog8E n3PL99sD2uTJhI1Dw/xpvWJDofoXIxzbz7tTpDz3kWST8U8V3iFcOkj+x+nznW0Q 7Fk= -----END CERTIFICATE----- kubeuser@kubenewmaster1:~/curl_access$ echo $certificate_authority_data | base64 -d - > ./certificate_authority_data.pem
kubeuser@kubenewmaster1:~/curl_access$ ls certificate_authority_data.pem client_certificate_data.pem client_key_data.pem
REST API(curl)の実施
では、REST APIをcurlで実施してみます。
kubeuser@kubenewmaster1:~/curl_access$ curl --cert ./client_certificate_data.pem --key ./client_key_data.pem --cacert ./certificate_authority_data.pem https://kubenewmaster1:6443/api/v1/namespaces { "kind": "NamespaceList", "apiVersion": "v1", "metadata": { "resourceVersion": "63124337" }, "items": [ { "metadata": { "name": "area1", "uid": "c94f3542-1a0f-4683-9dfa-483caa87b77a", "resourceVersion": "4594018", "creationTimestamp": "2022-03-14T13:45:00Z", "labels": { "kubernetes.io/metadata.name": "area1" }, "managedFields": [ { "manager": "kubectl-create", "operation": "Update", "apiVersion": "v1", "time": "2022-03-14T13:45:00Z", "fieldsType": "FieldsV1", "fieldsV1": { "f:metadata": { "f:labels": { ".": {}, "f:kubernetes.io/metadata.name": {} } } } } ] }, "spec": { "finalizers": [ "kubernetes" ] }, "status": { "phase": "Active" } }, { "metadata": { "name": "area2", "uid": "2f77b3a9-60a0-4479-8d2d-042e4ce06f77", "resourceVersion": "4594035", "creationTimestamp": "2022-03-14T13:45:05Z", "labels": { "kubernetes.io/metadata.name": "area2" }, "managedFields": [ { "manager": "kubectl-create", "operation": "Update", "apiVersion": "v1", "time": "2022-03-14T13:45:05Z", "fieldsType": "FieldsV1", "fieldsV1": { "f:metadata": { "f:labels": { ".": {}, "f:kubernetes.io/metadata.name": {} } } } } ] }, "spec": { "finalizers": [ "kubernetes" ] }, "status": { "phase": "Active" } }, { "metadata": { "name": "default", "uid": "a97cd8bf-dbc7-4aab-9485-62e62a815893", "resourceVersion": "209", "creationTimestamp": "2022-02-14T13:06:08Z", "labels": { "kubernetes.io/metadata.name": "default" }, "managedFields": [ { "manager": "kube-apiserver", "operation": "Update", "apiVersion": "v1", "time": "2022-02-14T13:06:08Z", "fieldsType": "FieldsV1", "fieldsV1": { "f:metadata": { "f:labels": { ".": {}, "f:kubernetes.io/metadata.name": {} } } } } ] }, "spec": { "finalizers": [ "kubernetes" ] }, "status": { "phase": "Active" } }, { "metadata": { "name": "ingress-nginx", "uid": "47e467e5-754a-43dc-b85e-4c93722b5d25", "resourceVersion": "127242", "creationTimestamp": "2022-02-15T16:07:06Z", "labels": { "app.kubernetes.io/instance": "ingress-nginx", "app.kubernetes.io/name": "ingress-nginx", "kubernetes.io/metadata.name": "ingress-nginx" }, "annotations": { "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Namespace\",\"metadata\":{\"annotations\":{},\"labels\":{\"app.kubernetes.io/instance\":\"ingress-nginx\",\"app.kubernetes.io/name\":\"ingress-nginx\"},\"name\":\"ingress-nginx\"}}\n" }, "managedFields": [ { "manager": "kubectl-client-side-apply", "operation": "Update", "apiVersion": "v1", "time": "2022-02-15T16:07:06Z", "fieldsType": "FieldsV1", "fieldsV1": { "f:metadata": { "f:annotations": { ".": {}, "f:kubectl.kubernetes.io/last-applied-configuration": {} }, "f:labels": { ".": {}, "f:app.kubernetes.io/instance": {}, "f:app.kubernetes.io/name": {}, "f:kubernetes.io/metadata.name": {} } } } } ] }, "spec": { "finalizers": [ "kubernetes" ] }, "status": { "phase": "Active" } }, { "metadata": { "name": "kube-node-lease", "uid": "331c5eaf-705f-456c-a0b4-bd8f60f8acb0", "resourceVersion": "50", "creationTimestamp": "2022-02-14T13:06:03Z", "labels": { "kubernetes.io/metadata.name": "kube-node-lease" }, "managedFields": [ { "manager": "kube-apiserver", "operation": "Update", "apiVersion": "v1", "time": "2022-02-14T13:06:03Z", "fieldsType": "FieldsV1", "fieldsV1": { "f:metadata": { "f:labels": { ".": {}, "f:kubernetes.io/metadata.name": {} } } } } ] }, "spec": { "finalizers": [ "kubernetes" ] }, "status": { "phase": "Active" } }, { "metadata": { "name": "kube-public", "uid": "fe0f94f0-0a50-4af7-9f72-44d0809274e4", "resourceVersion": "46", "creationTimestamp": "2022-02-14T13:06:02Z", "labels": { "kubernetes.io/metadata.name": "kube-public" }, "managedFields": [ { "manager": "kube-apiserver", "operation": "Update", "apiVersion": "v1", "time": "2022-02-14T13:06:02Z", "fieldsType": "FieldsV1", "fieldsV1": { "f:metadata": { "f:labels": { ".": {}, "f:kubernetes.io/metadata.name": {} } } } } ] }, "spec": { "finalizers": [ "kubernetes" ] }, "status": { "phase": "Active" } }, { "metadata": { "name": "kube-system", "uid": "3d3f6102-548b-4932-9384-e0a3ef9944d8", "resourceVersion": "29", "creationTimestamp": "2022-02-14T13:06:02Z", "labels": { "kubernetes.io/metadata.name": "kube-system" }, "managedFields": [ { "manager": "kube-apiserver", "operation": "Update", "apiVersion": "v1", "time": "2022-02-14T13:06:02Z", "fieldsType": "FieldsV1", "fieldsV1": { "f:metadata": { "f:labels": { ".": {}, "f:kubernetes.io/metadata.name": {} } } } } ] }, "spec": { "finalizers": [ "kubernetes" ] }, "status": { "phase": "Active" } }, { "metadata": { "name": "lens-metrics", "uid": "b2ca0471-e099-42d8-a4db-8ae4842337b8", "resourceVersion": "50540864", "creationTimestamp": "2022-12-12T23:36:41Z", "labels": { "app.kubernetes.io/created-by": "resource-stack", "app.kubernetes.io/managed-by": "Lens", "app.kubernetes.io/name": "lens-metrics", "kubernetes.io/metadata.name": "lens-metrics" }, "annotations": { "extensionVersion": "v2.26.0-lens1", "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Namespace\",\"metadata\":{\"annotations\":{\"extensionVersion\":\"v2.26.0-lens1\"},\"labels\":{\"app.kubernetes.io/created-by\":\"resource-stack\",\"app.kubernetes.io/managed-by\":\"Lens\",\"app.kubernetes.io/name\":\"lens-metrics\"},\"name\":\"lens-metrics\"}}\n" }, "managedFields": [ { "manager": "kubectl-client-side-apply", "operation": "Update", "apiVersion": "v1", "time": "2022-12-12T23:36:41Z", "fieldsType": "FieldsV1", "fieldsV1": { "f:metadata": { "f:annotations": { ".": {}, "f:extensionVersion": {}, "f:kubectl.kubernetes.io/last-applied-configuration": {} }, "f:labels": { ".": {}, "f:app.kubernetes.io/created-by": {}, "f:app.kubernetes.io/managed-by": {}, "f:app.kubernetes.io/name": {}, "f:kubernetes.io/metadata.name": {} } } } } ] }, "spec": { "finalizers": [ "kubernetes" ] }, "status": { "phase": "Active" } }, { "metadata": { "name": "metallb-system", "uid": "2214ae24-6278-454e-8c6f-393b49fde836", "resourceVersion": "168274", "creationTimestamp": "2022-02-15T23:59:16Z", "labels": { "app": "metallb", "kubernetes.io/metadata.name": "metallb-system" }, "annotations": { "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Namespace\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"metallb\"},\"name\":\"metallb-system\"}}\n" }, "managedFields": [ { "manager": "kubectl-client-side-apply", "operation": "Update", "apiVersion": "v1", "time": "2022-02-15T23:59:16Z", "fieldsType": "FieldsV1", "fieldsV1": { "f:metadata": { "f:annotations": { ".": {}, "f:kubectl.kubernetes.io/last-applied-configuration": {} }, "f:labels": { ".": {}, "f:app": {}, "f:kubernetes.io/metadata.name": {} } } } } ] }, "spec": { "finalizers": [ "kubernetes" ] }, "status": { "phase": "Active" } }, { "metadata": { "name": "newrelic", "uid": "3f8e8747-7db3-46b7-b2ad-6e1c7bb03c5f", "resourceVersion": "883627", "creationTimestamp": "2022-02-21T07:05:19Z", "labels": { "kubernetes.io/metadata.name": "newrelic" }, "managedFields": [ { "manager": "kubectl-create", "operation": "Update", "apiVersion": "v1", "time": "2022-02-21T07:05:19Z", "fieldsType": "FieldsV1", "fieldsV1": { "f:metadata": { "f:labels": { ".": {}, "f:kubernetes.io/metadata.name": {} } } } } ] }, "spec": { "finalizers": [ "kubernetes" ] }, "status": { "phase": "Active" } }, { "metadata": { "name": "sa", "uid": "62d53791-2091-40d3-a473-63d2bfa35330", "resourceVersion": "596058", "creationTimestamp": "2022-02-19T11:06:19Z", "labels": { "kubernetes.io/metadata.name": "sa" }, "managedFields": [ { "manager": "kubectl-create", "operation": "Update", "apiVersion": "v1", "time": "2022-02-19T11:06:19Z", "fieldsType": "FieldsV1", "fieldsV1": { "f:metadata": { "f:labels": { ".": {}, "f:kubernetes.io/metadata.name": {} } } } } ] }, "spec": { "finalizers": [ "kubernetes" ] }, "status": { "phase": "Active" } }, { "metadata": { "name": "tekton-pipelines", "uid": "1121f02f-b882-418d-b13e-769bae845049", "resourceVersion": "55639661", "creationTimestamp": "2023-01-11T05:11:57Z", "labels": { "app.kubernetes.io/instance": "default", "app.kubernetes.io/part-of": "tekton-pipelines", "kubernetes.io/metadata.name": "tekton-pipelines", "pod-security.kubernetes.io/enforce": "restricted" }, "annotations": { "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Namespace\",\"metadata\":{\"annotations\":{},\"labels\":{\"app.kubernetes.io/instance\":\"default\",\"app.kubernetes.io/part-of\":\"tekton-pipelines\",\"pod-security.kubernetes.io/enforce\":\"restricted\"},\"name\":\"tekton-pipelines\"}}\n" }, "managedFields": [ { "manager": "kubectl-client-side-apply", "operation": "Update", "apiVersion": "v1", "time": "2023-01-11T05:11:57Z", "fieldsType": "FieldsV1", "fieldsV1": { "f:metadata": { "f:annotations": { ".": {}, "f:kubectl.kubernetes.io/last-applied-configuration": {} }, "f:labels": { ".": {}, "f:app.kubernetes.io/instance": {}, "f:app.kubernetes.io/part-of": {}, "f:kubernetes.io/metadata.name": {}, "f:pod-security.kubernetes.io/enforce": {} } } } } ] }, "spec": { "finalizers": [ "kubernetes" ] }, "status": { "phase": "Active" } }, { "metadata": { "name": "tekton-pipelines-resolvers", "uid": "d10a16a2-3d97-4438-af38-d94a92e90e04", "resourceVersion": "55639772", "creationTimestamp": "2023-01-11T05:12:09Z", "labels": { "app.kubernetes.io/component": "resolvers", "app.kubernetes.io/instance": "default", "app.kubernetes.io/part-of": "tekton-pipelines", "kubernetes.io/metadata.name": "tekton-pipelines-resolvers", "pod-security.kubernetes.io/enforce": "restricted" }, "annotations": { "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Namespace\",\"metadata\":{\"annotations\":{},\"labels\":{\"app.kubernetes.io/component\":\"resolvers\",\"app.kubernetes.io/instance\":\"default\",\"app.kubernetes.io/part-of\":\"tekton-pipelines\",\"pod-security.kubernetes.io/enforce\":\"restricted\"},\"name\":\"tekton-pipelines-resolvers\"}}\n" }, "managedFields": [ { "manager": "kubectl-client-side-apply", "operation": "Update", "apiVersion": "v1", "time": "2023-01-11T05:12:09Z", "fieldsType": "FieldsV1", "fieldsV1": { "f:metadata": { "f:annotations": { ".": {}, "f:kubectl.kubernetes.io/last-applied-configuration": {} }, "f:labels": { ".": {}, "f:app.kubernetes.io/component": {}, "f:app.kubernetes.io/instance": {}, "f:app.kubernetes.io/part-of": {}, "f:kubernetes.io/metadata.name": {}, "f:pod-security.kubernetes.io/enforce": {} } } } } ] }, "spec": { "finalizers": [ "kubernetes" ] }, "status": { "phase": "Active" } }, { "metadata": { "name": "web", "uid": "f4772313-d1ef-4522-8bfb-9d2529543ce5", "resourceVersion": "50589269", "creationTimestamp": "2022-12-13T06:14:26Z", "labels": { "kubernetes.io/metadata.name": "web" }, "managedFields": [ { "manager": "kubectl-create", "operation": "Update", "apiVersion": "v1", "time": "2022-12-13T06:14:26Z", "fieldsType": "FieldsV1", "fieldsV1": { "f:metadata": { "f:labels": { ".": {}, "f:kubernetes.io/metadata.name": {} } } } } ] }, "spec": { "finalizers": [ "kubernetes" ] }, "status": { "phase": "Active" } } ] }
同様のことをkubectlで実施すると、以下になります。
kubeuser@kubenewmaster1:~/curl_access$ kubectl get namespaces NAME STATUS AGE area1 Active 323d area2 Active 323d default Active 351d ingress-nginx Active 350d kube-node-lease Active 351d kube-public Active 351d kube-system Active 351d lens-metrics Active 50d metallb-system Active 350d newrelic Active 344d sa Active 346d tekton-pipelines Active 21d tekton-pipelines-resolvers Active 21d web Active 49d
なお、REST APIの詳細はこちらで確認出来ます。
https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/
※リソースの確認だけでなく、作成や削除なども可能です。
おわりに
以前kubernetesクラスターを操作するLENSというツールを使ってみましたが、
今回と同様に裏ではREST APIが動作しています。
LENS(開発統合環境)でkubernetesクラスターを操作する
https://www.opensourcetech.tokyo/entry/20211216/1639659801