Authentication
JuliaHub uses Dex to authenticate users. Dex sits between JuliaHub and your identity provider, and is configured with one or more connectors — one per identity provider you want users to log in with.
There are two places a connector can be configured, and which one you want depends on whether you can already log in:
- The admin console, after the platform is running. Convenient for adding or adjusting a provider on a working installation.
- At install time, through Replicated or Helm values. Necessary when the identity provider is the only intended way in, since there is no administrator session to configure it from yet.
Configuring a connector in the admin console
Sign in as an administrator and go to Administration ➤ Server Configuration ➤ Authentication Configuration. The connector configuration is entered as YAML, in Dex's own schema.
JuliaHub identifies users by email address, so whichever provider you configure must supply one.
The sections below give a working example per provider. See the Dex connector reference for the full set of options each one supports.
LDAP and Active Directory
User entries are expected to have an email attribute (emailAttr) and a display name attribute (nameAttr). Dex first binds using bindDN and bindPW, searches for the given username, then binds as that user to verify their password. TLS is strongly recommended.
connectors:
- type: ldap
id: corp
name: Corporate LDAP
config:
host: ldap.example.com:636
bindDN: cn=service-account,ou=people,dc=example,dc=com
bindPW: $LDAP_BIND_PW
userSearch:
baseDN: ou=people,dc=example,dc=com
filter: "(objectClass=person)"
username: uid
idAttr: uid
emailAttr: mail
nameAttr: cn
groupSearch:
baseDN: ou=groups,dc=example,dc=com
userMatchers:
- userAttr: uid
groupAttr: member
nameAttr: cnSee Supplying credentials as secrets for the $LDAP_BIND_PW reference.
OpenID Connect (OIDC)
connectors:
- type: oidc
id: okta
name: Okta
config:
issuer: https://example.okta.com
clientID: <client-id>
clientSecret: $OIDC_CLIENT_SECRET
redirectURI: https://<hostname>/dex/callbackTo map identity provider groups onto JuliaHub group membership, set insecureEnableGroups: true. As the Dex documentation explains, this is flagged as insecure because group membership can only be refreshed when the ID token is refreshed, so revoking a group does not take effect immediately.
SAML
connectors:
- type: saml
id: oktasaml
name: Okta SAML
config:
ssoURL: <sso-url>
ssoIssuer: <sso-issuer>
redirectURI: https://<hostname>/dex/callback
caData: <base64-encoded PEM certificate>
usernameAttr: email
emailAttr: email
groupsAttr: groupscaData takes the identity provider's signing certificate inline, base64 encoded. Dex also accepts ca with a path to a certificate file, which requires that file to be present in the Dex container.
On the identity provider side, the endpoint you configure must match redirectURI, and the attributes it puts in the SAML claim must match usernameAttr, emailAttr, and groupsAttr.
GitHub and GitLab
connectors:
- type: github
id: github
name: GitHub
config:
clientID: <client-id>
clientSecret: $GITHUB_CLIENT_SECRET
redirectURI: https://<hostname>/dex/callback
orgs:
- name: my-organizationGitLab is configured the same way with type: gitlab, plus a baseURL for self-hosted instances.
Authenticating proxy (header-based SSO)
Use this when a reverse proxy — typically a corporate web single sign-on (WSSO) gateway — sits in front of JuliaHub, authenticates the user itself, and forwards the resulting identity in request headers. Dex trusts those headers instead of running a login flow of its own.
Only use this connector when the proxy is the only network path to JuliaHub. Any client that can reach JuliaHub directly can set these headers and impersonate any user.
JuliaHub does not strip these headers from incoming requests, and it does not restrict who can reach it: the default network policy permits all sources to reach the web server, and permits any pod in the same namespace to reach any JuliaHub service. Enforcing the "only network path" property is therefore your responsibility. Before enabling this connector:
- Restrict inbound access to JuliaHub to the proxy, at the load balancer or security group.
- Set
networkPolicy.allowedCIDRsin your Helm values to the proxy's addresses, so the platform's own network policy admits only those sources. Under Replicated, use Allowed Inbound CIDRs in the Network Policy section. Note that this only takes effect if your cluster's network plugin enforces NetworkPolicy — see below. - Configure the proxy to strip these headers from incoming client requests before setting its own, so a client cannot supply them itself.
NetworkPolicy is only enforced if your cluster's network plugin implements it. Embedded Cluster installations use Calico, which does. Older kURL-based installations use Flannel, which ignores NetworkPolicy — the resources are created but nothing acts on them, so restrict access at the load balancer or security group instead. On an existing cluster, check with your cluster administrator which plugin is in use.
None of this constrains traffic originating inside the cluster. On an installation with compute enabled, user-supplied jobs run without network restrictions and can reach the web server directly, so anyone able to submit a job can set these headers and authenticate as any user. Do not enable this connector on a compute-enabled installation without first discussing it with JuliaHub support.
connectors:
- type: authproxy
id: authproxy
name: Corporate SSO
config:
userIDHeader: X-Remote-User-Id
userHeader: X-Remote-User
emailHeader: X-Remote-User-Email
groupHeader: X-Remote-Group| Header | Purpose |
|---|---|
userIDHeader | Unique, stable identifier for the user. This keys the JuliaHub account, so it must not change for a given person. |
userHeader | The user's login name. |
emailHeader | The user's email address. JuliaHub identifies users by email, so the proxy should always set this. |
groupHeader | Comma-separated group names. Omit if the proxy does not provide groups. |
Each header falls back to the X-Remote-* name shown above when left unset.
Configuring a connector at install time
A connector configured in the admin console requires an administrator to log in first. When the identity provider you are configuring is the intended way in, configure it during installation instead.
Replicated
The Authentication section of the configuration screen offers an Identity Provider Connector setting:
- Configure later in the admin console — the default. Nothing is applied, and connectors remain configurable from the admin console.
- Authenticating proxy (header-based SSO) — builds the
authproxyconnector from the header names entered alongside it. - Custom YAML — applies Dex connector configuration you paste in, in the same format as the examples above.
Helm
Set dexsrvr.additionalConfig to the Dex configuration you want merged in:
dexsrvr:
additionalConfig:
connectors:
- type: authproxy
id: authproxy
name: Corporate SSO
config:
userIDHeader: X-Remote-User-Id
emailHeader: X-Remote-User-EmailIf the connector configuration is managed outside the chart — by an external secret operator, for instance — set dexsrvr.additionalConfigSecret to the name of an existing Kubernetes Secret instead. Each of its keys is read as a JSON or YAML document, according to the key's file extension.
additionalConfig must be a mapping of top-level Dex configuration keys, not a bare list of connectors. Each key given replaces that whole key in the generated configuration, so a value containing storage: or issuer: overrides the platform's own settings rather than adding to them.
Configuration declared at install time replaces the corresponding setting from the admin console and is reapplied on every deploy. Leave these values unset to manage connectors from the admin console instead.
Supplying credentials as secrets
Most connectors need a credential — an LDAP bind password, an OIDC client secret. Writing one directly into the connector configuration puts it in plain text in your Helm values, in the Replicated configuration screen, and in helm get values output.
Instead, put the credential in a Kubernetes Secret and reference it from the connector configuration as an environment variable. Dex expands $VAR and ${VAR} references in connector configuration, so any key of an injected Secret can be used directly.
This is also the only way to use a credential containing a $. Because Dex expands variables throughout each connector's config: block, a literal $ written inline is read as a variable reference and silently replaced with nothing — and $$ does not escape it. A password such as S3cret$word cannot be given inline at all, and must come from a Secret.
Create the Secret in the JuliaHub namespace:
kubectl create secret generic dex-connector-credentials \
--namespace <namespace> \
--from-literal=LDAP_BIND_PW='<password>'Under Helm, name the Secret in dexsrvr.additionalConfigEnvSecrets and reference its keys from the connector configuration:
dexsrvr:
additionalConfigEnvSecrets:
- dex-connector-credentials
additionalConfig:
connectors:
- type: ldap
id: corp
name: Corporate LDAP
config:
host: ldap.example.com:636
bindDN: cn=service-account,ou=people,dc=example,dc=com
bindPW: $LDAP_BIND_PWUnder Replicated, set Connector Credentials Secret in the Authentication section to the Secret name, and reference its keys the same way.
Every key in each named Secret becomes an environment variable in the Dex container. The Secrets are treated as optional, so one that does not exist yet will not block startup, and the Dex pod restarts automatically when any of them change.
Administrator access
At least one user matching an entry in the Administrators field must be able to log in through a configured connector, otherwise the configuration is rejected when saved from the admin console.
For LDAP, JuliaHub verifies this by searching the directory. For other connector types it cannot enumerate users ahead of time, so the configuration is accepted with a warning and you should confirm you can still sign in before ending your current session.