Skip to content

Commit

Permalink
Merge branch 'master' into sshah/ic-launchurl
Browse files Browse the repository at this point in the history
  • Loading branch information
flyinghermit authored Dec 13, 2024
2 parents 61a44e6 + e50dae9 commit 0aab12e
Show file tree
Hide file tree
Showing 199 changed files with 954 additions and 345 deletions.
52 changes: 50 additions & 2 deletions lib/auth/grpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ import (
apievents "github.com/gravitational/teleport/api/types/events"
"github.com/gravitational/teleport/api/types/installers"
"github.com/gravitational/teleport/api/types/wrappers"
apiutils "github.com/gravitational/teleport/api/utils"
"github.com/gravitational/teleport/lib/auth/accessmonitoringrules/accessmonitoringrulesv1"
"github.com/gravitational/teleport/lib/auth/authclient"
"github.com/gravitational/teleport/lib/auth/autoupdate/autoupdatev1"
Expand Down Expand Up @@ -1977,11 +1978,58 @@ func (g *GRPCServer) DeleteAllKubernetesServers(ctx context.Context, req *authpb
// version for some features of the role returns a shallow copy of the given
// role downgraded for compatibility with the older version.
func maybeDowngradeRole(ctx context.Context, role *types.RoleV6) (*types.RoleV6, error) {
// Teleport 16 supports all role features that Teleport 15 does,
// so no downgrade is necessary.
clientVersionString, ok := metadata.ClientVersionFromContext(ctx)
if !ok {
// This client is not reporting its version via gRPC metadata. Teleport
// clients have been reporting their version for long enough that older
// clients won't even support v6 roles at all, so this is likely a
// third-party client, and we shouldn't assume that downgrading the role
// will do more good than harm.
return role, nil
}

clientVersion, err := semver.NewVersion(clientVersionString)
if err != nil {
return nil, trace.BadParameter("unrecognized client version: %s is not a valid semver", clientVersionString)
}

role = maybeDowngradeRoleSSHPortForwarding(role, clientVersion)
return role, nil
}

var minSupportedSSHPortForwardingVersions = map[int64]semver.Version{
17: {Major: 17, Minor: 1, Patch: 0},
}

func maybeDowngradeRoleSSHPortForwarding(role *types.RoleV6, clientVersion *semver.Version) *types.RoleV6 {
sshPortForwarding := role.GetOptions().SSHPortForwarding
if sshPortForwarding == nil || (sshPortForwarding.Remote == nil && sshPortForwarding.Local == nil) {
return role
}

minSupportedVersion, ok := minSupportedSSHPortForwardingVersions[clientVersion.Major]
if ok {
if supported, err := utils.MinVerWithoutPreRelease(clientVersion.String(), minSupportedVersion.String()); supported || err != nil {
return role
}
}

role = apiutils.CloneProtoMsg(role)
options := role.GetOptions()

//nolint:staticcheck // this field is preserved for backwards compatibility
options.PortForwarding = types.NewBoolOption(services.RoleSet{role}.CanPortForward())
role.SetOptions(options)
reason := fmt.Sprintf(`Client version %q does not support granular SSH port forwarding. Role %q will be downgraded `+
`to simple port forwarding rules instead. In order to support granular SSH port forwarding, all clients must be `+
`updated to version %q or higher.`, clientVersion, role.GetName(), minSupportedVersion)
if role.Metadata.Labels == nil {
role.Metadata.Labels = make(map[string]string, 1)
}
role.Metadata.Labels[types.TeleportDowngradedLabel] = reason
return role
}

// GetRole retrieves a role by name.
func (g *GRPCServer) GetRole(ctx context.Context, req *authpb.GetRoleRequest) (*types.RoleV6, error) {
auth, err := g.authenticate(ctx)
Expand Down
250 changes: 250 additions & 0 deletions lib/auth/grpcserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import (
clusterconfigpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/clusterconfig/v1"
mfav1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/mfa/v1"
"github.com/gravitational/teleport/api/internalutils/stream"
"github.com/gravitational/teleport/api/metadata"
"github.com/gravitational/teleport/api/mfa"
"github.com/gravitational/teleport/api/observability/tracing"
"github.com/gravitational/teleport/api/types"
Expand Down Expand Up @@ -4604,6 +4605,255 @@ func TestGRPCServer_GetInstallers(t *testing.T) {
}
}

func TestRoleVersions(t *testing.T) {
t.Parallel()
srv := newTestTLSServer(t)

newRole := func(name string, version string, spec types.RoleSpecV6) types.Role {
role, err := types.NewRoleWithVersion(name, version, spec)
meta := role.GetMetadata()
role.SetMetadata(meta)
require.NoError(t, err)
return role
}

enabledRole := newRole("test_role_enabled", types.V7, types.RoleSpecV6{
Allow: types.RoleConditions{
Rules: []types.Rule{
types.NewRule(types.KindRole, services.RW()),
},
},
Options: types.RoleOptions{
SSHPortForwarding: &types.SSHPortForwarding{
Remote: &types.SSHRemotePortForwarding{
Enabled: types.NewBoolOption(true),
},
Local: &types.SSHLocalPortForwarding{
Enabled: types.NewBoolOption(true),
},
},
},
})

disabledRole := newRole("test_role_disabled", types.V7, types.RoleSpecV6{
Allow: types.RoleConditions{
Rules: []types.Rule{
types.NewRule(types.KindRole, services.RW()),
},
},
Options: types.RoleOptions{
SSHPortForwarding: &types.SSHPortForwarding{
Remote: &types.SSHRemotePortForwarding{
Enabled: types.NewBoolOption(false),
},
Local: &types.SSHLocalPortForwarding{
Enabled: types.NewBoolOption(false),
},
},
},
})

undefinedRole := newRole("test_role_implicit", types.V7, types.RoleSpecV6{
Allow: types.RoleConditions{
Rules: []types.Rule{
types.NewRule(types.KindRole, services.RW()),
},
},
})

user, err := CreateUser(context.Background(), srv.Auth(), "user", enabledRole, disabledRole, undefinedRole)
require.NoError(t, err)

client, err := srv.NewClient(TestUser(user.GetName()))
require.NoError(t, err)

for _, tc := range []struct {
desc string
clientVersions []string
expectError bool
inputRole types.Role
expectedRole types.Role
expectDowngraded bool
}{
{
desc: "up to date - enabled",
clientVersions: []string{
"17.1.0", "17.1.0-dev", "",
},
inputRole: enabledRole,
expectedRole: enabledRole,
},
{
desc: "up to date - disabled",
clientVersions: []string{
"17.1.0", "17.1.0-dev", "",
},
inputRole: disabledRole,
expectedRole: disabledRole,
},
{
desc: "up to date - undefined",
clientVersions: []string{
"17.1.0", "17.1.0-dev", "",
},
inputRole: undefinedRole,
expectedRole: undefinedRole,
},
{
desc: "downgrade SSH access control granularity - enabled",
clientVersions: []string{
"17.0.0",
},
inputRole: enabledRole,
expectedRole: newRole(enabledRole.GetName(), types.V7, types.RoleSpecV6{
Allow: types.RoleConditions{
Rules: []types.Rule{
types.NewRule(types.KindRole, services.RW()),
},
},
Options: types.RoleOptions{
PortForwarding: types.NewBoolOption(true),
SSHPortForwarding: enabledRole.GetOptions().SSHPortForwarding,
},
}),
expectDowngraded: true,
},
{
desc: "downgrade SSH access control granularity - disabled",
clientVersions: []string{
"17.0.0",
},
inputRole: disabledRole,
expectedRole: newRole(disabledRole.GetName(), types.V7, types.RoleSpecV6{
Allow: types.RoleConditions{
Rules: []types.Rule{
types.NewRule(types.KindRole, services.RW()),
},
},
Options: types.RoleOptions{
PortForwarding: types.NewBoolOption(false),
SSHPortForwarding: disabledRole.GetOptions().SSHPortForwarding,
},
}),
expectDowngraded: true,
},
{
desc: "downgrade SSH access control granularity - undefined",
clientVersions: []string{
"17.0.0",
},
inputRole: undefinedRole,
expectedRole: undefinedRole,
expectDowngraded: false,
},
} {
t.Run(tc.desc, func(t *testing.T) {
for _, clientVersion := range tc.clientVersions {
t.Run(clientVersion, func(t *testing.T) {
// setup client metadata
ctx := context.Background()
if clientVersion == "" {
ctx = context.WithValue(ctx, metadata.DisableInterceptors{}, struct{}{})
} else {
ctx = metadata.AddMetadataToContext(ctx, map[string]string{
metadata.VersionKey: clientVersion,
})
}

checkRole := func(gotRole types.Role) {
t.Helper()
if tc.expectError {
return
}
require.Empty(t, cmp.Diff(tc.expectedRole, gotRole,
cmpopts.IgnoreFields(types.Metadata{}, "Revision", "Labels")))
// The downgraded label value won't match exactly because it
// includes the client version, so just check it's not empty
// and ignore it in the role diff.
if tc.expectDowngraded {
require.NotEmpty(t, gotRole.GetMetadata().Labels[types.TeleportDowngradedLabel])
}
}
checkErr := func(err error) {
t.Helper()
if tc.expectError {
require.Error(t, err)
} else {
require.NoError(t, err)
}
}

// Test GetRole
gotRole, err := client.GetRole(ctx, tc.inputRole.GetName())
checkErr(err)
checkRole(gotRole)

// Test GetRoles
gotRoles, err := client.GetRoles(ctx)
checkErr(err)
if !tc.expectError {
foundTestRole := false
for _, gotRole := range gotRoles {
if gotRole.GetName() != tc.inputRole.GetName() {
continue
}
checkRole(gotRole)
foundTestRole = true
break
}
require.True(t, foundTestRole, "GetRoles result does not include expected role")
}

ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()

// Test WatchEvents
watcher, err := client.NewWatcher(ctx, types.Watch{Name: "roles", Kinds: []types.WatchKind{{Kind: types.KindRole}}})
require.NoError(t, err)
defer watcher.Close()

// Swallow the init event
e := <-watcher.Events()
require.Equal(t, types.OpInit, e.Type)

// Re-upsert the role so that the watcher sees it, do this
// on the auth server directly to avoid the
// TeleportDowngradedLabel check in ServerWithRoles
tc.inputRole, err = srv.Auth().UpsertRole(ctx, tc.inputRole)
require.NoError(t, err)

gotRole, err = func() (types.Role, error) {
for {
select {
case <-watcher.Done():
return nil, watcher.Error()
case e := <-watcher.Events():
if gotRole, ok := e.Resource.(types.Role); ok && gotRole.GetName() == tc.inputRole.GetName() {
return gotRole, nil
}
}
}
}()
checkErr(err)
checkRole(gotRole)

if !tc.expectError {
// Try to re-upsert the role we got. If it was
// downgraded, it should be rejected due to the
// TeleportDowngradedLabel
_, err = client.UpsertRole(ctx, gotRole)
if tc.expectDowngraded {
require.Error(t, err)
} else {
require.NoError(t, err)
}
}
})
}
})
}
}

func TestUpsertApplicationServerOrigin(t *testing.T) {
t.Parallel()

Expand Down
Loading

0 comments on commit 0aab12e

Please sign in to comment.