Fork me on GitHub

Teleport

Single Sign-On (SSO) for SSH

Improve

Users of the Enterprise edition of Teleport can log in to servers, Kubernetes clusters, databases, web applications, and Windows desktops through their organization's Single Sign-On (SSO) provider.

How does SSO work?

Execute the following command to log in to your Teleport cluster using the CLI.

This command will automatically open the default web browser and take a user

through the login process with an SSO provider

tsh login --proxy=proxy.example.com --auth=github

The command opens a browser window and shows a URL the user can visit in the terminal to complete their SSO flow:

If browser window does not open automatically, open it by clicking on the link:
http://127.0.0.1:45235/055a310a-1099-43ea-8cf6-ffc41d88ad1f

Teleport will wait for up to 3 minutes for a user to authenticate. If authentication succeeds, Teleport will retrieve SSH and X.509 certificates and store them in the ~/.tsh/keys/<clustername> directory. The tool will also will add SSH cert to an SSH agent if there's one running.

Configuring SSO

Teleport works with SSO providers by relying on the concept of an authentication connector. An authentication connector is a plugin that controls how a user logs in and which group they belong to.

Supported connectors

The following authentication connectors are supported:

  • local connector type uses the built-in user database. This database can be manipulated by the tctl users command.
  • saml connector type uses the SAML protocol to authenticate users and query their group membership.
  • oidc connector type uses the OpenID Connect protocol to authenticate users and query their group membership.

Creating an authentication connector

Before you can create an authentication connector, you must enable authentication via that connector's protocol.

To set the default authentication type as saml or oidc, either modify your Auth Service configuration file or create a cluster_auth_preference resource.

Update /etc/teleport.yaml in the auth_service section and restart the teleport daemon.

auth_service:
  authentication:
    # Set as saml or oidc
    type: saml|oidc

Create a file called cap.yaml:

kind: cluster_auth_preference
metadata:
  name: cluster-auth-preference
spec:
  authentication:
    # set as saml or oidc
    type: saml|oidc
version: v2

Create the resource:

Log in to your cluster with tsh so you can run tctl commands.

You can also run tctl directly on the Auth Service host.

tsh login --proxy=teleport.example.com --user=myuser
tctl create -f cap.yaml

Next, define an authentication connector. Create a file called connector.yaml based on one of the following examples.

# connector.yaml
kind: saml
version: v2
metadata:
  name: corporate
spec:
  # display allows to set the caption of the "login" button
  # in the Web interface
  display: "Okta"

  acs: https://teleport-proxy.example.com:3080/v1/webapi/saml/acs
  attributes_to_roles:
    - {name: "groups", value: "okta-admin", roles: ["access"]}
    - {name: "groups", value: "okta-dev", roles: ["dev"]}

     # note that wildcards can also be used. the next line instructs Teleport
     # to assign "access" role to any user who has the SAML attribute that begins with "admin":
     - { name: "group", value: "admin*", roles: ["access"] }
     # regular expressions with capture are also supported. the next line instructs Teleport
     # to assign users to roles `admin-1` if his SAML "group" attribute equals 'ssh_admin_1':
     - { name: "group", value: "^ssh_admin_(.*)$", roles: ["admin-$1"] }

  entity_descriptor: |
    <paste SAML XML contents here>```

</TabItem>
<TabItem label="OneLogin">

```yaml
kind: saml
version: v2
metadata:
  name: OneLogin
spec:
  acs: https://<cluster-url>/v1/webapi/saml/acs
  attributes_to_roles:
    - {name: "groups", value: "admin", roles: ["editor"]}
    - {name: "groups", value: "dev", roles: ["access"]}
  display: OneLogin
  issuer: https://app.onelogin.com/saml/metadata/123456
  sso: https://mycompany.onelogin.com/trust/saml2/http-redirect/sso/123456
  entity_descriptor: |
    # Paste in downloaded content from OneLogin Dashboard.
    <md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="http://www.example.com/00000000000000000000">
      <md:IDPSSODescriptor WantAuthnRequestsSigned="false"

#
# Example resource for an OIDC connector
# We recommend using OIDC for G Suite, Auth0 and Keycloak
#
kind: oidc
version: v2
metadata:
  name: new_oidc_connector
spec:
  redirect_url: "https://<cluster-url>/v1/webapi/oidc/callback"
  client_id: <client id>
  # connector display name that will be appended to the title of "Login with"
  # button on the cluster login screen so it will say "Login with Google".
  # Teleport will provide custom CSS for 'Google'.
  display: Google
  client_secret: <client secret>
  issuer_url: https://<issuer-url>
  scope: [<scope value>]
  claims_to_roles:
    - {claim: "hd", value: "example.com", roles: ["editor"]}

kind: oidc
metadata:
  name: google
spec:
  claims_to_roles:
  - claim: groups
    roles:
    - auditor
    value: <[email protected]>
  - claim: groups
    roles:
    - access
    value: [email protected]
  client_id: <GOOGLE_WORKSPACE_CLIENT_ID>.apps.googleusercontent.com
  client_secret: <OAUTH_CLIENT_SECRET>
  display: Google
  google_admin_email: <GOOGLE_WORKSPACE_ADMIN_EMAIL>
  google_service_account: |
    {
     "type": "service_account",
     "project_id": "<project_id>",
     "private_key_id": "<private_key_id>",
     "private_key": "-----BEGIN PRIVATE KEY-----\n<private key contents>\n-----END PRIVATE KEY-----\n",
     "client_email": "<teleport service account email>",
     "client_id": "<client_id>",
     "auth_uri": "https://accounts.google.com/o/oauth2/auth",
     "token_uri": "https://oauth2.googleapis.com/token",
     "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
     "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/teleport-service-account%40access-304316.iam.gserviceaccount.com"
    }
  issuer_url: https://accounts.google.com
  redirect_url: https://<cluster-url>:3080/v1/webapi/oidc/callback
  scope:
  - openid
  - email
version: v3

# This example connector uses SAML to authenticate against
# Active Directory Federation Services (ADFS)
kind: saml
version: v2
metadata:
  name: adfs_connector
spec:
  # display allows to set the caption of the "login" button
  # in the Web interface
  # Using the work 'Microsoft' will show the windows symbol in the UI.
  display: Microsoft

  # "adfs" provider setting tells Teleport that this SAML connector uses ADFS
  # as a provider
  provider: adfs

  # entity_descriptor XML can either be copied into connector or fetched from a URL
  entity_descriptor: |
    <EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata">
    ...
    </md:EntityDescriptor>
  # entity_descriptor_url is commented out, as only one is required to setup adfs.
  # if you're running Teleport in FIPS mode entity_descriptor_url with Azure AD may
  # fail
  #entity_descriptor_url: "https://example.com"

  # issuer typically comes from the "entity_descriptor" but can be overridden here
  issuer: "foo"
  # sso typically comes from the "entity_descriptor" but can be overridden here
  sso: "bar"
  # cert typically comes from the "entity_descriptor" but can be overridden here
  cert: |
    -----BEGIN RSA PRIVATE KEY-----
    ...
    -----END RSA PRIVATE KEY-----

  acs: "https://<cluster-url>.example.com:3080/v1/webapi/saml/acs"
  # if "service_provider_issuer" is not set, comes from "acs"
  service_provider_issuer: "https://<cluster-url>.example.com:3080/v1/webapi/saml/acs"
  # if "audience" is not set, comes from "acs"
  audience: "https://<cluster-url>.example.com:3080/v1/webapi/saml/acs"

  # if "signing_key_pair" is not set, teleport will generate a self signed
  # signing key pair
  signing_key_pair:
    private_key: |
      -----BEGIN RSA PRIVATE KEY-----
      ...
      -----END RSA PRIVATE KEY-----
    cert:
      -----BEGIN RSA PRIVATE KEY-----
      ...
      -----END RSA PRIVATE KEY-----

  attributes_to_roles:
    - name: "http://schemas.xmlsoap.org/claims/Group"
      value: "Administrators"
      roles: ["editor"]
    - name: "http://schemas.xmlsoap.org/claims/Group"
      value: "Users"
      roles: ["access"]

#
# Example resource for a SAML connector
# This connector can be used for SAML endpoints like Okta
#
kind: saml
version: v2
metadata:
  # the name of the connector
  name: new_saml_connector
spec:
  # connector display name that will be appended to the title of "Login with"
  # button on the cluster login screen so it will say "Login with Okta"
  display: Okta
  # SAML provider will make a callback to this URL after successful authentication
  # cluster-url is the address the cluster UI is reachable at
  acs: https://<cluster-url>/v1/webapi/saml/acs
  attributes_to_roles:
  - name: groups
    roles:
    - editor
    value: okta-admin
  - name: groups
    roles:
    - access
    value: okta-dev
  # Note that the entire XML document is indented by 4 spaces. This is
  # required because the pipe symbol indicates what follows is raw text.
  # Below is an Example. Replace with your XML
  entity_descriptor: |
    <md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="http://www.example.com/00000000000000000000">
      <md:IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
        <md:KeyDescriptor use="signing">
          <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
            <ds:X509Data>
              <ds:X509Certificate>Lu4bLZ57YSPClo5x1RHtXihqSdBfwqTU1tiPnL3i5QrHAXnyrmwscJ1VnutbfaTWCsPlICYQAVin
    vSAArSQU5WTjvZut9UeEenrYY72xDCLNe5vHimOEHFRvPeP626vx7/gkKSSL5F0Se+YYhLLCWcz8
    DYrQn41YZb72PBt5T0vIRS3FMZOYz55Ww8XbIWAwIKKmRfm00bPpMYPTD34ZCnVGTXSkHzHDCehu
    pQMug4IpWIcy45ffbi6sXoFD1ud8vG8H0RFhUk8MBFSCSsYHkrgz5cB8sbPLs0PocxN/nYIFJ2A1
    U68y2d3U/ClLfOb/kh4w3EcKvqtSwsMdLgxHjrDGtPgiAZDJhriZnpCQ0WvgBcAOYjRjsFncTRWH
    DqpTXsQzjkRa3A/KD3pA6bd5aYSF21nKAR7aVj7Aq0ogWEb4owZL5/W2lEnuwKSfGcnrz6GmJSaT
    113wKahleH/VPb1KoaGJ81h5Om1DZI3ohYuxQYC/jwDhOlPXpdECkJe11gSTp34WQ1a93uSYkGo9
    MZ/7WI2LXpD6pjGtz5YSVKR1naj2pci5jwGi86KwL2MqXX288vguvGqcGZXUwi+383Ct99WLBNgo
    9A6kIFvexILcscyeKthsoBGzu+MBipoGnSYuw+vlSa/0jIoluQqYpqYIg7ZBWoOjrKDDFdv01BtL
    nnVBFR43wCIm77obPQ5+103KYWcs42wpAxtX78HdlTav/D35D45GnGxM/fadpth65BSejgoPnd+z
    MXwMOv2W8B+fuolEcQGLrXw+mHtc2p3A7XKGhexY5A+FkSlAs3RMa0weizcylDlW2vj7ksdmZ/Ag
    AQ6EetT85DS6gV9wn3pBaWRhFU/OqFT/PezFcnxjiHVwfil+G9nhYhmjaspLqSLTkGPnyYabReZw
    ZtnSnKnWfwEr5GDqfYxHkBdZUtiofNhu/K/gs/aLTGoxWVac6F9y1xzXYnXPEPkmNsFfwn/H+LuL
    M01dKisWCfMPHCeBTxKSMB3IrixUym64cxlqkvk/rPXrUcktfvPhd/1I9jWIzQwPfbWyW9wpYzBm
    xYqZ1MocFyZhfh1UHOwaOiMlgAlOTDn6irtT1BW/a45nAkCl8jqgFKPSJ6kusj+HffSL6xDQJ0vA
    L5BGENThmToTm7euueLzYY0JDqhqo18wnha5MSCJtB3dcqKTeK+jiyF7FRHfZt/qJolXCufZyN48
    DQGrdrUjjolHvE8jmtgPkYuq9pdTciUnJIQN8vtQ/tOgk0Ui3n03FSM0YNARyaTZ0vgj+GLfGMc6
    VFKf6t/sSgFO8W4dgi2e0VwryOd8Etrq5NFul</ds:X509Certificate>
            </ds:X509Data>
          </ds:KeyInfo>
        </md:KeyDescriptor>
        <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>
        <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>
        <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://www.example.com/app/teleport/00000000000000000000"/>
        <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://www.example.com/app/teleport/00000000000000000000"/>
      </md:IDPSSODescriptor>
    </md:EntityDescriptor>

You may use entity_descriptor_url, in lieu of entity_descriptor, to fetch the entity descriptor from your IDP.

We recommend "pinning" the entity descriptor by including the XML rather than fetching from a URL.

Create the connector:

tctl create -f connector.yaml

User logins

Often it is required to restrict SSO users to their unique UNIX logins when they connect to Teleport Nodes. To support this:

  • Use the SSO provider to create a field called unix_login (you can use another name).
  • Make sure the unix_login field is exposed as a claim via SAML/OIDC.
  • Update a Teleport role to include the {{external.unix_login}} variable in the list of allowed logins:
kind: role
version: v5
metadata:
  name: sso_user
spec:
  allow:
    logins:
    - '{{external.unix_login}}'
    node_labels:
      '*': '*'

Provider-Specific Workarounds

Certain SSO providers may require or benefit from changes to Teleport's SSO flow. These provider-specific changes can be enabled by setting the spec.provider property of the connector definition to one of the following values to match your identity provider:

  • adfs (SAML): Required for compatibility with Active Directory (ADFS); refer to the full ADFS guide for details.
  • netiq (OIDC): Used to enable NetIQ-specific ACR value processing; refer to the OIDC guide for details.
  • ping (SAML and OIDC): Required for compatibility with Ping Identity (including PingOne and PingFederate).
  • okta (OIDC): Required when using Okta as an OIDC provider.

At this time, the spec.provider field should not be set for any other identity providers.

Working with External Email Identity

Along with sending groups, an SSO provider will also provide a user's email address. In many organizations, the username that a person uses to log in to a system is the same as the first part of their email address, the "local" part. For example, [email protected] might log in with the username dave.smith. Teleport provides an easy way to extract the first part of an email address so it can be used as a username. This is the {{email.local}} function.

If the email claim from the identity provider (which can be accessed via {{external.email}}) is sent and contains an email address, you can extract the "local" part of the email address before the @ sign like this: {{email.local(external.email)}}

Here's how this looks in a Teleport role:

kind: role
version: v5
metadata:
  name: sso_user
spec:
  allow:
    logins:
    # Extracts the local part of [email protected], so the login will
    # now support dave.smith.
    - '{{email.local(external.email)}}'
    node_labels:
      '*': '*'

Multiple SSO Providers

Teleport can also support multiple connectors, i.e. a Teleport administrator can define and create multiple connector resources using tctl create as shown above.

To see all configured connectors, execute this on the auth server:

tctl get connectors

To delete/update connectors, use the usual tctl rm and tctl create commands as described in the Resources Reference.

If multiple authentication connectors exist, the clients must supply a connector name to tsh login via --auth argument:

use "okta" SAML connector:

tsh --proxy=proxy.example.com login --auth=okta

use local Teleport user DB:

tsh --proxy=proxy.example.com login --auth=local --user=admin

Refer to the following guides to configure authentication connectors of both SAML and OIDC types:

SSO Customization

ProviderYAMLExample
GitHubdisplay: GitHubgithub
Microsoftdisplay: Microsoftmicrosoft
Googledisplay: Googlegoogle
BitBucketdisplay: Bitbucketbitbucket
OpenIDdisplay: OktaOkta

Troubleshooting

Troubleshooting SSO configuration can be challenging. Usually a Teleport administrator must be able to:

  • Ensure that HTTP/TLS certificates are configured properly for both Teleport proxy and the SSO provider.
  • Be able to see what SAML/OIDC claims and values are getting exported and passed by the SSO provider to Teleport.
  • Be able to see how Teleport maps the received claims to role mappings as defined in the connector.

If something is not working, we recommend to:

  • Double-check the host names, tokens and TCP ports in a connector definition.

Using the Web UI

If you get "access denied" or other login errors, the number one place to check is the Audit Log. You can access it in the Activity tab of the Teleport Web UI.

Audit Log Entry for SSO Login error

Example of a user being denied because the role clusteradmin wasn't set up:

{
  "code": "T1001W",
  "error": "role clusteradmin is not found",
  "event": "user.login",
  "method": "oidc",
  "success": false,
  "time": "2019-06-15T19:38:07Z",
  "uid": "cd9e45d0-b68c-43c3-87cf-73c4e0ec37e9"
}

Teleport does not show the expected Nodes

When Teleport's Auth Service receives a request to list Teleport Nodes (e.g., to display Nodes in the Web UI or via tsh ls), it only returns the Nodes that the current user is authorized to view.

For each Node in the user's Teleport cluster, the Auth Service applies the following checks in order and, if one check fails, hides the Node from the user:

  • None of the user's roles contain a deny rule that matches the Node's labels.
  • None of the user's roles contain a deny rule that matches the user's login.
  • At least one of the user's roles contains an allow rule that matches the Node's labels.
  • At least one of the user's roles contains an allow rule that matches the user's login.

If you are not seeing Nodes when expected, make sure that your user's roles include the appropriate allow and deny rules as documented in the Teleport Access Controls Reference.

When configuring SSO, ensure that the identity provider is populating each user's traits correctly. For a user to see a Node in Teleport, the result of populating a template variable in a role's allow.logins must match at least one of a user's traits.logins.

In this example a user will have usernames ubuntu, debian and usernames from the SSO trait logins for Nodes that have a env: dev label. If the SSO trait username is bob then the usernames would include ubuntu, debian, and bob.

kind: role
metadata:
  name: example-role
spec:
  allow:
    logins: ['{{external.logins}}', ubuntu, debian]
    node_labels:
      'env': 'dev'
version: v5