Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add V4 Roles #7118

Merged
merged 21 commits into from
Jun 10, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions api/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/gravitational/teleport/api/client/webclient"
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/defaults"
"github.com/gravitational/teleport/api/metadata"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/types/events"
"github.com/gravitational/teleport/api/utils"
Expand Down Expand Up @@ -278,6 +279,8 @@ func (c *Client) dialGRPC(ctx context.Context, addr string) error {
c.c.DialOpts,
grpc.WithContextDialer(c.grpcDialer()),
grpc.WithTransportCredentials(credentials.NewTLS(c.tlsConfig)),
grpc.WithUnaryInterceptor(metadata.UnaryClientInterceptor),
grpc.WithStreamInterceptor(metadata.StreamClientInterceptor),
)

var err error
Expand Down
5 changes: 5 additions & 0 deletions api/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,8 @@ const (
// requests a connection to the remote auth server.
RemoteAuthServer = "@remote-auth-server"
)

const (
// TODO(Joerger): change this to generated value
Version = "7.0.0-dev"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have version in version.go file. Is this in addition to that? Does this mean we'd need to bump version in 3 places now when doing releases (Makefile, version.go and here)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

teleport/api/ doesn't depend on teleport/ which is why I can't really use the value from version.go. It sounds like @Joerger is working on something so that api/ will have access to the generated client version, the suggestion was to copy this here for now and let him fill in the generated value when his changes come in. I'm not sure how quickly those changes are coming or if we should put in something to get the generated value for this PR @Joerger ? fyi I think we want this going out in the 6.2 train

Copy link
Contributor

@Joerger Joerger Jun 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's the PR for the client version generation - #7157
If this PR is merged before that one, I'll update it to use the new generated version in #7157

)
1 change: 1 addition & 0 deletions api/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/gogo/protobuf v1.3.1
github.com/golang/protobuf v1.4.2
github.com/google/go-cmp v0.5.4
github.com/google/uuid v1.2.0
github.com/gravitational/trace v1.1.15
github.com/jonboulle/clockwork v0.2.2 // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
Expand Down
2 changes: 2 additions & 0 deletions api/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gravitational/trace v1.1.15 h1:dfaFcARt110nCX6RSvrcRUbvRawEYAasXyCqnhXo0Xg=
github.com/gravitational/trace v1.1.15/go.mod h1:RvdOUHE4SHqR3oXlFFKnGzms8a5dugHygGw1bqDstYI=
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
Expand Down
78 changes: 78 additions & 0 deletions api/metadata/metadata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
Copyright 2021 Gravitational, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package metadata

import (
"context"

"github.com/gravitational/teleport/api/constants"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
)

const versionKey = "version"

var (
versionValue = constants.Version
disabled = false
)

// SetVersion is used only for tests to set the version value that the client
// will send in the GRPC metadata.
func SetVersion(version string) {
versionValue = version
nklaassen marked this conversation as resolved.
Show resolved Hide resolved
}

// DisabledIs disables (or enables) including metadata in GRPC requests. Used in tests.
func DisabledIs(metadataDisabled bool) {
disabled = metadataDisabled
}

func addMetadataToContext(ctx context.Context) context.Context {
return metadata.AppendToOutgoingContext(ctx, versionKey, versionValue)
}

// StreamClientInterceptor intercepts a GRPC client stream call and adds metadata to the context
func StreamClientInterceptor(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
if !disabled {
ctx = addMetadataToContext(ctx)
}
return streamer(ctx, desc, cc, method, opts...)
}

// UnaryClientInterceptor intercepts a GRPC client unary call and adds metadata to the context
func UnaryClientInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
if !disabled {
ctx = addMetadataToContext(ctx)
}
return invoker(ctx, method, req, reply, cc, opts...)
}

// VersionFromContext can be called from a GRPC server method to return the
// client version that was added to the GRPC metadata by
// StreamClientInterceptor or UnaryClientInterceptor on the client.
func VersionFromContext(ctx context.Context) (string, bool) {
nklaassen marked this conversation as resolved.
Show resolved Hide resolved
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return "", false
}
versionList, ok := md[versionKey]
if !ok || len(versionList) != 1 {
return "", false
}
return versionList[0], true
}
3 changes: 3 additions & 0 deletions api/types/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,9 @@ const (
// KindBilling represents access to cloud billing features
KindBilling = "billing"

// V4 is the fourth version of resources.
V4 = "v4"

// V3 is the third version of resources.
V3 = "v3"

Expand Down
70 changes: 55 additions & 15 deletions api/types/role.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/gravitational/teleport/api/utils"

"github.com/gogo/protobuf/proto"
"github.com/google/uuid"
"github.com/gravitational/trace"
)

Expand Down Expand Up @@ -518,25 +519,33 @@ func (r *RoleV3) CheckAndSetDefaults() error {
if r.Spec.Allow.Namespaces == nil {
r.Spec.Allow.Namespaces = []string{defaults.Namespace}
}
if r.Spec.Allow.NodeLabels == nil {
if len(r.Spec.Allow.Logins) == 0 {
// no logins implies no node access
r.Spec.Allow.NodeLabels = Labels{}
} else {
r.Spec.Allow.NodeLabels = Labels{Wildcard: []string{Wildcard}}

switch r.Version {
case V3:
if r.Spec.Allow.NodeLabels == nil {
if len(r.Spec.Allow.Logins) == 0 {
// no logins implies no node access
r.Spec.Allow.NodeLabels = Labels{}
} else {
r.Spec.Allow.NodeLabels = Labels{Wildcard: []string{Wildcard}}
}
}
}

if r.Spec.Allow.AppLabels == nil {
r.Spec.Allow.AppLabels = Labels{Wildcard: []string{Wildcard}}
}
if r.Spec.Allow.AppLabels == nil {
r.Spec.Allow.AppLabels = Labels{Wildcard: []string{Wildcard}}
}

if r.Spec.Allow.KubernetesLabels == nil {
r.Spec.Allow.KubernetesLabels = Labels{Wildcard: []string{Wildcard}}
}
if r.Spec.Allow.KubernetesLabels == nil {
r.Spec.Allow.KubernetesLabels = Labels{Wildcard: []string{Wildcard}}
}

if r.Spec.Allow.DatabaseLabels == nil {
r.Spec.Allow.DatabaseLabels = Labels{Wildcard: []string{Wildcard}}
if r.Spec.Allow.DatabaseLabels == nil {
r.Spec.Allow.DatabaseLabels = Labels{Wildcard: []string{Wildcard}}
}
case V4:
// Labels default to nil/empty for v4 roles
default:
return trace.BadParameter("unrecognized role version: %v", r.Version)
}

if r.Spec.Deny.Namespaces == nil {
Expand Down Expand Up @@ -612,6 +621,37 @@ func (r *RoleV3) CheckAndSetDefaults() error {
return nil
}

// DowngradeToV3 converts a V4 role to V3 so that it will be compatible with older instances.
nklaassen marked this conversation as resolved.
Show resolved Hide resolved
// DELETE IN 8.0.0
func (r *RoleV3) DowngradeToV3() error {
switch r.Version {
case V3:
nklaassen marked this conversation as resolved.
Show resolved Hide resolved
case V4:
r.Version = V3

// V3 roles will set the default labels to wildcard allow if they are
// empty. To prevent this for roles which are created as V4 and
// downgraded, set a placeholder label
const labelKey = "__teleport_no_labels"
labelVal := uuid.NewString()
if r.Spec.Allow.NodeLabels == nil || len(r.Spec.Allow.NodeLabels) == 0 {
nklaassen marked this conversation as resolved.
Show resolved Hide resolved
r.Spec.Allow.NodeLabels = Labels{labelKey: []string{labelVal}}
}
if r.Spec.Allow.AppLabels == nil || len(r.Spec.Allow.AppLabels) == 0 {
r.Spec.Allow.AppLabels = Labels{labelKey: []string{labelVal}}
}
if r.Spec.Allow.KubernetesLabels == nil || len(r.Spec.Allow.KubernetesLabels) == 0 {
r.Spec.Allow.KubernetesLabels = Labels{labelKey: []string{labelVal}}
}
if r.Spec.Allow.DatabaseLabels == nil || len(r.Spec.Allow.DatabaseLabels) == 0 {
r.Spec.Allow.DatabaseLabels = Labels{labelKey: []string{labelVal}}
}
default:
return trace.BadParameter("unrecognized role version %T", r.Version)
}
return nil
}

// String returns the human readable representation of a role.
func (r *RoleV3) String() string {
return fmt.Sprintf("Role(Name=%v,Options=%v,Allow=%+v,Deny=%+v)",
Expand Down
36 changes: 34 additions & 2 deletions lib/auth/grpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/client/proto"
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/metadata"
"github.com/gravitational/teleport/api/types"
apievents "github.com/gravitational/teleport/api/types/events"
"github.com/gravitational/teleport/lib/auth/u2f"
Expand All @@ -36,6 +37,7 @@ import (
"github.com/gravitational/teleport/lib/session"
"github.com/gravitational/teleport/lib/utils"

"github.com/coreos/go-semver/semver"
"github.com/golang/protobuf/ptypes/empty"
"github.com/gravitational/trace"
"github.com/gravitational/trace/trail"
Expand Down Expand Up @@ -294,7 +296,7 @@ func (g *GRPCServer) WatchEvents(watch *proto.Watch, stream proto.AuthService_Wa
case <-watcher.Done():
return trail.ToGRPC(watcher.Error())
case event := <-watcher.Events():
out, err := eventToGRPC(event)
out, err := eventToGRPC(stream.Context(), event)
if err != nil {
return trail.ToGRPC(err)
}
Expand All @@ -306,7 +308,7 @@ func (g *GRPCServer) WatchEvents(watch *proto.Watch, stream proto.AuthService_Wa
}

// eventToGRPC converts a types.Event to an proto.Event
func eventToGRPC(in types.Event) (*proto.Event, error) {
func eventToGRPC(ctx context.Context, in types.Event) (*proto.Event, error) {
eventType, err := eventTypeToGRPC(in.Type)
if err != nil {
return nil, trace.Wrap(err)
Expand Down Expand Up @@ -347,6 +349,9 @@ func eventToGRPC(in types.Event) (*proto.Event, error) {
User: r,
}
case *types.RoleV3:
if err = downgradeRole(ctx, r); err != nil {
return nil, trace.Wrap(err)
}
out.Resource = &proto.Event_Role{
Role: r,
}
Expand Down Expand Up @@ -1374,6 +1379,27 @@ func (g *GRPCServer) DeleteAllKubeServices(ctx context.Context, req *proto.Delet
return &empty.Empty{}, nil
}

// downgradeRole tests the client version passed through the GRPC metadata, and
// downgrades the given role to V3 if V4 roles are not known to be supported (client version is unknown or < 6.3).
func downgradeRole(ctx context.Context, role *types.RoleV3) error {
var clientVersion *semver.Version
clientVersionString, ok := metadata.VersionFromContext(ctx)
if ok {
var err error
clientVersion, err = semver.NewVersion(clientVersionString)
if err != nil {
return trace.BadParameter("unrecognized client version: %s is not a valid semver", clientVersionString)
}
}

minSupportedVersionForV4Roles := semver.New("6.3.0-aa") // "aa" is included so that this compares before v6.3.0-alpha
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will need to update this to the first version which will support V4 roles before merging

if clientVersion == nil || clientVersion.LessThan(*minSupportedVersionForV4Roles) {
log.Debugf(`Client version "%s" is unknown or less than 6.3.0, converting role to v3`, clientVersionString)
return trace.Wrap(role.DowngradeToV3())
}
return nil
}

// GetRole retrieves a role by name.
func (g *GRPCServer) GetRole(ctx context.Context, req *proto.GetRoleRequest) (*types.RoleV3, error) {
auth, err := g.authenticate(ctx)
Expand All @@ -1388,6 +1414,9 @@ func (g *GRPCServer) GetRole(ctx context.Context, req *proto.GetRoleRequest) (*t
if !ok {
return nil, trail.ToGRPC(trace.Errorf("encountered unexpected role type"))
}
if err = downgradeRole(ctx, roleV3); err != nil {
return nil, trail.ToGRPC(err)
}
return roleV3, nil
}

Expand All @@ -1407,6 +1436,9 @@ func (g *GRPCServer) GetRoles(ctx context.Context, _ *empty.Empty) (*proto.GetRo
if !ok {
return nil, trail.ToGRPC(trace.BadParameter("unexpected type %T", r))
}
if err = downgradeRole(ctx, role); err != nil {
return nil, trail.ToGRPC(err)
}
rolesV3 = append(rolesV3, role)
}
return &proto.GetRolesResponse{
Expand Down
Loading