[Teleport Simple ABAC. Infrastructure Access Managed With GitHub Teams in the Open-Source Version of Teleport]
Preliminary requirements:
- Deployed Teleport cluster
- Experience with GitHub SSO settings
- Experience connecting Teleport agents
- A basic understanding of RBAC in Teleport
Problem
Faced with the task of ensuring easy and safe access to a varied infrastructure (several VPS in different clouds, various services on geographically distributed servers, etc.), we began using Teleport since it fit our needs. However, once we began deploying a Teleport cluster and connected the first dozen services, we encountered growing challenges with managing access for different people and services.
As infrastructure grows (new offices, projects, etc.), so do the number of roles that need to be assigned to the necessary employees. This adds routine work and many logical connections that complicate the understanding of these roles and their management.
This problem is often connected with RBAC (role-based access control), and Teleport's documentation examines it in detail:
Limited scalability: As the number of roles and users in an organization grows, the number of role-permission assignments can grow exponentially, making management and maintenance of the access control system difficult.
Another approach to access management, ABAC (attribute-based access control), can help resolve this issue and is available in the free version of Teleport using GitHub single sign on (SSO).
Some theory
It's important to know why we need to do this.
If you understand ABAC, then you can skip to the more interesting parts.
Imagine we’re a small company that makes and sells stuffed bears, and we have the following roles:
Accountant | finances |
Warehouse clerk | inventory |
Manager | orders |
Soon, we'll be launching the production of dolls and toy cars. We trust one accountant to manage the finances of both factories, and the managers are given access to inventory to streamline order processing.
Now, our roles look like this:
Accountant 1 | Finances 1, Finances 2 |
Accountant 2 | Finances 3 |
Warehouse clerk 1 | Inventory 1 |
Warehouse clerk 2 | Inventory 2 |
Warehouse clerk 3 | Inventory 3 |
Manager 1 | Orders 1, Inventory 1 |
Manager 2 | Orders 2, Inventory 2 |
Manager 3 | Orders 3, Inventory 3 |
Using GitHub teams In Teleport, we can easily create this new entity, known in RBAC systems as a "group." It helps with access management.
As we scale production with three more factories for other toys, we hire high-level managers who need access to all the systems. Then, we hire an analyst who needs access to our BI instruments and establish compact corporate portals accessible to all employees within each production line. This creates a new level of complexity for our permissions.
ABAC emerges as a solution to this issue by using flexible roles grounded in attributes. Instead of creating a new "Finance 3" role, we assign all accountants the "finances" role and the "toy type" attribute that will determine their final access permissions.
Concept and planning
Let's return to the world of IT and access management.
Technically speaking, Teleport doesn't offer a "clean" ABAC implementation https://goteleport.com/learn/what-is-abac/
Using SSO and the Teleport predicate language, though, we can imitate this approach and achieve similar results.
ABAC uses service labels and traits as attributes added to users when connecting with SSO.
The primary difficulty is properly designing an access matrix that considers all the requirements. We can add to the matrix when personnel changes occur, or new services are added, but the better the initial design, the fewer changes will need to be made to roles. This will simplify management compared to RBAC.
To do this, clearly define the rules, for example:
- Developers have access to the databases in the projects where they're working.
- QA engineers have access to the BI instruments in the projects where they're working.
- DevOps engineers have access to all the infrastructure components and databases in the projects where they're working.
- Interns don't have access to production regardless of the project.
- All technical specialists have access to monitoring instruments in projects.
- Everyone has access to general resources in the projects where they're working.
Since Teleport doesn't offer full ABAC implementation, I suggest following this process for defining access rules. In short:
- Who – access/no access – to what – conditions (arguments)
Reverse rules are also possible, but these instructions show the step-by-step implementation using this pattern.
The most important entities that we'll operate are GitHub teams and Teleport services labels
Implementation and configuration
1. After planning the role matrix, create new teams in GitHub teams in the organization through which we will connect Teleport to our cluster using SSO.
To implement, we'll need to divide our teams into:
- functional (add the prefix t- for clarity)
t-developers | t-devops | t-qa-engineers | t-interns | t-common-access |
- project (add the prefix p- for clarity)
p-project-1 | p-project-2 | p-laboratory | p-office-1 | p-office-2 |
The functional teams will be used as "groups" that will be assigned different roles (or several different roles).
The project teams will be used as an attribute for assigning access.
In this case, access will only be granted if a user is part of at least two teams (function + project), and GitHub allows for easily managing them.
An organization's participants may be in several GitHub teams, and each team can be associated with several roles.
All the roles of a user in Teleport will be combined into one, with deny rules always maintaining priority.
Teleport requires one configuration file to work, which is located here: /etc/teleport.yaml
.
For ease of management as an element configuration code, it's also possible to record them in any file, for example:
/etc/teleport/github-connector.yaml
/etc/teleport/abac.yaml
To create resources from the console, use the tctl create [
Connector configuration
Save here: /etc/teleport/github-connector.yaml
And create it with the tctl create /etc/teleport/github-connector.yam
command
kind: github
metadata:
name: github-connector
spec:
api_endpoint_url: ""
client_id: {client_id}
client_secret: "{client_secret}"
display: GitHub
endpoint_url: ""
redirect_url: https://{teleport_url}/v1/webapi/github/callback
teams_to_logins: null
Teams_to_roles:
- organization: {organization}
team: t-developers
roles:
- database_access
- common_access
- monitoring_access
- organization: {organization}
team: t-qa-engineers
roles:
- bi_access
- common_access
- monitoring_access
- organization: {organization}
team: t-common-access
roles:
- common_access
- organization: {organization}
team: t-devops
roles:
- database_access
- common_access
- office_network_access
- monitoring_access
- servers_temp_user_admin
- organization: {organization}
team: t-interns
roles:
- deny_producion
version: v3
Be aware that roles are associated only with functional GitHub teams (with the "t-" prefix)
2. Roles settings:
Save it in the file /etc/teleport/abac.yaml
And create it with the tctl create /etc/teleport/abac.yaml
command
kind: role
metadata:
name: common_access
spec:
allow:
app_labels_expression: labels["type"] == "common" && contains_any(regexp.replace(user.spec.traits["github_teams"],
"^p-(.*)$", "$1"), labels["project"])
deny: {}
options:
max_session_ttl: 8h0m0s
version: v5
Here
labels["type"] == "common"
Verifies the existence of app_label type = common
contains_any(regexp.replace(user.spec.traits["github_teams"], "^p-(.*)$", "$1"), labels["project"])
Verifies the existence of element user.spec.traits["github_teams"]
list (without the prefix p-
) correspond to the value app_label project
The list is created from the github_teams
of the user (get this by launching tctl get user on the Teleport master server)
If both conditions are fulfilled, access is granted.
Other roles:
kind: role
metadata:
name: database_access
spec:
allow:
app_labels_expression: labels["type"] == "database" && contains_any(regexp.replace(user.spec.traits["github_teams"],
"^p-(.*)$", "$1"), labels["project"])
deny: {}
options:
max_session_ttl: 8h0m0s
version: v5
---
kind: role
metadata:
name: monitoring_access
spec:
allow:
app_labels_expression: labels["type"] == "monitoring" && contains_any(regexp.replace(user.spec.traits["github_teams"],
"^p-(.*)$", "$1"), labels["project"])
deny: {}
options:
max_session_ttl: 8h0m0s
version: v5
---
kind: role
metadata:
name: bi_access
spec:
allow:
app_labels_expression: labels["type"] == "bi-analyst" && contains_any(regexp.replace(user.spec.traits["github_teams"],
"^p-(.*)$", "$1"), labels["project"])
deny: {}
options:
max_session_ttl: 8h0m0s
version: v5
---
kind: role
metadata:
name: office_network_access
spec:
allow:
app_labels_expression: labels["type"] == "office-network" && contains_any(regexp.replace(user.spec.traits["github_teams"],
"^p-(.*)$", "$1"), labels["project"])
deny: {}
options:
max_session_ttl: 4h0m0s
version: v5
---
kind: role
metadata:
name: servers_temp_user_admin
spec:
allow:
host_groups:
- sudo
logins:
- '{{internal.logins}}'
node_labels_expression: contains_any(regexp.replace(user.spec.traits["github_teams"],
"^p-(.*)$", "$1"), labels["project"])
deny: {}
options:
create_host_user_mode: drop
max_session_ttl: 4h0m0s
ssh_file_copy: true
version: v5
---
kind: role
metadata:
name: deny_producion
spec:
allow: {}
deny:
app_labels:
'environment': 'production'
node_labels:
'environment': 'production'
options:
max_session_ttl: 4h0m0s
version: v5
3. Example of Teleport agent configuration:
ssh_service:
enabled: "yes"
labels:
project: "project-1"
commands:
- name: hostname
command: [hostname]
period: 1h0m0s
app_service:
enabled: "yes"
apps:
- name: project-1-grafana
uri: http://localhost:3000
public_addr: "project-1-grafana.{teleport_url}"
rewrite:
headers:
- "Host: project-1-grafana.{teleport_url}"
- "Origin: https://project-1-grafana.{teleport_url}"
labels:
project: "project-1"
type: "monitoring"
- name: project-1-metabase
uri: http://localhost:3010
public_addr: ""
labels:
project: "project-1"
type: "bi-analyst"
- name: project-1-postgres
uri: tcp://localhost:5432
labels:
project: "project-1"
environment: "production"
type: "database"
To update the configuration from yaml files, use the -f(--force) flag.
-f, --force
– Overwrite the resource if already exists
For example: tctl create -f /etc/teleport/github-connector.yaml
The current configuration allows access to project-1-grafana for anyone belonging to the GitHub team "p-project-1" and to either "t-developers", "t-qa-engineers", or "t-devops." Access to project-1-postgres is granted to all developers on the project ("p-project-1" and either "t-developers" or "t-devops"). Access to the production database can be restricted by adding a person to the "t-interns" group.
This configuration is not universal, and the access matrix must be planned in each specific case. However, the concept itself, when properly configured, makes it possible to almost never modify the roles again by managing employee access solely through GitHub teams, and even adding new projects by setting up new GitHub teams and Teleport labels for services.