Joining Services with any OIDC provider via Generic OIDC
While Teleport natively supports many cloud platforms and identity providers, it is still often necessary to join Teleport services using identity providers that do not have a dedicated join method. Generic OIDC allows you to join Teleport services from any platform or provider where workloads are issued OIDC compatible JWTs, and allows you to define custom rules to allow only the intended hosts or workloads to authenticate to Teleport.
This guide will explain how to use the Generic OIDC join method to configure Teleport services to join your Teleport cluster by establishing trust with an OIDC-compatible issuer of your choice.
How it works
Instead of using shared secrets, the generic_oidc join method allows Teleport
Agents and Bots to authenticate to the Teleport Auth Service using a JWT issued
by any OIDC-compatible provider.
Many CI/CD platforms and cloud providers act as OIDC providers and can issue
short-lived JWTs to workloads that serve as proof of the workload's identity.
In fact, this is how many of Teleport's join methods work today, including
the github, kubernetes (in oidc mode), and gcp join methods, among
others.
The generic_oidc join method extends this support to any provider that can
issue compatible JWTs, allowing you to freely specify any compatible provider
and a set of free-form joining rules to select exactly which workloads are
allowed to join, without requiring a dedicated join method for the provider.
Be aware that due to the nature of generic_oidc, this guide is meaningfully
less directed than other deployment guides. The configuration shown here is only
useful as an example or reference, and you will need to thoroughly customize
and verify any configuration made before deploying it into a production
environment.
Be aware that not all OIDC providers are equally secure. When using
generic_oidc, you are responsible for both properly vetting the provider and
writing secure rules that will not allow unintended clients to join.
Where possible, one of Teleport's dedicated join
methods should be used instead of
generic_oidc, which benefit from dedicated documentation and vetted,
predefined rules.
Additionally, due to its generic nature, this document cannot fully guide you in
setting up the tbot client to run seamlessly on arbitrary platforms.
Teleport's dedicated join methods have guides and supporting documentation
tailored specifically for use with those platforms; with generic_oidc you will
need to determine the best deployment method yourself.
Prerequisites
-
A running Teleport (v18.11.0 or higher) cluster accessible at a hostname with a valid TLS certificate. If you want to get started with Teleport, sign up for a free trial or set up a demo environment.
-
The
tctlandtshclients.Installing
tctlandtshclients-
Determine the version of your Teleport cluster. The
tctlandtshclients must be at most one major version behind your Teleport cluster version. Send a GET request to the Proxy Service at/v1/webapi/findand use a JSON query tool to obtain your cluster version. Replace teleport.example.com:443 with the web address of your Teleport Proxy Service:- Mac/Linux
- Windows - Powershell
TELEPORT_DOMAIN=teleport.example.com:443TELEPORT_VERSION="$(curl -s https://$TELEPORT_DOMAIN/v1/webapi/find | jq -r '.server_version')"$TELEPORT_DOMAIN = "teleport.example.com:443"$TELEPORT_VERSION = (Invoke-RestMethod -Uri "https://${TELEPORT_DOMAIN}/v1/webapi/find").server_version -
Follow the instructions for your platform to install
tctlandtshclients:- Mac
- Windows - Powershell
- Linux
Download the signed macOS .pkg installer for Teleport, which includes the
tctlandtshclients:curl -O https://cdn.teleport.dev/teleport-${TELEPORT_VERSION?}.pkgIn Finder double-click the
pkgfile to begin installation.dangerUsing Homebrew to install Teleport is not supported. The Teleport package in Homebrew is not maintained by Teleport and we can't guarantee its reliability or security.
curl.exe -O https://cdn.teleport.dev/teleport-v$TELEPORT_VERSION-windows-amd64-bin.zipUnzip the archive and move the `tctl` and `tsh` clients to your %PATH%
NOTE: Do not place the `tctl` and `tsh` clients in the System32 directory, as this can cause issues when using WinSCP.
Use %SystemRoot% (C:\Windows) or %USERPROFILE% (C:\Users\<username>) instead.
All of the Teleport binaries in Linux installations include the
tctlandtshclients. For more options (including RPM/DEB packages and downloads for i386/ARM/ARM64) see our installation page.curl -O https://cdn.teleport.dev/teleport-v${TELEPORT_VERSION?}-linux-amd64-bin.tar.gztar -xzf teleport-v${TELEPORT_VERSION?}-linux-amd64-bin.tar.gzcd teleportsudo ./installTeleport binaries have been copied to /usr/local/bin
Connecting with TLS routing disabled
This guide's commands assume your Teleport cluster uses TLS routing (
proxy_listener_mode: multiplex), where thetctlandtshclients reach every Teleport service through the Proxy Service's web address on port443. If you're not sure whether this applies to your cluster, check with whoever manages it.If your cluster uses separate listener ports instead, adjust ports as follows:
-
tshcommands (e.g.,tsh login --proxy=...): continue using the Proxy Service web address on port3080(or443if behind a load balancer). Do not change these to port3025. -
Direct
tctlor Auth Service API commands: use port3025for the Auth Service gRPC listener:tctl status --auth-server=teleport.example.com:3025
-
- An OIDC-compatible issuer of your choice (see below for specific requirements)
- An environment or workload that can fetch JWTs from your OIDC-compatible issuer
- One of the following client tools for managing Teleport resources:
- The
tctlCLI, which you can install along with Teleport on your workstation (documentation) on your workstation. - Teleport Terraform provider
- Teleport Kubernetes operator
- The
Security considerations and limitations
Before getting started with generic_oidc, it is important to consider the
potential security impacts associated with trusting arbitrary identity
providers, as not all providers are created equal.
When Teleport adds support for a new identity provider, care is taken to ensure they meet minimum security requirements and that joining rules ensure a minimum bar for security. This includes:
- Ensuring the provider is actually OIDC compliant and issues sane credentials to workloads
- Ensuring those issued credentials contain useful identifying claims, and that one or more of those claims are required
When using generic_oidc, you should ensure any providers whose tokens you
decide to trust provide at least the above properties.
As a hypothetical example, consider Google Cloud Platform. Note that Teleport
has a dedicated
gcp join method
which should be used instead of generic_oidc, but it makes for a useful
example. If you naively issue a token using the gcloud tool it contains very
few useful claims:
gcloud auth print-identity-token --audiences=example.teleport.sh | jwt decode -[...]{ "aud": "example.teleport.sh", "azp": "115001032080118155850", "exp": 1784168534, "iat": 1784164934, "iss": "https://accounts.google.com", "sub": "115001032080118155850"}
Note the following:
- There are no custom claims to match against
- Only
subcontains any identifying information at all - The issuer (
iss) is global (https://accounts.google.com) and applies to all GCP users - The audience (
aud) is determined by the client and is generally not a claim that should be used for client verification
Given this, if you configured a generic_oidc join token to trust these GCP
tokens, it would be trivially easy to write a rule that accidentally allowed
any GCP user to authenticate to your Teleport cluster if your rule for the
single useful claim (sub) was written incorrectly. As such, we would not
recommend using generic_oidc to allow clients to join with a token this
limited.
In GCP's case, you can pass an additional flag (--token-format=full) to the
gcloud tool to include a larger set of useful claims in the issued JWT, like
service account, instance, and project information - this is what Teleport's
dedicated gcp join method
ensures - and is why built-in join methods are generally preferred where
available.
Limitations
Be aware that not all possible rules can be configured at this time. The
generic_oidc join method has 3 different rule types, but it still may not
always possible to encode arbitrarily complex logic into the token to account
for especially unusual providers.
Limitations to be aware of include:
- Teleport's predicate language currently cannot compare floating point numbers,
so
expressionrules withinallow_anycannot validate numeric fields. Usemust_match_fieldsorconditionsif you need to compare a number field. Note that numbers encoded as strings in the JWT can be compared as strings without issue. - Teleport's predicate language currently has limited support for lists and
unary strings within its built-in functions, so string and list values must be
wrapped using the
set()helper to be used certain functions likeregexp.match(),contains_all(), orcontains_any(). - The
generic_oidcjoin method will refuse to compare integers encoded in number fields if they are larger than 2^53-1. This is because JSON numbers (64 bit IEEE floating point values) cannot uniquely represent numbers larger than 2^53-1, meaning multiple values would would match your expected number beyond this limit. If your token tries to compare a field with an integer value larger than this - on either side of the comparison - the join attempt will be rejected. - Teleport's Terraform provider currently does not support
must_match_fieldsand will drop any values you enter into the field. - Teleport's Kubernetes operator currently has limited support for
must_match_fieldsand only top-level claims fields (i.e. not nested fields) can be validated with this rule type.
Identity provider requirements
Additionally, there are a number of minimum security requirements that identity
providers must meet to work with generic_oidc:
- They must use a modern signature algorithm, including:
RS256or larger,PS256or larger,ES256or larger, orEdDSA - They must provide a
kidin the token header - They must issue JWTs with standard fields set, including
exp,iat,sub,aud, andiss - They must be able to serve OIDC discovery and JWKS resources within 10
seconds (unless
static_jwksis used) - They must serve OIDC discovery and JWKS resources via HTTPS (unless
insecure_allow_http_issueris set in the token) at the standard path:$ISSUER/.well-known/openid-configuration
Step 1/2. Create a Generic OIDC join token
To allow your new agent to authenticate with Teleport, you'll need to create a
join token. Join tokens define the criteria by which the Teleport Auth Service
decides whether a join attempt will be allowed or rejected. In this step, you'll
create a generic_oidc-type join token.
Retrieving a reference token
To create a generic_oidc join token, you'll first need to determine which OIDC
provider you're using and then find or generate a reference JWT to use as a
template for building your join token and rules.
Many providers provide documented token examples, for example:
Note that many of these providers (like GitHub, GitLab, and Google Cloud) have
dedicated Teleport join methods which should be used instead of generic_oidc.
Alternatively, if you can fetch a JWT inside your existing workload, then you
can decode it with a tool like jwt-cli:
echo "$EXAMPLE_TOKEN" | jwt decode -
Different providers issue tokens to workloads in different ways, for example:
- Many CI/CD providers insert a token into a job's environment as an environment variable before the job starts. Note that many providers require a config parameter to enable this for a given step or workflow, or require a project-level setting to enable OIDC token issuance.
- Some platforms have a CLI tool that can fetch tokens and write them to stdout.
- Other platforms have an internal private HTTP API you can query to request a token.
Regardless of your provider, to continue you'll need to determine which of the
two options you'll use to configure the tbot client to use the token:
- An environment variable that contains a JWT
- A command to run that fetches a JWT. For providers that provide an HTTP
endpoint, you may need to provide your own script that fetches the token, for
example using
curl.
For this example, we'll use a hypothetical provider named ExampleCI. They
provide a command to run to fetch tokens with a given audience (aud) value,
and their tokens look like this:
example-ci issue-token --audience=teleport.example.com/example-agent | jwt decode -{ "namespace_id": "123", "namespace_path": "acme-corp", "project_id": "456", "project_path": "acme-corp/example-project", "user_id": "1", "user_login": "alice", "user_email": "[email protected]", "job_id": "100", "ref": "feature-branch-1", "ref_type": "branch", "ref_path": "refs/heads/feature-branch-1", "ref_protected": "false", "runner": { "environment": "self-hosted", "protected": "false", "action": "start" }, "job_source": "push", "jti": "235b3a54-b797-45c7-ae9a-f72d7bc6ef5b", "iss": "https://example.com", "iat": 1681395193, "nbf": 1681395188, "exp": 1681398793, "sub": "project_path:acme-corp/example-project:ref_type:branch:ref:feature-branch-1", "aud": "teleport.example.com/example-agent"}
Creating a join token
With this JWT template in mind, you will create a join token that grants access
to any CI/CD workflow runs within the acme-corp/example-project repository,
with some additional restrictions included for demonstration purposes. You can
find a full list of the available rules and syntax on the
join token reference page.
To do so, decide how you wish to create the token - directly via tctl, via
Teleport's Terraform provider, or via Teleport's Kubernetes operator - and
refer to the matching token template to use as a starting point:
- tctl
- Terraform
- Kubernetes
Create token.yaml with this initial content:
kind: token
version: v2
metadata:
# name identifies the token. When configuring a bot or node to join using this
# token, this name should be specified.
name: generic-oidc-token
spec:
# For Machine & Workload Identity bots, roles will always be "Bot". For
# standard Teleport agents, roles may include "Node", "Kube", "App", "db", or
# similar, depending on the desired services.
roles: [Node]
# This field is always "generic_oidc" for Generic OIDC joining.
join_method: generic_oidc
# bot_name specifies the name of the bot that this token will grant access to
# when it is used. If joining a standard Teleport agent, this field should be
# omitted. It is required for bots.
# bot_name: generic-oidc-demo
generic_oidc:
# The OIDC issuer. Must exactly match the `iss` value in incoming OIDC
# tokens. This value is always required.
#
# Unless `static_jwks` is configured, this issuer must be accessible over
# HTTPS to the Teleport cluster and must serve valid OIDC metadata,
# including discovery configuration and JWKS keys.
issuer: https://example.com
# If set, this flag allows the use of HTTP-only issuers. Not recommended for
# production use.
# insecure_allow_http_issuer: false
# The audience (`aud`) value to require in the incoming JWTs. This value is
# always required.
#
# Note that not all issuers allow you to configure or request an audience
# value of your choosing. For issuers that require use of prescribed `aud`
# values, that value should be entered here exactly.
#
# Otherwise, for providers that do allow you to configure or request a
# value, we recommend making the value unique to this cluster and join
# token. You can include your Teleport cluster name (teleport.example.com)
# and the name of the token, as shown below, or use a random UUID if you
# prefer.
audience: teleport.example.com/generic-oidc-token
# The TLS CA certificate or certificates, if your OIDC provider's TLS
# certificates are not trusted by the standard system trust store. If set,
# this value replaces the system CA store outright, so you can include
# multiple concatenated certificates if necessary.
#
# The CAs specified here are only used to validate requests using this token
# and will not be used to verify any other join attempts or TLS connections
# for other Teleport features. Be aware that if the issuer's TLS certificate
# is rotated such that it is no longer trusted by the CA certs specified
# here, join attempts will be rejected and this token will need to be
# updated to include the updated CA certificates.
#
# Most users will not need to configure this value.
#
# tls_ca: |
# -----BEGIN CERTIFICATE-----
# ...
# -----END CERTIFICATE-----
# These fields perform simple equality matches against the incoming JWT
# using "AND" semantics. Rules are written by mirroring the structure of the
# JWT, and each written value must be equal to the value in the JWT
# presented by the client.
#
# These field rules can be used as simple "global" matchers that apply to
# all join attempts using this token. For example, you can use this to
# ensure all `allow_any` rules also check `organization_name` to ensure they
# can never accidentally skip an important check.
#
# If any rules are specified here, all must match. If any rules are
# specified in `allow_any`, these rules are evaluated first, if any exist,
# and `allow_any` is only evaluated if all of these checks pass. If no rules
# are specified here, only `allow_any` rules are evaluated. At least one
# rule must exist between `must_match_fields` and `allow_any`.
#
# Note the following limitations for field rules:
# - This field currently cannot be configured via the Teleport Terraform
# provider and `allow_any` must be used instead.
# - Strings, numbers, booleans, and nested objects are supported.
# - List values are currently not supported.
# - Integer comparisons must be less than 2^53-1, as integers larger than
# this cannot be accurately represented in JSON's 64-bit floating point
# number type. If either a rule or incoming value is larger than this, the
# join attempt will be rejected.
#
# NOTE: must_match_fields is currently NOT SUPPORTED in Teleport's Terraform
# provider, and any rules you specify here will be SILENTLY IGNORED. If
# using the Terraform provider, you must rely exclusively on `allow_any`
# rules instead.
#
# NOTE: must_match_fields currently has limited support if using Teleport's
# Kubernetes operator to provision resources. Nested fields are currently
# not supported and will result in an error at creation time. Top-level
# fields (direct children of `must_match_fields`) will work as expected.
must_match_fields:
organization_name: "acme-corp"
# Fields can be nested freely to match the structure of the incoming JWT.
example:
project_id: example-1234567
# Numeric comparisons are supported, but be mindful of the datatype as
# determined by YAML parsing rules.
# This will be parsed as a number, and the datatype of the equivalent
# field on the incoming JWT must also be a number. To compare as a
# string, it must be quoted, e.g.: "1234"
number_field: 1234
# Boolean values are supported, but as above, this also requires that
# the equivalent field on the incoming JWT is a boolean. To compare it
# as a string, the value must be quoted, e.g.: "true"
boolean_field: true
# If specified, at least one of these rules must match. These are evaluated
# with "OR" semantics, after all `must_match_fields` rules have been
# evaluated successfully (if any were written). At least one rule must be
# written between `must_match_fields` and `allow_any`.
#
# Note that for a given entry, at most one of `expression` or `conditions`
# can be configured.
allow_any:
# A Teleport predicate expression. Claims are available under the `claims`
# object.
# Note the following limitations with Teleport's predicate language and
# claim evaluation:
# - Numbers cannot be compared; use `conditions` or `must_match_fields`
# for numeric comparisons instead.
# - Lists must be wrapped in `set()` for use with most predicate
# functions.
- expression: 'claims.organization_name == "acme-corp"'
# A list of simple field conditions. Unlike expressions, attributes are
# not prefixed with "claims" and instead are a dot-separated list of
# fields starting from the top level of the JWT.
#
# Valid operators include: eq, not_eq, in, not_in
- conditions:
- attribute: email_verified
eq:
value: "true"
# Nested fields are supported. This denies join requests where the
# project named "invalid-project" is present in the JWT.
- attribute: example.project_name
not_eq:
value: "invalid-project"
# `in` can be used to accept one from a set of values, for example this
# allows join attempts where `project_name` is any of "foo", "bar", or
# "baz".
- attribute: example.project_name
in:
values: [foo, bar, baz]
# `not_in` can be used to exclude a set of values, for example this
# denies join attempts where `zone` is `us-central1-a|b|c`
- attribute: zone
not_in:
values: ["us-central1-a", "us-central1-b", "us-central1-c"]
Customize it as necessary, and when ready, create the token:
tctl create token.yaml
Finally, validate the token was created:
tctl get token/generic-oidc-token
kind: tokenmetadata: expires: "3000-01-01T00:00:00Z" name: generic-oidc-tokenspec: join_method: generic_oidc roles: - Nodeversion: v2
Note that must_match_fields is currently not supported in Teleport's Terraform
provider and any rules you specify in that field will be silently ignored.
For now, either write all rules instead within allow_any, or create your
generic_oidc tokens via tctl.
Add the following resource to your Terraform configuration, adjusting values and rules as necessary:
resource "teleport_provision_token" "generic-oidc-token" {
version = "v2"
metadata = {
# name identifies the token. When configuring a bot or node to join using
# this token, this name should be specified.
name = "generic-oidc-token"
labels = {
// This label is added on the Teleport side by default
"teleport.dev/origin" = "dynamic"
}
}
spec = {
// For Machine & Workload Identity joining, roles will always be "Bot". For
// standard Teleport agents, roles may include "Node", "Kube", "App", "db",
// or similar, depending on the desired services.
roles = ["Node"]
// This field is always "generic_oidc" for Generic OIDC joining.
join_method = "generic_oidc"
// bot_name specifies the name of the bot that this token will grant access
// to when it is used. If joining a standard Teleport agent, this field
// should be omitted. This field is required for bots
// bot_name = "generic-oidc-demo"
generic_oidc = {
// The OIDC issuer. Must exactly match the `iss` value in incoming OIDC
// tokens. This value is always required.
//
// Unless `static_jwks` is configured, this issuer must be accessible over
// HTTPS to the Teleport cluster and must serve valid OIDC metadata,
// including discovery configuration and JWKS keys.
issuer = "https://example.com"
// If set, this flag allows the use of HTTP-only issuers. Not recommended
// for production use.
// insecure_allow_http_issuer = false
// The audience (`aud`) value to require in the incoming JWTs. This value
// is always required.
//
// Note that not all issuers allow you to configure or request an audience
// value of your choosing. For issuers that require use of prescribed
// `aud` values, that value should be entered here exactly.
//
// Otherwise, for providers that do allow you to configure or request a
// value, we recommend making the value unique to this cluster and join
// token. You can include your Teleport cluster name (example.teleport.sh)
// and the name of the token, as shown below, or use a random UUID if you
// prefer.
audience = "teleport.example.com/generic-oidc-token"
// The TLS CA certificate or certificates, if your OIDC provider's TLS
// certificates are not trusted by the standard system trust store. If
// set, this value replaces the system CA store outright, so you can
// include multiple concatenated certificates if necessary.
//
// The CAs specified here are only used to validate requests using this
// token and will not be used to verify any other join attempts or TLS
// connections for other Teleport features. Be aware that if the issuer's
// TLS certificate is rotated such that it is no longer trusted by the CA
// certs specified here, join attempts will be rejected and this token
// will need to be updated to include the updated CA certificates.
//
// Most users will not need to configure this value.
//
// tls_ca = <<-EOT
// -----BEGIN CERTIFICATE-----
// ...
// -----END CERTIFICATE-----
// EOT
// NOTE: `must_match_fields` is currently NOT SUPPORTED in Teleport's
// Terraform provider, and any rules you specify here will be SILENTLY
// IGNORED. When using the Terraform provider, you must rely exclusively
// on `allow_any` rules instead.
// must_match_fields = { ... }
// If specified, at least one of these rules must match. These are
// evaluated with "OR" semantics, after all `must_match_fields` rules have
// been evaluated successfully (if any were written). At least one rule
// must be written between `must_match_fields` and `allow_any`. Because the
// Terraform provider does not support `must_match_fields`, `allow_any`
// must contain at least one rule here.
//
// Note that for a given entry, at most one of `expression` or
// `conditions` can be configured.
allow_any = [
{
// A Teleport predicate expression. Claims are available under the
// `claims` object.
// Note the following limitations with Teleport's predicate language
// and claim evaluation:
// - Numbers cannot be compared; use `conditions` for numeric
// comparisons instead.
// - Lists must be wrapped in `set()` for use with most predicate
// functions.
expression = "claims.organization_name == \"acme-corp\""
},
{
// A list of simple field conditions. Unlike expressions, attributes
// are not prefixed with "claims" and instead are a dot-separated list
// of fields starting from the top level of the JWT.
//
// Valid operators include: eq, not_eq, in, not_in
conditions = [
{
attribute = "email_verified"
eq = {
value = "true"
}
},
{
// Nested fields are supported. This denies join requests where
// the project named "invalid-project" is present in the JWT.
attribute = "example.project_name"
not_eq = {
value = "invalid-project"
}
},
{
// `in` can be used to accept one from a set of values, for
// example this allows join attempts where `project_name` is any
// of "foo", "bar", or "baz".
attribute = "example.project_name"
in = {
values = ["foo", "bar", "baz"]
}
},
{
// `not_in` can be used to exclude a set of values, for example
// this denies join attempts where `zone` is `us-central1-a|b|c`
attribute = "zone"
not_in = {
values = ["us-central1-a", "us-central1-b", "us-central1-c"]
}
},
]
},
]
}
}
}
Note that must_match_fields has limited support in Teleport's Kubernetes
operator: rules can only be written against root-level claims on the incoming
JWT. Attempting to check nested claim fields will result in an error.
If you need to check nested fields, either write those checks within allow_any
rules, or create your generic_oidc token via tctl which does not have this
limitation.
Add the following Kubernetes resource manifest, adjusting values and rules as necessary:
apiVersion: "resources.teleport.dev/v2"
kind: TeleportProvisionToken
metadata:
# name identifies the token. When configuring a bot or node to join using this
# token, this name should be specified.
name: generic-oidc-token
labels:
# This label is added on the Teleport side by default
"teleport.dev/origin": "dynamic"
spec:
# For Machine & Workload Identity bots, roles will always be "Bot". For
# standard Teleport agents, roles may include "Node", "Kube", "App", "db", or
# similar, depending on the desired services.
roles: [Node]
# This field is always "generic_oidc" for Generic OIDC joining.
join_method: generic_oidc
# bot_name specifies the name of the bot that this token will grant access to
# when it is used. If joining a standard Teleport agent, this field should be
# omitted. This field is required for bots.
# bot_name: generic-oidc-demo
generic_oidc:
# The OIDC issuer. Must exactly match the `iss` value in incoming OIDC
# tokens. This value is always required.
#
# Unless `static_jwks` is configured, this issuer must be accessible over
# HTTPS to the Teleport cluster and must serve valid OIDC metadata,
# including discovery configuration and JWKS keys.
issuer: https://example.com
# If set, this flag allows the use of HTTP-only issuers. Not recommended for
# production use.
# insecure_allow_http_issuer: false
# The audience (`aud`) value to require in the incoming JWTs. This value is
# always required.
#
# Note that not all issuers allow you to configure or request an audience
# value of your choosing. For issuers that require use of prescribed `aud`
# values, that value should be entered here exactly.
#
# Otherwise, for providers that do allow you to configure or request a
# value, we recommend making the value unique to this cluster and join
# token. You can include your Teleport cluster name (example.teleport.sh)
# and the name of the token, as shown below, or use a random UUID if you
# prefer.
audience: teleport.example.com/generic-oidc-token
# The TLS CA certificate or certificates, if your OIDC provider's TLS
# certificates are not trusted by the standard system trust store. If set,
# this value replaces the system CA store outright, so you can include
# multiple concatenated certificates if necessary.
#
# The CAs specified here are only used to validate requests using this token
# and will not be used to verify any other join attempts or TLS connections
# for other Teleport features. Be aware that if the issuer's TLS certificate
# is rotated such that it is no longer trusted by the CA certs specified
# here, join attempts will be rejected and this token will need to be
# updated to include the updated CA certificates.
#
# Most users will not need to configure this value.
#
# tls_ca: |
# -----BEGIN CERTIFICATE-----
# ...
# -----END CERTIFICATE-----
# These fields perform simple equality matches against the incoming JWT
# using "AND" semantics. Rules are written by mirroring the structure of the
# JWT, and each written value must be equal to the value in the JWT
# presented by the client.
#
# These field rules can be used as simple "global" matchers that apply to
# all join attempts using this token. For example, you can use this to
# ensure all `allow_any` rules also check `organization_name` to ensure they
# can never accidentally skip an important check.
#
# If any rules are specified here, all must match. If any rules are
# specified in `allow_any`, these rules are evaluated first, if any exist,
# and `allow_any` is only evaluated if all of these checks pass. If no rules
# are specified here, only `allow_any` rules are evaluated. At least one
# rule must exist between `must_match_fields` and `allow_any`.
#
# Note the following limitations for field rules:
# - Strings, numbers, booleans, and nested objects are supported.
# - List values are currently not supported.
# - Integer comparisons must be less than 2^53-1, as integers larger than
# this cannot be accurately represented in JSON's 64-bit floating point
# number type. If either a rule or incoming value is larger than this, the
# join attempt will be rejected.
#
# NOTE: must_match_fields currently has limited support when using
# Teleport's Kubernetes operator to provision resources. Nested fields are
# currently not supported and will result in an error at creation time.
# Only top-level fields (direct children of `must_match_fields`) will work
# as expected. The nested `example` block below is included for parity with
# the source token, but will be rejected by the operator as written; remove
# it or flatten it to top-level fields when using the Kubernetes operator.
must_match_fields:
organization_name: "acme-corp"
# Fields can be nested freely to match the structure of the incoming JWT.
example:
project_id: example-1234567
# Numeric comparisons are supported, but be mindful of the datatype as
# determined by YAML parsing rules.
# This will be parsed as a number, and the datatype of the equivalent
# field on the incoming JWT must also be a number. To compare as a
# string, it must be quoted, e.g.: "1234"
number_field: 1234
# Boolean values are supported, but as above, this also requires that
# the equivalent field on the incoming JWT is a boolean. To compare it
# as a string, the value must be quoted, e.g.: "true"
boolean_field: true
# If specified, at least one of these rules must match. These are evaluated
# with "OR" semantics, after all `must_match_fields` rules have been
# evaluated successfully (if any were written). At least one rule must be
# written between `must_match_fields` and `allow_any`.
#
# Note that for a given entry, at most one of `expression` or `conditions`
# can be configured.
allow_any:
# A Teleport predicate expression. Claims are available under the `claims`
# object.
# Note the following limitations with Teleport's predicate language and
# claim evaluation:
# - Numbers cannot be compared; use `conditions` or `must_match_fields`
# for numeric comparisons instead.
# - List support is currently limited.
- expression: 'claims.organization_name == "acme-corp"'
# A list of simple field conditions. Unlike expressions, attributes are
# not prefixed with "claims" and instead are a dot-separated list of
# fields starting from the top level of the JWT.
#
# Valid operators include: eq, not_eq, in, not_in
- conditions:
# Condition values are always compared as strings: the incoming
# attribute is cast to a string before it is compared, so the value must
# be quoted (e.g. "true" rather than true).
- attribute: email_verified
eq:
value: "true"
# Nested fields are supported. This denies join requests where the
# project named "invalid-project" is present in the JWT.
- attribute: example.project_name
not_eq:
value: "invalid-project"
# `in` can be used to accept one from a set of values, for example this
# allows join attempts where `project_name` is any of "foo", "bar", or
# "baz".
- attribute: example.project_name
in:
values: [foo, bar, baz]
# `not_in` can be used to exclude a set of values, for example this
# denies join attempts where `zone` is `us-central1-a|b|c`
- attribute: zone
not_in:
values: ["us-central1-a", "us-central1-b", "us-central1-c"]
Be aware that the examples shown above are for demonstrative purposes. Make sure to replace all rules with values tailored to your provider and environment, using the reference token you fetched as a guide.
Broadly, we recommend the following minimum rules:
-
Define some "global" requirements in
must_match_fields, like an organization name or ID. These are always required, and are cheap insurance to protect against anallow_anyrule that might accidentally let unintended clients in. At worst, these can limit the blast radius to within your organization.In the example JWT shown above,
namespace_idandnamespace_pathmight be good global rules to configure for all joining clients. -
Define one or more
allow_anyrules to allow individual workloads or other compute units that should be granted access, using eitherexpressionorconditions.With the example JWT in mind,
project_idandproject_pathmight be useful fields to perform an equality check on, to ensure join attempts are coming from an authorized project.-
The
conditionsrule allows you to specify a list of simple conditions (eq,not_eq,in,not_in) that claim attributes must match. -
The
expressionrule type uses Teleport's predicate language to evaluate claims based on complex logic.See the limitations section above for some specific compatibility notes when using some built-in predicate functions.
-
Additionally, be aware of the rule evaluation order:
- First, all
must_match_fieldschecks are executed. All values specified here must both exist and match the corresponding values on the incoming JWT. Ifmust_match_fieldsis empty, this is skipped. - Next, each rule listed in
allow_anyis evaluated sequentially and evaluation stops after the first matching rule (either anexpressionorconditions). - You may combine as many rules as you'd like between either
must_match_fieldsorallow_any, or skip either variant entirely if you prefer. At least one rule or value must be matched between both rule types.
Step 2/2. Set up Teleport on your provider
The Generic OIDC join method can be used for Teleport processes running the SSH, Proxy, Kubernetes, Application, Database, or Desktop Service.
-
Install Teleport on your target environment (VM or physical machine)
To install Teleport binaries on your Linux server, the recommended installation method is the cluster install script. This script is served by your Teleport cluster's Proxy Service and automatically selects the correct version, edition, and installation mode to match your cluster.
-
Remove any existing Teleport binaries on your system:
sudo rm -f /usr/local/bin/{tsh,teleport,tctl,tbot,fdpass-teleport,teleport-update} -
Assign teleport.example.com:443 to your Teleport cluster hostname and port, but not the scheme (https://).
-
Run your cluster's install script:
curl "https://teleport.example.com:443/scripts/install.sh" | sudo bash
-
-
Configure your Teleport process with a custom
teleport.yamlfile. Use thejoin_paramssection withtoken_namematching your token created in Step 1 andmethod: generic_oidcas shown in the following example config:# /etc/teleport.yaml version: v3 teleport: join_params: token_name: generic-oidc-token method: generic_oidc generic_oidc: # Specify a command to run. The first value must be the executable to run, # followed by arguments, one per list entry. This example calls # `example-provider issue-token ...` with a client-specified audience # request. Your provider will differ. command: ["example-ci", "issue-token", "--audience=teleport.example.com/example-agent"] # By default, commands timeout after 1 minute. If your OIDC provider # needs a different value, specify it in `timeout` # timeout: 1m # Alternatively, if the token can be found inside an environment # variable, you can simply specify the name of the variable containing # the token. This cannot be combined with `command`. # env: "ENV_VAR_WITH_JWT" proxy_server: teleport.example.com:443 ssh_service: enabled: true auth_service: enabled: false proxy_service: enabled: false -
Start Teleport:
Start your Teleport instance. The instructions depend on how you installed your Teleport instance and whether your system supports systemd:
- Package Manager
- TAR Archive
- No systemd
Configure your Teleport instance to start automatically when the host boots up by creating a systemd service for it. On the host where you will run your Teleport instance, enable and start Teleport:
sudo systemctl enable teleportsudo systemctl start teleportYou can check the status of your Teleport instance with
systemctl status teleportand view its logs withjournalctl -fu teleport.Configure your Teleport instance to start automatically when the host boots up by creating a systemd service for it. On the host where you will run your Teleport instance, create a systemd service configuration for Teleport, enable the Teleport service, and start Teleport:
sudo teleport install systemd -o /etc/systemd/system/teleport.servicesudo systemctl enable teleportsudo systemctl start teleportYou can check the status of your Teleport instance with
systemctl status teleportand view its logs withjournalctl -fu teleport.On the host where you will run your Teleport instance, start Teleport:
sudo teleport start --config=/etc/teleport.yamlTeleport runs in the foreground and outputs logs for the services it is running.
-
Confirm that your Teleport process is able to connect to and join your cluster. You're all set!