{"record":{"id":"c586702a9c63c3d5","repo":"kubernetes/kops","slug":"secrets-path-is-not-cluster-readable-v","errorCode":null,"errorMessage":"secrets path is not cluster readable: %v","messagePattern":"secrets path is not cluster readable: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/cloudup/populate_cluster_spec.go","lineNumber":209,"sourceCode":"\t}\n\n\tsecretStore, err := clientset.SecretStore(cluster)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif cluster.Spec.ConfigStore.Secrets == \"\" {\n\t\thasVFSPath, ok := secretStore.(fi.HasVFSPath)\n\t\tif !ok {\n\t\t\t// We will mirror to ConfigBase\n\t\t\tbasedir := configBase.Join(\"secrets\")\n\t\t\tcluster.Spec.ConfigStore.Secrets = basedir.Path()\n\t\t} else if vfs.IsClusterReadable(hasVFSPath.VFSPath()) {\n\t\t\tvfsPath := hasVFSPath.VFSPath()\n\t\t\tcluster.Spec.ConfigStore.Secrets = vfsPath.Path()\n\t\t} else {\n\t\t\t// We could implement this approach, but it seems better to get all clouds using cluster-readable storage\n\t\t\treturn fmt.Errorf(\"secrets path is not cluster readable: %v\", hasVFSPath.VFSPath())\n\t\t}\n\t}\n\n\t// Normalize k8s version\n\tversionWithoutV := strings.TrimSpace(cluster.Spec.KubernetesVersion)\n\tversionWithoutV = strings.TrimPrefix(versionWithoutV, \"v\")\n\tif cluster.Spec.KubernetesVersion != versionWithoutV {\n\t\tklog.V(2).Infof(\"Normalizing kubernetes version: %q -> %q\", cluster.Spec.KubernetesVersion, versionWithoutV)\n\t\tcluster.Spec.KubernetesVersion = versionWithoutV\n\t}\n\n\tif cluster.Spec.CloudProvider.Openstack == nil {\n\t\tif cluster.Spec.API.DNS == nil && cluster.Spec.API.LoadBalancer == nil {\n\t\t\tsubnetTypesByName := map[string]kopsapi.SubnetType{}\n\t\t\tfor _, subnet := range cluster.Spec.Networking.Subnets {\n\t\t\t\tsubnetTypesByName[subnet.Name] = subnet.Type\n\t\t\t}\n","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/cloudup/populate_cluster_spec.go#L191-L227","documentation":"Analogous to the keypair store, the secrets store path must be readable by cluster nodes. run() throws this when the resolved SecretStore's VFS path fails vfs.IsClusterReadable. Without node-readable secrets storage, bootstrap cannot fetch required secrets.","triggerScenarios":"cluster.spec.configStore.secrets is empty and the SecretStore's VFS path (via fi.HasVFSPath) is not cluster readable — e.g. a secrets bucket inaccessible to node IAM roles.","commonSituations":"Secrets stored in a separate bucket with restrictive permissions; local file:// secret store used with a real cloud cluster; cloned cluster configs referencing another account's storage.","solutions":["Set configStore.secrets to a cluster-readable path (or leave unset to mirror into <configBase>/secrets).","Grant node instance profiles/roles read access to the secrets bucket.","Verify with the path in the error message that the scheme and bucket are correct and accessible from nodes."],"exampleFix":"# before\nconfigStore:\n  secrets: file:///etc/kops/secrets\n# after\nconfigStore:\n  secrets: s3://my-kops-state-bucket/cluster.example.com/secrets","handlingStrategy":"validation","validationCode":"if cluster.Spec.ConfigStore.Secrets != \"\" && !strings.HasPrefix(cluster.Spec.ConfigStore.Secrets, \"s3://\") {\n\treturn fmt.Errorf(\"secrets store path must be cluster readable\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Leave configStore.secrets unset to mirror into configBase/secrets","Keep secrets in the same bucket as config with node read access","Re-check secret store permissions after IAM or account changes"],"tags":["vfs","secrets","storage-permissions"],"backgroundTag":"invalid-storage-path","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}