Security
PaletteAI is designed with a security-first principle. The following sections detail the security features and design decisions currently in place.
Security Components
PaletteAI's security architecture is made up of the following components:
-
Kubernetes Role-Based Access Control (RBAC)
-
User Interface (UI) components, client-side and server-side
-
Hypertext Transfer Protocol Secure (HTTPS)
The components work together to provide a secure and reliable authentication and authorization system. PaletteAI uses the Kubernetes RBAC system to manage access control. Users can perform the same operations through the Kubernetes API server that they perform in the PaletteAI UI. Dex supports several authentication mechanisms and issues tokens you use to authenticate to the Kubernetes API server and the PaletteAI UI.
All internal PaletteAI components receive a unique TLS certificate through cert-manager. Cert-manager is deployed as part of the PaletteAI installation and setup process. You can customize cert-manager to use your own Certificate Authority (CA) to issue the certificates. Unless you configure a different cert-manager issuer, cert-manager issues self-signed certificates to each of the PaletteAI components deployed inside the hub cluster. The certificate enables HTTPS communication between the PaletteAI components. All PaletteAI components use HTTPS by default.
The following sections cover in greater detail how authentication and authorization are handled in PaletteAI.
Authentication
PaletteAI uses Dex for user authentication. Dex is an open-source identity service broker that supports a number of different authentication mechanisms. Through Dex, you can leverage your existing SAML, OIDC, or LDAP infrastructure to authenticate with PaletteAI and the hub cluster's Kubernetes API server without having to manage another set of credentials. Dex was chosen to enable you to integrate PaletteAI with your existing Identity Provider (IdP), or other authentication services your organization may already have in place.
PaletteAI supports creating and managing local users through Dex. This capability is limited and does not natively support RBAC. We recommend using an IdP for production usage. If you must use local users, enable user impersonation to map them to Kubernetes groups. Refer to the Local Dex Users section for more information.
By default, PaletteAI expects the hub cluster to be configured to trust Dex as an OIDC provider. By trusting Dex as an OIDC issuer, the hub cluster accepts authentication tokens issued by Dex for the Kubernetes API server. This also allows you to configure tools, such as kubectl, to authenticate with the Kubernetes API server using authentication tokens issued by Dex. This latter point is important for non-UI workflows, or GitOps workflows, as it enables you to authenticate with the Kubernetes API server using the same mechanism as the PaletteAI UI. You can configure Dex during the PaletteAI installation process to map OIDC groups to the PaletteAI RBAC roles. The OIDC group mapping is what bridges your IdP groups to the PaletteAI RBAC roles.
You can learn more about how to configure Kubernetes to use an OIDC provider in the official Kubernetes documentation. The formal Dex documentation also has a page that explains how Kubernetes Authentication Through Dex operates.
The PaletteAI UI has a server-side component, Canvas, inside the hub cluster. Canvas communicates with Dex and the Kubernetes API server. The PaletteAI UI client-side component is what you observe and interact with in your browser. The client-side component communicates only with Canvas and Dex and does not communicate directly with the Kubernetes API server. This design decision provides PaletteAI with a better security posture by keeping credentials out of the browser and ensuring sensitive operations are performed on the server side rather than the client side. The PaletteAI UI creates a cookie that maps the user to a session. The session cookie itself does not contain any credentials; it only points to the server-side session data. Canvas stores the authentication tokens inside that session entry and uses them to authenticate with the Kubernetes API server.
The following diagram provides a high-level overview of the in-browser authentication and authorization flow in PaletteAI.

- A user navigates to the PaletteAI UI in their browser.
- Canvas detects that no active session exists and initiates the login flow.
- If the user selects an SSO connector, the browser is redirected to Dex's
/auth/<connector>endpoint to begin the OAuth2 authorization code flow. If local email/password authentication is selected, the flow skips to step 9. - Dex redirects the browser to the external Identity Provider (IdP).
- The IdP login page loads in the browser. The user enters their credentials, which are sent directly from the browser to the IdP. After successful authentication, the IdP redirects the browser back to Dex with an IdP authorization code.
- Dex exchanges the IdP authorization code with the IdP token endpoint and receives IdP tokens. Dex then generates an authorization code for the Canvas client.
- Dex redirects the browser back to the PaletteAI callback endpoint with a Dex authorization code.
- The Canvas backend exchanges the Dex authorization code with Dex's
/tokenendpoint to obtain signed tokens, including a JWT and optionally a refresh token. - If the user selects local email/password authentication instead, the browser submits the credentials to Canvas, and Canvas exchanges them with Dex to create the session.
- Canvas stores the JWT in its server-side session cache and issues a session cookie to the user’s browser. The session cookie maps the user to the server-side session. Its expiration is tied to the refresh token lifetime (if present), falling back to the JWT expiration when no refresh token is available.
- For subsequent requests, Canvas includes the user’s JWT in the
Authorization: Bearer <JWT>header when making API calls to the Kubernetes API server. The API server performs OIDC validation and RBAC authorization to determine whether the requested action is allowed.
Preventing Cross-Site Request Forgery and Replay Attacks
In addition to PaletteAI's session cookie that maps the user to a session, PaletteAI also creates a temporary cookie during the sign-in process that uses a unique state parameter to prevent Cross-Site Request Forgery and replay attacks:
- The PaletteAI UI client generates a unique state parameter value and includes it in the authentication request.
- The unique state value is propagated to the IdP.
- The IdP inserts the state parameter in the redirect to the callback route.
- The callback route compares the received state token with the original value the client created and stored as a cookie.
- If the two values do not match, the authentication flow is canceled.
- The temporary cookie is deleted after the authentication flow completes.
- This process occurs in all SSO sign-in requests so that the request originates from the PaletteAI UI client.
- The state cookie also has a maximum age of five minutes. If the authentication flow does not complete within five minutes, the browser deletes the state cookie automatically.
This is an additional security measure to ensure that requests come from the PaletteAI UI, not from a malicious source. You can learn more about the benefit of unique state parameters in authentication flows in the article Prevent Attacks and Redirect Users with OAuth 2.0 State Parameters from Auth0.
All communication between the PaletteAI UI client side component and Canvas is done over HTTPS. By design, all PaletteAI UI requests that interact with the Kubernetes API server are proxied through Canvas. This ensures that all requests are authenticated before being sent to the Kubernetes API server. The authorization is delegated to the Kubernetes API server, which determines if the request is allowed based on the user's RBAC permissions.
Authorization
PaletteAI uses Kubernetes RBAC to manage access control. Your PaletteAI access is based off the PaletteAI role assigned to you. This access spans the PaletteAI UI and direct interaction with the Kubernetes API server in the hub cluster through GitOps workflows. During installation and setup, operators map the IdP group assigned to you to a PaletteAI role. The PaletteAI roles have access to the various Custom Resources (CRs) that PaletteAI creates and manages. You can find a complete list of all the PaletteAI CRs on the Custom Resources page.
The following diagram provides a high-level overview of the server-side authentication and authorization flow in PaletteAI.

- Canvas sends a CRUD request to the Kubernetes API Server with an
Authorization: Bearer <JWT>header. - The API Server fetches Dex’s JWKS (public signing keys) if they are not already cached.
- The API Server authenticates and authorizes the request:
- Verifies JWT signature
- Validates issuer (
iss), audience (aud), and expiry (exp) - Extracts claims from the JWT and maps them to user and groups (authentication complete)
- Evaluates RBAC rules for the requested verb/resource/namespace (authorization complete)
- The API Server returns the result:
- Allowed → operation performed
- Denied → 403 Forbidden
- Invalid token → 401 Unauthorized
Dex Issuer and API Server Trust
The Dex issuer endpoint is typically exposed through ingress. The API server must trust the CA certificate of the component that terminates TLS, whether that is a load balancer or Dex itself. If you use a self-signed CA certificate, configure the Kubernetes API server --oidc-ca-file flag appropriately.
Local Dex Users
PaletteAI supports local user creation and management through Dex. When user impersonation is enabled, you can map local Dex users to Kubernetes groups and Kubernetes users.
When Dex local users are enabled, Dex creates a default admin user with the credentials admin@example.com / password.
For Day 1 customization, edit values.yaml before installation at dex.config.staticPasswords. This is the pre-install customization point for Dex local users.
dex:
config:
staticPasswords:
- email: 'admin@example.com'
hash: '$2a$12$Ot2dJ0pmdIC2oXUDW/Ez1OIfhkSzLZIbsumsxkByuU3CUr02DtiC.'
username: 'admin'
userID: '08a8684b-db88-4b73-90a9-3cd1661f5466'
The hash value must be a bcrypt hash of the desired password. For example, if you have htpasswd installed, you can generate one with the following command:
htpasswd -bnBC 12 "" 'REPLACE_WITH_PASSWORD' | tr --delete ':\n'
For Day 2 customization, edit the same values after installation in the mural HelmRelease resource at spec.values.dex.config.staticPasswords.
kubectl edit helmrelease mural --namespace mural-system
For installations that are not managed through a HelmRelease, update the Helm release values through Helm instead.
If Dex local users are disabled, you must customize values.yaml before installation and configure at least one valid Dex connector. Refer to the upstream Dex connector reference for the available connector types and configuration examples.
Dex stores local users inside the cluster by default using the Kubernetes storage method. If operators delete the hub cluster or Dex storage is lost, local users are lost as well. You can configure Dex storage to use a different backend, such as a database, to persist local users. With an IdP, user records are managed outside the hub cluster and are not lost if the cluster fails.
If user impersonation is disabled, local Dex users have no RBAC capability. Dex does not assign groups to local users. All local Dex users inherit the same permissions as the PaletteAI UI service account. Because the PaletteAI UI service account is highly privileged, this means that without impersonation, all local users effectively become full administrators. If user impersonation is enabled, you can use the dexGroupMap configuration to map local user emails to Kubernetes groups, which provides RBAC for local users. If you configured your Kubernetes cluster to trust Dex as an OIDC provider, use OIDC to manage users and groups. We strongly recommend using an IdP for production usage to enforce the least privilege principle.