Contrast-Security-OSS / agent-operator

A K8s operator to inject agents into existing K8s workloads.
Other
15 stars 5 forks source link

Negating glob patterns not usable in AgentInjector resource #189

Open lkallas opened 8 months ago

lkallas commented 8 months ago

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!

Silvenga commented 8 months ago

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

lkallas commented 8 months ago

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

lkallas commented 7 months ago

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
marklacasse commented 7 months ago

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.

johnament commented 3 months ago

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

lkallas commented 3 months ago

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?