Skip to content

Commit

Permalink
Make bot resource expire (#45101) (#45133)
Browse files Browse the repository at this point in the history
  • Loading branch information
hugoShaka authored Aug 6, 2024
1 parent fdcc82b commit 815d8a0
Show file tree
Hide file tree
Showing 2 changed files with 245 additions and 2 deletions.
30 changes: 28 additions & 2 deletions lib/auth/machineid/machineidv1/bot_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/jonboulle/clockwork"
"github.com/sirupsen/logrus"
"google.golang.org/protobuf/types/known/emptypb"
"google.golang.org/protobuf/types/known/timestamppb"

"github.com/gravitational/teleport"
headerv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1"
Expand Down Expand Up @@ -658,11 +659,14 @@ func botFromUserAndRole(user types.User, role types.Role) (*pb.Bot, error) {
return nil, trace.BadParameter("user missing bot label")
}

expiry := botExpiryFromUser(user)

b := &pb.Bot{
Kind: types.KindBot,
Version: types.V1,
Metadata: &headerv1.Metadata{
Name: botName,
Name: botName,
Expires: expiry,
},
Status: &pb.BotStatus{
UserName: user.GetName(),
Expand Down Expand Up @@ -725,6 +729,7 @@ func botToUserAndRole(bot *pb.Bot, now time.Time, createdBy string) (types.User,
roleMeta.Labels = map[string]string{
types.BotLabel: bot.Metadata.Name,
}
roleMeta.Expires = userAndRoleExpiryFromBot(bot)
role.SetMetadata(roleMeta)

// Setup user
Expand All @@ -746,7 +751,7 @@ func botToUserAndRole(bot *pb.Bot, now time.Time, createdBy string) (types.User,
// We always set this to zero here - but in Upsert, we copy from the
// previous user before writing if necessary
userMeta.Labels[types.BotGenerationLabel] = "0"

userMeta.Expires = userAndRoleExpiryFromBot(bot)
user.SetMetadata(userMeta)

traits := map[string][]string{}
Expand All @@ -767,3 +772,24 @@ func botToUserAndRole(bot *pb.Bot, now time.Time, createdBy string) (types.User,

return user, role, nil
}

func userAndRoleExpiryFromBot(bot *pb.Bot) *time.Time {
if bot.Metadata.GetExpires() == nil {
return nil
}

expiry := bot.Metadata.GetExpires().AsTime()
if expiry.IsZero() || expiry.Unix() == 0 {
return nil
}
return &expiry
}

func botExpiryFromUser(user types.User) *timestamppb.Timestamp {
userMeta := user.GetMetadata()
userExpiry := userMeta.Expiry()
if userExpiry.IsZero() || userExpiry.Unix() == 0 {
return nil
}
return timestamppb.New(userExpiry)
}
217 changes: 217 additions & 0 deletions lib/auth/machineid/machineidv1/machineidv1_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/testing/protocmp"
"google.golang.org/protobuf/types/known/fieldmaskpb"
"google.golang.org/protobuf/types/known/timestamppb"

"github.com/gravitational/teleport/api/client/proto"
"github.com/gravitational/teleport/api/constants"
Expand Down Expand Up @@ -117,6 +118,7 @@ func TestCreateBot(t *testing.T) {
},
)
require.NoError(t, err)
expiry := time.Now().Add(time.Hour)

tests := []struct {
name string
Expand Down Expand Up @@ -278,6 +280,118 @@ func TestCreateBot(t *testing.T) {
},
},
},
{
name: "success with expiry",
user: botCreator.GetName(),
req: &machineidv1pb.CreateBotRequest{
Bot: &machineidv1pb.Bot{
Metadata: &headerv1.Metadata{
Name: "success-with-expiry",
Labels: map[string]string{
"my-label": "my-value",
"my-other-label": "my-other-value",
},
Expires: timestamppb.New(expiry),
},
Spec: &machineidv1pb.BotSpec{
Roles: []string{testRole.GetName()},
Traits: []*machineidv1pb.Trait{
{
Name: constants.TraitLogins,
Values: []string{"root"},
},
{
Name: constants.TraitKubeUsers,
Values: []string{},
},
},
},
},
},

assertError: require.NoError,
want: &machineidv1pb.Bot{
Kind: types.KindBot,
Version: types.V1,
Metadata: &headerv1.Metadata{
Name: "success-with-expiry",
Labels: map[string]string{
"my-label": "my-value",
"my-other-label": "my-other-value",
},
Expires: timestamppb.New(expiry),
},
Spec: &machineidv1pb.BotSpec{
Roles: []string{testRole.GetName()},
Traits: []*machineidv1pb.Trait{
{
Name: constants.TraitLogins,
Values: []string{"root"},
},
},
},
Status: &machineidv1pb.BotStatus{
UserName: "bot-success-with-expiry",
RoleName: "bot-success-with-expiry",
},
},
wantUser: &types.UserV2{
Kind: types.KindUser,
Version: types.V2,
Metadata: types.Metadata{
Name: "bot-success-with-expiry",
Namespace: defaults.Namespace,
Labels: map[string]string{
types.BotLabel: "success-with-expiry",
types.BotGenerationLabel: "0",
"my-label": "my-value",
"my-other-label": "my-other-value",
},
Expires: &expiry,
},
Spec: types.UserSpecV2{
CreatedBy: types.CreatedBy{
User: types.UserRef{Name: botCreator.GetName()},
},
Roles: []string{"bot-success-with-expiry"},
Traits: map[string][]string{
constants.TraitLogins: {"root"},
},
},
Status: types.UserStatusV2{
PasswordState: types.PasswordState_PASSWORD_STATE_UNSET,
},
},
wantRole: &types.RoleV6{
Kind: types.KindRole,
Version: types.V7,
Metadata: types.Metadata{
Name: "bot-success-with-expiry",
Namespace: defaults.Namespace,
Labels: map[string]string{
types.BotLabel: "success-with-expiry",
},
Description: "Automatically generated role for bot success-with-expiry",
Expires: &expiry,
},
Spec: types.RoleSpecV6{
Options: types.RoleOptions{
MaxSessionTTL: types.Duration(12 * time.Hour),
},
Allow: types.RoleConditions{
Impersonate: &types.ImpersonateConditions{
Roles: []string{testRole.GetName()},
},
Rules: []types.Rule{
types.NewRule(
types.KindCertAuthority,
[]string{types.VerbReadNoSecrets},
),
},
},
},
},
},
{
name: "bot already exists",
user: botCreator.GetName(),
Expand Down Expand Up @@ -814,6 +928,7 @@ func TestUpsertBot(t *testing.T) {
},
})
require.NoError(t, err)
expiry := time.Now().Add(time.Hour)

// We find the user associated with the Bot and set the generation label. This allows us to ensure that the
// generation label is preserved when UpsertBot is called.
Expand Down Expand Up @@ -935,6 +1050,108 @@ func TestUpsertBot(t *testing.T) {
},
},
},
{
name: "new with expiry",
user: botCreator.GetName(),
req: &machineidv1pb.UpsertBotRequest{
Bot: &machineidv1pb.Bot{
Metadata: &headerv1.Metadata{
Name: "new-with-expiry",
Labels: map[string]string{
"my-label": "my-value",
"my-other-label": "my-other-value",
},
Expires: timestamppb.New(expiry),
},
Spec: &machineidv1pb.BotSpec{
Roles: []string{testRole.GetName()},
Traits: []*machineidv1pb.Trait{
{
Name: constants.TraitLogins,
Values: []string{"root"},
},
},
},
},
},

assertError: require.NoError,
want: &machineidv1pb.Bot{
Kind: types.KindBot,
Version: types.V1,
Metadata: &headerv1.Metadata{
Name: "new-with-expiry",
Labels: map[string]string{
"my-label": "my-value",
"my-other-label": "my-other-value",
},
Expires: timestamppb.New(expiry),
},
Spec: &machineidv1pb.BotSpec{
Roles: []string{testRole.GetName()},
Traits: []*machineidv1pb.Trait{
{
Name: constants.TraitLogins,
Values: []string{"root"},
},
},
},
Status: &machineidv1pb.BotStatus{
UserName: "bot-new-with-expiry",
RoleName: "bot-new-with-expiry",
},
},
wantUser: &types.UserV2{
Kind: types.KindUser,
Version: types.V2,
Metadata: types.Metadata{
Name: "bot-new-with-expiry",
Namespace: defaults.Namespace,
Labels: map[string]string{
types.BotLabel: "new-with-expiry",
types.BotGenerationLabel: "0",
"my-label": "my-value",
"my-other-label": "my-other-value",
},
Expires: &expiry,
},
Spec: types.UserSpecV2{
Roles: []string{"bot-new-with-expiry"},
Traits: map[string][]string{
constants.TraitLogins: {"root"},
},
CreatedBy: types.CreatedBy{
User: types.UserRef{Name: botCreator.GetName()},
},
},
},
wantRole: &types.RoleV6{
Kind: types.KindRole,
Version: types.V7,
Metadata: types.Metadata{
Name: "bot-new-with-expiry",
Namespace: defaults.Namespace,
Labels: map[string]string{
types.BotLabel: "new-with-expiry",
},
Description: "Automatically generated role for bot new-with-expiry",
Expires: &expiry,
},
Spec: types.RoleSpecV6{
Options: types.RoleOptions{
MaxSessionTTL: types.Duration(12 * time.Hour),
},
Allow: types.RoleConditions{
Impersonate: &types.ImpersonateConditions{
Roles: []string{testRole.GetName()},
},
Rules: []types.Rule{
types.NewRule(types.KindCertAuthority, []string{types.VerbReadNoSecrets}),
},
},
},
},
},
{
name: "already exists",
user: botCreator.GetName(),
Expand Down

0 comments on commit 815d8a0

Please sign in to comment.