Skip to content

Networking & security

Image URI validation

The Workload API can check a container's imageUri against an allowlist and a denylist of glob patterns when an artifact is created, updated, or cloned.

On self-managed installations this is an important safeguard. Workloads run user-supplied images, and without validation a user could reference an internal DataRobot registry image (for example, docker.io/datarobot/*) or a private cloud-registry host the platform can reach (for example, a private ECR or Artifact Registry). Enabling validation with a denylist covering your internal and private registries contains this supply-chain risk. Because host matching is case-insensitive, a registry can't be slipped past the denylist by changing the case of its hostname.

Configuration lives under the imageUriValidation key. It includes an enabled flag, an allowlist, and a denylist.

enabled flag settings

  • Type: bool. Default: false.
  • When false, no imageUri is checked or blocked—the feature is a no-op regardless of what allowlist/denylist contain.
  • When true, every container imageUri is checked against allowlist and denylist.

For example:

workload-api:
  imageUriValidation:
    enabled: false

allowlist and denylist settings

  • Both are lists of glob patterns (fnmatch-style, * wildcard) matched against the image URI.
  • denylist takes priority over allowlist—a URI matching any denylist pattern is rejected even if it also matches allowlist.
  • A URI that doesn't match denylist must match at least one allowlist pattern to be accepted; otherwise it's rejected.
  • Before matching, DataRobot strips any tag (:tag) or digest (@sha256:...), normalizes Docker Hub short forms to their canonical docker.io/... form (for example, alpine and library/alpine both normalize to docker.io/library/alpine), and lowercases the registry host. Host matching is therefore case-insensitive (MyRegistry.AzureCR.io and myregistry.azurecr.io are equivalent). The image path and tag remain case-sensitive.

The base chart (values.yaml) ships the following defaults, which apply on every cluster unless an overlay overrides them:

workload-api:
  imageUriValidation:
    enabled: false
    allowlist:
      - "docker.io/*"
      - "*.docker.io/*"
      - "quay.io/*"
      - "ghcr.io/*"
      - "public.ecr.aws/*"
      - "registry.gitlab.com/*"
      - "*.jfrog.io/*"
      - "nvcr.io/*"
    denylist:
      - "*.dkr.ecr.*.amazonaws.com/*"   # AWS ECR (private)
      - "*.azurecr.io/*"                # Azure ACR
      - "gcr.io/*"                      # GCP GCR
      - "*.gcr.io/*"                    # GCP GCR (regional hosts, e.g. us.gcr.io)
      - "*.pkg.dev/*"                   # GCP Artifact Registry
      - "docker.io/datarobot/*"
      - "docker.io/datarobotdev/*"
      - "*.docker.io/datarobot/*"
      - "*.docker.io/datarobotdev/*"

Configure per cluster

allowlist and denylist are only declared once, in the base chart—an overlay inherits both as-is unless it explicitly overrides one.

Helm replaces list values entirely rather than merging them, so overriding allowlist or denylist means re-declaring the full list you want, not just the entries you're adding or removing. For example, to keep every default denylist entry except the two datarobotdev globs:

workload-api:
  imageUriValidation:
    enabled: true
    denylist:
      - "*.dkr.ecr.*.amazonaws.com/*"
      - "*.azurecr.io/*"
      - "gcr.io/*"
      - "*.gcr.io/*"
      - "*.pkg.dev/*"
      - "docker.io/datarobot/*"
      - "*.docker.io/datarobot/*"
      # datarobotdev/* intentionally omitted

To permit an additional registry, add it to allowlist alongside the existing entries:

workload-api:
  imageUriValidation:
    enabled: true
    allowlist:
      - "docker.io/*"
      - "*.docker.io/*"
      - "quay.io/*"
      - "ghcr.io/*"
      - "public.ecr.aws/*"
      - "registry.gitlab.com/*"
      - "*.jfrog.io/*"
      - "nvcr.io/*"
      - "registry.example.com/*"

Artifact and workload validation

Existing artifacts and Workloads aren't re-validated—the check only runs on write (create, update, clone).

Image pull secrets for private registries

To allow workloads to pull images from private registries, supply extra image pull secrets to be mounted on each LRS instance:

covalent-cloud-server:
  imagePullSecrets:
    - secret-name-1
    - secret-name-2

Networking

The LRS operator applies a default NetworkPolicy to all workload pods in the LRS namespace. No additional configuration is required for most deployments. The defaults are:

  • A deny-all ingress/egress base policy applied to all LRS pods.
  • Egress to the public internet (0.0.0.0/0, all ports) by default, excluding the private RFC 1918 ranges (see the warning below).
  • Ingress to the workload's service port from the configured ingress controller and pod selectors.

To add extra egress NetworkPolicies—applied to all LRS instances, including Custom Models and Custom Apps—use lrsEgressNetworkPolicies. Each entry selects pods by a label key/value within the LRS namespace and opens the specified ports for egress:

lrs-operator:
  operator:
    lrsEgressNetworkPolicies:
      - name: "allow-some-label-name-egress"
        podSelectorLabel: "some-label-name"
        podSelectorValue: "some-label-value"
        namespaceSelector: "some-namespace"
        ports:
          - protocol: "TCP"
            port: 9999

Default egress blocks private (RFC 1918) ranges

In addition to the LRS operator policy above, covalent-cloud-server supplies a default egress policy (created by the LRS operator) that allows 0.0.0.0/0 except the private ranges 10.0.0.0/8, 172.16.0.0/12, and 192.168.0.0/16. On self-managed clusters this silently blocks workloads from reaching internal services (databases, object storage such as MinIO, internal APIs) at private addresses.

These rules live at covalent-cloud-server.workload.lrs.networkPolicies.egress. Because Helm replaces a list wholesale, re-declare the default rule and then adjust it. Egress rules are additive—a connection is allowed if any rule permits it—and port: -1 means all ports.

To allow one internal subnet while keeping the rest of the private ranges blocked, add a targeted rule alongside the default:

covalent-cloud-server:
  workload:
    lrs:
      networkPolicies:
        egress:
          - cidr: 0.0.0.0/0          # default: public internet, minus private ranges
            except:
              - 10.0.0.0/8
              - 172.16.0.0/12
              - 192.168.0.0/16
            ports:
              - port: -1
          - cidr: 10.20.0.0/16       # allow one internal subnet (for example, a database VLAN)
            ports:
              - port: 5432

To open an entire private range instead, drop it from the except list:

covalent-cloud-server:
  workload:
    lrs:
      networkPolicies:
        egress:
          - cidr: 0.0.0.0/0
            except:
              - 172.16.0.0/12
              - 192.168.0.0/16       # 10.0.0.0/8 removed, now reachable
            ports:
              - port: -1

Restrict workload service accounts

By default the LRS operator restricts which Kubernetes service accounts workloads may run under. Keep the allowlist enabled and list only the service accounts workloads are permitted to assume:

lrs-operator:
  operator:
    config:
      enableServiceAccountAllowList: true
      allowedServiceAccountNames:
        - prediction-server-sa

If workloads are placed in a namespace other than the release namespace, covalent-cloud-server also needs cross-namespace RBAC to create and watch their LRS resources:

covalent-cloud-server:
  workload:
    lrs:
      crossNamespaceRbac:
        enabled: true