spring-cloud / spring-cloud-kubernetes

Kubernetes integration with Spring Cloud Discovery Client, Configuration, etc...
Apache License 2.0
3.47k stars 1.04k forks source link

Namespace filter for service discovery #1000

Closed vchegwidden closed 1 year ago

vchegwidden commented 2 years ago

This request is to add the ability to filter/limit namespaces to use for service discovery in the DiscoveryClient through a property

When working as a tenant on a shared cluster we don't have permissions to view resources in all namespaces. We also don't have permissions to create cluster roles and cluster rolebindings. In these situations you can't use the spring.cloud.kubernetes.discovery.all-namespaces=true property since it will throw an exception when trying to get resources in namespaces you don't have access to. Not having the option to list namespaces to take into consideration means you would be limited to a single namespace for all your deployments, which isn't great...

I've been trying to run Spring boot admin in a cluster where my access is limited to only the namespaces I own. When trying to use the DiscoveryClient and setting all-namespaces=true the following exception is thrown since the service account doesn't have the correct permissions

"stack_trace":"i.f.k.c.KubernetesClientException: Failure executing: GET at: https://<nodeIP>/api/v1/namespaces/some-namespace/services. 
    Message: Forbidden!Configured service account doesn't have access. Service account may have been revoked. services is forbidden: 
    User \"system:serviceaccount:sba:spring-boot-admin\" cannot list resource \"services\" in API group \"\" in the namespace \"some-namespace\".

The work-around I've used is to implement my own DiscoveryClient that takes a list of namespaces from a property, then overriding the behaviour of getServices and getEndpointList to only look in the namespaces supplied (Note I've only tested this with the fabric8 client)

It would however be great if this was included out of the box.

@Value("#{'${spring.cloud.kubernetes.discovery.filter-namespaces:}'.split(',')}")
private List<String> filteredNamespaces;

@Override
public List<String> getServices(Predicate<Service> filter) {
    if (filteredNamespaces.isEmpty()) {
        return super.getServices(filter);
    }

    List<String> services = new ArrayList<>();
    for (String ns : filteredNamespaces) {
        services.addAll(
            this.client.services()
                .inNamespace(ns).list().getItems().stream().filter(filter).map(s -> s.getMetadata().getName().toList());
    }
}

@Override
public List<Endpoints> getEndPointsList(String serviceId) {
    if (filteredNamespaces.isEmpty()) {
        return super.getEndpointList(serviceId);
    }

    List<Endpoints> endpoints = new ArrayList<>();
    for (String ns : filteredNamespaces) {
        endpoints.addAll(
            this.client.endpoints()
                .inNamespace(ns).withField("metadata.name", seviceId).withLabels(properties.getServiceLabels().list().getItems());
    }
}
wind57 commented 2 years ago

this is for sure doable, and it is on my radar, just dropping a note here.

mehdikhelif commented 2 years ago

That would be really a nice improvement

bj-1795 commented 2 years ago

hi @mbialkowski1 is there any progress on this one?

vbose commented 2 years ago

We badly need this feature. Can someone provide an update on this issue? We are still in Java 8 and we would really appreciate it if the fix is backward compatible with Java 8.

wind57 commented 2 years ago

The PR is in review, I assume a few weeks and it will be part of main. No gurantees whatsoever. The thing is, its going to be part of main branch, jdk-17 only.

vbose commented 2 years ago

Also, spring.cloud.kubernetes.discover.filter: "metadata.labels['spring-boot']" does not have any effect. It discovers all services regardless. Any workaround for this issue as well is available? We use spring-cloud-starter-kubernetes-fabric8 dependency in the pom.

wind57 commented 2 years ago

Looks like a diff issue, please raise a separate issue. Thank you.

luisalves00 commented 1 year ago

Also, spring.cloud.kubernetes.discover.filter: "metadata.labels['spring-boot']" does not have any effect. It discovers all services regardless. Any workaround for this issue as well is available? We use spring-cloud-starter-kubernetes-fabric8 dependency in the pom.

This seems to ("almost") work:

spring:
    cloud:
        kubernetes:
            discovery:
                all-namespaces: true
                filter: "#root.metadata.namespace matches '^uat.*$'"

On the health endpoint (show-details: always) it seems to work as there the filter seems to be applied, but my openfeign client still finds services that are not in the list and I cannot figure out why.

Any ideas why openfeign might ignore the filter?

For the ones that are not using fabric8 this might help: 990

luisalves00 commented 1 year ago

Opened a new ticket as this one is closed.