mirror of
https://github.com/gravitational/teleport
synced 2024-10-20 01:03:40 +00:00
Go API Docs improved descriptions/examples (#4704)
* Update go-client example readme and code. * Update docs for go-api. * Build up each section of the docs with more info examples.
This commit is contained in:
parent
85703618fe
commit
4abb0cb40e
|
@ -5,63 +5,805 @@ description: The detailed guide to Teleport API
|
||||||
|
|
||||||
# Teleport API Reference
|
# Teleport API Reference
|
||||||
|
|
||||||
Teleport is currently working on documenting our API.
|
In most cases, you can interact with Teleport using our CLI tools, [tsh](cli-docs.md#tsh) and [tctl](cli-docs.md#tctl). However, there are some scenarios where you may need to interact with Teleport programmatically. For this purpose, you can directly use the same API that `tctl` and `tsh` use.
|
||||||
|
|
||||||
!!! warning
|
!!! Note
|
||||||
|
We are currently working on improving our API Documentation. If you have an API suggestion, [please complete our survey](https://docs.google.com/forms/d/1HPQu5Asg3lR0cu5crnLDhlvovGpFVIIbDMRvqclPhQg/viewform).
|
||||||
|
|
||||||
We are currently working on this project. If you have an API suggestion, [please complete our survey](https://docs.google.com/forms/d/1HPQu5Asg3lR0cu5crnLDhlvovGpFVIIbDMRvqclPhQg/edit).
|
## Go Examples
|
||||||
|
|
||||||
## Authentication
|
!!! Note
|
||||||
In order to interact with the Access Request API, you will need to provision appropriate
|
The Go examples depend on some features and changes that won't be released until Teleport 5.0. Until then, it would be best to start experimenting with the API with the latest [master branch of Teleport](https://github.com/gravitational/teleport).
|
||||||
TLS certificates. In order to provision certificates, you will need to create a
|
|
||||||
user with appropriate permissions:
|
|
||||||
|
|
||||||
```bash
|
Below are some code examples that can be used with Teleport to perform a few key tasks.
|
||||||
$ cat > rscs.yaml <<EOF
|
|
||||||
kind: user
|
Before you begin:
|
||||||
metadata:
|
|
||||||
name: access-plugin
|
- Install [Go](https://golang.org/doc/install) 1.13+ and Setup Go Dev Environment
|
||||||
spec:
|
- Have access to a Teleport Auth server ([quickstart](quickstart.md))
|
||||||
roles: ['access-plugin']
|
|
||||||
version: v2
|
The easiest way to get started with the Teleport API is to clone the [Go Client Example](https://github.com/gravitational/teleport/tree/master/examples/go-client) in our github repo. Follow the README there to quickly authenticate the API with your Teleport Auth Server.
|
||||||
---
|
|
||||||
kind: role
|
Or if you prefer, follow the Authentication, Go Client, and Go Packages sections below to add the necessary files to a new directory called `/api-examples`. At the end, you should have this file structure:
|
||||||
metadata:
|
|
||||||
name: access-plugin
|
```
|
||||||
spec:
|
api-examples
|
||||||
allow:
|
+-- api-admin.yaml
|
||||||
rules:
|
+-- certs
|
||||||
- resources: ['access_request']
|
| +-- api-admin.cas
|
||||||
verbs: ['list','read','update']
|
| +-- api-admin.crt
|
||||||
# teleport currently refuses to issue certs for a user with 0 logins,
|
| +-- api-admin.key
|
||||||
# this restriction may be lifted in future versions.
|
+-- client.go
|
||||||
logins: ['access-plugin']
|
+-- go.mod
|
||||||
version: v3
|
+-- go.sum
|
||||||
EOF
|
+-- main.go
|
||||||
# ...
|
|
||||||
$ tctl create rscs.yaml
|
|
||||||
# ...
|
|
||||||
$ tctl auth sign --format=tls --user=access-plugin --out=auth
|
|
||||||
# ...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The above sequence should result in three PEM encoded files being generated:
|
## Authentication
|
||||||
`auth.crt`, `auth.key`, and `auth.cas` (certificate, private key, and CA certs respectively).
|
|
||||||
|
|
||||||
Note: by default, tctl auth sign produces certificates with a relatively short lifetime.
|
In order to interact with the API, you will need to provision appropriate TLS certificates. In order to provision certificates, you will need to create a user with appropriate permissions. You should only give the API user permissions for what it actually needs.
|
||||||
For production deployments, the --ttl flag can be used to ensure a more practical
|
|
||||||
certificate lifetime.
|
|
||||||
|
|
||||||
# gRPC APIs
|
To quickly get started with the API, you can use this api-admin user. However in production usage, make sure to have stringent permissions in place.
|
||||||
|
|
||||||
## Audit Events API
|
```bash
|
||||||
Coming Soon
|
# Copy and Paste the below and run on the Teleport Auth server.
|
||||||
|
$ cat > api-admin.yaml <<EOF
|
||||||
|
{!examples/go-client/api-admin.yaml!}
|
||||||
|
EOF
|
||||||
|
|
||||||
## Certificate Generation API
|
$ tctl create -f api-admin.yaml
|
||||||
Coming Soon
|
$ mkdir -p certs
|
||||||
|
$ tctl auth sign --format=tls --user=api-admin --out=certs/api-admin
|
||||||
|
```
|
||||||
|
|
||||||
## Tokens API
|
This should result in three PEM encoded files being generated in the `/certs` directory: `api-admin.crt`, `api-admin.key`, and `api-admin.cas` (certificate, private key, and CA certs respectively).
|
||||||
Coming Soon
|
|
||||||
|
|
||||||
## Workflow API
|
Move the `/certs` folder into your `/api-examples` folder.
|
||||||
Coming Soon
|
|
||||||
|
!!! Note
|
||||||
|
By default, `tctl auth sign` produces certificates with a relatively short lifetime. See our [Kubernetes Section](kubernetes-ssh.md#using-teleport-kubernetes-with-automation) for more information on automating the signing process for short lived certificates.
|
||||||
|
|
||||||
|
While we encourage you to use short lived certificates, we understand you may not have all the infrastructure to issues and obtain them at the onset. You can use the --ttl flag to extend the lifetime of a certificate in these cases but understand this reduces your security posture
|
||||||
|
|
||||||
|
## Go Client
|
||||||
|
|
||||||
|
The client below interfaces with the Teleport gRPC API, and relies on the `certs` generated above for TLS.
|
||||||
|
|
||||||
|
Add `client.go` into your `/api-examples` folder.
|
||||||
|
|
||||||
|
**client.go**
|
||||||
|
|
||||||
|
```go
|
||||||
|
{!examples/go-client/client.go!}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Go Packages
|
||||||
|
|
||||||
|
Copy the Teleport module's go.mod below into `/api-examples` and then run `go mod tidy` to slim it down to only what's needed for these api examples.
|
||||||
|
|
||||||
|
```
|
||||||
|
{!go.mod!}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Main file
|
||||||
|
|
||||||
|
Add this main file to your `/api-examples` folder. Now you can simply plug in the examples below and then run `go run .` to see them in action.
|
||||||
|
|
||||||
|
**main.go**
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.Printf("Starting Teleport client...")
|
||||||
|
client, err := connectClient()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to create client: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Object Resource
|
||||||
|
|
||||||
|
All Teleport resources, such as `Roles` and `Tokens`, share several fields for database management. To keep the documentation below clear, we will refer to these fields as `Resource Fields`.
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Resource is a group of fields that all Teleport resources have
|
||||||
|
type Resource struct {
|
||||||
|
// Kind is a resource kind
|
||||||
|
Kind string
|
||||||
|
// SubKind is an optional resource sub kind, used in some resources
|
||||||
|
SubKind string
|
||||||
|
// Version is version
|
||||||
|
Version string
|
||||||
|
// Metadata is User metadata
|
||||||
|
Metadata struct {
|
||||||
|
// Name is an object name
|
||||||
|
Name string
|
||||||
|
// Namespace is object namespace.
|
||||||
|
Namespace string
|
||||||
|
// Description is object description
|
||||||
|
Description string
|
||||||
|
// Labels is a set of labels
|
||||||
|
Labels map[string]string
|
||||||
|
// Expires is a global expiry time header can be set on any resource in the system.
|
||||||
|
Expires *time.Time
|
||||||
|
// ID is a record ID
|
||||||
|
ID int64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Roles
|
||||||
|
|
||||||
|
Every user in Teleport is assigned a set of [roles](enterprise/ssh-rbac.md#roles). A user's roles defines what actions or resources the user is allowed or denied to access. We offer a wide array of permissions, allowing you to safely and precisely give developers access to the resources they need.
|
||||||
|
|
||||||
|
Some of the permissions a role could define include:
|
||||||
|
|
||||||
|
- Which SSH nodes a user can or cannot access.
|
||||||
|
- Ability to replay recorded sessions.
|
||||||
|
- Ability to update cluster configuration.
|
||||||
|
- Which UNIX logins a user is allowed to use when logging into servers.
|
||||||
|
|
||||||
|
!!! Note
|
||||||
|
The open source edition of Teleport automatically assigns every user to the built-in `admin` role, but Teleport Enterprise allows administrators to define their own roles with far greater control over the user permissions.
|
||||||
|
|
||||||
|
You can manage roles with the Teleport CLI tool [tctl](cli-docs.md#tctl), or programmatically with the RPC calls documented below.
|
||||||
|
|
||||||
|
You may want to use the API to manage roles if:
|
||||||
|
|
||||||
|
- You want to write a program that can always ensure certain roles exist on your system and do not want to orchestrate `tctl` to do this.
|
||||||
|
- You want to dynamically create short lived roles.
|
||||||
|
- You want to dynamically create roles with fields filled that Teleport currently does not support.
|
||||||
|
|
||||||
|
### The Role Object
|
||||||
|
|
||||||
|
A Teleport `role` is defined by its `Allow` rules, `Deny` rules, and OpenSSH `Options`. We'll break these down piece by piece below.
|
||||||
|
|
||||||
|
To see a role example in `yaml` form, look at the the admin role in the [RBAC documentation](enterprise/ssh-rbac.md). You'll notice that the role object below has the same exact nested structure as its `yaml` counterpart.
|
||||||
|
|
||||||
|
```go
|
||||||
|
// RoleV3 represents role resource specification
|
||||||
|
type RoleV3 struct {
|
||||||
|
// Resource Fields are fields that all Teleport resources have - see above
|
||||||
|
Resource Fields
|
||||||
|
// Spec is the role specification
|
||||||
|
Spec RoleSpecV3 struct {
|
||||||
|
// Options is for OpenSSH options like agent forwarding.
|
||||||
|
Options RoleOptions
|
||||||
|
// Allow is the set of conditions evaluated to grant access.
|
||||||
|
Allow RoleConditions
|
||||||
|
// Deny is the set of conditions evaluated to deny access. Deny takes priority over allow.
|
||||||
|
Deny RoleConditions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Role Options**
|
||||||
|
|
||||||
|
The `RoleOptions` struct defines what OpenSSH actions a user is allowed to use.
|
||||||
|
|
||||||
|
```go
|
||||||
|
// RoleOptions is a set of role options
|
||||||
|
type RoleOptions struct {
|
||||||
|
// ForwardAgent is SSH agent forwarding. default true.
|
||||||
|
ForwardAgent Bool
|
||||||
|
// MaxSessionTTL defines how long a SSH session can last for.
|
||||||
|
MaxSessionTTL Duration
|
||||||
|
// PortForwarding defines if the certificate will have "permit-port-forwarding"
|
||||||
|
// in the certificate. PortForwarding is true if not set.
|
||||||
|
PortForwarding *BoolOption // struct { Value bool } - Nullable boolean
|
||||||
|
// CertificateFormat defines the format of the user certificate to allow
|
||||||
|
// compatibility with older versions of OpenSSH.
|
||||||
|
CertificateFormat string
|
||||||
|
// ClientIdleTimeout sets disconnect clients on idle timeout behavior,
|
||||||
|
// if set to 0 means do not disconnect, otherwise is set to the idle duration.
|
||||||
|
ClientIdleTimeout Duration
|
||||||
|
// DisconnectExpiredCert sets disconnect clients on expired certificates.
|
||||||
|
DisconnectExpiredCert Bool
|
||||||
|
// BPF defines what events to record for the BPF-based session recorder.
|
||||||
|
BPF []string
|
||||||
|
// PermitX11Forwarding authorizes use of X11 forwarding.
|
||||||
|
PermitX11Forwarding Bool
|
||||||
|
// MaxConnections defines the maximum number of
|
||||||
|
// concurrent connections a user may hold.
|
||||||
|
MaxConnections int64
|
||||||
|
// MaxSessions defines the maximum number of
|
||||||
|
// concurrent sessions per connection.
|
||||||
|
MaxSessions int64
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Role Conditions**
|
||||||
|
|
||||||
|
The `RoleConditions` struct allows for precise permission combinations between a role's `Allow` and `Deny` fields.
|
||||||
|
|
||||||
|
`Deny` conditions are evaluated first and logically OR'ed. That means that when a user attempts an action, if any of their roles has a deny section matching that action, the user will be denied access. If the user is not denied access, then the `Allow` conditions are evaluated and logically AND'ed. So if a user has any roles with an allow section matching the action, the action will be permitted.
|
||||||
|
|
||||||
|
However, this also allows you to take a modular approach to defining roles. Splitting roles up into logical parts will allow you to manage roles for many developers more effectively. It may also be effective to keep your deny and allow conditions in separate roles so that conflicting roles are obvious.
|
||||||
|
|
||||||
|
```go
|
||||||
|
// RoleConditions is a set of conditions that must all match to be allowed or denied access.
|
||||||
|
type RoleConditions struct {
|
||||||
|
// Logins is a list of *nix system logins, e.g. "root".
|
||||||
|
Logins []string
|
||||||
|
// Namespaces is a list of namespaces (used to partition a cluster).
|
||||||
|
Namespaces []string
|
||||||
|
// NodeLabels is a map of node labels (used to dynamically grant access to nodes).
|
||||||
|
NodeLabels Labels
|
||||||
|
// Rules is a list of rules and their access levels. Rules represents allow or deny rule
|
||||||
|
// that is executed to check if user or service have access to resource
|
||||||
|
Rules []Rule
|
||||||
|
// KubeGroups is a list of kubernetes groups that Teleport users with this role will be
|
||||||
|
KubeGroups []string
|
||||||
|
// A list of roles that this role can request access to
|
||||||
|
Request *AccessRequestConditions // type AccessRequestConditions struct { Roles []string }
|
||||||
|
// KubeUsers is an optional list of kubernetes users that Teleport users with this role will be
|
||||||
|
KubeUsers []string
|
||||||
|
// AppLabels is a map of labels used as part of the RBAC system.
|
||||||
|
AppLabels Labels
|
||||||
|
// ClusterLabels is a map of node labels (used to dynamically grant access to clusters).
|
||||||
|
ClusterLabels Labels
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Labels**
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Label map[string]utils.Strings
|
||||||
|
```
|
||||||
|
Labels are arbitrary key-value pairs that can be used to differentiate nodes, apps, or leaf clusters by key attributes. For example, `NodeLabels` might have the key `environment`, with its value set to `development`, `staging`, or `production`, according to the node's location.
|
||||||
|
|
||||||
|
```go
|
||||||
|
services.Labels{"environment": utils.Strings{"development", "staging"}}
|
||||||
|
```
|
||||||
|
|
||||||
|
Depending on which field you put these labels in, you can allow/deny access to any nodes, apps, or leaf clusters with the given labels. These labels can be very useful in systems where you need to carefully manage access across many clusters, e.g. if you are managing clusters for several outside groups.
|
||||||
|
|
||||||
|
**Rules**
|
||||||
|
|
||||||
|
The primary building blocks of a rule are its resources and verbs. The optional `Where` and `Actions` fields can be used for more advanced rules.
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Rule struct {
|
||||||
|
// Resources is a list of resources
|
||||||
|
Resources []string
|
||||||
|
// Verbs is a list of verbs
|
||||||
|
Verbs []string
|
||||||
|
// Where specifies optional advanced matcher
|
||||||
|
Where string
|
||||||
|
// Actions specifies optional actions taken when this rule matches
|
||||||
|
Actions []string
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Here's an example of a rule describing `read only` verbs applied to the SSH `session` resource. Depending on if it's under `Allow` or `Deny`, it means "allow/deny users of this role the ability to read or list active SSH sessions".
|
||||||
|
|
||||||
|
```go
|
||||||
|
services.NewRule(
|
||||||
|
services.KindSession,
|
||||||
|
services.RO(), // helper function to get 'read only' verbs ("list" and "read")
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Resources**
|
||||||
|
|
||||||
|
Resources include values like the ones below, and much more. The rest of the Teleport resources can be found in the `services` package.
|
||||||
|
|
||||||
|
```go
|
||||||
|
KindRole = "role"
|
||||||
|
KindAccessRequest = "access_request"
|
||||||
|
KindToken = "token"
|
||||||
|
KindCertAuthority = "cert_authority"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Verbs**
|
||||||
|
|
||||||
|
These are all of the possible resource values, which can be found in the `services` package.
|
||||||
|
|
||||||
|
```go
|
||||||
|
VerbList = "list"
|
||||||
|
VerbCreate = "create"
|
||||||
|
VerbRead = "read"
|
||||||
|
VerbReadNoSecrets = "readnosecrets"
|
||||||
|
VerbUpdate = "update"
|
||||||
|
VerbDelete = "delete"
|
||||||
|
VerbRotate = "rotate"
|
||||||
|
```
|
||||||
|
|
||||||
|
There are also helper functions `RW()`, `RO()`, and `ReadNoSecrets()` in the `services` package to quickly get read/write verbs, read only verbs, and read only verbs with `readnosecrets` respectively.
|
||||||
|
|
||||||
|
### Retrieve Role
|
||||||
|
|
||||||
|
This is the equivalent of `tctl get role/admin`.
|
||||||
|
|
||||||
|
```go
|
||||||
|
role, err := client.GetRole("admin")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create Role
|
||||||
|
|
||||||
|
You can use the `UpsertRole` RPC to programmatically create a new role. This is the equivalent of `tctl create -f auditor-role.yaml`, where the `-f` flag signals to overwrite the auditor role if it exists already.
|
||||||
|
|
||||||
|
Suppose you wanted to create a role for an auditor that could view all sessions, but could not access any servers. Using `tctl`, you would first create a role that allows reading and listing of the session resource like below. In addition, the user is explicitly denied access to all nodes in the deny block.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cat << EOF > /tmp/auditor-role.yaml
|
||||||
|
kind: role
|
||||||
|
version: v3
|
||||||
|
metadata:
|
||||||
|
name: auditor
|
||||||
|
spec:
|
||||||
|
options:
|
||||||
|
max_session_ttl: 8h
|
||||||
|
allow:
|
||||||
|
rules:
|
||||||
|
- resources: [session]
|
||||||
|
verbs: [list, read]
|
||||||
|
deny: {}
|
||||||
|
node_labels: '*': '*'
|
||||||
|
EOF
|
||||||
|
$ tctl create -f /tmp/auditor-role.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
To do something similar with the API:
|
||||||
|
|
||||||
|
```go
|
||||||
|
role, err := services.NewRole("auditor", services.RoleSpecV3{
|
||||||
|
Options: services.RoleOptions{
|
||||||
|
MaxSessionTTL: services.Duration(time.Hour),
|
||||||
|
},
|
||||||
|
Allow: services.RoleConditions{
|
||||||
|
Logins: []string{"auditor"},
|
||||||
|
Rules: []services.Rule{
|
||||||
|
services.NewRule(services.KindSession, services.RO()),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Deny: services.RoleConditions{
|
||||||
|
NodeLabels: services.Labels{"*": []string{"*"}},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = client.UpsertRole(ctx, role); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Update Role
|
||||||
|
|
||||||
|
The `UpsertRole` RPC can also be used to update an existing role. You can change a role's field with the setter functions available, or directly.
|
||||||
|
|
||||||
|
```go
|
||||||
|
// retrieve role
|
||||||
|
role, err := client.GetRole("auditor")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the auditor role to be expired
|
||||||
|
role.SetExpiry(time.Now())
|
||||||
|
if err := client.UpsertRole(ctx, role); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Delete Role
|
||||||
|
|
||||||
|
This is the equivalent of `tctl rm auditor-role.yaml`.
|
||||||
|
|
||||||
|
```go
|
||||||
|
if err := client.DeleteRole(ctx, "auditor"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tokens
|
||||||
|
|
||||||
|
Teleport is a "clustered" system, meaning it only allows access to hosts that had been previously granted cluster membership. To achieve this, a cluster has "join tokens" which can be shared to extend trust.
|
||||||
|
|
||||||
|
A remote host can exchange one of these tokens with the cluster's auth server to receive signed certificates and become a trusted Teleport host (auth, node, proxy, app, or kubernetes server). Likewise, a remote Teleport cluster can exchange a token to become a leaf cluster in a [trusted cluster](admin-guide.md#trusted-clusters).
|
||||||
|
|
||||||
|
These tokens can be predefined static tokens, or dynamic tokens with a short life time. The latter can be generated by `tctl` or this API, and is more secure.
|
||||||
|
|
||||||
|
You may want to use this API to manage tokens if:
|
||||||
|
|
||||||
|
- You have a program that dynamically adds new hosts to clusters
|
||||||
|
- You want to programmatically add leaf clusters to a trusted cluster
|
||||||
|
|
||||||
|
### The Token Object
|
||||||
|
|
||||||
|
The Token has a Roles field, which defines what roles this token provides in the root cluster.
|
||||||
|
|
||||||
|
```go
|
||||||
|
type ProvisionTokenV2 struct {
|
||||||
|
// Resource Fields are fields that all Teleport resources have - see above
|
||||||
|
Resource Fields
|
||||||
|
// Spec is the token specification
|
||||||
|
Spec ProvisionTokenSpecV2 struct {
|
||||||
|
// Roles is a list of roles associated with the token
|
||||||
|
Roles []teleport.Role // teleport.Role is a custom string type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Roles**
|
||||||
|
|
||||||
|
Not to be confused with [RBAC Roles](api-reference.md#roles), the `Roles` field on a Token determines what server roles a new host can take on in a cluster.
|
||||||
|
|
||||||
|
These are all of the possible role values, which can be found in the `teleport` package.
|
||||||
|
|
||||||
|
```go
|
||||||
|
RoleAuth Role = "Auth"
|
||||||
|
RoleWeb Role = "Web"
|
||||||
|
RoleNode Role = "Node"
|
||||||
|
RoleProxy Role = "Proxy"
|
||||||
|
RoleAdmin Role = "Admin"
|
||||||
|
RoleProvisionToken Role = "ProvisionToken"
|
||||||
|
RoleTrustedCluster Role = "Trusted_cluster"
|
||||||
|
RoleSignup Role = "Signup"
|
||||||
|
RoleNop Role = "Nop"
|
||||||
|
RoleRemoteProxy Role = "RemoteProxy"
|
||||||
|
RoleKube Role = "Kube"
|
||||||
|
RoleApp Role = "App"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Retrieve Token
|
||||||
|
|
||||||
|
The closest equivalent to this is `tctl tokens ls`.
|
||||||
|
|
||||||
|
```go
|
||||||
|
token, err := client.GetToken(tokenString)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create Token
|
||||||
|
|
||||||
|
You can use the `GenerateToken` RPC to programmatically create a new token. This is the equivalent of `tctl tokens add --type=[Roles] --value=[Token] --ttl=[TTL]`.
|
||||||
|
|
||||||
|
By default, Teleport will create a random 16 byte string using the `CryptoRandomHex` function in our `utils` package. If you want to customize this yourself, simply provide the `Token` field, though we strongly recommend utilizing best security practices.
|
||||||
|
|
||||||
|
You can also set `TTL` to a maximum of 48 hours. The shorter the lifetime, the more secure your cluster will be.
|
||||||
|
|
||||||
|
```go
|
||||||
|
// generate a token for adding a new proxy host to a cluster
|
||||||
|
tokenString, err := client.GenerateToken(ctx, auth.GenerateTokenRequest{
|
||||||
|
Roles: teleport.Roles{teleport.RoleProxy},
|
||||||
|
// Token will be a randomly generated 16 byte hex string
|
||||||
|
// TTL will default to 30 minutes
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate a token for adding a remote cluster to a trusted cluster
|
||||||
|
tokenString, err := client.GenerateToken(ctx, auth.GenerateTokenRequest{
|
||||||
|
Token: "this-is-a-secure-token-string",
|
||||||
|
Roles: teleport.Roles{teleport.RoleTrustedCluster},
|
||||||
|
TTL: time.Minute,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Update Token
|
||||||
|
|
||||||
|
`UpsertToken` is essentially the same as `GenerateToken` but without default values, so it is best used only for updating existing tokens.
|
||||||
|
|
||||||
|
```go
|
||||||
|
token, err := client.GetToken(tokenString)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the token to be expired
|
||||||
|
token.SetExpiry(time.Now())
|
||||||
|
if err := client.UpsertToken(token); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Delete Token
|
||||||
|
|
||||||
|
This is equivalent to `tctl tokens rm [tokenString]`.
|
||||||
|
|
||||||
|
```go
|
||||||
|
if err := client.DeleteToken(tokenString); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cluster Labels
|
||||||
|
|
||||||
|
!!! Note
|
||||||
|
This feature will be released with Teleport 5.0.
|
||||||
|
|
||||||
|
Cluster Labels can be used to differentiate between leaf clusters in a [Trusted Cluster](trustedclusters.md). These can be useful when defining [roles](enterprise/ssh-rbac.md) within a trusted cluster, where each cluster has its own requirements for access.
|
||||||
|
|
||||||
|
For example, if each cluster corresponds to a different product that should only be accessed by its product team, you can give each cluster a label like `product: A`. Then create a role for each product which allows access to clusters with its respective product label.
|
||||||
|
|
||||||
|
You may want to use the following RPCs to manage your cluster labels if:
|
||||||
|
|
||||||
|
- You want to programmatically manage cluster labels from your root cluster
|
||||||
|
- You have a complex cluster labeling system that would benefit from automation with the API
|
||||||
|
- You have a large distributed trusted cluster where explicit cluster based access control is crucial
|
||||||
|
|
||||||
|
### Create a Leaf Cluster Join Token with Labels
|
||||||
|
|
||||||
|
To create a leaf cluster with cluster labels, you can create a token with the desired labels, and use that token to add the leaf cluster. Check the [Tokens](api-reference.md#tokens) section of this page for more information on tokens.
|
||||||
|
|
||||||
|
```go
|
||||||
|
tokenString, err := client.GenerateToken(ctx, auth.GenerateTokenRequest{
|
||||||
|
Roles: teleport.Roles{teleport.RoleTrustedCluster},
|
||||||
|
// Leaf clusters added with this token will inherit these labels
|
||||||
|
Labels: map[string]string{
|
||||||
|
"env": "staging",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
This is the equivalent of `tctl tokens add --type=trusted_cluster --labels=env=staging`.
|
||||||
|
|
||||||
|
!!! Note
|
||||||
|
Currently, it is not straightforward to add new leaf clusters with the API, but it is possible with the `RegisterUsingToken` RPC. Until we properly document this, follow the trusted cluster [join token docs](trustedclusters.md#join-tokens) to create a leaf cluster with this token using `tctl`.
|
||||||
|
|
||||||
|
### Update a Leaf Cluster's labels
|
||||||
|
|
||||||
|
You can also update an existing leaf cluster's labels from the root cluster using the `UpdateRemoteCluster` RPC.
|
||||||
|
|
||||||
|
This is the equivalent of `tctl update rc/[leafClusterName] --set-labels=env=prod`.
|
||||||
|
|
||||||
|
```go
|
||||||
|
rc, err := client.GetRemoteCluster("leafClusterName")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
md := rc.GetMetadata()
|
||||||
|
md.Labels = map[string]string{"env": "prod"}
|
||||||
|
rc.SetMetadata(md)
|
||||||
|
|
||||||
|
if err = client.UpdateRemoteCluster(ctx, rc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Access Workflows
|
||||||
|
|
||||||
|
[Access Workflows](enterprise/workflow/index.md) can be used by Teleport users to request one or more additional roles on the fly. These requests can be partially or fully approved or denied by a Teleport Administrator.
|
||||||
|
|
||||||
|
You may want to use manage Access Workflows using the API if:
|
||||||
|
|
||||||
|
- You want to automatically administer the scaling up and down of permissions for developers depending on their task
|
||||||
|
- You want to utilize our supported [external tools](enterprise/workflow/index.md#integrating-with-an-external-tool) or other third party tools to control the flow of access
|
||||||
|
|
||||||
|
For example, you could have a team of contractors which need database access for some tasks, but should not have it permanently. To do this, you can give them the `contractor` role below, which allows them to request the `dba` role.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
kind: role
|
||||||
|
metadata:
|
||||||
|
name: contractor
|
||||||
|
spec:
|
||||||
|
options:
|
||||||
|
# ...
|
||||||
|
allow:
|
||||||
|
request:
|
||||||
|
roles: ['dba']
|
||||||
|
# ...
|
||||||
|
deny:
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
kind: role
|
||||||
|
metadata:
|
||||||
|
name: dba
|
||||||
|
spec:
|
||||||
|
options:
|
||||||
|
# ...
|
||||||
|
# Only allows the contractor to use this role for 1 hour from time of request.
|
||||||
|
max_session_ttl: 1h
|
||||||
|
allow:
|
||||||
|
# ...
|
||||||
|
deny:
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Now if a contractor has a task requiring `dba` access, they can request `dba` access. To approve the request, you need an administrator with read and write permissions to access requests.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
kind: role
|
||||||
|
metadata:
|
||||||
|
name: request-admin
|
||||||
|
spec:
|
||||||
|
options:
|
||||||
|
# ...
|
||||||
|
allow:
|
||||||
|
rules:
|
||||||
|
- resources: [access_request]
|
||||||
|
verbs: [list, read, update, delete]
|
||||||
|
deny:
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
A `request-admin` can list all current requests, resolve them, and delete them. Notice that `request-admin` might not be a great job to handle manually.
|
||||||
|
|
||||||
|
With the API, you can automatically manage the requesting and or resolution of requests in order to streamline this process. Better yet, this opens up the ability to leverage external identity providers by attaching relevant information as `Annotations` to requests, such as `ticket_id` or `task_id`. You can also use our custom [integrations with external tools](enterprise/workflow/index.md#integrating-with-an-external-tool), such as Slack, to manage requests according to your custom configuration.
|
||||||
|
|
||||||
|
### The Access Request Object
|
||||||
|
|
||||||
|
An `AccessRequest` is made by a `User` for a set of `Roles`. Once its `State` is resolved to "approved", this user has access to the permissions in those roles until the `Expiry` time, which can be set upon resolution.
|
||||||
|
|
||||||
|
There are also optional `Reasons` and `Annotations` which can be used for audit logs, to integrate external identity information into requests, and other custom usages you may have.
|
||||||
|
|
||||||
|
```go
|
||||||
|
// AccessRequest represents an access request resource specification
|
||||||
|
type AccessRequestV3 struct {
|
||||||
|
// Resource Fields are fields that all Teleport resources have - see above
|
||||||
|
Resource Fields
|
||||||
|
// Spec is an AccessRequest specification
|
||||||
|
Spec AccessRequestSpecV3 struct {
|
||||||
|
// User is the name of the user to whom the roles will be applied.
|
||||||
|
User string
|
||||||
|
// Roles is a list of the roles being requested.
|
||||||
|
Roles []string
|
||||||
|
// State is the current state of this access request. Possible values are pending, approved, and denied.
|
||||||
|
State RequestState
|
||||||
|
// Created encodes the time at which the request was registered with the auth server.
|
||||||
|
Created time.Time
|
||||||
|
// Expires constrains the maximum lifetime of any login session for which this request is active.
|
||||||
|
Expires time.Time
|
||||||
|
// RequestReason is an optional message explaining the reason for the request.
|
||||||
|
RequestReason string
|
||||||
|
// ResolveReason is an optional message explaining the reason for the resolution
|
||||||
|
// of the request (approval, denial, etc...).
|
||||||
|
ResolveReason string
|
||||||
|
// ResolveAnnotations is a set of arbitrary values received from plugins or other resolving parties during approval/denial. Importantly, these annotations are included in the access_request.update event, allowing plugins to propagate arbitrary structured data to the audit log.
|
||||||
|
ResolveAnnotations wrappers.Traits
|
||||||
|
// SystemAnnotations is a set of programmatically generated annotations attached to pending access requests by teleport. These annotations serve as a mechanism for administrators to pass extra information to plugins when they process pending access requests.
|
||||||
|
SystemAnnotations wrappers.Traits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**State**
|
||||||
|
|
||||||
|
These are the RequestState constants, which can be found in the services package.
|
||||||
|
|
||||||
|
```go
|
||||||
|
// NONE variant exists to allow RequestState to be explicitly omitted
|
||||||
|
// in certain circumstances (e.g. in an AccessRequestFilter).
|
||||||
|
RequestState_NONE RequestState = 0
|
||||||
|
// PENDING variant is the default for newly created requests.
|
||||||
|
RequestState_PENDING RequestState = 1
|
||||||
|
// APPROVED variant indicates that a request has been accepted by
|
||||||
|
// an administrating party.
|
||||||
|
RequestState_APPROVED RequestState = 2
|
||||||
|
// DENIED variant indicates that a request has been rejected by
|
||||||
|
// an administrating party.
|
||||||
|
RequestState_DENIED RequestState = 3
|
||||||
|
```
|
||||||
|
|
||||||
|
### Retrieve Access Requests
|
||||||
|
|
||||||
|
The closest equivalent to this is `tctl request ls`, which does not have the filter functionality.
|
||||||
|
|
||||||
|
```go
|
||||||
|
// retrieve all pending access requests
|
||||||
|
filter := services.AccessRequestFilter{State: services.RequestState_PENDING}
|
||||||
|
ars, err := client.GetAccessRequests(ctx, filter)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**AccessRequestFilter**
|
||||||
|
|
||||||
|
The `AccessRequestFilter` struct allows you to filter by `ID`, `User`, and `State`.
|
||||||
|
|
||||||
|
```go
|
||||||
|
type AccessRequestFilter struct {
|
||||||
|
// ID specifies a request ID if set.
|
||||||
|
ID string
|
||||||
|
// User specifies a username if set.
|
||||||
|
User string
|
||||||
|
// RequestState filters for requests in a specific state.
|
||||||
|
State RequestState
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create Access Request
|
||||||
|
|
||||||
|
This is equivalent to `tctl request create contractor --roles=dba --reason="I need more power"`.
|
||||||
|
|
||||||
|
However with the RPC below, you can also set other useful fields. For example, `SystemAnnotations` can be used to store relevant information for external tools, such as a ticket id from a ticket management system.
|
||||||
|
|
||||||
|
```go
|
||||||
|
// create a new access request for contractor to temporarily use the dba role in the cluster
|
||||||
|
ar, err := services.NewAccessRequest("contractor", "dba")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// use AccessRequest setters to set optional fields
|
||||||
|
accessReq.SetRequestReason("I need more power.")
|
||||||
|
accessReq.SetAccessExpiry(time.Now().Add(time.Hour))
|
||||||
|
accessReq.SetSystemAnnotations(map[string][]string{
|
||||||
|
"ticket": []string{"137"},
|
||||||
|
})
|
||||||
|
|
||||||
|
if err = client.CreateAccessRequest(ctx, accessReq); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Approve Access Request
|
||||||
|
|
||||||
|
This is equivalent to `tctl request approve [accessReqID] --roles=dba1 --reason="dba2 is not for you"`.
|
||||||
|
|
||||||
|
You can approve a subset of the roles in the request with the `Roles` field.
|
||||||
|
|
||||||
|
```go
|
||||||
|
aruApprove := services.AccessRequestUpdate{
|
||||||
|
RequestID: accessReqID,
|
||||||
|
State: services.RequestState_APPROVED,
|
||||||
|
Reason: "dba2 is not for you",
|
||||||
|
Roles: []string{"dba1"},
|
||||||
|
}
|
||||||
|
if err := client.SetAccessRequestState(ctx, aruApprove); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Deny Access Request
|
||||||
|
|
||||||
|
This is equivalent to `tctl request deny [accessReqID] --reason="Not today"`.
|
||||||
|
|
||||||
|
```go
|
||||||
|
aruDeny := services.AccessRequestUpdate{
|
||||||
|
RequestID: accessReqID,
|
||||||
|
State: services.RequestState_DENIED,
|
||||||
|
Reason: "Not today",
|
||||||
|
}
|
||||||
|
if err := client.SetAccessRequestState(ctx, aruDeny); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Delete Access Request
|
||||||
|
|
||||||
|
This is equivalent to `tctl request rm [accessReqID]`.
|
||||||
|
|
||||||
|
```go
|
||||||
|
if err := client.DeleteAccessRequest(ctx, accessReqID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Certificate Authority
|
||||||
|
|
||||||
|
It might be useful to retrieve your [Certificate Authority](architecture/authentication.md#ssh-certificates) through the API if it is rotating frequently.
|
||||||
|
|
||||||
|
```go
|
||||||
|
ca, err := client.GetCertAuthority(services.CertAuthID{
|
||||||
|
DomainName: clusterName,
|
||||||
|
Type: services.HostCA,
|
||||||
|
}, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
1
examples/go-client/.gitignore
vendored
Normal file
1
examples/go-client/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
certs
|
|
@ -3,11 +3,13 @@
|
||||||
### Introduction
|
### Introduction
|
||||||
|
|
||||||
Teleport Auth Server has an API which hasn't been officially documented (yet).
|
Teleport Auth Server has an API which hasn't been officially documented (yet).
|
||||||
Both `tctl` and `tsh` use the Auth API to:
|
Both `tctl` and `tsh` use the Auth API to perform various actions
|
||||||
|
|
||||||
* Request certificates (`tsh login` or `tctl auth sign`)
|
This program demonstrates how to...
|
||||||
* Add nodes and users (`tctl users` and `tctl nodes`)
|
|
||||||
* Manipulate cluster state (`tctl` resources)
|
1. Authenticate against the Auth API using certificates.
|
||||||
|
2. Make API calls to issue CRUD requests for cluster join tokens, roles, and labels.
|
||||||
|
3. Receive, allow, and deny access requests.
|
||||||
|
|
||||||
### API Authentication
|
### API Authentication
|
||||||
|
|
||||||
|
@ -18,30 +20,26 @@ Auth API clients must perform two-way authentication using x509 certificates:
|
||||||
2. They must offer their x509 certificate, which has been previously issued
|
2. They must offer their x509 certificate, which has been previously issued
|
||||||
by the auth sever.
|
by the auth sever.
|
||||||
|
|
||||||
### Demo
|
Start up a teleport auth server and then run the following commands to create an `api-user` with a signed certificate. Make sure to run this from the `go-client` directory for proper output.
|
||||||
|
|
||||||
This little program demonstrates how to:
|
|
||||||
|
|
||||||
1. Authenticate against the Auth API using two certificates.
|
|
||||||
2. Makes an API call to issue a server join token, i.e. an equivalent
|
|
||||||
of `tctl node add`
|
|
||||||
|
|
||||||
Before running it, you have to use `tctl` to issue an API certificate,
|
|
||||||
i.e. on the auth server:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ tctl auth export --type=tls > /var/lib/teleport/ca.cert
|
|
||||||
```
|
|
||||||
|
|
||||||
This should work as long as you execute it on the same auth server:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ go get github.com/gravitational/teleport/lib/auth
|
$ tctl create -f api-user-role.yaml
|
||||||
$ go run main.go
|
$ mkdir -p certs
|
||||||
|
$ tctl auth sign --format=tls --ttl=87600h --user=api-user --out=certs/api-user
|
||||||
|
```
|
||||||
|
|
||||||
|
### Demo
|
||||||
|
|
||||||
|
With the user and certificate created, you can run the go example.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ go run .
|
||||||
```
|
```
|
||||||
|
|
||||||
### TODO
|
### TODO
|
||||||
|
|
||||||
This Auth server API allows clients to "jump" to API endpoints of all trusted
|
Turn this example into a seperate repository (or git/go submodule if that has the same effect), so that users can quickly clone it and run it without worrying about dependencies.
|
||||||
clusters connected to it. We need to add a snippet how to enumerate trusted
|
|
||||||
clusters and connect to their API endpoints later.
|
Replace Upsert with Create/Update once these are implemented. (update docs as well)
|
||||||
|
|
||||||
|
We need to add a snippet on how to enumerate trusted clusters and connect to their API endpoints later.
|
||||||
|
|
84
examples/go-client/access_workflow.go
Normal file
84
examples/go-client/access_workflow.go
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gravitational/teleport/lib/auth"
|
||||||
|
"github.com/gravitational/teleport/lib/services"
|
||||||
|
)
|
||||||
|
|
||||||
|
// accessWorkflow performs the necessary access management functions as an example
|
||||||
|
func accessWorkflow(ctx context.Context, client *auth.Client) {
|
||||||
|
// create access request for api-admin to temporarily use the admin role in the cluster
|
||||||
|
accessReq, err := services.NewAccessRequest("api-admin", "admin")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to make new access request: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// use AccessRequest setters to set optional fields
|
||||||
|
accessReq.SetAccessExpiry(time.Now().Add(time.Hour))
|
||||||
|
accessReq.SetRequestReason("I need more power.")
|
||||||
|
accessReq.SetSystemAnnotations(map[string][]string{
|
||||||
|
"ticket": []string{"137"},
|
||||||
|
})
|
||||||
|
|
||||||
|
if err = client.CreateAccessRequest(ctx, accessReq); err != nil {
|
||||||
|
log.Printf("Failed to create access request: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Created Access Request: %v", accessReq)
|
||||||
|
|
||||||
|
// defer deletion in case of an error below
|
||||||
|
defer func() {
|
||||||
|
// delete access request
|
||||||
|
if err = client.DeleteAccessRequest(ctx, accessReq.GetName()); err != nil {
|
||||||
|
log.Printf("Failed to delete access request: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Deleted Access Request")
|
||||||
|
}()
|
||||||
|
|
||||||
|
// retrieve all pending access requests
|
||||||
|
filter := services.AccessRequestFilter{State: services.RequestState_PENDING}
|
||||||
|
accessReqs, err := client.GetAccessRequests(ctx, filter)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to retrieve access requests: %v", accessReqs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Retrieved access requests:")
|
||||||
|
for _, a := range accessReqs {
|
||||||
|
log.Printf(" %v", a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// approve access request
|
||||||
|
if err = client.SetAccessRequestState(ctx, services.AccessRequestUpdate{
|
||||||
|
RequestID: accessReq.GetName(),
|
||||||
|
State: services.RequestState_APPROVED,
|
||||||
|
Reason: "seems legit",
|
||||||
|
// Roles: If you don't want to grant all the roles requested,
|
||||||
|
// you can provide a subset of role with the Roles field.
|
||||||
|
}); err != nil {
|
||||||
|
log.Printf("Failed to accept request: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Approved Access Request")
|
||||||
|
|
||||||
|
// deny access request
|
||||||
|
if err = client.SetAccessRequestState(ctx, services.AccessRequestUpdate{
|
||||||
|
RequestID: accessReq.GetName(),
|
||||||
|
State: services.RequestState_DENIED,
|
||||||
|
Reason: "not today",
|
||||||
|
}); err != nil {
|
||||||
|
log.Printf("Failed to deny request: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Denied Access Request")
|
||||||
|
}
|
34
examples/go-client/api-admin.yaml
Normal file
34
examples/go-client/api-admin.yaml
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
kind: user
|
||||||
|
metadata:
|
||||||
|
name: api-admin
|
||||||
|
spec:
|
||||||
|
roles: ['api-admin']
|
||||||
|
version: v2
|
||||||
|
---
|
||||||
|
kind: role
|
||||||
|
metadata:
|
||||||
|
name: api-admin
|
||||||
|
spec:
|
||||||
|
# allow section declares a list of resource/verb combinations that are
|
||||||
|
# allowed for the users of this role. By default nothing is allowed.
|
||||||
|
allow:
|
||||||
|
rules:
|
||||||
|
- resources: ['role']
|
||||||
|
verbs: ['create', 'list', 'read', 'update', 'delete']
|
||||||
|
- resources: ['token']
|
||||||
|
verbs: ['create', 'list', 'read', 'update', 'delete']
|
||||||
|
- resources: ['access_request']
|
||||||
|
verbs: ['create', 'list', 'read', 'update', 'delete']
|
||||||
|
- resources: ['remote_cluster']
|
||||||
|
verbs: ['create', 'list', 'read', 'update', 'delete']
|
||||||
|
# teleport currently refuses to issue certs for a user with 0 logins,
|
||||||
|
# this restriction may be lifted in future versions.
|
||||||
|
logins: ['api-admin']
|
||||||
|
request:
|
||||||
|
roles: ['*']
|
||||||
|
# the deny section uses the identical format as the 'allow' section.
|
||||||
|
# the deny rules always override allow rules.
|
||||||
|
deny:
|
||||||
|
node_labels:
|
||||||
|
'*': '*'
|
||||||
|
version: v3
|
60
examples/go-client/client.go
Normal file
60
examples/go-client/client.go
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/gravitational/teleport/lib/auth"
|
||||||
|
"github.com/gravitational/teleport/lib/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// connectClient establishes a gRPC connection to an auth server.
|
||||||
|
func connectClient() (*auth.Client, error) {
|
||||||
|
tlsConfig, err := LoadTLSConfig("certs/api-admin.crt", "certs/api-admin.key", "certs/api-admin.cas")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to setup TLS config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace 127.0.0.1:3025 (default) with your auth server address
|
||||||
|
authServerAddr := utils.MustParseAddrList("127.0.0.1:3025")
|
||||||
|
clientConfig := auth.ClientConfig{Addrs: authServerAddr, TLS: tlsConfig}
|
||||||
|
|
||||||
|
return auth.NewTLSClient(clientConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadTLSConfig loads and sets up client TLS config for authentication
|
||||||
|
func LoadTLSConfig(certPath, keyPath, rootCAsPath string) (*tls.Config, error) {
|
||||||
|
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
caPool, err := LoadTLSCertPool(rootCAsPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
conf := &tls.Config{
|
||||||
|
Certificates: []tls.Certificate{cert},
|
||||||
|
RootCAs: caPool,
|
||||||
|
}
|
||||||
|
return conf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadTLSCertPool is used to load root CA certs from file path.
|
||||||
|
func LoadTLSCertPool(path string) (*x509.CertPool, error) {
|
||||||
|
caFile, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
caCerts, err := ioutil.ReadAll(caFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pool := x509.NewCertPool()
|
||||||
|
if ok := pool.AppendCertsFromPEM(caCerts); !ok {
|
||||||
|
return nil, fmt.Errorf("invalid CA cert PEM")
|
||||||
|
}
|
||||||
|
return pool, nil
|
||||||
|
}
|
|
@ -18,63 +18,25 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"path/filepath"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gravitational/teleport"
|
|
||||||
"github.com/gravitational/teleport/lib/auth"
|
|
||||||
"github.com/gravitational/teleport/lib/utils"
|
|
||||||
"github.com/gravitational/trace"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log.Printf("Starting teleport client...")
|
log.Printf("Starting Teleport client...")
|
||||||
|
client, err := connectClient()
|
||||||
// Teleport HTTPS client uses TLS client authentication
|
|
||||||
// so we have to set up certificates there
|
|
||||||
tlsConfig, err := setupClientTLS(context.Background())
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to parse TLS config: %v", err)
|
|
||||||
}
|
|
||||||
authServerAddr := []utils.NetAddr{*utils.MustParseAddr("127.0.0.1:3025")}
|
|
||||||
clientConfig := auth.ClientConfig{Addrs: authServerAddr, TLS: tlsConfig}
|
|
||||||
|
|
||||||
client, err := auth.NewTLSClient(clientConfig)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to create client: %v", err)
|
log.Fatalf("Failed to create client: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
// make an API call to generate a cluster join token for
|
|
||||||
// adding another proxy to a cluster.
|
fmt.Println("")
|
||||||
token, err := client.GenerateToken(ctx, auth.GenerateTokenRequest{
|
roleCRUD(ctx, client)
|
||||||
Token: "mytoken-proxy",
|
|
||||||
Roles: teleport.Roles{teleport.RoleProxy},
|
fmt.Println("")
|
||||||
TTL: time.Hour,
|
tokenCRUD(ctx, client)
|
||||||
})
|
|
||||||
if err != nil {
|
fmt.Println("")
|
||||||
log.Fatalf("Failed to generate token: %v", err)
|
accessWorkflow(ctx, client)
|
||||||
}
|
|
||||||
log.Printf("Generated token: %v\n", token)
|
|
||||||
}
|
|
||||||
|
|
||||||
// setupClientTLS sets up client TLS authentiction between TLS client
|
|
||||||
// and Teleport Auth server. This function uses hardcoded certificate paths,
|
|
||||||
// assuming program runs alongside auth server, but it can be ran
|
|
||||||
// on a remote location, assuming client has all the client certificates.
|
|
||||||
func setupClientTLS(ctx context.Context) (*tls.Config, error) {
|
|
||||||
storage, err := auth.NewProcessStorage(ctx, filepath.Join("/var/lib/teleport", teleport.ComponentProcess))
|
|
||||||
if err != nil {
|
|
||||||
return nil, trace.Wrap(err)
|
|
||||||
}
|
|
||||||
defer storage.Close()
|
|
||||||
|
|
||||||
identity, err := storage.ReadIdentity(auth.IdentityCurrent, teleport.RoleAdmin)
|
|
||||||
if err != nil {
|
|
||||||
return nil, trace.Wrap(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return identity.TLSConfig(nil)
|
|
||||||
}
|
}
|
||||||
|
|
70
examples/go-client/role.go
Normal file
70
examples/go-client/role.go
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gravitational/teleport/lib/auth"
|
||||||
|
"github.com/gravitational/teleport/lib/services"
|
||||||
|
)
|
||||||
|
|
||||||
|
// rolesCRUD performs each roles crud function as an example
|
||||||
|
func roleCRUD(ctx context.Context, client *auth.Client) {
|
||||||
|
// create a new auditor role which has very limited permissions
|
||||||
|
role, err := services.NewRole("auditor", services.RoleSpecV3{
|
||||||
|
Options: services.RoleOptions{
|
||||||
|
MaxSessionTTL: services.Duration(time.Hour),
|
||||||
|
},
|
||||||
|
Allow: services.RoleConditions{
|
||||||
|
Logins: []string{"auditor"},
|
||||||
|
Rules: []services.Rule{
|
||||||
|
services.NewRule(services.KindSession, services.RO()),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Deny: services.RoleConditions{
|
||||||
|
NodeLabels: services.Labels{"*": []string{"*"}},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to make new role %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = client.UpsertRole(ctx, role); err != nil {
|
||||||
|
log.Printf("Failed to create role: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Created Role: %v", role.GetName())
|
||||||
|
|
||||||
|
// defer deletion in case of an error below
|
||||||
|
defer func() {
|
||||||
|
// delete the auditor role we just created
|
||||||
|
if err = client.DeleteRole(ctx, "auditor"); err != nil {
|
||||||
|
log.Printf("Failed to delete role: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Deleted role")
|
||||||
|
}()
|
||||||
|
|
||||||
|
// retrieve auditor role
|
||||||
|
role, err = client.GetRole("auditor")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to retrieve role for updating: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Retrieved Role: %v", role.GetName())
|
||||||
|
|
||||||
|
// update the auditor role's ttl to one day
|
||||||
|
role.SetOptions(services.RoleOptions{
|
||||||
|
MaxSessionTTL: services.Duration(time.Hour * 24),
|
||||||
|
})
|
||||||
|
if err = client.UpsertRole(ctx, role); err != nil {
|
||||||
|
log.Printf("Failed to update role: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Updated role")
|
||||||
|
}
|
53
examples/go-client/token.go
Normal file
53
examples/go-client/token.go
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gravitational/teleport"
|
||||||
|
"github.com/gravitational/teleport/lib/auth"
|
||||||
|
)
|
||||||
|
|
||||||
|
// tokenCRUD performs each token crud function as an example.
|
||||||
|
func tokenCRUD(ctx context.Context, client *auth.Client) {
|
||||||
|
// create a randomly generated token for proxy servers to join the cluster with
|
||||||
|
tokenString, err := client.GenerateToken(ctx, auth.GenerateTokenRequest{
|
||||||
|
Roles: teleport.Roles{teleport.RoleProxy},
|
||||||
|
TTL: time.Hour,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to generate token: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Generated token: %v", tokenString)
|
||||||
|
|
||||||
|
// defer deletion in case of an error below
|
||||||
|
defer func() {
|
||||||
|
// delete token
|
||||||
|
if err = client.DeleteToken(tokenString); err != nil {
|
||||||
|
log.Printf("Failed to delete token: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Deleted token")
|
||||||
|
}()
|
||||||
|
|
||||||
|
// retrieve token
|
||||||
|
token, err := client.GetToken(tokenString)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to retrieve token for update: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Retrieved token: %v", token.GetName())
|
||||||
|
|
||||||
|
// update the token to be expired
|
||||||
|
token.SetExpiry(time.Now())
|
||||||
|
if err = client.UpsertToken(token); err != nil {
|
||||||
|
log.Printf("Failed to update token: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Updated token")
|
||||||
|
}
|
Loading…
Reference in a new issue