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

[v15] Set the access request max duration to 14 days, add okta-requester role. #38224

Merged
merged 10 commits into from
Feb 21, 2024
10 changes: 10 additions & 0 deletions constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,16 @@ const (
// during the setup of Connect My Computer. The prefix is followed by the name of the cluster
// user. See teleterm.connectmycomputer.RoleSetup.
ConnectMyComputerRoleNamePrefix = "connect-my-computer-"

// SystemOktaRequesterRoleName is a name of a system role that allows
zmb3 marked this conversation as resolved.
Show resolved Hide resolved
// for requesting access to Okta resources. This differs from the requester role
// in that it allows for requesting longer lived access.
SystemOktaRequesterRoleName = "okta-requester"

// SystemOktaAccessRoleName is the name of the system role that allows
// access to Okta resources. This will be used by the Okta requester role to
// search for Okta resources.
SystemOktaAccessRoleName = "okta-access"
)

var PresetRoles = []string{PresetEditorRoleName, PresetAccessRoleName, PresetAuditorRoleName}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ spec:
max_duration: 4d
```

The value of `max_duration` can never exceed seven days.
The value of `max_duration` can never exceed fourteen days.

### How long Access Requests are valid

Expand Down
2 changes: 2 additions & 0 deletions lib/auth/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,8 @@ func GetPresetRoles() []types.Role {
services.NewPresetDeviceAdminRole(),
services.NewPresetDeviceEnrollRole(),
services.NewPresetRequireTrustedDeviceRole(),
services.NewSystemOktaAccessRole(),
services.NewSystemOktaRequesterRole(),
}

// Certain `New$FooRole()` functions will return a nil role if the
Expand Down
2 changes: 2 additions & 0 deletions lib/auth/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,8 @@ func TestPresets(t *testing.T) {

enterpriseSystemRoleNames := []string{
teleport.SystemAutomaticAccessApprovalRoleName,
teleport.SystemOktaAccessRoleName,
teleport.SystemOktaRequesterRoleName,
}

enterpriseUsers := []types.User{
Expand Down
9 changes: 4 additions & 5 deletions lib/services/access_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const day = 24 * time.Hour

// maxAccessDuration is the maximum duration that an access request can be
// granted for.
const maxAccessDuration = 7 * day
const maxAccessDuration = 14 * day

// ValidateAccessRequest validates the AccessRequest and sets default values
func ValidateAccessRequest(ar types.AccessRequest) error {
Expand Down Expand Up @@ -369,7 +369,7 @@ func ValidateAccessPredicates(role types.Role) error {

if maxDuration := role.GetAccessRequestConditions(types.Allow).MaxDuration; maxDuration.Duration() != 0 &&
maxDuration.Duration() > maxAccessDuration {
return trace.BadParameter("max access duration must be less or equal 7 days")
return trace.BadParameter("max access duration must be less than or equal to %v", maxAccessDuration)
}

return nil
Expand Down Expand Up @@ -1230,17 +1230,16 @@ func (m *RequestValidator) calculateMaxAccessDuration(req types.AccessRequest) (

maxDuration := maxDurationTime.Sub(req.GetCreationTime())

// For dry run requests, the max_duration is set to 7 days.
// For dry run requests, use the maximum possible duration.
// This prevents the time drift that can occur as the value is set on the client side.
// TODO(jakule): Replace with MaxAccessDuration that is a duration (5h, 4d etc), and not a point in time.
if req.GetDryRun() {
maxDuration = maxAccessDuration
} else if maxDuration < 0 {
return 0, trace.BadParameter("invalid maxDuration: must be greater than creation time")
}

if maxDuration > maxAccessDuration {
return 0, trace.BadParameter("max_duration must be less or equal 7 days")
return 0, trace.BadParameter("max_duration must be less than or equal to %v", maxAccessDuration)
}

minAdjDuration := maxDuration
Expand Down
134 changes: 121 additions & 13 deletions lib/services/presets.go
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,70 @@ func NewPresetRequireTrustedDeviceRole() types.Role {
}
}

// SystemOktaAccessRoleName is the name of the system role that allows
mdwn marked this conversation as resolved.
Show resolved Hide resolved
// access to Okta resources. This will be used by the Okta requester role to
// search for Okta resources.
func NewSystemOktaAccessRole() types.Role {
if modules.GetModules().BuildType() != modules.BuildEnterprise {
return nil
}

role := &types.RoleV6{
Kind: types.KindRole,
Version: types.V7,
Metadata: types.Metadata{
Name: teleport.SystemOktaAccessRoleName,
Namespace: apidefaults.Namespace,
Description: "Request Okta resources",
Labels: map[string]string{
types.TeleportInternalResourceType: types.SystemResource,
},
},
Spec: types.RoleSpecV6{
Allow: types.RoleConditions{
AppLabels: types.Labels{
types.OriginLabel: []string{types.OriginOkta},
},
GroupLabels: types.Labels{
types.OriginLabel: []string{types.OriginOkta},
},
Rules: []types.Rule{
types.NewRule(types.KindUserGroup, RO()),
},
},
},
}
return role
}

// SystemOktaRequesterRoleName is a name of a system role that allows
// for requesting access to Okta resources. This differs from the requester role
// in that it allows for requesting longer lived access.
func NewSystemOktaRequesterRole() types.Role {
if modules.GetModules().BuildType() != modules.BuildEnterprise {
return nil
}

role := &types.RoleV6{
Kind: types.KindRole,
Version: types.V7,
Metadata: types.Metadata{
Name: teleport.SystemOktaRequesterRoleName,
Namespace: apidefaults.Namespace,
Description: "Request Okta resources",
Labels: map[string]string{
types.TeleportInternalResourceType: types.SystemResource,
},
},
Spec: types.RoleSpecV6{
Allow: types.RoleConditions{
Request: defaultAllowAccessRequestConditions(true)[teleport.SystemOktaRequesterRoleName],
},
},
}
return role
}

// bootstrapRoleMetadataLabels are metadata labels that will be applied to each role.
// These are intended to add labels for older roles that didn't previously have them.
func bootstrapRoleMetadataLabels() map[string]map[string]string {
Expand Down Expand Up @@ -520,14 +584,25 @@ func defaultAllowRules() map[string][]types.Rule {
// defaultAllowLabels has the Allow labels that should be set as default when they were not explicitly defined.
// This is used to update existing builtin preset roles with new permissions during cluster upgrades.
// The following Labels are supported:
// - AppLabels
// - DatabaseServiceLabels (db_service_labels)
func defaultAllowLabels() map[string]types.RoleConditions {
return map[string]types.RoleConditions{
// - GroupLabels
func defaultAllowLabels(enterprise bool) map[string]types.RoleConditions {
conditions := map[string]types.RoleConditions{
teleport.PresetAccessRoleName: {
DatabaseServiceLabels: types.Labels{types.Wildcard: []string{types.Wildcard}},
DatabaseRoles: []string{teleport.TraitInternalDBRolesVariable},
},
}

if enterprise {
conditions[teleport.SystemOktaAccessRoleName] = types.RoleConditions{
AppLabels: types.Labels{types.OriginLabel: []string{types.OriginOkta}},
GroupLabels: types.Labels{types.OriginLabel: []string{types.OriginOkta}},
}
}

return conditions
}

// defaultAllowAccessRequestConditions has the access request conditions that should be set as default when they were
Expand All @@ -541,6 +616,12 @@ func defaultAllowAccessRequestConditions(enterprise bool) map[string]*types.Acce
teleport.PresetGroupAccessRoleName,
},
},
teleport.SystemOktaRequesterRoleName: {
SearchAsRoles: []string{
teleport.SystemOktaAccessRoleName,
},
MaxDuration: types.NewDuration(maxAccessDuration),
},
}
}

Expand Down Expand Up @@ -597,7 +678,8 @@ func AddRoleDefaults(role types.Role) (types.Role, error) {
// labels because we set the role metadata labels for roles that have been well established (access,
// editor, auditor) that may not already have this label set, but we don't set it for newer roles
// (group-access, reviewer, requester) that may have customer definitions.
if role.GetMetadata().Labels[types.TeleportInternalResourceType] != types.PresetResource {
resourceType := role.GetMetadata().Labels[types.TeleportInternalResourceType]
if resourceType != types.PresetResource && resourceType != types.SystemResource {
return nil, trace.AlreadyExists("not modifying user created role")
}

Expand All @@ -619,25 +701,37 @@ func AddRoleDefaults(role types.Role) (types.Role, error) {
}
}

enterprise := modules.GetModules().BuildType() == modules.BuildEnterprise

// Labels
defaultLabels, ok := defaultAllowLabels()[role.GetName()]
defaultLabels, ok := defaultAllowLabels(enterprise)[role.GetName()]
if ok {
if unset, err := labelMatchersUnset(role, types.KindDatabaseService); err != nil {
return nil, trace.Wrap(err)
} else if unset && len(defaultLabels.DatabaseServiceLabels) > 0 {
role.SetLabelMatchers(types.Allow, types.KindDatabaseService, types.LabelMatchers{
Labels: defaultLabels.DatabaseServiceLabels,
})
changed = true
for _, kind := range []string{
types.KindApp,
types.KindDatabaseService,
types.KindUserGroup,
} {
var labels types.Labels
switch kind {
case types.KindApp:
labels = defaultLabels.AppLabels
case types.KindDatabaseService:
labels = defaultLabels.DatabaseServiceLabels
case types.KindUserGroup:
labels = defaultLabels.GroupLabels
}
labelsUpdated, err := updateAllowLabels(role, kind, labels)
if err != nil {
return nil, trace.Wrap(err)
}
changed = changed || labelsUpdated
}
if len(defaultLabels.DatabaseRoles) > 0 && len(role.GetDatabaseRoles(types.Allow)) == 0 {
role.SetDatabaseRoles(types.Allow, defaultLabels.DatabaseRoles)
changed = true
}
}

enterprise := modules.GetModules().BuildType() == modules.BuildEnterprise

if role.GetAccessRequestConditions(types.Allow).IsEmpty() {
arc := defaultAllowAccessRequestConditions(enterprise)[role.GetName()]
if arc != nil {
Expand Down Expand Up @@ -685,3 +779,17 @@ func resourceBelongsToRules(rules []types.Rule, resources []string) bool {

return false
}

func updateAllowLabels(role types.Role, kind string, defaultLabels types.Labels) (bool, error) {
var changed bool
if unset, err := labelMatchersUnset(role, kind); err != nil {
return false, trace.Wrap(err)
} else if unset && len(defaultLabels) > 0 {
mdwn marked this conversation as resolved.
Show resolved Hide resolved
role.SetLabelMatchers(types.Allow, kind, types.LabelMatchers{
Labels: defaultLabels,
})
changed = true
}

return changed, nil
}