Skip to content

Latest commit

 

History

History
774 lines (608 loc) · 20.1 KB

File metadata and controls

774 lines (608 loc) · 20.1 KB

Authentication Setup Guide

This guide documents authentication configuration in Posit Team Operator. Team Operator supports multiple authentication methods for both Posit Connect and Posit Workbench.

Table of Contents

  1. Overview
  2. Authentication Types
  3. OIDC Configuration
  4. SAML Configuration
  5. Password Authentication
  6. Role-Based Access Control
  7. Keycloak Integration
  8. Secrets Management
  9. Troubleshooting

Overview

Team Operator uses the AuthSpec structure to configure authentication for Posit products. Authentication is configured per-product (Connect and Workbench) through the auth field in each product's spec.

AuthSpec Structure

The complete AuthSpec type definition:

type AuthSpec struct {
    Type               AuthType `json:"type,omitempty"`
    ClientId           string   `json:"clientId,omitempty"`
    Issuer             string   `json:"issuer,omitempty"`
    Groups             bool     `json:"groups,omitempty"`
    UsernameClaim      string   `json:"usernameClaim,omitempty"`
    EmailClaim         string   `json:"emailClaim,omitempty"`
    UniqueIdClaim      string   `json:"uniqueIdClaim,omitempty"`
    GroupsClaim        string   `json:"groupsClaim,omitempty"`
    DisableGroupsClaim bool     `json:"disableGroupsClaim,omitempty"`
    SamlMetadataUrl    string   `json:"samlMetadataUrl,omitempty"`
    SamlIdPAttributeProfile  string   `json:"samlIdPAttributeProfile,omitempty"`
    SamlUsernameAttribute    string   `json:"samlUsernameAttribute,omitempty"`
    SamlFirstNameAttribute   string   `json:"samlFirstNameAttribute,omitempty"`
    SamlLastNameAttribute    string   `json:"samlLastNameAttribute,omitempty"`
    SamlEmailAttribute       string   `json:"samlEmailAttribute,omitempty"`
    Scopes                   []string `json:"scopes,omitempty"`
    ViewerRoleMapping        []string `json:"viewerRoleMapping,omitempty"`
    PublisherRoleMapping     []string `json:"publisherRoleMapping,omitempty"`
    AdministratorRoleMapping []string `json:"administratorRoleMapping,omitempty"`
}

Authentication Types

Team Operator supports three authentication types:

Type Value Use Case
Password password Development, simple deployments
OpenID Connect (OIDC) oidc Enterprise SSO with OAuth2/OpenID Connect
Security Assertion Markup Language (SAML) saml Enterprise SSO with SAML 2.0

OIDC Configuration

OpenID Connect (OIDC) is recommended for enterprise deployments.

Basic OIDC Configuration

apiVersion: core.posit.team/v1beta1
kind: Site
metadata:
  name: production
  namespace: posit-team
spec:
  connect:
    auth:
      type: "oidc"
      clientId: "connect-client-id"
      issuer: "https://idp.example.com"

Required IdP Settings

Before configuring OIDC in Team Operator, configure your Identity Provider:

  1. Create an OAuth2/OIDC Application in your IdP
  2. Configure Redirect URIs:
    • Connect: https://connect.example.com/__login__/callback
    • Workbench: https://workbench.example.com/oidc/callback
  3. Note the Client ID (used in the spec)
  4. Generate a Client Secret (stored in secrets)

Client Secret Configuration

The client secret must be stored in your secrets provider:

For Kubernetes secrets:

  • Connect: pub-client-secret key
  • Workbench: dev-client-secret key

For AWS Secrets Manager:

  • Connect: pub-client-secret in your vault
  • Workbench: dev-client-secret in your vault

Claims Mapping

Configure how OIDC claims map to user attributes:

spec:
  connect:
    auth:
      type: "oidc"
      clientId: "connect-client-id"
      issuer: "https://idp.example.com"
      usernameClaim: "preferred_username"  # Claim for username
      emailClaim: "email"                  # Claim for email
      uniqueIdClaim: "sub"                 # Claim for unique identifier

Default behavior:

  • If emailClaim is set but uniqueIdClaim is not, the email claim is used for unique ID
  • Default uniqueIdClaim is email

Group Claim Configuration

Enable group synchronization from your IdP:

spec:
  connect:
    auth:
      type: "oidc"
      clientId: "connect-client-id"
      issuer: "https://idp.example.com"
      groups: true                    # Enable group auto-provisioning
      groupsClaim: "groups"           # Claim containing group membership
      scopes:
        - "openid"
        - "profile"
        - "email"
        - "groups"                    # Scope to request groups

Disabling Groups Claim:

Some IdPs do not support a groups claim. Disable it explicitly:

spec:
  connect:
    auth:
      type: "oidc"
      clientId: "connect-client-id"
      issuer: "https://idp.example.com"
      groups: true                    # Still auto-provision groups
      disableGroupsClaim: true        # But don't try to read from token

Custom Scopes

Override the default OIDC scopes:

spec:
  connect:
    auth:
      type: "oidc"
      clientId: "connect-client-id"
      issuer: "https://idp.example.com"
      scopes:
        - "openid"
        - "profile"
        - "email"
        - "offline_access"

OIDC Examples by IdP

Okta

spec:
  connect:
    auth:
      type: "oidc"
      clientId: "0oaxxxxxxxxx"
      issuer: "https://your-org.okta.com"
      usernameClaim: "preferred_username"
      emailClaim: "email"
      groups: true
      groupsClaim: "groups"
      scopes:
        - "openid"
        - "profile"
        - "email"
        - "groups"

Azure AD / Entra ID

spec:
  connect:
    auth:
      type: "oidc"
      clientId: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
      issuer: "https://login.microsoftonline.com/{tenant-id}/v2.0"
      usernameClaim: "preferred_username"
      emailClaim: "email"
      uniqueIdClaim: "oid"           # Azure object ID
      groups: true
      groupsClaim: "groups"
      scopes:
        - "openid"
        - "profile"
        - "email"

Note: Azure AD requires specific application permissions to include group claims. Configure "Groups claim" in the Token configuration.

Auth0

spec:
  connect:
    auth:
      type: "oidc"
      clientId: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      issuer: "https://your-tenant.auth0.com/"
      usernameClaim: "email"
      emailClaim: "email"
      groups: true
      groupsClaim: "https://your-namespace/groups"  # Custom claim namespace
      scopes:
        - "openid"
        - "profile"
        - "email"

Keycloak

spec:
  connect:
    auth:
      type: "oidc"
      clientId: "connect"
      issuer: "https://keycloak.example.com/realms/posit"
      usernameClaim: "preferred_username"
      emailClaim: "email"
      groups: true
      groupsClaim: "groups"
      scopes:
        - "openid"
        - "profile"
        - "email"
        - "groups"

SAML Configuration

SAML 2.0 authentication is supported for enterprise environments.

Basic SAML Configuration

spec:
  connect:
    auth:
      type: "saml"
      samlMetadataUrl: "https://idp.example.com/saml/metadata"

Required: samlMetadataUrl must be set for SAML authentication.

Attribute Profiles

Team Operator supports two approaches for SAML attribute mapping.

1. Using IdP Attribute Profiles

Use a predefined attribute profile matching your IdP:

spec:
  connect:
    auth:
      type: "saml"
      samlMetadataUrl: "https://idp.example.com/saml/metadata"
      samlIdPAttributeProfile: "azure"   # Options: default, azure, etc.

Built-in profiles:

  • default - Standard SAML attributes
  • azure - Microsoft Azure AD attributes

2. Custom Attribute Mapping

Specify individual attribute URIs for complete control:

spec:
  connect:
    auth:
      type: "saml"
      samlMetadataUrl: "https://idp.example.com/saml/metadata"
      samlUsernameAttribute: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"
      samlFirstNameAttribute: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"
      samlLastNameAttribute: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"
      samlEmailAttribute: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"

Important: samlIdPAttributeProfile and individual attribute mappings are mutually exclusive. The operator will return an error if both are specified.

SAML Service Provider (SP) Configuration

Configure your IdP with these Service Provider details:

Connect:

  • Entity ID: https://connect.example.com/__login__
  • ACS URL: https://connect.example.com/__login__/callback

Workbench:

  • Entity ID: https://workbench.example.com/saml
  • ACS URL: https://workbench.example.com/saml/acs

SAML Examples by IdP

Azure AD / Entra ID

spec:
  connect:
    auth:
      type: "saml"
      samlMetadataUrl: "https://login.microsoftonline.com/{tenant-id}/federationmetadata/2007-06/federationmetadata.xml"
      samlIdPAttributeProfile: "azure"

Or with custom attributes:

spec:
  connect:
    auth:
      type: "saml"
      samlMetadataUrl: "https://login.microsoftonline.com/{tenant-id}/federationmetadata/2007-06/federationmetadata.xml"
      samlUsernameAttribute: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"
      samlEmailAttribute: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"
      samlFirstNameAttribute: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"
      samlLastNameAttribute: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"

Okta

spec:
  connect:
    auth:
      type: "saml"
      samlMetadataUrl: "https://your-org.okta.com/app/xxxxxxxx/sso/saml/metadata"
      samlUsernameAttribute: "NameID"
      samlEmailAttribute: "email"
      samlFirstNameAttribute: "firstName"
      samlLastNameAttribute: "lastName"

Workbench SAML Configuration

Workbench SAML uses the usernameClaim field for the username attribute:

spec:
  workbench:
    auth:
      type: "saml"
      samlMetadataUrl: "https://idp.example.com/saml/metadata"
      usernameClaim: "email"   # Maps to auth-saml-sp-attribute-username

Password Authentication

Password authentication is the simplest method, suitable for development environments.

Configuration

spec:
  connect:
    auth:
      type: "password"
  workbench:
    auth:
      type: "password"

When to Use Password Authentication

  • Development and testing environments
  • Quick proof-of-concept deployments
  • Environments without enterprise SSO requirements

Security Considerations

  • Password authentication stores credentials in the product's database
  • Not recommended for production environments with security requirements
  • Does not provide SSO capabilities
  • User management must be done within each product

Role-Based Access Control

Team Operator supports automatic role mapping based on IdP group membership.

Connect Role Mappings

Connect supports three roles:

  • Viewer - Can view published content
  • Publisher - Can publish and manage content
  • Administrator - Full administrative access

Configure role mappings:

spec:
  connect:
    auth:
      type: "oidc"
      clientId: "connect-client-id"
      issuer: "https://idp.example.com"
      groups: true
      groupsClaim: "groups"
      viewerRoleMapping:
        - "connect-viewers"
        - "readonly-users"
      publisherRoleMapping:
        - "connect-publishers"
        - "data-scientists"
      administratorRoleMapping:
        - "connect-admins"
        - "platform-admins"

How Role Mapping Works

  1. When a user logs in, Connect reads their group membership from the groupsClaim
  2. The user is assigned the highest matching role:
    • If any group matches administratorRoleMapping -> Administrator
    • Else if any group matches publisherRoleMapping -> Publisher
    • Else if any group matches viewerRoleMapping -> Viewer
    • Else -> Default role (configured separately)

Role Mapping with SAML

Role mappings work the same way with SAML authentication, provided your IdP sends group membership in the SAML assertion.

Workbench Role Mappings

Workbench uses admin groups for administrative access:

spec:
  workbench:
    # Admin groups have access to the administrative dashboard
    adminGroups:
      - "workbench-admin"
      - "platform-admins"
    # Superuser groups have elevated administrative privileges
    adminSuperuserGroups:
      - "workbench-superusers"

Default User Role

Set the default role for users who don't match any role mapping:

spec:
  connect:
    config:
      Authorization:
        DefaultUserRole: "viewer"   # Options: viewer, publisher, administrator

Keycloak Integration

Team Operator can deploy and manage a Keycloak instance for authentication.

Enabling Keycloak

apiVersion: core.posit.team/v1beta1
kind: Site
metadata:
  name: production
  namespace: posit-team
spec:
  keycloak:
    enabled: true
    image: "quay.io/keycloak/keycloak:latest"
    imagePullPolicy: IfNotPresent

Keycloak Features

When enabled, Team Operator does the following:

  • Deploys a Keycloak instance in the namespace
  • Creates a PostgreSQL database for Keycloak
  • Configures ingress routing to key.<domain>
  • Sets up service accounts and RBAC

Using Keycloak with Products

Configure products to use the deployed Keycloak:

spec:
  keycloak:
    enabled: true
  connect:
    auth:
      type: "oidc"
      clientId: "connect"
      issuer: "https://key.example.com/realms/posit"
      groups: true
      groupsClaim: "groups"

Keycloak Realm Configuration

After Keycloak deploys, complete these steps:

  1. Access the Keycloak admin console at https://key.<domain>
  2. Create a realm (e.g., "posit")
  3. Create clients for each product
  4. Configure client credentials and redirect URIs
  5. Set up user federation if needed (LDAP, AD, etc.)

Secrets Management

Authentication requires properly configured secrets in your secrets provider.

Kubernetes Secrets

For secret.type: kubernetes, create a secret with the required keys:

apiVersion: v1
kind: Secret
metadata:
  name: site-secrets
  namespace: posit-team
type: Opaque
stringData:
  # Connect OIDC
  pub-client-secret: "your-connect-client-secret"

  # Workbench OIDC
  dev-client-secret: "your-workbench-client-secret"
  dev-admin-token: "generated-admin-token"
  dev-user-token: "generated-user-token"

AWS Secrets Manager

For secret.type: aws, store secrets in AWS Secrets Manager:

Secret Key Description
pub-client-secret Connect OIDC client secret
dev-client-secret Workbench OIDC client secret
dev-admin-token Workbench admin authentication token
dev-user-token Workbench user authentication token

Secret Structure Reference

Product Auth Type Secret Key Purpose
Connect OIDC pub-client-secret OAuth2 client secret
Workbench OIDC dev-client-secret OAuth2 client secret
Workbench OIDC dev-admin-token Admin API token
Workbench OIDC dev-user-token User API token

Troubleshooting

Common OIDC Issues

1. "Invalid redirect URI" Error

Cause: The redirect URI in the IdP doesn't match what the product sends.

Solution: Verify redirect URIs are configured exactly:

  • Connect: https://<connect-url>/__login__/callback
  • Workbench: https://<workbench-url>/oidc/callback

2. Groups Not Syncing

Cause: Groups claim not configured or not included in token.

Debug steps:

  1. Verify groups: true is set
  2. Verify groupsClaim matches what your IdP sends
  3. Verify the groups scope is requested
  4. Check if your IdP requires special configuration for group claims

Enable OIDC logging for Connect:

spec:
  connect:
    debug: true   # Enables OAuth2 logging

3. User Identity Issues

Cause: Claims mapping doesn't match IdP token.

Solution: Verify your IdP token contains the expected claims:

spec:
  connect:
    auth:
      usernameClaim: "preferred_username"  # Must exist in token
      emailClaim: "email"                  # Must exist in token

Common SAML Issues

1. "Metadata URL Not Accessible"

Cause: The SAML metadata URL is unreachable from the cluster.

Solutions:

  • Verify the metadata URL is accessible from pods
  • Verify network policies allow outbound connections
  • Verify DNS resolution works

2. "IdPAttributeProfile Cannot Be Specified Together..."

Cause: Both samlIdPAttributeProfile and individual attributes are set.

Solution: Use one approach:

# Option 1: Use profile
samlIdPAttributeProfile: "azure"

# Option 2: Use individual attributes
samlUsernameAttribute: "..."
samlEmailAttribute: "..."

3. Attribute Mapping Not Working

Debug steps:

  1. Check the SAML assertion from your IdP
  2. Verify attribute names match exactly (case-sensitive)
  3. Use full URIs for standard attributes:
    samlUsernameAttribute: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"

Debugging Token Claims

To debug OIDC token claims:

  1. Enable Debug Logging:

    spec:
      connect:
        debug: true
  2. Check Pod Logs:

    kubectl logs -n posit-team deploy/<site>-connect -f
  3. Decode JWT Tokens: Use jwt.io to inspect tokens and verify claims.

Group Membership Issues

If users aren't getting the correct roles:

  1. Verify group claim is present:

    • Check the groupsClaim field matches your IdP
    • Some IdPs use nested claims (e.g., realm_access.roles)
  2. Check group name matching:

    • Group names in role mappings must match exactly
    • Group names are case-sensitive
  3. Verify IdP configuration:

    • Verify groups are included in the token
    • Check token size limits (large group lists may be truncated)

Workbench-Specific Issues

OIDC Callback URL Issues

Workbench may include port numbers in redirect URIs. The operator sets a header to prevent this:

X-Rstudio-Request: https://<workbench-url>

If you see port 443 in redirect URIs, verify Traefik middleware is correctly applied.

User Provisioning

For Workbench with OIDC/SAML:

spec:
  workbench:
    createUsersAutomatically: true  # Create system users on first login

Complete Example

A complete Site configuration with OIDC authentication:

apiVersion: core.posit.team/v1beta1
kind: Site
metadata:
  name: production
  namespace: posit-team
spec:
  domain: posit.example.com

  secret:
    type: "kubernetes"
    vaultName: "production-secrets"

  connect:
    image: ghcr.io/rstudio/rstudio-connect:ubuntu22-2024.10.0
    auth:
      type: "oidc"
      clientId: "connect-production"
      issuer: "https://login.microsoftonline.com/tenant-id/v2.0"
      usernameClaim: "preferred_username"
      emailClaim: "email"
      uniqueIdClaim: "oid"
      groups: true
      groupsClaim: "groups"
      scopes:
        - "openid"
        - "profile"
        - "email"
      viewerRoleMapping:
        - "Connect-Viewers"
      publisherRoleMapping:
        - "Connect-Publishers"
        - "Data-Scientists"
      administratorRoleMapping:
        - "Connect-Admins"

  workbench:
    image: ghcr.io/rstudio/rstudio-workbench:jammy-2024.12.0
    createUsersAutomatically: true
    auth:
      type: "oidc"
      clientId: "workbench-production"
      issuer: "https://login.microsoftonline.com/tenant-id/v2.0"
      usernameClaim: "preferred_username"
      scopes:
        - "openid"
        - "profile"
        - "email"
    adminGroups:
      - "Workbench-Admins"
    adminSuperuserGroups:
      - "Platform-Admins"

Related Documentation