Skip to content

Operator RBAC roles testing with LocallyRunOperatorExtension #2753

Open
@k-wall

Description

@k-wall

Is your feature request related to a problem? Please describe.

We want to test our operator's RBAC roles from test cases written using LocallyRunOperatorExtension.

why: we found that we weren't finding RBAC misconfigurations until quite late in the development cycle - until the operator was deployed to Kubernetes with the real RBAC resource deployed. We want a way to shift left. We already had a suite of operator tests written using LocallyRunOperatorExtension, but these weren't flagging the errors, because the framework makes no consideration for RBAC.

To work around, we wrote a Junit extension LocallyRunningOperatorRbacHandler that cooperates with LocallyRunOperatorExtension. It configures the Kubernetes client passed to the LocallyRunOperatorExtension so that it uses an impersonated user. It also takes responsibility to load the RBAC roles and creates bindings between the impersonated user and the roles. It also exposes a second Kubernetes client that is effectively root - so that the test can create the resources it needs without being impeded.

You can see it in action https://github.com/kroxylicious/kroxylicious/blob/edfc2267cac9853169e0a32c7c1aa00cb11189ae/kroxylicious-operator/src/test/java/io/kroxylicious/kubernetes/operator/KafkaProtocolFilterReconcilerIT.java#L56.

Basically the key parts:

    @RegisterExtension
    static LocallyRunningOperatorRbacHandler rbacHandler = new LocallyRunningOperatorRbacHandler("install", "*.ClusterRole.kroxylicious-operator-watched.yaml");

    @RegisterExtension
    LocallyRunOperatorExtension extension = LocallyRunOperatorExtension.builder()
            .withReconciler(new KafkaProtocolFilterReconciler(Clock.systemUTC(), SecureConfigInterpolator.DEFAULT_INTERPOLATOR))
            .withAdditionalCustomResourceDefinition(KafkaProtocolFilter.class)
            .withKubernetesClient(rbacHandler.operatorClient()) // <--- injects impersonated user.
            .waitForNamespaceDeletion(true)
            .withConfigurationService(x -> x.withCloseClientOnStop(false))
            .build();

    private final LocallyRunningOperatorRbacHandler.TestActor testActor = rbacHandler.testActor(extension);

    @Test
    void myTest() {
        testActor.create(myCr());
        makeAssertions();
    }

The solution described above is working reasonable well for us. We wanted to share it, and also ask if you can suggest improvements.

There is a shortcoming. The kubernetesClient passed to the LocallyRunOperatorExtension has to have the RBAC required and the operator and the RBAC required by the LocallyRunOperatorExtension itself. This is a bit weak as it means the operator runs with more permissions than ought.

Describe the solution you'd like

Ideas for changes to LocallyRunOperatorExtension that would help this pattern:

  1. Have LocallyRunOperatorExtension distinguish between the kubernetesClient is uses for its own purposes and the client given to the operator. That would resolve the shortcoming I describe above.
 LocallyRunOperatorExtension extension = LocallyRunOperatorExtension.builder()
         ...
         .withOperatorKubernetesClient(aClientConfiguredWithAnImpersonatedUser())
         .withFrameworkKubernetesClient(aClient())
  1. Give LocallyRunOperatorExtension (or son of LocallyRunOperatorExtension) the smarts to handle RBAC and user impersonation itself. This would make our LocallyRunningOperatorRbacHandler redundant. I appreciate this would be a deeper change.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions