Contrast-Security-OSS/agent-operator

Negating glob patterns not usable in AgentInjector resource

lkallas opened this issue ยท 6 comments

Background

Consider a case where there are many pods running with main container and some sidecar container (e.g. db-proxy) inside a Kubernetes cluster.

Contrast Kuberentes operator AgentInjector custom resource allows one to specify to which containers the agent is injected to inside a pod.

spec.selector.images allows specifying a glob pattern which is used to find a matching container where agent is injected.

I can see that there is C# Glob package in use.

Problem

In my case, I would like to inject nodejs agent into every container in many pods. But I do not want to inject it to db-proxy container which is not a nodejs workload and also does not need any instrumentation.

I struggle to find a good glob pattern for this purpose.
Seems that there actually isn't one for my case - well, Glob package does not implement it (not correctly at least IMO).

So to illustrate this problem, take this small program snippet I used to determine the glob pattern for my case.
I would like to match any Docker image that does not have "some-proxy" in it's name using negating pattern.

using System;
using GlobExpressions;

public interface IGlobMatcher
{
	bool Matches(string pattern, string value);
}

public class GlobMatcher : IGlobMatcher
{
	public bool Matches(string pattern, string value)
	{
		var glob = new Glob(pattern, GlobOptions.CaseInsensitive | GlobOptions.Compiled);
		return glob.IsMatch(value);
	}
}

public class Program
{
	public static void Main()
	{
		var matcher = new GlobMatcher();		
		var pattern = "!(*some-proxy*)";  // Trying to use negating pattern
		
		var result1 = matcher.Matches(pattern, "europe-docker.pkg.dev/project/docker-images/whatever:latest"); // This should return True
		Console.WriteLine(result1); // False
		
		var result2 = matcher.Matches(pattern, "europe-docker.pkg.dev/project/docker-images/some-proxy:latest"); // This should return False
		Console.WriteLine(result2); // False
	}
}

I see that you do not have a such test-case either in your tests.

I could use something like:

var pattern = "**/[!some-proxy]*";

But it will match characters "s", "o", "m", "e", "-", "p", "r", "o", "x", "y" and not in that particular order + char "o" does not have to repeat.
So it matches any permutation of those characters e.g. "oepx-msyro" and "sexy-prom". Not accurate enough.

Workaround

The only workaround right now is to specify each image name in the manifest I wish to have agent injected to. The list grows really long if there are hundreds of unique microservices/images.

apiVersion: agents.contrastsecurity.com/v1beta1
kind: AgentInjector
metadata:
  name: contrast-agent-injector
  namespace: somenamespace
spec:
  enabled: true
  version: latest
  type: nodejs
  selector:
    images:
      - "*important-web*"
      - "*some-other-web*"
      - "*restful-api*"
    labels:
      - name: contrast
        value: enabled

Could you assist/profide a fix?
@Silvenga @gamingrobot

Thank you!

Just a heads up, I no longer work for @Contrast-Security-OSS.

Just a heads up, I no longer work for @Contrast-Security-OSS.

I'm so sorry for mentioning you!
Just looked at the contributions insights and you were the top contributor - therefore mentioned you

Perhaps adding a regex support instead of glob patterns or adding glob + regex pattern support for image matching would make it a better/more flexible solution.

For example create new matcher e.g. RegexMatcher that will be used in the matching function if the spec.selector.images has image with a prefix re# indicating that regular expression should be used for matching (not glob).
That way it wouldn't be a breaking change either.

In my case negative lookahead regex would do the trick.

^(?!.*some-proxy).*$

So given my suggestion the manifest could look like this:

apiVersion: agents.contrastsecurity.com/v1beta1
kind: AgentInjector
metadata:
  name: contrast-agent-injector
  namespace: somenamespace
spec:
  enabled: true
  version: latest
  type: nodejs
  selector:
    images:
      - "re#^(?!.*some-proxy).*$"
    labels:
      - name: contrast
        value: enabled

Hi @lkallas Thanks for the suggestion. I've chatted with our developers and have submitted an enhancement request for the behavior updates on the operator. (Ref: CUST-4301 our ticket tracking system is internal.)

While in this case, since the other container is not a nodeJS application, we would essentially do nothing. Though it is still going through the motions, it should have no effects on the application. There is still a case for this and tighter control over the containers we inject into would be great.

The operator now supports additional labels that you can add like contrast-agent: nodejs. Does that work for your use case here @lkallas ?

The operator now supports additional labels that you can add like contrast-agent: nodejs. Does that work for your use case here @lkallas ?

@johnament Can you elaborate how does it work, provide the documentation link?