Skip to content

Commit

Permalink
Merge pull request #415 from gravitational/ev/multi-role
Browse files Browse the repository at this point in the history
Multi-role and multi-use provisioning tokens
  • Loading branch information
kontsevoy committed May 17, 2016
2 parents 76cc2b4 + 49256d1 commit 33efb9c
Show file tree
Hide file tree
Showing 51 changed files with 989 additions and 178 deletions.
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ assets:
docs:
$(MAKE) -C build.assets docs

#
# Runs the documentation site inside a container on localhost with live updates
# Convenient for editing documentation.
#
.PHONY:run-docs
run-docs:
$(MAKE) -C build.assets run-docs

#
# tests everything: called by Jenkins
#
Expand Down
1 change: 1 addition & 0 deletions build.assets/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,4 @@ ENV LANGUAGE="en_US.UTF-8" \
GO15VENDOREXPERIMENT="1"

VOLUME ["/gopath/src/github.com/gravitational/teleport"]
EXPOSE 6600
8 changes: 7 additions & 1 deletion build.assets/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ test: integration

.PHONY:integration
integration: bbox
echo "Disabled for now"
docker run $(DOCKERFLAGS) $(NOROOT) -i $(BBOX) \
/bin/bash -c "$(MAKE) -C $(SRCDIR) FLAGS='-cover' integration"

Expand All @@ -46,6 +45,13 @@ docs: bbox
docker run $(DOCKERFLAGS) -ti $(NOROOT) -e HOME=$(SRCDIR)/build.assets $(BBOX) mkdocs build
@echo "\nSUCCESS: Teleport docs ----> build/docs\n"

#
# Runs docs website on localhost
#
.PHONY:run-docs
run-docs: bbox
docker run $(DOCKERFLAGS) -ti $(NOROOT) -e HOME=$(SRCDIR)/build.assets -p 6600:6600 -w $(SRCDIR) $(BBOX) mkdocs serve -a 0.0.0.0:6600

#
# Starts shell inside the build container
#
Expand Down
117 changes: 98 additions & 19 deletions docs/admin-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ below. By default it is stored in `/etc/teleport.yaml`
When editing YAML configuration, please pay attention to how your editor
handles white space. YAML requires consistent handling of tab characters.

```yaml
```bash
# By default, this file should be stored in /etc/teleport.yaml

# This section of the configuration file applies to all teleport
Expand All @@ -166,6 +166,10 @@ teleport:
# subsequent starts
auth_token: xxxx-token-xxxx

# when running in multi-homed or NATed environments Teleport nodes need
# to know which IP it will be reachable at by other nodes
advertise_ip: 10.1.0.5

# list of auth servers in a cluster. you will have more than one auth server
# if you configure teleport auth to run in HA configuration
auth_servers:
Expand Down Expand Up @@ -193,12 +197,27 @@ teleport:
# This section configures the 'auth service':
auth_service:
enabled: yes
listen_addr: 127.0.0.1:3025
# IP and the port to bind to. Other Teleport nodes will be connecting to
# this port (AKA "Auth API" or "Cluster API") to validate client
# certificates
listen_addr: 0.0.0.0:3025

# Pre-defined tokens for adding new nodes to a cluster. Each token specifies
# the role a new node will be allowed to assume. The more secure way to
# add nodes is to use `ttl node add --ttl` command to generate auto-expiring
# tokens.
#
# We recommend to use tools like `pwgen` to generate sufficiently random
# tokens of 32+ byte length.
tokens:
- "proxy,node:xxxxx"
- "auth:yyyy"

# This section configures the 'node service':
ssh_service:
enabled: yes
listen_addr: 127.0.0.1:3022
# IP and the port for SSH service to bind to.
listen_addr: 0.0.0.0:3022
# See explanation of labels in "Labeling Nodes" section below
labels:
role: master
Expand Down Expand Up @@ -294,31 +313,59 @@ and re-create it.

The user will have to re-initialize Google Authenticator on their phone.

## Adding nodes to the cluster
## Adding Nodes to the Cluster

Gravitational Teleport is a cluster SSH manager. It only allows SSH access to nodes
who had been previously granted cluster membership.
who had been previously granted cluster membership, which means that every node in
a cluster has its own "host certificate" signed by the cluster's certificate
authority (CA). This prevents an attacker from creating a "honeypot" node within a
cluster.

A new Teleport node needs an "invite token" to join a cluster. An invitation token
also defines which role a new node can assume within a cluster: `auth`, `proxy` or
`node`.

There are two ways to create invitation tokens.

Use `tctl` tool to "invite" a new node to the Teleport cluster:
**Static Tokens**

You can pre-generate your own tokens and add them to certificate authority (CA)
config file:

```bash
tctl nodes add
# Example CA section in `/etc/teleport/teleport.yaml` file for the CA node running on 10.0.10.5
auth_service:
enabled: true
listen_addr: 0.0.0.0:3025
tokens:
- "proxy,node:xxxxxx"
```

Just like with adding users above, Teleport generates a single-use auto-expiring token
with a TTL of 15 minutes and prints the following:
Now you can start a new Teleport node by setting its invitation token via `--token`
flag to "xxxxxx". This node will join the cluster as a regular node but also
as a proxy server:

```bash
teleport start --roles=node,auth --token=xxxxx --auth-server=10.0.10.5
```
The invite token: n7305ee47a3829e118a7466ac7a0d78ad
Run this on the new node to join the cluster:
> teleport start --roles=node --token=n7305ee47a3829e118a7466ac7a0d78ad --auth-server=<Address>
```

`tctl` shows you the exact command you would need to use on the
new member node to start a `teleport` node service on it.

When a new node comes online, it will start sending ping requests every few seconds
to the auth server. This allows everyone to see which nodes are up:
**Short-lived Tokens**

A more secure way to add nodes to a cluster is to generate tokens as they are
needed. Such token can be used multiple times until its time to live (TTL)
expires.

Use `tctl` tool to invite a new node into the cluster with `node` and `auth`
roles:

```bash
tctl nodes --ttl=5m --roles=node,proxy add
```

As new nodes come online, they start sending ping requests every few seconds
to the CA of the cluster. This allows everyone to explore cluster membership
and size:

```bash
> tctl nodes ls
Expand All @@ -329,6 +376,37 @@ turing d52527f9-b260-41d0-bb5a-e23b0cfe0f8f 10.1.0.5:3022 distro
dijkstra c9s93fd9-3333-91d3-9999-c9s93fd98f43 10.1.0.6:3022 distro:debian
```

## Revoking Invitations

As you have seen above, Teleport uses tokens to invite users to a cluster (sign-up tokens) or
to add new nodes to it (provisioning tokens).

Both types of tokens can be revoked before they can be used. To see a list of outstanding tokens,
run this command:

```bash
> tctl tokens ls

Token Role Expiry Time (UTC)
----- ---- -----------------
eoKoh0caiw6weoGupahgh6Wuo7jaTee2 Proxy never
696c0471453e75882ff70a761c1a8bfa Node 17 May 16 03:51 UTC
6fc5545ab78c2ea978caabef9dbd08a5 Signup 17 May 16 04:24 UTC
```

In this exampe, wthe first token with "never" expiry date is a static token configured via
a config file. It cannot be revoked.

The 2nd token with "Node" role was generated to invite a new node to this cluster. And the
3rd token was generated to invite a new user.

The latter two tokens can be deleted (revoked) via `tctl tokens del` command:

```bash
> tctl tokens del 696c0471453e75882ff70a761c1a8bfa
Token 696c0471453e75882ff70a761c1a8bfa has been deleted
```

### Labeling Nodes

In addition to specifying a custom nodename, Teleport also allows to apply arbitrary
Expand Down Expand Up @@ -447,7 +525,7 @@ configuring OpenSSH client to work with Teleport Proxy:
scp_if_ssh = True
```

## Authentication with OpenID Connect / OAuth2
## OpenID / OAuth2

Teleport supports [OpenID Connect](http://openid.net/connect/) (also known as `OIDC`) to
provide external authentication using OpenID providers like Google Apps.
Expand Down Expand Up @@ -479,7 +557,7 @@ OIDC integration with applications like Teleport.
```
auth_service:
enabled: true
domain_name: localhost
cluster_name: magadan
oidc_connectors:
- id: google
redirect_url: https://localhost:3080/v1/webapi/oidc/callback
Expand Down Expand Up @@ -561,6 +639,7 @@ store the updated list locally in `/var/lib/teleport/authservers.json`. The valu
file, if present, will take precendence over configuration file's values.
You can simply remove the file so that the configuration file's values can take effect again.


## Troubleshooting

To diagnose problems you can configure `teleport` to run with verbose logging enabled.
Expand Down
10 changes: 8 additions & 2 deletions docs/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ localhost xxxxx-xxxx-xxxx-xxxxxxx 10.0.10.1:3022
new-node xxxxx-xxxx-xxxx-xxxxxxx 10.0.10.2:3022
```

**NOTE:** Teleport also supports static pre-defined invitation tokens which can be set in
the [configuration file](admin-guide.md#adding-nodes-to-the-cluster)

## Using Node Labels

Notice the "Labels" column in the output above. It is currently not populated. Teleport lets
Expand Down Expand Up @@ -160,7 +163,7 @@ on all servers located in Virginia:
> tsh --proxy=localhost ssh location=virginia ls -l /
```

## Sharing SSH Sessions with Colleagues
## Sharing SSH Sessions

Suppose you are trying to troubleshoot a problem on a node. Sometimes it makes sense to ask
another team member for help. Traditionally this could be done by letting them know which
Expand Down Expand Up @@ -190,7 +193,7 @@ Also, people can join your session via terminal assuming they have Teleport inst
NOTE: for this to work, both of you must have proper user mappings allowing you
access `db` under the same OS user.

## Inviting Colleagues to your Laptop
## Sharing Localhost

Sometimes you may want to temporarily share the terminal on your own laptop (if you
trust your guests, of course). First, you will have to start teleport with `--roles=node` in
Expand All @@ -212,6 +215,9 @@ Now you can invite someone into your localhost session. They will need to have a
user mapping, of course, to be allowed to join your session. To disconnect, shut down
`teleport` daemon or simply exit the `tsh` session.

**NOTE:** For this to work, your laptop has to join the SSH cluster first. See
[adding nodes](admin-guide.md#adding-nodes-to-the-cluster) for more information.

## Running in Production

We hope this Guide helped you to quickly set up Teleport to play with on
Expand Down
3 changes: 3 additions & 0 deletions docs/theme/css/teleport.css
Original file line number Diff line number Diff line change
Expand Up @@ -292,3 +292,6 @@ a:visited{
/* size of the content area */
div.wy-nav-content { max-width: 1000px; }
.rst-content table.docutils td { overflow: hidden; white-space: normal }

h3 { margin: 0 auto 0 auto; width: 100% }

2 changes: 1 addition & 1 deletion integration/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func NewInstance(siteName string, hostName string, ports []int, priv, pub []byte
priv, pub, _ = keygen.GenerateKeyPair("")
}
cert, err := keygen.GenerateHostCert(priv, pub,
hostName, siteName, teleport.RoleAdmin, time.Duration(time.Hour*24))
hostName, siteName, teleport.Roles{teleport.RoleAdmin}, time.Duration(time.Hour*24))
fatalIf(err)
secrets := InstanceSecrets{
SiteName: siteName,
Expand Down
34 changes: 28 additions & 6 deletions lib/auth/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ func NewAPIServer(a *AuthWithRoles) *APIServer {
srv.POST("/v1/oidc/requests/create", httplib.MakeHandler(srv.createOIDCAuthRequest))
srv.POST("/v1/oidc/requests/validate", httplib.MakeHandler(srv.validateOIDCAuthCallback))

// Provisioning tokens
srv.GET("/v1/tokens", httplib.MakeHandler(srv.getTokens))
srv.DELETE("/v1/tokens/:token", httplib.MakeHandler(srv.deleteToken))

// Audit logs AKA events
srv.POST("/v1/events", httplib.MakeHandler(srv.emitAuditEvent))
srv.GET("/v1/events", httplib.MakeHandler(srv.searchEvents))
Expand Down Expand Up @@ -238,6 +242,24 @@ func (s *APIServer) deleteReverseTunnel(w http.ResponseWriter, r *http.Request,
return message(fmt.Sprintf("reverse tunnel %v deleted", domainName)), nil
}

// getTokens returns a list of active provisioning tokens. expired (inactive) tokens are not returned
func (s *APIServer) getTokens(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
tokens, err := s.a.GetTokens()
if err != nil {
return nil, trace.Wrap(err)
}
return tokens, nil
}

// deleteToken deletes (revokes) a token by its value
func (s *APIServer) deleteToken(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
token := p.ByName("token")
if err := s.a.DeleteToken(token); err != nil {
return nil, trace.Wrap(err)
}
return message(fmt.Sprintf("Token %v deleted", token)), nil
}

func (s *APIServer) deleteWebSession(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
user, sid := p[0].Value, p[1].Value
err := s.a.DeleteWebSession(user, sid)
Expand Down Expand Up @@ -407,19 +429,19 @@ func (s *APIServer) generateKeyPair(w http.ResponseWriter, r *http.Request, _ ht
}

type generateHostCertReq struct {
Key []byte `json:"key"`
Hostname string `json:"hostname"`
AuthDomain string `json:"auth_domain"`
Role teleport.Role `json:"role"`
TTL time.Duration `json:"ttl"`
Key []byte `json:"key"`
Hostname string `json:"hostname"`
AuthDomain string `json:"auth_domain"`
Roles teleport.Roles `json:"roles"`
TTL time.Duration `json:"ttl"`
}

func (s *APIServer) generateHostCert(w http.ResponseWriter, r *http.Request, _ httprouter.Params) (interface{}, error) {
var req *generateHostCertReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
cert, err := s.a.GenerateHostCert(req.Key, req.Hostname, req.AuthDomain, req.Role, req.TTL)
cert, err := s.a.GenerateHostCert(req.Key, req.Hostname, req.AuthDomain, req.Roles, req.TTL)
if err != nil {
return nil, trace.Wrap(err)
}
Expand Down
2 changes: 1 addition & 1 deletion lib/auth/apiserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ func (s *APISuite) TestGenerateKeysAndCerts(c *C) {
c.Assert(err, IsNil)

// make sure we can parse the private and public key
cert, err := s.clt.GenerateHostCert(pub, "localhost", "localhost", teleport.RoleNode, time.Hour)
cert, err := s.clt.GenerateHostCert(pub, "localhost", "localhost", teleport.Roles{teleport.RoleNode}, time.Hour)
c.Assert(err, IsNil)

_, _, _, _, err = ssh.ParseAuthorizedKey(cert)
Expand Down
Loading

0 comments on commit 33efb9c

Please sign in to comment.