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

Stable UNIX users: storage and auth API #51102

Open
wants to merge 13 commits into
base: master
Choose a base branch
from

Conversation

espadolini
Copy link
Contributor

@espadolini espadolini commented Jan 16, 2025

This RFD implements the storage and storage manipulation in the auth server, and defines and implements the auth API to store and fetch stable UIDs for automatically provisioned UNIX users.

Related RFD: #50414
Part of #50292

@espadolini espadolini added the no-changelog Indicates that a PR does not require a changelog entry label Jan 16, 2025
@espadolini espadolini requested a review from eriktate January 16, 2025 01:35
@espadolini espadolini force-pushed the espadolini/stable-unix-user-api branch from 0fb9265 to d0943ca Compare January 16, 2025 01:46

This comment was marked as off-topic.

@espadolini espadolini force-pushed the espadolini/stable-unix-user-api branch from 39de628 to b7c7571 Compare January 21, 2025 11:41
Copy link
Contributor

@rosstimothy rosstimothy left a comment

Choose a reason for hiding this comment

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

Is there a way to specify the stable unix user config via teleport.yaml?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is still not quite right? If following the existing pattern the package should be lib/auth/stableunixusersv1.

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 don't think I'd ever version the implementation like that unless I really had to - I'm almost sure there's going to be overlap in functionality, etc. - and all you'd need is a newtype to implement both services with "the same" object, which would strongly benefit from being in the same go package.

Comment on lines +877 to +878
// Validate implements [AuthPreference].
func (c *AuthPreferenceV2) Validate() error {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should CASD defer to Validate?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If you mean that CheckAndSetDefaults should call Validate, no - Validate is specifically intended to not get called after UnmarshalAuthPreference.

If you mean the opposite I'm not sure - I like the guarantee that Validate is read-only, and the intended usecase is to validate after getting the AuthPreferenceV2 out of storage which would've already done a CASD.

lib/auth/stableunixusers/stableunixusers.go Outdated Show resolved Hide resolved
Comment on lines +210 to +214
ap, err := s.clusterConfiguration.GetAuthPreference(ctx)
if err != nil {
return 0, trace.Wrap(err)
}
authPref = ap
Copy link
Contributor

Choose a reason for hiding this comment

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

Why doesn't the readonly cache sufice on subsequent attempts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The readonly cache has a TTL, and we don't have a way to distinguish the specific condition that failed in Backend.AtomicWrite (dynamodb doesn't return that, iirc) so on our subsequent attempts we should fetch the freshest possible AuthPreference with the freshest possible revision.

func (s *server) createNewStableUNIXUser(ctx context.Context, username string, authPref readonly.AuthPreference) (int32, error) {
cfg := authPref.GetStableUNIXUserConfig()
if cfg == nil || !cfg.Enabled {
return 0, trace.LimitExceeded("stable UNIX users are not enabled")
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this a limit exceeded because when the feature is disabled the UID range is 0? Would a concrete error be a better marker for this condition?

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 changed the logic to rely on returned boolean flags instead in 02cac25

@@ -48,6 +48,8 @@ type ClusterConfigurationService struct {
backend.Backend
}

var _ services.ClusterConfigurationInternal = (*ClusterConfigurationService)(nil)
Copy link
Contributor

Choose a reason for hiding this comment

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

The appearance of the blank identifier in this construct indicates that the declaration exists only for the type checking, not to create a variable. Don't do this for every type that satisfies an interface, though. By convention, such declarations are only used when there are no static conversions already present in the code, which is a rare event.

https://go.dev/doc/effective_go#blank_implements

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There's no such conversion in the file or package, AFAICT, and I personally really like that the assertion shows which interface I intended to implement and if it's by pointer or value.

Backend backend.Backend
}

var _ services.StableUNIXUsersInternal = (*StableUNIXUsersService)(nil)
Copy link
Contributor

Choose a reason for hiding this comment

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

The appearance of the blank identifier in this construct indicates that the declaration exists only for the type checking, not to create a variable. Don't do this for every type that satisfies an interface, though. By convention, such declarations are only used when there are no static conversions already present in the code, which is a rare event.

https://go.dev/doc/effective_go#blank_implements

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There's no such conversion in the file or package, AFAICT.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
no-changelog Indicates that a PR does not require a changelog entry size/lg
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants