Connect managed control planes to external services

Managed control planes use Crossplane providers to interact with external services. External services could be hyper scale cloud providers, version control services, ticketing platforms, or anything else that has a public API. Control planes can connect to an unlimited number of external services–just as long as there’s an associated Crossplane provider in the Upbound Marketplace.

How to connect to an external service

Install a provider on your control plane

Install a Crossplane provider on your managed control plane by declaring a dependency on the desired provider package in your control plane’s configuration source.

Configure the provider

Crossplane providers use a ProviderConfig Kubernetes object to supply credentials to authenticate with external services. For example, Upbound’s AWS provider offers a ProviderConfig that supports supplying credentials via a Kubernetes Secret, environment variable, file, and more.

Connect to multiple accounts within a service

You can create multiple ProviderConfig objects for a single provider in a managed control plane. You can create new ProviderConfigs on your control plane via the Upbound Console or by connecting directly to your control plane and creating new ProviderConfigs on it.

For example, imagine you have team-a and team-b sharing a single managed control plane. Suppose each team should only be able to create resources in their respective cloud account in AWS. You would create two ProviderConfigs as demonstrated below:

# This is the ProviderConfig that will get used by team-a
apiVersion: aws.upbound.io/v1beta1
kind: ProviderConfig
metadata:
  name: team-a-config
spec:
  credentials:
    secretRef:
      key: credentials
      name: team-a-account-creds
      namespace: upbound-system
    source: Secret
---
# This is the ProviderConfig that will get used by team-b
apiVersion: aws.upbound.io/v1beta1
kind: ProviderConfig
metadata:
  name: team-b-config
spec:
  credentials:
    secretRef:
      key: credentials
      name: team-b-account-creds
      namespace: upbound-system
    source: Secret

When a team creates resources with the control plane, reference the appropriate ProviderConfig to use from the spec.providerConfigRef field of any managed resource.

Tip
The example above demonstrates ProviderConfigs using simple account credentials. Don’t use this auth method for production purposes. Instead, configure your providers to use Upbound Identity, which is based on OIDC and described below.

Use OpenID Connect with Upbound

Providers in Upbound managed control planes can also use an Upbound credential source called Provider Identity. The Upbound credential source uses OpenID Connect (OIDC) to authenticate to providers without requiring users to store credentials on Upbound. This authentication method is comparable to “workload identity” offered by some managed Kubernetes services.

OpenID Connect (OIDC) is a protocol that’s built on top of OAuth 2.0. It serves to establish the identity of an entity in one environment that’s attempting to access resources in another.

Configure Upbound identity for a provider

To configure a provider on a managed control plane to use Upbound Identity, use the auth method called Upbound. Exact configuration varies depending on the provider. Find examples below for AWS, Azure, and GCP:

Upbound identity for AWS example

First, add Upbound as an OpenID Connect provider in your AWS account. Then, create a ProviderConfig on your control plane with the following configuration:

apiVersion: aws.upbound.io/v1beta1
kind: ProviderConfig
metadata:
  name: default
spec:
  credentials:
    source: Upbound
    upbound:
      webIdentity:
        roleARN: 

Upbound identity for Azure example

First, register Upbound with Azure Active Directory in your Azure account. Then, create a ProviderConfig on your control plane with the following configuration:

apiVersion: azure.upbound.io/v1beta1
kind: ProviderConfig
metadata:
  name: default
spec:
  clientID: 
  tenantID: 
  subscriptionID: 
  credentials:
    source: Upbound

Upbound identity for GCP example

First, configure Upbound for workload identity federation in your GCP account. Then, create a ProviderConfig on your control plane with the following configuration:

apiVersion: gcp.upbound.io/v1beta1
kind: ProviderConfig
metadata:
  name: default
spec:
  projectID: 
  credentials:
    source: Upbound
    upbound:
      federation:
        providerID: 
        serviceAccount: 

OIDC explained

OIDC calls the two parties the OpenID Providers (OPs) and Relying Parties (RPs). Managed control planes define these roles as follows:

  • OpenID Provider: Upbound
  • Relying Party: an external service that supports OIDC, for example, AWS, Azure or GCP

Users set up a trust relationship between Upbound and the external service. Upbound uses the trust relationship instead of storing credentials for an external service in the managed control plane.

Upbound injects an identity token into the file system of every provider Pod. Upbound sends the token to the external service and exchanges it for a short-lived credential. Upbound uses the short-lived credential to perform operations against the external service.

Creating trust relationships

Every OIDC relying party implements its own mechanism for establishing a trust relationship and associating permissions. Typically, the process involves the following broad steps:

  1. Specifying the issuer. For Upbound, this is https://proidc.upbound.io.
  2. Specifying an audience. This may vary by relying party.
  3. Creating a role or service account that an entity possessing a valid identity token can assume.
  4. Associating permissions with the role or service account.

Different OIDC relaying parties may define valid tokens differently. Typically it involves writing a policy that restricts what subject can assume the role or service account.

Warning
Authoring a policy with appropriate access controls is critical to ensure that only your provider in your managed control plane is able to assume the role or service account.

Identity tokens are JSON Web Tokens (JWTs). The OIDC claims supported by Upbound are available in the OIDC metadata endpoint at https://proidc.upbound.io/.well-known/openid-configuration.

{
  "issuer": "https://proidc.upbound.io",
  "jwks_uri": "https://proidc.upbound.io/.well-known/jwks",
  "response_types_supported": [
    "id_token"
  ],
  "subject_types_supported": [
    "public"
  ],
  "id_token_signing_alg_values_supported": [
    "RS256"
  ],
  "claims_supported": [
    "sub",
    "aud",
    "exp",
    "iat",
    "iss",
    "jti",
    "nbf"
  ]
}

OIDC relying parties use this information to validate identity tokens. Relying parties use the jwks_uri to ensure that the OIDC provider signed the identity token with their private key. The private key must correspond to one of the public keys from https://proidc.upbound.io/.well-known/jwks.

The iss and aud claims of an identity token should match the issuer and audience configured for the relying party. The sub should be a valid subject based on the authored policy. For providers running in Upbound managed control planes, the subject adheres to the following format.

mcp:<account>/<mcp-name>:provider:<provider-name>

For example, the following would be a valid subject for provider-aws in a managed control plane named prod-1 in the my-org account.

mcp:my-org/prod-1:provider:provider-aws

The claims for an identity token injected into the file system of a provider in a managed control plane looks like the following.

{
  "iss": "https://proidc.upbound.io",
  "sub": "mcp:my-org/prod1:provider:provider-aws",
  "aud": [
    "sts.amazonaws.com"
  ],
  "exp": 1680124165,
  "nbf": 1680120565,
  "iat": 1680120565,
  "jti": "YL1ouQ5KJiTY2QShIRczqQ=="
}
Tip
Identity tokens injected into a provider Pod are valid for 1 hour. They are automatically refreshed before expiration to ensure there is no interruption in service.

Add Upbound OIDC to a Crossplane provider

Any provider that can run in a managed control plane can support the Upbound credential source. Upbound injects identity tokens into the file system of every provider Pod whether they support OIDC or not. A provider wishing to support OIDC can access its identity token in the /var/run/secrets/upbound.io/provider/token file.

View this Pull Request for a reference implementation.