From 72ee02ea0ff0b52f723ec31ac5f4b4af83aedd64 Mon Sep 17 00:00:00 2001 From: Silenio Quarti Date: Thu, 9 Jan 2025 11:41:02 -0500 Subject: [PATCH] CAA: add support to look up imagePullSecrets for pods This PR initializes auth.json with the imagePullSecrets listed on the pod and service account. Fixes: #2231 Signed-off-by: Silenio Quarti --- .../install/rbac/peer-pod.yaml | 6 ++ .../pkg/adaptor/cloud/cloud.go | 11 +++ .../pkg/adaptor/k8sops/node.go | 91 +++++++++++++++++++ 3 files changed, 108 insertions(+) diff --git a/src/cloud-api-adaptor/install/rbac/peer-pod.yaml b/src/cloud-api-adaptor/install/rbac/peer-pod.yaml index 79bf50c39..f033c4b21 100644 --- a/src/cloud-api-adaptor/install/rbac/peer-pod.yaml +++ b/src/cloud-api-adaptor/install/rbac/peer-pod.yaml @@ -14,6 +14,12 @@ rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "list"] +- apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "list"] +- apiGroups: [""] + resources: ["serviceaccounts"] + verbs: ["get", "list"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding diff --git a/src/cloud-api-adaptor/pkg/adaptor/cloud/cloud.go b/src/cloud-api-adaptor/pkg/adaptor/cloud/cloud.go index b956f8284..65a98dfd0 100644 --- a/src/cloud-api-adaptor/pkg/adaptor/cloud/cloud.go +++ b/src/cloud-api-adaptor/pkg/adaptor/cloud/cloud.go @@ -259,6 +259,17 @@ func (s *cloudService) CreateVM(ctx context.Context, req *pb.CreateVMRequest) (r _, err = os.Stat(SrcAuthfilePath) if err != nil { logger.Printf("credential file %s is not present, skipping image auth config", SrcAuthfilePath) + + // Look up image pull secrets for the pod + authJSON, err = k8sops.GetImagePullSecrets(pod, namespace) + if err != nil { + // Ignore errors + logger.Printf("error reading image pull secrets: %v", err) + } + if authJSON != nil { + logger.Printf("successfully retrieved pod image pull secrets for %s/%s", namespace, pod) + authFilePath = AuthFilePath + } } else { authJSON, err = os.ReadFile(SrcAuthfilePath) if err != nil { diff --git a/src/cloud-api-adaptor/pkg/adaptor/k8sops/node.go b/src/cloud-api-adaptor/pkg/adaptor/k8sops/node.go index c55282aee..3538ccdd9 100644 --- a/src/cloud-api-adaptor/pkg/adaptor/k8sops/node.go +++ b/src/cloud-api-adaptor/pkg/adaptor/k8sops/node.go @@ -81,6 +81,97 @@ func RemoveExtendedResources() error { return nil } +// Auths contains Registries with credentials +type Auths struct { + Registries Registries `json:"auths"` +} + +// Registries contains credentials for hosts +type Registries map[string]Auth + +// Auth contains credentials for a given host +type Auth struct { + Auth string `json:"auth"` +} + +// GetImagePullSecrets gets image pull secrets for the specified pod +func GetImagePullSecrets(podName string, namespace string) ([]byte, error) { + + config, err := getKubeConfig() + if err != nil { + return nil, fmt.Errorf("failed to get k8s config: %v", err) + } + + cli, err := getClient(config) + if err != nil { + return nil, fmt.Errorf("failed to get k8s client: %v", err) + } + + pod, err := cli.CoreV1().Pods(namespace).Get(context.TODO(), podName, metav1.GetOptions{}) + if err != nil { + return nil, err + } + + accountName := pod.Spec.ServiceAccountName + if accountName == "" { + accountName = "default" + } + serviceaAccount, err := cli.CoreV1().ServiceAccounts(namespace).Get(context.TODO(), accountName, metav1.GetOptions{}) + if err != nil { + return nil, err + } + + auths := Auths{} + auths.Registries = make(map[string]Auth) + for _, secret := range serviceaAccount.ImagePullSecrets { + err := getAuths(cli, namespace, secret.Name, &auths) + if err != nil { + return nil, err + } + } + for _, secret := range pod.Spec.ImagePullSecrets { + err := getAuths(cli, namespace, secret.Name, &auths) + if err != nil { + return nil, err + } + } + + if len(auths.Registries) > 0 { + authJSON, err := json.Marshal(auths) + if err != nil { + return nil, err + } + return authJSON, nil + } + return nil, nil +} + +// getAuths get auth credentials from specified docker secret +func getAuths(cli *k8sclient.Clientset, namespace string, secretName string, auths *Auths) error { + secret, err := cli.CoreV1().Secrets(namespace).Get(context.TODO(), secretName, metav1.GetOptions{}) + if err != nil { + return err + } + registries := Registries{} + if secretData, ok := secret.Data[".dockerconfigjson"]; ok { + auths := Auths{} + err := json.Unmarshal(secretData, &auths) + if err != nil { + return err + } + registries = auths.Registries + } else if secretData, ok := secret.Data[".dockercfg"]; ok { + err = json.Unmarshal(secretData, ®istries) + if err != nil { + return err + } + } + for registry, creds := range registries { + auths.Registries[registry] = creds + } + return nil +} + // patchNodeStatus patches the status of a node func patchNodeStatus(c *k8sclient.Clientset, nodeName string, patches []jsonPatch) error { if len(patches) > 0 {