Skip to content

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

RHTPA exploit intelligence operator

The RHTPA exploit intelligence operator automates the deployment and lifecycle management of the RHTPA exploit intelligence stack on Red Hat OpenShift clusters.

Components

The operator manages the following components:

  • Cache: Reverse proxy and cache for external APIs (NVD, GitHub, FIRST, NVIDIA NIM, OpenAI)
  • Engine: Primary vulnerability analysis engine
  • WebApp: Quarkus-based UI and API

Database: RHTPA exploit intelligence requires a MongoDB instance for data persistence. The operator does not provision MongoDB — you must create the database separately and configure the connection before deploying the stack.

Prerequisites

Cluster requirements

  • OpenShift: Version 4.16 or later
  • Persistent storage (default storage class configured)
  • Cluster-admin privileges

API keys

Obtain the following credentials before you begin. You will use these in Step 3: Configure secrets.

Key Description Where to get it
ghsa_api_key GitHub Personal Access Token for the GitHub Security Advisory API github.com/settings/tokens — generate a new fine-grained token
nvidia_api_key NVIDIA NGC API key org.ngc.nvidia.com
ngc_api_key NVIDIA NGC organization key org.ngc.nvidia.com
nvd_api_key National Vulnerability Database API key nvd.nist.gov/developers/request-an-api-key
serpapi_api_key SerpAPI key for web search serpapi.com/manage-api-key

For a MongoDB instance, see Connecting to MongoDB.

Installation

Step 1: Install the operator

Navigate to Operators → OperatorHub or Ecosystem → Software Catalog and search for RHTPA exploit intelligence, then click Install.

Wait for the operator to become ready. The namespace depends on the install mode selected during installation (for example, openshift-operators for all-namespaces mode):

oc get csv -n <operator-namespace> | grep exploit-iq-operator

Step 2: Create a project

oc new-project exploit-iq

Step 3: Configure secrets

API keys

Generate the HMAC_SECRET_KEY:

export HMAC_SECRET_KEY=$(openssl rand -base64 32)
oc create secret generic exploit-iq-secret \
  --from-literal=ghsa_api_key="<YOUR_GITHUB_TOKEN>" \
  --from-literal=nvidia_api_key="<YOUR_NVIDIA_KEY>" \
  --from-literal=ngc_api_key="<YOUR_NGC_KEY>" \
  --from-literal=nvd_api_key="<YOUR_NVD_KEY>" \
  --from-literal=serpapi_api_key="<YOUR_SERPAPI_KEY>" \
  --from-literal=hmac-secret-key="$HMAC_SECRET_KEY"

Red Hat registry credentials

Download the pull secret file from your registry service account, then run the following command. The operator does not generate this Docker config Secret from exploit-iq-secret.

oc create secret generic image-registry-credentials \
  --from-file=.dockerconfigjson=<path-to-pull-secret.json> \
  --type=kubernetes.io/dockerconfigjson

MongoDB connection

Mode A — host and port:

oc create secret generic mongodb-credentials \
  --from-literal=host="<your-host>:27017" \
  --from-literal=username="<app-username>" \
  --from-literal=password="<app-password>"

Mode B — connection string (Atlas or SRV):

oc create secret generic mongodb-credentials \
  --from-literal=connection-string="mongodb+srv://<user>:<password>@<cluster>/"

OAuth client secret (optional)

The operator generates a random OAuth client secret automatically. To provide your own:

export OAUTH_CLIENT_SECRET=$(openssl rand -base64 32)
export OPENSHIFT_DOMAIN=$(oc get dns cluster -o jsonpath='{.spec.baseDomain}')

oc create secret generic oauth-client-secret \
  --from-literal=client-secret="$OAUTH_CLIENT_SECRET" \
  --from-literal=openshift-domain="$OPENSHIFT_DOMAIN"

Step 4: Deploy the stack

Create an ExploitIQStack instance:

Important: The sample CR file is an example only. You must review and verify that the secret names in the file match the secrets you created in Step 3 before applying it.

For available sample CRs and guidance on which to use for your environment, see the samples README.

oc apply -f config/samples/exploitiq_v1alpha1_exploitiqstack.yaml

Example CR:

apiVersion: exploitiq.openshift.io/v1alpha1
kind: ExploitIQStack
metadata:
  name: exploit-iq
spec:
  webApp:
    registryCredentialsSecretRef:
      name: image-registry-credentials
  database:
    mode: hostPort       # or: connectionString (see Step 3 — MongoDB connection)
    secretRef:
      name: mongodb-credentials

Embedding model (optional)

By default the stack sends text-embedding requests to the NVIDIA-hosted endpoint. To self-host the embedder (or point it at a different endpoint), set spec.embedding:

Parameter Role Default when unset
spec.embedding.baseURL Upstream endpoint the cache proxies embedding requests to (drives NGINX_UPSTREAM_NIM_EMBED). Set this to a self-hosted embedding NIM to keep embeddings in-cluster. NVIDIA-hosted endpoint (https://integrate.api.nvidia.com)
spec.embedding.modelName Identifier of the embedding model served at baseURL (drives the engine's EMBEDDER_MODEL_NAME). Engine built-in default (nvidia/nv-embedqa-e5-v5)
spec.embedding.authSecretRef Secret key holding the API key for the embedding upstream. Supplies NVIDIA_API_KEY to the nginx-cache init container, which rewrites the Authorization header on /nim_embed requests. Required when llm.mode: openai, llm.authSecretRef is set, and the embedding upstream requires API key auth — without it the cache sends Authorization: Bearer EMPTY and receives 401. Unset — falls back to exploit-iq-secret.nvidia_api_key (nim mode) or EMPTY (openai mode with LLM key configured)
spec:
  embedding:
    baseURL: http://nim-embed.<namespace>.svc:8000
    modelName: nvidia/nv-embedqa-e5-v5
    # authSecretRef: required when llm.mode is 'openai' with llm.authSecretRef set
    #   and the embedding upstream requires API key auth.
    # authSecretRef:
    #   name: exploit-iq-secret
    #   key: nvidia_api_key

Omit the embedding block entirely to keep the NVIDIA-hosted default. See the API reference for the full field list.

Monitor progress:

oc get exploitiqstack exploit-iq -w

Step 5: Post-deployment tasks

After the stack is running, grant users access by adding them to the exploit-iq-view group:

oc adm groups new exploit-iq-view
oc adm groups add-users exploit-iq-view <username>

For additional post-deployment options, see the kustomize deployment guide.

Network policies

On OpenShift, the operator creates one NetworkPolicy for each operand of an ExploitIQStack: exploit-iq (engine), exploit-iq-client (web app) and nginx-cache (cache). Each policy selects only the pods of that CR and operand, by app.kubernetes.io/instance=<CR name> and app.kubernetes.io/component (engine, webapp or cache). The CR owns the policies, so deleting the CR deletes them. The operator creates no NetworkPolicy on Kubernetes.

Each policy sets both Ingress and Egress. Every operand can always reach cluster DNS (openshift-dns, UDP and TCP port 5353), the OpenShift router on TCP 443 and 80, and the internet on any port. The internet rule allows 0.0.0.0/0 (when the cluster has IPv4 CIDRs) and ::/0 (when it has IPv6 CIDRs), each except the pod and service CIDRs of the same IP family. IPv4-only, dual-stack and IPv6-only clusters are supported. Pod and service IPs of other workloads are blocked unless a rule below allows them.

The operator watches the cluster network config (network.config.openshift.io/cluster). When the pod or service CIDRs change, it reconciles every ExploitIQStack and updates the policies.

The router egress rule keeps Route hostnames (*.apps.<domain>) reachable. On some platforms (Azure, GCP, MetalLB) the router address is a cluster virtual IP, so traffic to a Route hostname reaches router pod IPs inside the pod network, which the internet rule excludes. Without the router rule, OpenShift OAuth login, an in-cluster Keycloak exposed by a Route, and a KServe model exposed by a Route would fail.

Every ingress rule that allows the OpenShift router also allows the host-network namespaces (policy-group.network.openshift.io/host-network). On bare metal, vSphere, OpenStack and Nutanix the default router runs on the host network, and OVN-Kubernetes treats its traffic as host-network traffic, not as traffic from the router namespace.

Operand Ingress allowed Other egress allowed
engine TCP 8080 from the cache and web app pods of the stack. TCP 6006 (tracing UI) from the OpenShift router and the host network, only when spec.engine.tracingRouteEnabled is true. TCP 8080 to the cache pods. TCP 8443 to the web app pods. Any port to the namespace of an in-cluster spec.auth.oidc.issuerURL.
webapp TCP 8443 from the OpenShift router and the host network, from the engine pods of the stack, and from add-on pods (see below). TCP 9000 from the router and the host network, only when spec.webApp.swaggerUIRouteEnabled is true, and from openshift-user-workload-monitoring, only when the cluster has the monitoring API. TCP 8080 to the cache and engine pods. Any port to the namespace of an in-cluster MongoDB host (taken from the database Secret) or OIDC issuer.
cache TCP 8080 from the engine and web app pods of the stack. TCP 8080 to the engine pods. Any port to the namespace of an in-cluster spec.llm.baseURL or spec.embedding.baseURL.

An "in-cluster" target is a Service host such as <service>.<namespace>, <service>.<namespace>.svc or <service>.<namespace>.svc.cluster.local. The operator allows any port in that namespace, because a policy matches the pod port and not the Service port. An external name with two labels, such as example.com, also gets an unused rule for a namespace named com. Traffic to that host still works. A short host without dots (for example mongodb:27017) allows any pod in the CR namespace. Other hosts, such as internet names and IPs outside the cluster CIDRs, use the internet rule (Route hostnames use the router egress rule or the internet rule).

Turn off the policies

To stop the operator from managing the policies, set spec.networkPolicy.enabled to false. The default is true. The operator then deletes the three policies.

spec:
  networkPolicy:
    enabled: false

Add-ons that call the web app

The web app accepts port 8443 from pods in the same namespace that carry the label app.kubernetes.io/part-of: exploitiq. Add this label to the pod template of an add-on that calls the web app, for example the ExploitIQ MCP server. Without it, calls from the add-on time out.

An MCP server that you installed before this label was required must be redeployed with the new label before, or together with, the operator upgrade. Otherwise its calls to the web app time out as soon as the operator creates the policies.

Headless ingress mode

When spec.ingress.mode is headless, the operator creates no Route. The web app policy still allows port 8443 only from the OpenShift router (and the host network), the engine pods and the add-on pods. A custom ingress controller or gateway that is not the OpenShift router cannot reach the web app on 8443. Create your own policy for it, for example:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-ingress-to-webapp
  namespace: <stack namespace>
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/instance: <CR name>
      app.kubernetes.io/component: webapp
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: <ingress controller namespace>
      ports:
        - protocol: TCP
          port: 8443

Destinations the operator cannot know

The operator cannot know a destination that depends on the analysis, for example a git server or a container registry that runs in another namespace of the cluster (such as the OpenShift image registry on port 5000), or a serverless KServe model whose traffic goes through a gateway in another namespace. The policies block these. NetworkPolicies are additive, so create your own policy to allow them. This example lets the engine pods connect to a git server in another namespace:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-engine-to-<destination>
  namespace: <stack namespace>
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/instance: <CR name>
      app.kubernetes.io/component: engine
  policyTypes:
    - Egress
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: <destination namespace>
      ports:
        - protocol: TCP
          port: <destination pod port>

Use app.kubernetes.io/component: webapp for registry scans by the web app, and app.kubernetes.io/component: cache for a model behind a gateway in another namespace.

Operator permissions

To read the cluster pod and service CIDRs, the operator needs get, list and watch on networks in the config.openshift.io API group. The role in the operator bundle already grants this. If the operator cannot read the CIDRs, it does not apply the policies or roll out the Deployments, and it retries.

Uninstalling

Delete the stack instance:

oc delete exploitiqstack --all -n <namespace>

Uninstall the operator from Operators → Installed Operators in the web console.

Additional resources

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages