Fork me on GitHub

Teleport

Using Machine ID With GitHub Actions and Kubernetes

Improve

Machine ID for GitHub Actions is available starting from Teleport v11.0.

GitHub Actions is a popular CI/CD platform that works as a part of the larger GitHub ecosystem.

In this guide, you will use Teleport Machine ID and Kubernetes Access to allow a GitHub Actions workflow to securely connect to a Kubernetes cluster without the need for long-lived secrets.

Teleport supports secure joining on both GitHub-hosted and self-hosted GitHub Actions runners as well as GitHub Enterprise Server.

Prerequisites

  • A running Teleport cluster. For details on how to set this up, see one of our Getting Started guides.

  • The tctl admin tool and tsh client tool version >= 11.3.1.

    tctl version

    Teleport v11.3.1 go1.19

    tsh version

    Teleport v11.3.1 go1.19

    See Installation for details.

  • A running Teleport cluster. For details on how to set this up, see our Enterprise Getting Started guide.

  • The tctl admin tool and tsh client tool version >= 11.3.1, which you can download by visiting the customer portal.

    tctl version

    Teleport v11.3.1 go1.19

    tsh version

    Teleport v11.3.1 go1.19

  • A Teleport Cloud account. If you do not have one, visit the sign up page to begin your free trial.

  • The tctl admin tool and tsh client tool version >= 11.2.1. To download these tools, visit the Downloads page.

    tctl version

    Teleport v11.2.1 go1.19

    tsh version

    Teleport v11.2.1 go1.19

To connect to Teleport, log in to your cluster using tsh, then use tctl remotely:

tsh login --proxy=teleport.example.com [email protected]
tctl status

Cluster teleport.example.com

Version 11.3.1

CA pin sha256:abdc1245efgh5678abdc1245efgh5678abdc1245efgh5678abdc1245efgh5678

You can run subsequent tctl commands in this guide on your local machine.

For full privileges, you can also run tctl commands on your Auth Service host.

To connect to Teleport, log in to your cluster using tsh, then use tctl remotely:

tsh login --proxy=myinstance.teleport.sh [email protected]
tctl status

Cluster myinstance.teleport.sh

Version 11.2.1

CA pin sha256:sha-hash-here

You must run subsequent tctl commands in this guide on your local machine.

  • A Kubernetes cluster connected to your Teleport cluster. If you do not already have one configured, try our Kubernetes Access getting started guide. In our examples, this Kubernetes cluster will be named my-kubernetes-cluster.
  • Your Teleport user should have the privileges to create token resources.
  • A GitHub repository with GitHub Actions enabled. This guide uses the example gravitational/example, and this value should be replaced with your own unique repository.

1/4. Create a join token for GitHub Actions

In order to allow your GitHub Actions workflow to authenticate with your Teleport cluster, you'll first need to create a join token. These tokens set out criteria by which the Auth Server decides whether or not to allow a bot or node to join.

To create a token, we can write the resource's YAML to a file on disk, and then use tctl to apply it.

Create a file named tokenconfig.yaml and insert the following contents:

kind: token
version: v2
metadata:
  name: github-token
  expires: "2100-01-01T00:00:00Z"
spec:
  roles: [Bot]
  join_method: github
  bot_name: github-demo
  github:
    allow:
      - repository: gravitational/example

From Teleport 11.1.4, users with Teleport Enterprise are able to permit workflows within GitHub Enterprise Server instances to authenticate using the GitHub join method.

This is configured by an additional enterprise_server_host field in the Token resource. This should be set to the host of your GHES instance.

kind: token
version: v2
metadata:
  name: github-token
  expires: "2100-01-01T00:00:00Z"
spec:
  roles: [Bot]
  join_method: github
  bot_name: github-demo
  github:
    enterprise_server_host: your.ghes.instance.example.com
    allow:
      - repository: gravitational/example

Let's go over the token resource YAML's fields in more detail:

  • metadata.name defines the name of the token. Note that this value will need to be used in other parts of the configuration later.
  • metadata.expires defines the date that the join token will expire. This example is set to the year 2100.
  • spec.bot_name is the name of the Machine ID bot that this token will grant access to. Note that this value will need to be used in other parts of the configuration later.
  • spec.roles defines which roles that this token will grant access to. The value of [Bot] states that this token grants access to a Machine ID bot.
  • spec.join_method defines the join method the token is applicable for. Since this guide only focuses on GitHub Actions, we will set this to to github.
  • spec.github.allow is used to set rules for what GitHub Actions will be able to authenticate by using the token. In this example, the gravitational/example repository is used, and this example repository should be replaced with your own repo.

You can find a full list of the token configuration options for GitHub Actions joining on the GitHub Actions reference page.

Once the resource file has been written, create the token with tctl:

tctl create -f tokenconfig.yaml

Check that token github-token has been created with the following command:

tctl tokens ls

Token Type Labels Expiry Time (UTC)

----------- ---- ------ ----------------------------------------------

github-token Bot 01 Jan 00 00:00 UTC (2562047h47m16.854775807s)

Step 2/4. Create a Machine ID bot

With the join token for the GitHub Actions workflow created, you now need to create a Machine ID bot that the token will grant access to. A Machine ID bot is a special type of Teleport user designed for access by machines, and can authenticate using a join token rather than forms of authentication more suitable to users (e.g. SSO.)

Before creating the bot, first you must create a role in Teleport which grants the bot access to your Kubernetes clusters and defines the Kubernetes RBAC groups that will be added to your bot's requests to the Kubernetes cluster.

Create role.yaml:

kind: role
metadata:
  name: github-demo-kube-access
version: v5
spec:
  allow:
    kubernetes_labels:
      # This grants access to any Kubernetes cluster attached to your Teleport
      # cluster.
      '*': '*'
    kubernetes_groups:
      # This group will be attached to requests made by users with this role
      # to the Kubernetes API. Configure a cluster role binding that binds this 
      # group to a Kubernetes cluster role to grant privileges to this Teleport 
      # role in the Kubernetes cluster.
      - github-demo
  deny: {}

Note the value provided in kubernetes_groups. In a later step, you will bind this group to a Kubernetes role with a cluster role binding.

Apply this to your Teleport cluster:

tctl create -f role.yaml

Now create the Machine ID bot, assigning it the role you created above:

tctl bots add github-demo --roles=github-demo-kube-access --token=github-token

Step 3/4. Configure a Kubernetes cluster role binding

With the bot configured and a Teleport role that will pass a group to Kubernetes with the bot's requests, you now need a role binding to associate a Kubernetes role to that group.

This is the stage where you determine what level of access the bot will have when accessing your cluster. In our example, we are applying the view cluster role which will allow our bot to read, but not write, all resources in the Kubernetes cluster.

In your case you may want a custom Kubernetes role that lists the exact permissions your bot needs. You can also use a role binding rather than a cluster role binding to limit the bot's access to an individual Kubernetes namespace.

Create a file named clusterolebinding.yaml:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: github-demo-group
subjects:
  - kind: Group
    # Name field should match the group configured in the 
    # `allow.kubernetes_groups` of a role belonging to the user you wish to
    # grant access to.
    name: github-demo
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  # "view" is a default ClusterRole that grants read-only access to resources
  # See: https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles
  name: view
  apiGroup: rbac.authorization.k8s.io

Create this cluster role binding in your Kubernetes cluster using kubectl:

$ kubectl apply -f ./clusterolebinding.yaml

Step 4/4. Create the GitHub Actions workflow

With all the configuration in Teleport and Kubernetes complete, you can now create a GitHub Actions workflow that can connect to your Kubernetes cluster.

Our example workflow will list all of the pods contained within the cluster, but this could just as easily be modified to deploy to a Kubernetes cluster with kubectl or helm.

In the GitHub workflows directory of your repository (.github/workflows/) create a new workflow YAML file actionstest.yml.

Insert the following, replacing my-kubernetes-cluster with the name of your Kubernetes cluster and example.domain:443 with the address of your Teleport Proxy or cloud tenant (e.g. example.teleport.sh).

# This is a basic workflow to help you get started, modify it for your needs.
on:
  push:
    branches:
      - main
jobs:
  demo:
    permissions:
      # The "id-token: write" permission is required or Machine ID will not be 
      # able to authenticate with the cluster.
      id-token: write
      contents: read
    name: guide-demo
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/[email protected]
      - name: Fetch kubectl
        uses: azure/[email protected]
      - name: Fetch Teleport binaries
        uses: teleport-actions/[email protected]
        with:
          version: 11.3.1
      - name: Fetch credentials using Machine ID
        uses: teleport-actions/[email protected]
        with:
          # Use the address of the auth/proxy server for your own cluster.
          proxy: example.domain:443
          # Use the name of the join token resource you created in step 1.
          token: github-token
          # Use the name of your Kubernetes cluster
          kubernetes-cluster: my-kubernetes-cluster
      - name: List pods
        run: kubectl get pods -A

The auth-k8s action sets the KUBECONFIG for future steps to the credentials it has fetched from Teleport. This means that most existing tooling for Kubernetes (e.g kubectl and helm) can use your cluster with no additional configuration.

Add, commit, and push this new workflow file to the default branch of your repository.

Navigate to the Actions tab of your GitHub repository in your web browser. Select the Workflow that has now been created and triggered by the change, and select the guide-demo job.

Expand the List pods step of the action, where you can then confirm that the output shows a list of all the pods within your Kubernetes cluster.

A note on security implications and risk

Once teleport-actions/auth-k8s has been used in a workflow job, all successive steps in that job will have access to your Kubernetes cluster as your bot. Where possible, run as few steps as necessary after this action has been used. It may be a good idea to break your workflow up into multiple jobs in order to segregate these credentials from other code running in your CI/CD pipeline.

Most importantly, ensure that the role assigned to your GitHub Actions bot has access to only the resources in your Teleport cluster that your CI/CD needs to interact with.

Next steps

You can find out more about the teleport-action/setup and teleport-actions/auth-k8s actions on their GitHub repositories:

For more information about GitHub Actions itself, read their documentation.