Teleport Workload Identity with SPIFFE: Achieving Zero Trust in Modern Infrastructure
May 23
Virtual
Register Today
Teleport logoTry For Free
Fork me on GitHub

Teleport

Protect Azure CLIs with Teleport Application Access on AKS

You can use Teleport to manage access to CLI tools that interact with Azure's APIs. This lets you control access to your infrastructure's management APIs using the same RBAC system that you use to protect your infrastructure itself.

In this guide, you will:

  1. Create an Azure managed identity for the Application Service and set it as the default Workload ID for your Kubernetes service account.
  2. Create an Azure managed identity for user access and attach it to the same Kubernetes service account.
  3. Deploy a Teleport Application Service with an Azure app in your Teleport cluster.
  4. Assume the managed identity and run az commands via tsh.

How it works

The Teleport Application Service installed in an AKS pod uses Microsoft Entra Workload ID to obtain authentication tokens from Azure. When a user authenticates to Teleport, they can assume one of the respective user-assigned managed identities to execute Azure CLI commands.

You can configure which Teleport users or roles have access to specific Azure identities, giving you control over who can obtain credentials for different levels of access to Azure CLIs.

The Teleport Application Service connects to the Teleport Proxy Service over a reverse tunnel, so you can run the Application Service in a private network and prevent unauthorized access to your organization's Azure identities.

Prerequisites

  • A running Teleport cluster version 15.2.4 or above. If you want to get started with Teleport, sign up for a free trial or set up a demo environment.

  • The tctl admin tool and tsh client tool.

    On Teleport Enterprise, you must use the Enterprise version of tctl, which you can download from your Teleport account workspace. Otherwise, visit Installation for instructions on downloading tctl and tsh for Teleport Community Edition.

  • An Azure Kubernetes Service (AKS) cluster and admin permissions to manage the cluster.
  • The ability to manage user-assigned Azure managed identities, role policies, and federated identity credentials.
  • The az CLI tool installed on your workstation. You need to login as your Azure admin account to configure the AKS cluster and create managed identities. Teleport's tsh client also uses the az binary to execute commands. See the Azure documentation for how to install the az CLI on your operating system.
  • kubectl and helm for AKS deployments.
  • To check that you can connect to your Teleport cluster, sign in with tsh login, then verify that you can run tctl commands using your current credentials. tctl is supported on macOS and Linux machines. For example:
    tsh login --proxy=teleport.example.com --user=[email protected]
    tctl status

    Cluster teleport.example.com

    Version 15.3.0

    CA pin sha256:abdc1245efgh5678abdc1245efgh5678abdc1245efgh5678abdc1245efgh5678

    If you can connect to the cluster and run the tctl status command, you can use your current credentials to run subsequent tctl commands from your workstation. If you host your own Teleport cluster, you can also run tctl commands on the computer that hosts the Teleport Auth Service for full permissions.

Step 1/6. Create an Azure managed identity for Teleport Application Service

The Teleport Application Service requires a managed identity that can retrieve the client IDs of the managed identities for user access. This managed identity will be assigned as the default identity for the Kubernetes service account.

Log in to your Azure admin account with az login command if you haven't already, and prepare some environment variables for later steps:

export SUBSCRIPTION="$(az account show --query id --output tsv)"
export LOCATION="eastus"
export RESOURCE_GROUP="myResourceGroup"
export AKS_CLUSTER_NAME="myASKCluster"
export USER_ASSIGNED_IDENTITY_NAME="teleport-azure-cli-aks-agent"

Now create the managed identity, and remember the client ID for a later step:

az identity create --name "${USER_ASSIGNED_IDENTITY_NAME}" --resource-group "${RESOURCE_GROUP}" --location "${LOCATION}" --subscription "${SUBSCRIPTION}"
export USER_ASSIGNED_CLIENT_ID="$(az identity show --resource-group "${RESOURCE_GROUP}" --name "${USER_ASSIGNED_IDENTITY_NAME}" --query 'clientId' -o tsv)"

Next, create a role with Microsoft.ManagedIdentity/userAssignedIdentities/read permission and assign it to the managed identity:

cat > ${USER_ASSIGNED_IDENTITY_NAME}-role.json <<EOF{ "Name": "${USER_ASSIGNED_IDENTITY_NAME}-role", "Description": "Role for Teleport Azure CLI Access on AKS", "AssignableScopes": [ "/subscriptions/${SUBSCRIPTION}" ], "Actions": [ "Microsoft.ManagedIdentity/userAssignedIdentities/read" ], "notActions": []}EOF
az role definition create --role-definition ./${USER_ASSIGNED_IDENTITY_NAME}-role.json
az role assignment create --role "${USER_ASSIGNED_IDENTITY_NAME}-role" --scope "/subscriptions/${SUBSCRIPTION}" --assignee-object-id $(az identity show --name "${USER_ASSIGNED_IDENTITY_NAME}" --resource-group "${RESOURCE_GROUP}" --query principalId --output tsv) --assignee-principal-type ServicePrincipal

Step 2/6. Configure the AKS cluster for Workload ID

To use Microsoft Entra Workload ID, you need to enable OIDC issuer and Workload ID in your AKS cluster.

az aks update -g "${RESOURCE_GROUP}" -n "{AKS_CLUSTER_NAME}" --enable-oidc-issuer --enable-workload-identity

Before using kubectl, make sure your local Kubernetes config is updated to access your AKS cluster:

az aks get-credentials -n "${AKS_CLUSTER_NAME}" -g "${RESOURCE_GROUP}"

Create a Kubernetes service account and annotate it with the client ID of the managed identity created in the previous step:

cat > azure_access_aks_service_account.yaml <<EOFapiVersion: v1kind: ServiceAccountmetadata: annotations: azure.workload.identity/client-id: "${USER_ASSIGNED_CLIENT_ID}" name: "teleport-azure-cli-aks-service-account" namespace: "teleport-ns"EOF
kubectl apply -f azure_access_aks_service_account.yaml

Now create a federated credential to associate the managed identity created in the previous step with the Kubernetes service account.

export AKS_OIDC_ISSUER="$(az aks show -n "${AKS_CLUSTER_NAME}" -g "${RESOURCE_GROUP}" --query "oidcIssuerProfile.issuerUrl" -o tsv)"
az identity federated-credential create --name "federated-${USER_ASSIGNED_IDENTITY_NAME}" --identity-name "${USER_ASSIGNED_IDENTITY_NAME}" --resource-group "${RESOURCE_GROUP}" --issuer "${AKS_OIDC_ISSUER}" --subject system:serviceaccount:teleport-ns:teleport-azure-cli-aks-service-account --audience api://AzureADTokenExchange

Step 3/6. Create an Azure managed identity for user access

In this step, we will create an user-assigned managed identity that a Teleport user can assume later with tsh and associate this managed identity with the Kubernetes service account.

If you have another managed identity you intend to use for user access, you can skip the creation of a new identity.

Create an Azure managed identity

Create the managed identity with az:

az identity create --name "teleport-reader" --resource-group "${RESOURCE_GROUP}" --location "${LOCATION}" --subscription "${SUBSCRIPTION}"

Remember the resource ID URI of the managed identity as it will be required in your Teleport role or user traits:

az identity show --name "teleport-reader" -g "${RESOURCE_GROUP}" --query id -o tsv

Next assign the managed identity desired permissions that the Teleport user should have. In this example, the "Reader" role is assigned to the managed identity:

az role assignment create --role "Reader" --scope "/subscriptions/${SUBSCRIPTION}" --assignee-object-id $(az identity show --name "teleport-reader" --resource-group "${RESOURCE_GROUP}" --query principalId --output tsv) --assignee-principal-type ServicePrincipal

Associate the managed identity with the Kubernetes service account

A Kubernetes service account can have multiple managed identities assigned to it. The managed identity for the Application Service was assigned to the service account in a previous step. Now we are repeating that for the managed identity for user access:

export AKS_OIDC_ISSUER="$(az aks show -n "${AKS_CLUSTER_NAME}" -g "${RESOURCE_GROUP}" --query "oidcIssuerProfile.issuerUrl" -o tsv)"
az identity federated-credential create --name "federated-teleport-reader" --identity-name "teleport-reader" --resource-group "${RESOURCE_GROUP}" --issuer "${AKS_OIDC_ISSUER}" --subject system:serviceaccount:teleport-ns:teleport-azure-cli-aks-service-account --audience api://AzureADTokenExchange

Step 4/6 Enable your user to access Azure CLIs

The next step is to authorize your Teleport user to assume an Azure identity and execute Azure CLI commands via Teleport. You will protect access to this identity using Teleport's RBAC system, where a user's roles determine which Azure managed identities (if any) they can access.

There are two approaches you can take to authorize users to access Azure identities.

ApproachDescriptionSupported User Types
DynamicA Teleport role includes a template variable that grants a user access to all Azure identities assigned directly to them.Local users, OIDC, SAML
StaticA Teleport role explicitly specifies the Azure identities a user is allowed to assume.Local users, OIDC, SAML, GitHub

We recommend using the dynamic approach, since it scales well as you add Azure identities to your account. If you have configured a Teleport Community Edition cluster to authenticate users using GitHub SSO, you must use the static approach, as OAuth-based GitHub applications do not support custom claims.

Approach

Create a file called azure-cli-access.yaml with the following content:

kind: role
version: v5
metadata:
  name: azure-cli-access
spec:
  allow:
    app_labels:
      '*': '*'
    azure_identities:
      - '{{internal.azure_identities}}'

When a user with the azure-cli-access role authenticates to an Azure CLI via Teleport, the Teleport Auth Service populates the {{internal.azure_identities}} template variable with any Azure identities you have assigned to the user.

Assign the teleport-azure identity to your Teleport user by running the following command, pasting in the URI of the Azure identity you copied earlier as the value of --set-azure-identities:

tctl users update teleport-user \--set-azure-identities azure-identity-uri

This command uses the --set-azure-identities flag to add Azure identities to a user. You can assign --set-azure-identities to multiple identity URIs, separated by commas.

The identity URIs are Azure resource IDs in the following format:

/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/RESOURCE_GROUP_NAME/providers/Microsoft.ManagedIdentity/userAssignedIdentities/IDENTITY_NAME

Create the role:

tctl create -f azure-cli-access.yaml

In your identity provider, define a custom SAML attribute or OIDC claim called azure_identities. Each user's azure_identities attribute or claim must be a list of Azure identity URIs, using the following format:

/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/RESOURCE_GROUP_NAME/providers/Microsoft.ManagedIdentity/userAssignedIdentities/IDENTITY_NAME

Create a file called azure-cli-access.yaml with the following content:

kind: role
version: v5
metadata:
  name: azure-cli-access
spec:
  allow:
    app_labels:
      '*': '*'
    azure_identities:
      - '{{external.azure_identities}}'

When a user with the azure-cli-access role authenticates to an Azure CLI via Teleport, the Teleport Auth Service populates the {{external.azure_identities}} template variable with any Azure identities you have assigned to the user.

Create the role:

tctl create -f azure-cli-access.yaml

Define a role with access to specific Azure identities, which means that Teleport users who assume this role can use those (and only those) identities to execute commands via an Azure CLI.

Create a file called azure-cli-access.yaml with the following content:

kind: role
version: v5
metadata:
  name: azure-cli-access
spec:
  allow:
    app_labels:
      '*': '*'
    azure_identities:
      - /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/my-resource-group/providers/Microsoft.ManagedIdentity/userAssignedIdentities/teleport-azure

Edit the identity URI in the azure_identities field to match the one you copied in Step 1.

This role grants a user access to any Teleport-registered application, such as the azure-cli application we defined earlier, and allows that user to assume the teleport-azure identity you created earlier.

Create the role:

tctl create -f azure-cli-access.yaml

You can define a Teleport role that denies a user access to one or more Azure identities. To do so, assign values to the azure_identities field within the spec.deny section of a role resource.

For example, this role denies the user access to all Azure identities:

kind: role
version: v5
metadata:
  name: "no-azure-identities"
spec:
  allow:
    app_labels:
      '*': '*'
  deny:
    azure_identities:
      - '*'

The no-azure-identities role enables the user to access all registered applications, but makes use of the wildcard character (*) within the deny.azure_identities field to prevent the user from assuming any Azure identity.

Unlike values of allow.azure_identities, values of deny.azure_identities can include wildcard expressions in addition to the URIs of specific Azure identities.

The Teleport Auth Service gives deny rules precedence over allow rules when evaluating a user's roles.

Assign the azure-cli-access role to your Teleport user by running the appropriate commands for your authentication provider:

  1. Retrieve your local user's roles as a comma-separated list:

    ROLES=$(tsh status -f json | jq -r '.active.roles | join(",")')
  2. Edit your local user to add the new role:

    tctl users update $(tsh status -f json | jq -r '.active.username') \ --set-roles "${ROLES?},azure-cli-access"
  3. Sign out of the Teleport cluster and sign in again to assume the new role.

  1. Retrieve your github authentication connector:

    tctl get github/github --with-secrets > github.yaml

    Note that the --with-secrets flag adds the value of spec.signing_key_pair.private_key to the github.yaml file. Because this key contains a sensitive value, you should remove the github.yaml file immediately after updating the resource.

  2. Edit github.yaml, adding azure-cli-access to the teams_to_roles section.

    The team you should map to this role depends on how you have designed your organization's role-based access controls (RBAC). However, the team must include your user account and should be the smallest team possible within your organization.

    Here is an example:

      teams_to_roles:
        - organization: octocats
          team: admins
          roles:
            - access
    +       - azure-cli-access
    
  3. Apply your changes:

    tctl create -f github.yaml
  4. Sign out of the Teleport cluster and sign in again to assume the new role.

  1. Retrieve your saml configuration resource:

    tctl get --with-secrets saml/mysaml > saml.yaml

    Note that the --with-secrets flag adds the value of spec.signing_key_pair.private_key to the saml.yaml file. Because this key contains a sensitive value, you should remove the saml.yaml file immediately after updating the resource.

  2. Edit saml.yaml, adding azure-cli-access to the attributes_to_roles section.

    The attribute you should map to this role depends on how you have designed your organization's role-based access controls (RBAC). However, the group must include your user account and should be the smallest group possible within your organization.

    Here is an example:

      attributes_to_roles:
        - name: "groups"
          value: "my-group"
          roles:
            - access
    +       - azure-cli-access
    
  3. Apply your changes:

    tctl create -f saml.yaml
  4. Sign out of the Teleport cluster and sign in again to assume the new role.

  1. Retrieve your oidc configuration resource:

    tctl get oidc/myoidc --with-secrets > oidc.yaml

    Note that the --with-secrets flag adds the value of spec.signing_key_pair.private_key to the oidc.yaml file. Because this key contains a sensitive value, you should remove the oidc.yaml file immediately after updating the resource.

  2. Edit oidc.yaml, adding azure-cli-access to the claims_to_roles section.

    The claim you should map to this role depends on how you have designed your organization's role-based access controls (RBAC). However, the group must include your user account and should be the smallest group possible within your organization.

    Here is an example:

      claims_to_roles:
        - name: "groups"
          value: "my-group"
          roles:
            - access
    +       - azure-cli-access
    
  3. Apply your changes:

    tctl create -f oidc.yaml
  4. Sign out of the Teleport cluster and sign in again to assume the new role.

Step 5/6. Deploy the Teleport Application Service

In this step, you will launch the Teleport Application Service in your AKS cluster.

Get a join token

Establish trust between your Teleport cluster and your new Application Service instance by creating a join token:

tctl tokens add --type=app --ttl=1h --format=text
abcd123-insecure-do-not-use-this

Start the Teleport Application Service

Create a Helm values file called values.yaml, assigning token to the value of the join token you retrieved above, example.teleport.sh:443 to the host and port of your Teleport Proxy Service (e.g., teleport.example.com:443):

cat > azure_access_agent.values.yaml <<EOFauthToken: tokenproxyAddr: example.teleport.sh:443roles: appapps: - name: "azure-cli" cloud: "Azure" uri: "cloud://Azure"
serviceAccount: create: false name: teleport-azure-cli-aks-service-account
extraLabels: pod: azure.workload.identity/use: "true"EOF

Install the Helm chart for Teleport agent services, teleport-kube-agent:

helm -n teleport-ns install teleport-azure-access-agent \ teleport/teleport-kube-agent --values azure_access_agent.values.yaml

Make sure that the Teleport agent pod is running. You should see one teleport-azure-access-agent pod with a single ready container:

kubectl -n teleport-ns get pods
NAME READY STATUS RESTARTS AGEteleport-azure-access-agent-0 1/1 Running 0 99s

Step 6/6. Use Azure CLIs with Teleport

Now that you have authorized your Teleport user to assume the teleport-azure identity, you can use Teleport to authenticate to Azure's APIs and execute commands against it via the az CLI.

List your Azure CLI application

Verify that your Teleport user can see the azure-cli application you registered earlier:

tsh apps ls
Application Description Type Public Address Labels----------- ----------- ---- ------------------------------ -------------------azure-cli HTTP azure-cli.teleport.example.com teleport.dev/origin

Log in to use an Azure CLI

Log in to the application, specifying that you would like to assume the teleport-azure identity:

tsh apps login azure-cli --azure-identity teleport-azure

This command validates the value of the --azure-identity flag against the ones the user is authorized to assume. The value of the flag can either be the full URI of the identity (e.g., the URI you copied earlier in this guide) or the name of the identity, e.g., teleport-azure.

A user can omit the --azure-identity flag if they are only authorized to access a single Azure identity, but otherwise not including the --azure-identity flag will result in an error.

If the command succeeds, you will see information about the user's chosen Azure identity similar to the following:

[
  {
    "environmentName": "AzureCloud",
    "homeTenantId": "00000000-0000-0000-0000-000000000000",
    "id": "00000000-0000-0000-0000-000000000000",
    "isDefault": true,
    "managedByTenants": [],
    "name": "Microsoft Azure Sponsorship",
    "state": "Enabled",
    "tenantId": "00000000-0000-0000-0000-000000000000",
    "user": {
      "assignedIdentityInfo": "MSIResource-/subscriptions/0000000000000-0000-0000-000000000000/resourceGroups/my-resource-group/providers/Microsoft.ManagedIdentity/userAssignedIdentities/teleport-azure",
      "name": "userAssignedIdentity",
      "type": "servicePrincipal"
    }
  }
]

Logged into Azure app "azure-cli".
Your identity: /subscriptions/0000000000000-0000-0000-000000000000/resourceGroups/my-resource-group/providers/Microsoft.ManagedIdentity/userAssignedIdentities/teleport-azure
Example Azure CLI command: tsh az vm list

Execute Azure CLI commands

At this point, you can run az commands using the Teleport Application Service by prefixing them with tsh. To list VMs running in your Azure resource group, for example, run the following command:

tsh az vm list

If you're not seeing the expected VMs at this point, double-check that your Azure managed identity is assigned the "Reader" role at the scope of your resource group.

Use Azure CLI applications without tsh

In addition to running az commands via tsh, you can grant secure access to any CLI application that executes commands against Azure's APIs.

To do this, use tsh to start a local proxy that forwards traffic from your CLI application to the Teleport Application Service. The Application Service uses an Azure managed identity to fetch an authentication token from Azure, which your CLI application uses to authenticate requests to Azure's APIs.

To start the local proxy, run the following tsh command:

tsh proxy azure

The command tsh proxy az is an alias for tsh proxy azure.

The command will print the address of the local proxy server along with export commands for assigning environment variables. Azure CLI applications read these variables in order to request an authentication token for Azure's APIs:

Started Azure proxy on http://127.0.0.1:54321.
To avoid port randomization, you can choose the listening port using the --port flag.

Use the following credentials and HTTPS proxy setting to connect to the proxy:

  export AZURE_CONFIG_DIR=/Users/myuser/.tsh/azure/my.teleport.cluster/azure
  export HTTPS_PROXY=http://127.0.0.1:54321
  export HTTP_PROXY=http://127.0.0.1:54321
  export MSI_ENDPOINT=https://azure-msi.teleport.dev/123456789abcdef01234
  export REQUESTS_CA_BUNDLE=/Users/myuser/.tsh/keys/teleport.example.com/myuser-app/teleport.example.com/azure-cli-localca.pem

tsh proxy azure runs the local proxy in the foreground, so don't interrupt the process or exit the terminal where you ran the command until you're ready to close the local proxy.

Copy the export commands and paste them into a second terminal. In that terminal, you can now run your Azure CLI application of choice. For example, you can run the following command to list Azure VMs:

az vm list

Since the az CLI requests an authentication token using the teleport-azure identity you created earlier, and that identity is authorized to view resources in your resource group, the az vm list command will only list VMs in that resource group.

When you run an az command via tsh az, tsh starts the local proxy in the background and uses it to execute the command.

Next Steps