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:
- Create an Azure managed identity for the Application Service and set it as the default Workload ID for your Kubernetes service account.
- Create an Azure managed identity for user access and attach it to the same Kubernetes service account.
- Deploy a Teleport Application Service with an Azure app in your Teleport cluster.
- Assume the managed identity and run
az
commands viatsh
.
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 andtsh
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 downloadingtctl
andtsh
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'stsh
client also uses theaz
binary to execute commands. See the Azure documentation for how to install theaz
CLI on your operating system. kubectl
andhelm
for AKS deployments.- To check that you can connect to your Teleport cluster, sign in with
tsh login
, then verify that you can runtctl
commands using your current credentials.tctl
is supported on macOS and Linux machines. For example:If you can connect to the cluster and run the$ tsh login --proxy=teleport.example.com --user=email@example.com
$ tctl status
# Cluster teleport.example.com
# Version 15.4.22
# CA pin sha256:abdc1245efgh5678abdc1245efgh5678abdc1245efgh5678abdc1245efgh5678tctl status
command, you can use your current credentials to run subsequenttctl
commands from your workstation. If you host your own Teleport cluster, you can also runtctl
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 <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
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.
Approach | Description | Supported User Types |
---|---|---|
Dynamic | A Teleport role includes a template variable that grants a user access to all Azure identities assigned directly to them. | Local users, OIDC, SAML |
Static | A 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.
- Local Users
- SAML/OIDC Connectors
- All Authentication Methods
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
Denying access to Azure identities
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:
- Local User
- GitHub
- SAML
- OIDC
-
Retrieve your local user's roles as a comma-separated list:
$ ROLES=$(tsh status -f json | jq -r '.active.roles | join(",")')
-
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" -
Sign out of the Teleport cluster and sign in again to assume the new role.
-
Retrieve your
github
authentication connector:$ tctl get github/github --with-secrets > github.yaml
Note that the
--with-secrets
flag adds the value ofspec.signing_key_pair.private_key
to thegithub.yaml
file. Because this key contains a sensitive value, you should remove the github.yaml file immediately after updating the resource. -
Edit
github.yaml
, addingazure-cli-access
to theteams_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 -
Apply your changes:
$ tctl create -f github.yaml
-
Sign out of the Teleport cluster and sign in again to assume the new role.
-
Retrieve your
saml
configuration resource:$ tctl get --with-secrets saml/mysaml > saml.yaml
Note that the
--with-secrets
flag adds the value ofspec.signing_key_pair.private_key
to thesaml.yaml
file. Because this key contains a sensitive value, you should remove the saml.yaml file immediately after updating the resource. -
Edit
saml.yaml
, addingazure-cli-access
to theattributes_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 -
Apply your changes:
$ tctl create -f saml.yaml
-
Sign out of the Teleport cluster and sign in again to assume the new role.
-
Retrieve your
oidc
configuration resource:$ tctl get oidc/myoidc --with-secrets > oidc.yaml
Note that the
--with-secrets
flag adds the value ofspec.signing_key_pair.private_key
to theoidc.yaml
file. Because this key contains a sensitive value, you should remove the oidc.yaml file immediately after updating the resource. -
Edit
oidc.yaml
, addingazure-cli-access
to theclaims_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 -
Apply your changes:
$ tctl create -f oidc.yaml
-
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 <<EOF
authToken: token
proxyAddr: example.teleport.sh:443
roles: app
apps:
- 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 AGE
teleport-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
- See Microsoft's guide on Configure Workload ID on AKS.
- Now that you know how to protect Azure CLI access using Teleport, ensure that your Teleport users can only manage Azure resources temporarily, with no longstanding admin roles for attackers to hijack. View our documentation on Role Access Requests and Access Request plugins.
- Consult the Azure documentation for information about Azure managed identities and how to manage user-assigned managed identities.
- See the Azure
documentation
for the full list of
az
CLI commands. - For full details on how Teleport populates the
internal
andexternal
traits we illustrated in the Teleport roles within this guide, see the Teleport Access Controls Reference.