Skip to content

Commit

Permalink
Convert GenerateToken to gRPC. (#9024)
Browse files Browse the repository at this point in the history
  • Loading branch information
Joerger authored Jun 14, 2022
1 parent d4c3935 commit b5472d4
Show file tree
Hide file tree
Showing 16 changed files with 1,417 additions and 721 deletions.
8 changes: 8 additions & 0 deletions api/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1842,6 +1842,14 @@ func (c *Client) UpsertToken(ctx context.Context, token types.ProvisionToken) er
return trail.FromGRPC(err)
}

// GenerateToken generates a new auth token for the given service roles.
// This token can be used by corresponding services to authenticate with
// the Auth server and get a signed certificate and private key.
func (c *Client) GenerateToken(ctx context.Context, req *proto.GenerateTokenRequest) (string, error) {
resp, err := c.grpc.GenerateToken(ctx, req, c.callOpts...)
return resp.Token, trail.FromGRPC(err)
}

// DeleteToken deletes a provision token by name.
func (c *Client) DeleteToken(ctx context.Context, name string) error {
if name == "" {
Expand Down
1,952 changes: 1,309 additions & 643 deletions api/client/proto/authservice.pb.go

Large diffs are not rendered by default.

24 changes: 24 additions & 0 deletions api/client/proto/authservice.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1729,6 +1729,28 @@ message GetClusterCACertResponse {
bytes TLSCA = 1 [ (gogoproto.jsontag) = "tls_ca" ];
}

// GenerateTokenRequest is a request to generate auth token.
message GenerateTokenRequest {
// Token sets the token value. If not set, it will be auto generated.
string Token = 1 [ (gogoproto.jsontag) = "token" ];
// Roles is a list of roles this token authenticates as.
repeated string Roles = 2 [
(gogoproto.jsontag) = "roles",
(gogoproto.casttype) = "github.com/gravitational/teleport/api/types.SystemRole"
];
// TTL specifies how long the generated token will be valid for.
// Defaults to 30 minutes if not set.
int64 TTL = 3 [ (gogoproto.jsontag) = "ttl", (gogoproto.casttype) = "Duration" ];
// Labels is a label-based matcher if non-empty.
map<string, string> Labels = 4 [ (gogoproto.jsontag) = "labels" ];
}

// GenerateTokenResponse contains a generated auth token.
message GenerateTokenResponse {
// Token is the generated auth token.
string Token = 1 [ (gogoproto.jsontag) = "token" ];
}

// GetOIDCAuthRequestRequest is a request for GetOIDCAuthRequest.
message GetOIDCAuthRequestRequest {
// StateToken is an oidc auth request state token.
Expand Down Expand Up @@ -2104,6 +2126,8 @@ service AuthService {
rpc GetTokens(google.protobuf.Empty) returns (types.ProvisionTokenV2List);
// UpsertToken upserts a token in a backend.
rpc UpsertToken(types.ProvisionTokenV2) returns (google.protobuf.Empty);
// GenerateToken generates a new auth token.
rpc GenerateToken(GenerateTokenRequest) returns (GenerateTokenResponse);
// DeleteToken deletes an existing token in a backend described by the given request.
rpc DeleteToken(types.ResourceRequest) returns (google.protobuf.Empty);

Expand Down
5 changes: 3 additions & 2 deletions lib/auth/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -733,12 +733,13 @@ func (s *APIServer) generateHostCert(auth ClientI, w http.ResponseWriter, r *htt
return string(cert), nil
}

// DELETE IN 11.0.0
func (s *APIServer) generateToken(auth ClientI, w http.ResponseWriter, r *http.Request, _ httprouter.Params, version string) (interface{}, error) {
var req GenerateTokenRequest
var req proto.GenerateTokenRequest
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
token, err := auth.GenerateToken(r.Context(), req)
token, err := auth.GenerateToken(r.Context(), &req)
if err != nil {
return nil, trace.Wrap(err)
}
Expand Down
42 changes: 10 additions & 32 deletions lib/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -1970,46 +1970,24 @@ func (a *Server) CreateWebSession(user string) (types.WebSession, error) {
return sess, nil
}

// GenerateTokenRequest is a request to generate auth token
type GenerateTokenRequest struct {
// Token if provided sets the token value, otherwise will be auto generated
Token string `json:"token"`
// Roles is a list of roles this token authenticates as
Roles types.SystemRoles `json:"roles"`
// TTL is a time to live for token
TTL time.Duration `json:"ttl"`
// Labels sets token labels, e.g. {env: prod, region: us-west}.
// Labels are later passed to resources that are joining
// e.g. remote clusters and in the future versions, nodes and proxies.
Labels map[string]string `json:"labels"`
}

// CheckAndSetDefaults checks and sets default values of request
func (req *GenerateTokenRequest) CheckAndSetDefaults() error {
for _, role := range req.Roles {
if err := role.Check(); err != nil {
return trace.Wrap(err)
}
}
if req.TTL == 0 {
req.TTL = defaults.ProvisioningTokenTTL
// GenerateToken generates multi-purpose authentication token.
func (a *Server) GenerateToken(ctx context.Context, req *proto.GenerateTokenRequest) (string, error) {
expires := a.clock.Now().UTC()
if req.TTL != 0 {
expires.Add(req.TTL.Get())
} else {
expires.Add(defaults.ProvisioningTokenTTL)
}

if req.Token == "" {
token, err := utils.CryptoRandomHex(TokenLenBytes)
if err != nil {
return trace.Wrap(err)
return "", trace.Wrap(err)
}
req.Token = token
}
return nil
}

// GenerateToken generates multi-purpose authentication token.
func (a *Server) GenerateToken(ctx context.Context, req GenerateTokenRequest) (string, error) {
if err := req.CheckAndSetDefaults(); err != nil {
return "", trace.Wrap(err)
}
token, err := types.NewProvisionToken(req.Token, req.Roles, a.clock.Now().UTC().Add(req.TTL))
token, err := types.NewProvisionToken(req.Token, req.Roles, expires)
if err != nil {
return "", trace.Wrap(err)
}
Expand Down
10 changes: 5 additions & 5 deletions lib/auth/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ func TestTokensCRUD(t *testing.T) {
require.Empty(t, btokens, 0)

// generate persistent token
tokenName, err := s.a.GenerateToken(ctx, GenerateTokenRequest{Roles: types.SystemRoles{types.RoleNode}})
tokenName, err := s.a.GenerateToken(ctx, &proto.GenerateTokenRequest{Roles: types.SystemRoles{types.RoleNode}})
require.NoError(t, err)
require.Len(t, tokenName, 2*TokenLenBytes)
tokens, err := s.a.GetTokens(ctx)
Expand All @@ -577,7 +577,7 @@ func TestTokensCRUD(t *testing.T) {

// generate predefined token
customToken := "custom-token"
tokenName, err = s.a.GenerateToken(ctx, GenerateTokenRequest{Roles: types.SystemRoles{types.RoleNode}, Token: customToken})
tokenName, err = s.a.GenerateToken(ctx, &proto.GenerateTokenRequest{Roles: types.SystemRoles{types.RoleNode}, Token: customToken})
require.NoError(t, err)
require.Equal(t, tokenName, customToken)

Expand Down Expand Up @@ -629,7 +629,7 @@ func TestBadTokens(t *testing.T) {
require.Error(t, err)

// tampered
tok, err := s.a.GenerateToken(ctx, GenerateTokenRequest{Roles: types.SystemRoles{types.RoleAuth}})
tok, err := s.a.GenerateToken(ctx, &proto.GenerateTokenRequest{Roles: types.SystemRoles{types.RoleAuth}})
require.NoError(t, err)

tampered := string(tok[0]+1) + tok[1:]
Expand All @@ -643,13 +643,13 @@ func TestGenerateTokenEventsEmitted(t *testing.T) {

ctx := context.Background()
// test trusted cluster token emit
_, err := s.a.GenerateToken(ctx, GenerateTokenRequest{Roles: types.SystemRoles{types.RoleTrustedCluster}})
_, err := s.a.GenerateToken(ctx, &proto.GenerateTokenRequest{Roles: types.SystemRoles{types.RoleTrustedCluster}})
require.NoError(t, err)
require.Equal(t, s.mockEmitter.LastEvent().GetType(), events.TrustedClusterTokenCreateEvent)
s.mockEmitter.Reset()

// test emit with multiple roles
_, err = s.a.GenerateToken(ctx, GenerateTokenRequest{Roles: types.SystemRoles{
_, err = s.a.GenerateToken(ctx, &proto.GenerateTokenRequest{Roles: types.SystemRoles{
types.RoleNode,
types.RoleTrustedCluster,
types.RoleAuth,
Expand Down
2 changes: 1 addition & 1 deletion lib/auth/auth_with_roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ func (a *ServerWithRoles) DeactivateCertAuthority(id types.CertAuthID) error {
}

// GenerateToken generates multi-purpose authentication token.
func (a *ServerWithRoles) GenerateToken(ctx context.Context, req GenerateTokenRequest) (string, error) {
func (a *ServerWithRoles) GenerateToken(ctx context.Context, req *proto.GenerateTokenRequest) (string, error) {
if err := a.action(apidefaults.Namespace, types.KindToken, types.VerbCreate); err != nil {
return "", trace.Wrap(err)
}
Expand Down
22 changes: 1 addition & 21 deletions lib/auth/clt.go
Original file line number Diff line number Diff line change
Expand Up @@ -464,26 +464,6 @@ func (c *Client) DeactivateCertAuthority(id types.CertAuthID) error {
return trace.NotImplemented(notImplementedMessage)
}

// GenerateToken creates a special provisioning token for a new SSH server
// that is valid for ttl period seconds.
//
// This token is used by SSH server to authenticate with Auth server
// and get signed certificate and private key from the auth server.
//
// If token is not supplied, it will be auto generated and returned.
// If TTL is not supplied, token will be valid until removed.
func (c *Client) GenerateToken(ctx context.Context, req GenerateTokenRequest) (string, error) {
out, err := c.PostJSON(ctx, c.Endpoint("tokens"), req)
if err != nil {
return "", trace.Wrap(err)
}
var token string
if err := json.Unmarshal(out.Bytes(), &token); err != nil {
return "", trace.Wrap(err)
}
return token, nil
}

// RegisterUsingToken calls the auth service API to register a new node using a registration token
// which was previously issued via GenerateToken.
func (c *Client) RegisterUsingToken(ctx context.Context, req *types.RegisterUsingTokenRequest) (*proto.Certs, error) {
Expand Down Expand Up @@ -1573,7 +1553,7 @@ type IdentityService interface {
//
// If token is not supplied, it will be auto generated and returned.
// If TTL is not supplied, token will be valid until removed.
GenerateToken(ctx context.Context, req GenerateTokenRequest) (string, error)
GenerateToken(ctx context.Context, req *proto.GenerateTokenRequest) (string, error)

// GenerateHostCert takes the public key in the Open SSH ``authorized_keys``
// plain text format, signs it using Host Certificate Authority private key and returns the
Expand Down
15 changes: 15 additions & 0 deletions lib/auth/grpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -2788,6 +2788,21 @@ func (g *GRPCServer) UpsertToken(ctx context.Context, token *types.ProvisionToke
return &empty.Empty{}, nil
}

// GenerateToken generates a new auth token.
func (g *GRPCServer) GenerateToken(ctx context.Context, req *proto.GenerateTokenRequest) (*proto.GenerateTokenResponse, error) {
auth, err := g.authenticate(ctx)
if err != nil {
return nil, trace.Wrap(err)
}

token, err := auth.ServerWithRoles.GenerateToken(ctx, req)
if err != nil {
return nil, trace.Wrap(err)
}

return &proto.GenerateTokenResponse{Token: token}, nil
}

// DeleteToken deletes a token by name.
func (g *GRPCServer) DeleteToken(ctx context.Context, req *types.ResourceRequest) (*empty.Empty, error) {
auth, err := g.authenticate(ctx)
Expand Down
24 changes: 24 additions & 0 deletions lib/auth/httpfallback.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,30 @@ func (c *Client) GetClusterCACert(ctx context.Context) (*proto.GetClusterCACertR
}, nil
}

// DELETE IN 11.0.0, to remove fallback and grpc call is already defined in api/client/client.go
//
// GenerateToken generates a new auth token for the given service roles.
// This token can be used by corresponding services to authenticate with
// the Auth server and get a signed certificate and private key.
func (c *Client) GenerateToken(ctx context.Context, req *proto.GenerateTokenRequest) (string, error) {
switch resp, err := c.APIClient.GenerateToken(ctx, req); {
case err == nil:
return resp, nil
case !trace.IsNotImplemented(err):
return "", trace.Wrap(err)
}

out, err := c.PostJSON(ctx, c.Endpoint("tokens"), req)
if err != nil {
return "", trace.Wrap(err)
}
var token string
if err := json.Unmarshal(out.Bytes(), &token); err != nil {
return "", trace.Wrap(err)
}
return token, nil
}

// CreateOIDCAuthRequest creates OIDCAuthRequest.
// DELETE IN 11.0.0
func (c *Client) CreateOIDCAuthRequest(ctx context.Context, req types.OIDCAuthRequest) (*types.OIDCAuthRequest, error) {
Expand Down
4 changes: 2 additions & 2 deletions lib/auth/join_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ func TestAuth_RegisterUsingToken(t *testing.T) {
require.NoError(t, err)

// create a dynamic token
dynamicToken, err := a.GenerateToken(ctx, GenerateTokenRequest{
dynamicToken, err := a.GenerateToken(ctx, &proto.GenerateTokenRequest{
Roles: types.SystemRoles{types.RoleNode},
TTL: time.Hour,
TTL: proto.Duration(time.Hour),
})
require.NoError(t, err)
require.NotNil(t, dynamicToken)
Expand Down
10 changes: 5 additions & 5 deletions lib/auth/tls_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1074,7 +1074,7 @@ func (s *TLSSuite) TestTokens(c *check.C) {
clt, err := s.server.NewClient(TestAdmin())
c.Assert(err, check.IsNil)

out, err := clt.GenerateToken(ctx, GenerateTokenRequest{Roles: types.SystemRoles{types.RoleNode}})
out, err := clt.GenerateToken(ctx, &proto.GenerateTokenRequest{Roles: types.SystemRoles{types.RoleNode}})
c.Assert(err, check.IsNil)
c.Assert(len(out), check.Not(check.Equals), 0)
}
Expand Down Expand Up @@ -2301,11 +2301,11 @@ func (s *TLSSuite) TestTLSFailover(c *check.C) {
func (s *TLSSuite) TestRegisterCAPin(c *check.C) {
ctx := context.Background()
// Generate a token to use.
token, err := s.server.AuthServer.AuthServer.GenerateToken(ctx, GenerateTokenRequest{
token, err := s.server.AuthServer.AuthServer.GenerateToken(ctx, &proto.GenerateTokenRequest{
Roles: types.SystemRoles{
types.RoleProxy,
},
TTL: time.Hour,
TTL: proto.Duration(time.Hour),
})
c.Assert(err, check.IsNil)

Expand Down Expand Up @@ -2436,11 +2436,11 @@ func (s *TLSSuite) TestRegisterCAPin(c *check.C) {
func (s *TLSSuite) TestRegisterCAPath(c *check.C) {
ctx := context.Background()
// Generate a token to use.
token, err := s.server.AuthServer.AuthServer.GenerateToken(ctx, GenerateTokenRequest{
token, err := s.server.AuthServer.AuthServer.GenerateToken(ctx, &proto.GenerateTokenRequest{
Roles: types.SystemRoles{
types.RoleProxy,
},
TTL: time.Hour,
TTL: proto.Duration(time.Hour),
})
c.Assert(err, check.IsNil)

Expand Down
6 changes: 3 additions & 3 deletions lib/web/join_tokens.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,9 @@ func (h *Handler) getAppJoinScriptHandle(w http.ResponseWriter, r *http.Request,
}

func createJoinToken(ctx context.Context, m nodeAPIGetter, roles types.SystemRoles) (*nodeJoinToken, error) {
req := auth.GenerateTokenRequest{
req := &proto.GenerateTokenRequest{
Roles: roles,
TTL: defaults.NodeJoinTokenTTL,
TTL: proto.Duration(defaults.NodeJoinTokenTTL),
}

token, err := m.GenerateToken(ctx, req)
Expand Down Expand Up @@ -372,7 +372,7 @@ type nodeAPIGetter interface {
//
// If token is not supplied, it will be auto generated and returned.
// If TTL is not supplied, token will be valid until removed.
GenerateToken(ctx context.Context, req auth.GenerateTokenRequest) (string, error)
GenerateToken(ctx context.Context, req *proto.GenerateTokenRequest) (string, error)

// GetClusterCACert returns the CAs for the local cluster without signing keys.
GetClusterCACert(ctx context.Context) (*proto.GetClusterCACertResponse, error)
Expand Down
7 changes: 3 additions & 4 deletions lib/web/join_tokens_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,14 @@ import (

"github.com/gravitational/teleport/api/client/proto"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/fixtures"
"github.com/gravitational/trace"
)

func TestCreateNodeJoinToken(t *testing.T) {
m := &mockedNodeAPIGetter{}
m.mockGenerateToken = func(ctx context.Context, req auth.GenerateTokenRequest) (string, error) {
m.mockGenerateToken = func(ctx context.Context, req *proto.GenerateTokenRequest) (string, error) {
return "some-token-id", nil
}

Expand Down Expand Up @@ -620,12 +619,12 @@ func TestIsSameRuleSet(t *testing.T) {
}

type mockedNodeAPIGetter struct {
mockGenerateToken func(ctx context.Context, req auth.GenerateTokenRequest) (string, error)
mockGenerateToken func(ctx context.Context, req *proto.GenerateTokenRequest) (string, error)
mockGetProxyServers func() ([]types.Server, error)
mockGetClusterCACert func(ctx context.Context) (*proto.GetClusterCACertResponse, error)
}

func (m *mockedNodeAPIGetter) GenerateToken(ctx context.Context, req auth.GenerateTokenRequest) (string, error) {
func (m *mockedNodeAPIGetter) GenerateToken(ctx context.Context, req *proto.GenerateTokenRequest) (string, error) {
if m.mockGenerateToken != nil {
return m.mockGenerateToken(ctx, req)
}
Expand Down
2 changes: 1 addition & 1 deletion tool/tctl/common/node_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ func (c *NodeCommand) Invite(ctx context.Context, client auth.ClientI) error {
if err != nil {
return trace.Wrap(err)
}
token, err := client.GenerateToken(ctx, auth.GenerateTokenRequest{Roles: roles, TTL: c.ttl, Token: c.token})
token, err := client.GenerateToken(ctx, &proto.GenerateTokenRequest{Roles: roles, TTL: proto.Duration(c.ttl), Token: c.token})
if err != nil {
return trace.Wrap(err)
}
Expand Down
5 changes: 3 additions & 2 deletions tool/tctl/common/token_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (

"github.com/ghodss/yaml"
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/client/proto"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/asciitable"
"github.com/gravitational/teleport/lib/auth"
Expand Down Expand Up @@ -157,9 +158,9 @@ func (c *TokensCommand) Add(ctx context.Context, client auth.ClientI) error {
}

// Generate token.
token, err := client.GenerateToken(ctx, auth.GenerateTokenRequest{
token, err := client.GenerateToken(ctx, &proto.GenerateTokenRequest{
Roles: roles,
TTL: c.ttl,
TTL: proto.Duration(c.ttl),
Token: c.value,
Labels: labels,
})
Expand Down

0 comments on commit b5472d4

Please sign in to comment.