Search code examples
spring-cloudspring-cloud-kubernetes

How to use multiple secrets sources in Spring Cloud Kubernetes


I have multiple Secret resources in my Kubernetes cluster and I would like to have my Spring Boot application, which uses Spring Cloud Kubernetes, use them.

I have read the docs on it, but I still cannot wrap my head around it.

My Secret resource looks like this:

apiVersion: v1
kind: Secret
metadata:
  name: service1-secrets
  namespace: my-app-namespace
type: Opaque
data:
  config.service1.pass=<base64-encoded String>
  config.service1.user=<base64-encoded String>

I also have a similar Secret resource for other services my application needs.

The secrets are deployed in the same namespace and the ServiceAccount as well as I can see them.

How do I specify multiple secret sources? Do I have to use a bootstrap(-env).yaml instead of a properties file?

In my bootstrap.properties file (for the correct environment, -azure), I have:

    spring.cloud.kubernetes.secrets.enabled=true
    spring.cloud.kubernetes.enabled=true
    spring.cloud.kubernetes.config.namespace=my-app-namespace
    spring.cloud.kubernetes.client.namespace=my-app-namespace
    spring.cloud.kubernetes.secrets.namespace=my-app-namespace
    spring.cloud.kubernetes.secrets.sources={labels={name=service1-secrets}}, {name=service2-secrets}

Unfortunately when starting up the pod, I get the following error:

{"TIMESTAMP":"2023-03-26 03:34:35,697","SEVERITY":"WARN","APPLICATION":"springAppName_IS_UNDEFINED","CLASS":"o.s.c.a.AnnotationConfigApplicationContext","MESSAGE":"Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'secretsPropertySourceLocator' defined in class path resource [org/springframework/cloud/kubernetes/client/config/KubernetesClientBootstrapConfiguration.class]: Unsatisfied dependency expressed through method 'secretsPropertySourceLocator' parameter 0; nested exception is org.springframework.boot.context.properties.ConfigurationPropertiesBindException: Error creating bean with name 'spring.cloud.kubernetes.secrets-org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties': Could not bind properties to 'SecretsConfigProperties' : prefix=spring.cloud.kubernetes.secrets, ignoreInvalidFields=false, ignoreUnknownFields=true; nested exception is org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'spring.cloud.kubernetes.secrets.sources' to java.util.List<org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties$Source>",...}

followed by:

{"TIMESTAMP":"2023-03-26 03:34:35,994","SEVERITY":"ERROR","APPLICATION":"springAppName_IS_UNDEFINED","CLASS":"o.s.b.d.LoggingFailureAnalysisReporter","MESSAGE":"\n\n***************************\nAPPLICATION FAILED TO START\n***************************\n\nDescription:\n\nFailed to bind properties under 'spring.cloud.kubernetes.secrets.sources' to java.util.List<org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties$Source>:\n\n    Property: spring.cloud.kubernetes.secrets.sources\n    Value: \"{labels={name=service1-secrets}}, {name=service2-secrets}\"\n    Origin: class path resource [config/bootstrap-azure.properties] - 6:41\n    Reason: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [java.util.List<org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties$Source>]\n\nAction:\n\nUpdate your application's configuration\n",...}

Please note: I am currently not using volumeMounts or environment variables in my deployment.yaml file to pass secrets. From what I understand a Spring Cloud Kubernetes enabled application can "discover" or "find" secrets in a given namespace on the actual Kubernetes cluster via API. I know it's rather insecure (or discouraged), but I do not care for the time being.


Solution

  • Spring cloud kubernetes contributor here.

    Let's start slow, if you do not mind. First things first is to tell what version are you using of spring-cloud-kubernetes. Then, your link that you posted redirects to "namespace resolution" chapter, which is most probably not what you want. You might want to read this part. Though it is specific to ConfigMaps, secrets are computed the same way. So you need a configuration like this (for example):

    spring:
      application:
        name: cloud-k8s-app
      cloud:
        kubernetes:
          secrets:
            namespace: <YOUR_NAMESPACE>
            enableApi: true
            sources:
             - name: secret-1
             - name: secret-2
    

    Since you use a single namespace, you can define it at the secrets level. If on the other hand you will need to read secrets from different namespaces, you will need to change a bit your configuration:

    spring:
      application:
        name: cloud-k8s-app
      cloud:
        kubernetes:
          secrets:
            namespace: <YOUR_NAMESPACE>
            enableApi: true
            sources:
             - name: secret-1
               namespace: namespace_1
             - name: secret-2
    

    In this manner, secret-1 will be read from namespace_1, and secret-2 will be read from <YOUR_NAMESPACE>.


    If you use version 3.x.x of spring-cloud-kubernetes, things might be slightly involved, because we do not support bootstrap (by default), you need to opt-in into it. But that is the only way to read multiple secrets with version 3.x.x anyway. If that is the case, leave a comment and I will update this answer.