Security best practices¶
This guide is designed to help configure DataRobot following security best practices. DataRobot is a complex product with a wealth of configuration options -- at the time of writing (Jan 2023), this guide covers a small number of them and may be extended over time.
To configure these options, refer to the Tuning DataRobot Environment Variables section of this guide.
Session expiration¶
Leaving web UI sessions active forever is considered to be a security threat. DataRobot offers flexible control over the session expiration mechanism that allows you to configure session lifetime to be relative to last user visit or to expire after a certain amount of seconds after user login. Once a webui session has expired, a user must re-login and re-authenticate before they can get access to the application again.
Control over the session expiration logic is done by the boolean configuration option WEB_UI_SESSION_ABSOLUTE_EXPIRATION_ENABLED, which determines if the session lifetime should be treated as relative to last user action (request) or absolute to user login.
The lifetime (in seconds) of the session is configured by the WEB_UI_SESSION_LIFE_TIME configuration option. The default value is 0 which means that session lasts forever.
Configuration examples¶
The following configuration options in core.config_env_vars set session lifetime to 10 minutes relative to last user action:
# helm chart values snippet
core:
config_env_vars:
WEB_UI_SESSION_LIFE_TIME: 600
The following configuration options in core.config_env_vars set session lifetime to 10 minutes from the moment of user login:
# helm chart values snippet
core:
config_env_vars:
WEB_UI_SESSION_LIFE_TIME: 600
WEB_UI_SESSION_ABSOLUTE_EXPIRATION_ENABLED: true
Enable the inactive account policy¶
The inactive account policy disables inactive user accounts and marks them for deletion after organization-configured inactivity periods. You must enable the feature per cluster. After it is enabled, system administrators configure periods on each organization's profile. See Inactive account policy for the administrator workflow.
The disabling period you set in the UI must be longer than the web UI session lifetime.
The following configuration options in core.config_env_vars control the cluster-wide feature and the background review job:
| Variable | Type | Description |
|---|---|---|
ENABLE_AUTO_ACCOUNT_INACTIVITY_LIFECYCLE |
Boolean | Enables automated disabling and marking for deletion of inactive user accounts. |
AUTO_ACCOUNT_INACTIVITY_REVIEW_HOURS |
Integer | Interval, in hours, between runs of the background job that reviews inactive accounts. |
AUTO_ACCOUNT_INACTIVITY_REVIEW_USERS_LIMIT |
Integer | Maximum number of user accounts the background job processes in a single run. |
# helm chart values snippet
core:
config_env_vars:
ENABLE_AUTO_ACCOUNT_INACTIVITY_LIFECYCLE: true
To configure these options, refer to the section on tuning DataRobot environment variables .
Host header protection¶
To prevent host header poisoning, API servers validate the host header against an allowed list.
By default, the ALLOWED_HOSTS includes:
EXTERNAL_WEB_SERVER_URLfor the external load balancer (and for situations there is a proxy in front of the application)DATAROBOT_PUBLIC_LBfor theglobal.domainchart values setting for the application hostname when accessed via Ingress.DATAROBOT_INTERNAL_LBfor the service hostname within the Kubernetes cluster (e.g.datarobot-nginx) used by internal services.CUSTOM_MODEL_WEB_SERVER_URLfor a configurable hostname used by Custom Models for connecting to the API server
If necessary, the ALLOWED_HOSTS setting can be configured in chart values, using a comma-delimited string of hostnames:
core:
config_env_vars:
ALLOWED_HOSTS: "datarobot-nginx,datarobot.example.com"
Although not recommended, to disable host header protection:
core:
config_env_vars:
ALLOWED_HOSTS: "*"
Content Security Policy¶
A Content Security Policy (CSP) tells the browser which origins a page can load scripts, styles, images, and fonts from. The browser blocks resources from any other origin. CSP is disabled by default.
DataRobot can emit the header from either of two layers—application or core-nginx—which are described below:
| Layer | Applies to | Policy |
|---|---|---|
| Application | Application page routes only, not static assets | Strict. Uses default-src 'none' and a per-request nonce for scripts and styles. |
core-nginx |
Every web server response, including static assets | Permissive. Uses default-src 'self' and allows unsafe-eval and unsafe-inline. |
Enable only one layer. If a response has more than one CSP header, the browser enforces all of them. A resource loads only if every policy allows it, so the combined result is stricter than either policy alone and hard to debug.
Most installations should use the application layer. Use core-nginx only if the policy also needs to cover static assets.
Test with report-only mode¶
An enforcing policy blocks any resource it doesn't list. If a page depends on an unlisted resource, the page breaks immediately and gives no warning. To avoid this, either layer can send a Content-Security-Policy-Report-Only header instead. In report-only mode nothing is blocked. Each violation is posted to /report/csp and appears in the mmapp-app logs as CSP error.
Start in report-only mode. Leave it running long enough to cover real use, including maps, notebooks, file uploads, and model training. Add any origins that show up in the reports, then switch to enforcing.
Configure in the application layer¶
Start in report-only mode:
core:
config_env_vars:
ENABLE_CONTENT_SECURITY_POLICY_REPORT_ONLY: "True"
Add the origins that appeared in the reports. Each key is an origin, and its value lists the policy sections that origin is allowed in. Valid sections are script, style, img, media, connect, font and frame. Use all to allow the origin in every section.
core:
config_env_vars:
ENABLE_CONTENT_SECURITY_POLICY_REPORT_ONLY: "True"
CSP_ALLOWED_DOMAINS: '{"cdn.example.com": ["img", "font"], "sso.example.com": ["connect", "frame"]}'
When the reports show no more violations, switch to enforcing:
core:
config_env_vars:
ENABLE_CONTENT_SECURITY_POLICY: "True"
If both variables are set, ENABLE_CONTENT_SECURITY_POLICY takes precedence and the policy is enforced. You don't need to remove the report-only setting.
CSP_ALLOWED_DOMAINS must be valid JSON. If it isn't, the application won't start.
Each origin must include a host name with at least two labels, such as example.com. It can also include:
- A scheme, such as
https:// - A port, such as
:8443 - A leading wildcard, such as
*.example.com
The application rejects and logs the following values:
- A scheme on its own, such as
blob:. This turns off the section it's added to instead of widening it. - A bare
*. This also turns off the section. - A single-label host, such as
localhost. This can't be a real origin for a DataRobot installation.
Enforcing the policy also applies frame-ancestors 'none'. This overrides X-Frame-Options: SAMEORIGIN and blocks every site from embedding the application in a frame, including the application itself.
Configure in the nginx layer¶
component:
nginx:
csp:
enabled: true
reportOnly: true
allowedDomains:
https://cdn.example.com: {script: true, style: true, font: true}
https://example.com: {all: true}
To enforce the policy, set reportOnly: false.
The section names are the same as in the application layer. The difference is that here the value is a map instead of a list.
Integrate with customer HTTP proxy¶
It may be required to use a HTTP proxy for providing cluster access to specific resources over the internet or within the corporate network. To accomplish this, users may have a HTTP proxy such as Squid proxy configured to route cluster traffic.
To achieve this, network traffic from application services needs to be routed to the proxy, while internal traffic to services within the cluster remains internal and skips the proxy.
Configure the proxy¶
Set the global.proxy block. DataRobot injects HTTP_PROXY, HTTPS_PROXY, and NO_PROXY (in both upper and lower case) into every platform service pod, and maintains the NO_PROXY defaults for internal DataRobot services, so you only declare what is specific to your environment:
global:
proxy:
httpProxy: http://proxy.example.com:3128
httpsProxy: http://proxy.example.com:3128
noProxy:
- .dr-app # the namespace where DataRobot is installed
- datarobot.example.com # the global.domain host
- 10.100.0.1 # ClusterIP of the Kubernetes API (kubectl get svc kubernetes)
Make sure to replace proxy.example.com:3128 with your desired proxy address. Either proxy URL falls back to the other if only one is set.
If a service should never use the proxy because all of its traffic is internal, set proxy.disabled: true in that service's own values section.
Notebook and codespace session pods inherit these settings automatically. Custom-model dependency builds do not; they take their proxy settings from the CUSTOM_MODEL_DEPENDENCIES_* settings.
What belongs in noProxy¶
Entries are appended to the built-in defaults rather than replacing them. The defaults already cover internal service communication in Kubernetes, including every <service>.<namespace>.svc.cluster.local fully-qualified name for DataRobot services, so list only what is specific to your environment.
You MUST add the following based on your configuration:
- The namespace where DataRobot is installed (e.g.
.dr-appby default; replace it if you use another namespace) - The ClusterIP associated with the Kubernetes API accessed by pods, as returned by
kubectl get svc kubernetes(e.g.10.100.0.1for EKS by default) - The
global.domainhost where users access your DataRobot application (e.g.datarobot.example.com) - Hostnames for cloud services endpoints when deployed into a VPC.
For cloud providers' service endpoints:
- AWS: you can specify
.amazonaws.comfor all regions or.<region>.amazon.aws.comto limit to a specific region such asus-east-1. See AWS Service Endpoints for the supported list of hostnames. - GCP: you can specify
.googleapis.comto allow list all services, otherwise refer to Access Google APIs through endpoints for supported list of hostnames. - Azure: see Azure Private Endpoints for a list of supported hostnames.
Additionally, you SHOULD add the following if they apply to your situation:
- Internal IP addresses for corporate networks that shouldn't be accessed through the proxy (e.g.
1.2.3.4). NOTE - CIDR ranges aren't supported due to limitations in python. - Internal hostnames for resources that are accessible without the tunnel proxy (e.g.
.example.com)
Note
If you previously set HTTP_PROXY, HTTPS_PROXY, and NO_PROXY by hand in global.extraEnvVars or in a per-service section such as core.common_env_vars, remove them when you adopt global.proxy so that the two mechanisms cannot disagree. See Move forward-proxy settings to global.proxy.
Network policy for custom workloads on long running services (LRS)¶
Custom workloads (custom models, custom jobs, custom apps) running on LRS have a deny-by-default NetworkPolicy setup, accompanied by Network Policies
to allow specific ingress or egress with external services as needed.
These pages explain the specifics to each workload:
LRS's egress for custom workloads is governed by CUSTOM_WORKLOADS_PUBLIC_ACCESS_RULES, a JSON list of {cidr, except} rules. CUSTOM_WORKLOADS_PUBLIC_ACCESS_IGNORE_CIDRS is shorthand for the except list of a single default rule.
Default value of CUSTOM_WORKLOADS_PUBLIC_ACCESS_RULES
{"cidr": "0.0.0.0/0", "except": ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]}
CUSTOM_WORKLOADS_PUBLIC_ACCESS_RULES overrides CUSTOM_WORKLOADS_PUBLIC_ACCESS_IGNORE_CIDRS entirely
Setting CUSTOM_WORKLOADS_PUBLIC_ACCESS_RULES directly replaces the default above, so CUSTOM_WORKLOADS_PUBLIC_ACCESS_IGNORE_CIDRS no longer has any effect once you do—the two settings are not merged.
Allow egress to internal destinations¶
Some workloads need to reach an internal destination even though the default rule above denies egress to internal networks.
Because Kubernetes network policy rules are additive, the supported way to allow egress to a specific internal destination is to add a second rule to CUSTOM_WORKLOADS_PUBLIC_ACCESS_RULES for that destination, alongside the existing rule. If you've already customized CUSTOM_WORKLOADS_PUBLIC_ACCESS_IGNORE_CIDRS (see Restrict egress to specific CIDR ranges), carry those same CIDR ranges into the first rule's except list instead of the defaults shown below—setting CUSTOM_WORKLOADS_PUBLIC_ACCESS_RULES replaces that customization entirely rather than merging with it. For example, to allow egress to a target internal service at 172.16.5.10 while keeping the default except CIDR ranges for everything else:
core:
config_env_vars:
CUSTOM_WORKLOADS_PUBLIC_ACCESS_RULES: '[{"cidr":"0.0.0.0/0","except":["10.0.0.0/8","172.16.0.0/12","192.168.0.0/16"]},{"cidr":"172.16.5.10/32","except":[]}]'
The first rule keeps denying egress to the standard private ranges; the second rule adds an allow for 172.16.5.10/32 specifically, regardless of the first rule's except list.
If the additional rule has no effect
Although NetworkPolicy rules are meant to be additive, some clusters don't apply a second rule this way in practice—the effective behavior can depend on the CNI plugin and how it compiles multiple rules. If egress to the destination is still denied after adding the rule above, exclude the destination from the except CIDR ranges directly instead, as described in Restrict egress to specific CIDR ranges—narrowing the except list doesn't depend on multi-rule behavior, since it changes what a single rule excludes rather than relying on a second rule to add exceptions.
Restrict egress to specific CIDR ranges¶
To restrict egress for LRS to specific CIDR ranges, the CUSTOM_WORKLOADS_PUBLIC_ACCESS_IGNORE_CIDRS setting can be configured in chart values:
core:
config_env_vars:
CUSTOM_WORKLOADS_PUBLIC_ACCESS_IGNORE_CIDRS: '10.0.0.0/8,172.16.0.0/12,192.168.0.0/16'
This should be a comma-delimited list of CIDR ranges. This setting has no effect if CUSTOM_WORKLOADS_PUBLIC_ACCESS_RULES is already set directly (see the warning above)—edit that setting's first rule's except list instead.
It's important that LRS can egress to the datarobot-nginx pod for workloads to use the Public API.
Workloads should be using the internal server name datarobot-nginx instead of the external hostname (e.g. datarobot.example.com).
In order for LRS to communicate with the Public API using Ingress, the Network Policy would need to be modified to allow egress to the
CIDR range covering the ingress controller.
To stop restricting an entire range when nothing else in it needs to stay restricted, remove it from the except list entirely (e.g. 172.16.0.0/12), for example:
core:
config_env_vars:
CUSTOM_WORKLOADS_PUBLIC_ACCESS_IGNORE_CIDRS: '10.0.0.0/8,192.168.0.0/16'
To exclude specific IP addresses from an except CIDR while keeping the rest of it restricted, split the range into smaller CIDR ranges that leave those addresses out (e.g. 172.16.5.10/32). Compute the complement of the destination address against the excepted range with Python's ipaddress.address_exclude to avoid manual CIDR-math errors, for example:
import ipaddress
excepted_range = ipaddress.ip_network("172.16.0.0/12")
destination_host = ipaddress.ip_network("172.16.5.10/32")
remaining_cidrs = sorted(excepted_range.address_exclude(destination_host))
print(",".join(str(cidr) for cidr in remaining_cidrs))
This prints the comma-delimited list of CIDR ranges to use in CUSTOM_WORKLOADS_PUBLIC_ACCESS_IGNORE_CIDRS in place of 172.16.0.0/12:
172.16.0.0/22,172.16.4.0/24,172.16.5.0/29,172.16.5.8/31,172.16.5.11/32,172.16.5.12/30,172.16.5.16/28,172.16.5.32/27,172.16.5.64/26,172.16.5.128/25,172.16.6.0/23,172.16.8.0/21,172.16.16.0/20,172.16.32.0/19,172.16.64.0/18,172.16.128.0/17,172.17.0.0/16,172.18.0.0/15,172.20.0.0/14,172.24.0.0/13
core:
config_env_vars:
CUSTOM_WORKLOADS_PUBLIC_ACCESS_IGNORE_CIDRS: '10.0.0.0/8,172.16.0.0/22,172.16.4.0/24,172.16.5.0/29,172.16.5.8/31,172.16.5.11/32,172.16.5.12/30,172.16.5.16/28,172.16.5.32/27,172.16.5.64/26,172.16.5.128/25,172.16.6.0/23,172.16.8.0/21,172.16.16.0/20,172.16.32.0/19,172.16.64.0/18,172.16.128.0/17,172.17.0.0/16,172.18.0.0/15,172.20.0.0/14,172.24.0.0/13,192.168.0.0/16'
This narrows the except set so only 172.16.5.10 (target internal service) falls outside the excepted range, while every other address in 172.16.0.0/12—including the rest of 172.16.5.0/24—remains restricted.
Wrangler on Spark with external object storage¶
If your cluster is configured with an external s3-compatible object store (e.g. Minio), you must ensure that LRS pods hosting the interactive spark session for Data Wrangling have egress to the object store.
If your cluster uses Cilium CNI, create the following CiliumNetworkPolicy. Make sure to replace DATAROBOT-NAMESPACE and MINIO-API-HOSTNAME-GOES-HERE accordingly.
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: allow-lrs-spark-app-access-to-external-object-store
namespace: DATAROBOT-NAMESPACE
spec:
endpointSelector:
matchLabels:
datarobot-type: lrs
egress:
- toFQDNs:
- matchName: "MINIO-API-HOSTNAME-GOES-HERE"
toPorts:
- ports:
- port: "443"
protocol: TCP
Without Cilium, configure a NetworkPolicy to allow egress to the IP addresses that the DNS hostname resolves to. Make sure to replace DATAROBOT-NAMESPACE and X.X.X.X accordingly.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-lrs-spark-app-access-to-external-object-store
namespace: DATAROBOT_NAMESPACE
spec:
podSelector:
matchLabels:
datarobot-instance-name: datarobot-lrs
datarobot-lrs-type: spark_app
egress:
- ports:
- protocol: TCP
port: 443
to:
- ipBlock:
cidr: X.X.X.X/32
policyTypes:
- Egress
Set up custom DNS policies¶
A default configuration would create two policies for DNS access from LRS pods. The first policy is for standard DNS access in k8s cluster which covers all egress from LRSes to kube-dns pods on 53/5353 TCP/UDP ports. And the second policy covers the similar traffic but for OpenShift installations. Here are both policies:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-kube-dns-egress
namespace: <installation_namespace>
spec:
egress:
- ports:
- port: 53
protocol: UDP
- port: 53
protocol: TCP
- port: 5353
protocol: UDP
- port: 5353
protocol: TCP
to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
podSelector:
matchLabels:
datarobot-type: lrs
policyTypes:
- Egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-openshift-dns-egress
namespace: <installation_namespace>
spec:
egress:
- ports:
- port: 53
protocol: UDP
- port: 53
protocol: TCP
- port: 5353
protocol: UDP
- port: 5353
protocol: TCP
to:
- namespaceSelector: {}
podSelector:
matchLabels:
dns.operator.openshift.io/daemonset-dns: default
podSelector:
matchLabels:
datarobot-type: lrs
policyTypes:
- Egress
These policies should be enough to cover most cases. If any other custom policy is required then lrsEgressNetworkPolicies could be overwritten. For example, to define an egress DNS policy to pods with custom-label-name=custom-label-value selector on 53 UDP port in kube-system namespace. The overwritten piece should look like:
operator:
lrsEgressNetworkPolicies:
- name: "allow-custom-label-name-egress"
podSelectorLabel: "custom-label-name"
podSelectorValue: "custom-label-value"
namespaceSelector: "kube-system"
ports:
- protocol: "UDP"
port: 53
Note
The lrsEgressNetworkPolicies field can define any custom egress policy for LRSes, not only DNS-specific.
Security banner¶
DataRobot has the capability to display a Security Banner at the top of the screen.
Configuration¶
To configure these options, refer to the Tuning DataRobot Environment Variables section of this guide.
# helm chart values snippet
core:
config_env_vars:
SHOW_SECURITY_BANNER: True
SHOW_SECURITY_BANNER_COLOR: red
SHOW_SECURITY_BANNER_TEXT: This system has a security banner.
SHOW_SECURITY_BANNER¶
Default value: False Description: This environment variable controls the display of the banner. If it's False, then the banner isn't displayed.
SHOW_SECURITY_BANNER_COLOR¶
Default value: red Value choices: red, yellow, green Description: This controls the color of the banner and the text. The predefined choices are: * Red: Red background with white text * Yellow: Yellow background with black text * Green: Green background with white text
SHOW_SECURITY_BANNER_TEXT¶
Default value: 'Default banner text' Description: The text that's displayed in the banner. It's center aligned and it's recommended that the text be less than 100 characters long.
Tenancy enforcement¶
In DataRobot, privacy between organizations can be enforced through the ENABLE_MULTITENANCY_PRIVACY config.
9.0¶
By default, the config is set to true, which implies users with administrative roles (via RBAC) are unable to view, search, create, or modify entities (such as projects and deployments) outside their current organization.
If cross-organization visibility is necessary, ENABLE_MULTITENANCY_PRIVACY can be overridden to false in core.config_env_vars, however, this isn't a recommended practice in most cases.
9.1 and later¶
DataRobot in 9.1 requires all users to be a member of an Organization. The system now has a named default Organization. The database migration scripts in 9.1.0 place all users in the default Organization who aren't already members of an Organization.
Administrative roles (via RBAC) can view, search, create, or modify entities (such as projects and deployments) in their current organization. Additional Admin users are recommended per Organization to view all deployments/projects in a cluster.
Note: ENABLE_MULTITENANCY_PRIVACY cannot be disabled via config.
Seat licenses¶
The Seat License concept allows for detailed control over premium features.
Cluster administrators can manage the allocation of seat licenses to various organizations within the installation. At the same time, organization administrators can control the assignments of these allocated seats to users within their organization, enabling specific features for end users.
For 11.0, only the GenAI functionality is governed by the seat licenses, specifically through the GenAI Builder seat license.
Allocate seat licenses to organizations¶
To restrict the usage of seat licenses, a new license key that includes seat license limits must be applied.
A DataRobot engineer generates this key, which must include the limits corresponding to the seat licenses. For instance, genai_seat_licenses_limit param is used to restrict the number of GenAI Builder seats in the cluster.
Once the key is generated, it should be applied to the cluster by following these steps:
1. In the user settings page, turn on the Enable Application Management feature flag for the user who manages the cluster. The License page then appears in the admin menu.
2. Go to the License page, paste the license key provided by the DataRobot engineer into the New license key input field, and then press Validate.
3. If the validation is successful, a comparison between the new license and the current one appears. Press the Submit button. The License page displays the Seat Licenses section.
- Click on a specific Seat License card. A new
Allocate Seatssection appears for viewing the current allocations and creating new allocations for organizations. -
Click the
+ Allocate seatsbutton. A new modal window appears where you can search for an organization and allocate a number of seats to it. -
Click
Allocate. The organization admin then has access to theSeat Licenseadmin page for granting the seat to users.
Manage seat licenses assignments¶
To assign a seat to a user within your organization:
1. Navigate to the Seat License page in the admin menu. This page displays all seat licenses allocated to your organization, along with their limits and current number of assignments.
- Click on the specific seat license you want to assign. A new
Assigned seatssection appears, showing the users who currently have that seat. - Press the
+ Assign seatbutton to grant the seat to a new user. A modal window is displayed, allowing you to search for a user, view the number of remaining assignments, and remove existing user assignments. - Click the
Assignbutton. The page is updated with the new list of seat assignments. The user you assigned the seat to should now have access to the functionality associated with that seat license (for example, GenAI functionality for a GenAI Builder seat license).
Docker Hub artifacts¶
All images of DataRobot are available on Docker Hub under the DataRobot organization.
Authentication¶
Each repository requires a dedicated token, which is provided during the installation phase.
Endpoints¶
Docker Hub provides a curated list of endpoints for each type of service. To ensure complete connectivity, these are the required endpoints:
- https://auth.docker.io
- https://index.docker.io
- https://registry-1.docker.io
- https://production.cloudflare.docker.com