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 dynamic windows desktops #46738

Closed
wants to merge 13 commits into from
Closed

Conversation

probakowski
Copy link
Contributor

This change adds new resource: dynamic windows desktop.
The difference between this new type and current windows desktop is that it doesn't have host UUID - it is meant to be picked up by windows desktop services by matching labels similar to dynamic registration of apps and databases.

Copy link
Collaborator

@zmb3 zmb3 left a comment

Choose a reason for hiding this comment

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

This is a pretty big PR. It might make sense to do the proto changes in a dedicated. PR.

Comment on lines +1598 to +1599
// desktop host. If HostID is not specified, all Windows desktops with
// specified Name will be deleted
Copy link
Collaborator

Choose a reason for hiding this comment

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

Remove the info about HostID, as there is no HostID here.

}

// DynamicWindowsDesktopFilter are filters to apply when searching for windows desktops.
message DynamicWindowsDesktopFilter {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we need to duplicate this? Can we use the existing filter message and just ignore the HostID?

Comment on lines +177 to +199
if d.Spec.Addr == "" {
return trace.BadParameter("DynamicWindowsDesktopV1.Spec missing Addr field")
}

// We use SNI to identify the desktop to route a connection to,
// and '.' will add an extra subdomain, preventing Teleport from
// correctly establishing TLS connections.
if name := d.GetName(); strings.Contains(name, ".") {
return trace.BadParameter("invalid name %q: desktop names cannot contain periods", name)
}

d.setStaticFields()
if err := d.ResourceHeader.CheckAndSetDefaults(); err != nil {
return trace.Wrap(err)
}

if d.Spec.ScreenSize != nil {
if d.Spec.ScreenSize.Width > MaxRDPScreenWidth || d.Spec.ScreenSize.Height > MaxRDPScreenHeight {
return trace.BadParameter("invalid screen size %dx%d (maximum %dx%d)",
d.Spec.ScreenSize.Width, d.Spec.ScreenSize.Height, MaxRDPScreenWidth, MaxRDPScreenHeight)
}
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

This is a lot of copy-paste from the original desktop resource. I'm worried about the two diverging. Any suggestions for sharing more code?

}

// GetFieldVals returns list of select field values.
func (s DynamicWindowsDesktops) GetFieldVals(field string) ([]string, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

What is this used for?

@@ -262,6 +262,9 @@ const (
// ComponentWindowsDesktop is a Windows desktop access server.
ComponentWindowsDesktop = "windows_desktop"

// ComponentDynamicWindowsDesktop is a Windows desktop access server.
Copy link
Collaborator

Choose a reason for hiding this comment

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

This comment is exactly the same as the one on ComponentWindowsDesktop. I would expect the godoc to help me understand what is different about a dynamic desktop.

Then again, if this is just used for logging, then you can probably use the original component field.

@@ -2385,7 +2385,8 @@ type WindowsDesktopService struct {
// HostLabels optionally applies labels to Windows hosts for RBAC.
// A host can match multiple rules and will get a union of all
// the matched labels.
HostLabels []WindowsHostLabelRule `yaml:"host_labels,omitempty"`
HostLabels []WindowsHostLabelRule `yaml:"host_labels,omitempty"`
ResourceMatchers []ResourceMatcher `yaml:"resources,omitempty"`
Copy link
Collaborator

Choose a reason for hiding this comment

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

Add godoc for the new field?

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.

Agree with Zac, there is a lot going on here. It might be best to split this into PRs that operate on a single layer: proto, backend storage, rpc implementation, tctl, resource watcher, windows desktop server changes.

Comment on lines +3351 to +3352
// GetDynamicWindowsDesktops returns all registered Windows desktop hosts matching the supplied filter.
rpc GetDynamicWindowsDesktops(types.DynamicWindowsDesktopFilter) returns (GetDynamicWindowsDesktopsResponse);
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we use a List RPC instead of a GetAll RPC that returns an array of desktops? Is this separate RPC needed? Should these resource be Listed via ListResources or ListUnifiedResources?

Comment on lines +3354 to +3358
rpc CreateDynamicWindowsDesktop(types.DynamicWindowsDesktopV1) returns (google.protobuf.Empty);
// UpdateDynamicWindowsDesktop updates an existing Windows desktop host.
rpc UpdateDynamicWindowsDesktop(types.DynamicWindowsDesktopV1) returns (google.protobuf.Empty);
// UpsertDynamicWindowsDesktop updates a Windows desktop host, creating it if it doesn't exist.
rpc UpsertDynamicWindowsDesktop(types.DynamicWindowsDesktopV1) returns (google.protobuf.Empty);
Copy link
Contributor

Choose a reason for hiding this comment

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

Create, Update, and Upsert should all return the resource instead of empty.

Suggested change
rpc CreateDynamicWindowsDesktop(types.DynamicWindowsDesktopV1) returns (google.protobuf.Empty);
// UpdateDynamicWindowsDesktop updates an existing Windows desktop host.
rpc UpdateDynamicWindowsDesktop(types.DynamicWindowsDesktopV1) returns (google.protobuf.Empty);
// UpsertDynamicWindowsDesktop updates a Windows desktop host, creating it if it doesn't exist.
rpc UpsertDynamicWindowsDesktop(types.DynamicWindowsDesktopV1) returns (google.protobuf.Empty);
rpc CreateDynamicWindowsDesktop(types.DynamicWindowsDesktopV1) returns (types.DynamicWindowsDesktopV1);
// UpdateDynamicWindowsDesktop updates an existing Windows desktop host.
rpc UpdateDynamicWindowsDesktop(types.DynamicWindowsDesktopV1) returns (types.DynamicWindowsDesktopV1);
// UpsertDynamicWindowsDesktop updates a Windows desktop host, creating it if it doesn't exist.
rpc UpsertDynamicWindowsDesktop(types.DynamicWindowsDesktopV1) returns (types.DynamicWindowsDesktopV1);

Comment on lines +3363 to +3364
// DeleteAllDynamicWindowsDesktops removes all registered Windows desktop hosts.
rpc DeleteAllDynamicWindowsDesktops(google.protobuf.Empty) returns (google.protobuf.Empty);
Copy link
Contributor

Choose a reason for hiding this comment

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

This should not need to be exposed via an RPC. The main purpose of DeleteAll is to satisfy the cache interface, and that can all be done with the storage service.

Suggested change
// DeleteAllDynamicWindowsDesktops removes all registered Windows desktop hosts.
rpc DeleteAllDynamicWindowsDesktops(google.protobuf.Empty) returns (google.protobuf.Empty);

Comment on lines +5413 to +5414
// DynamicWindowsDesktopV1 represents a dynamic windows host for desktop access.
message DynamicWindowsDesktopV1 {
Copy link
Contributor

Choose a reason for hiding this comment

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

The name DynamicWindowsDesktopV1 made me initially think that something about this desktop was dynamic, not that it is a windows desktop host that is discovered dynamically. All of the comments refer to this as a dynamic windows desktop host - perhaps we should consider renaming this to WindowsDesktopHostV1?

Comment on lines +6592 to +6624
func (a *ServerWithRoles) CreateDynamicWindowsDesktop(ctx context.Context, s types.DynamicWindowsDesktop) error {
if err := a.action(apidefaults.Namespace, types.KindDynamicWindowsDesktop, types.VerbCreate); err != nil {
return trace.Wrap(err)
}
return a.authServer.CreateDynamicWindowsDesktop(ctx, s)
}

// UpdateDynamicWindowsDesktop updates an existing dynamic windows desktop host.
func (a *ServerWithRoles) UpdateDynamicWindowsDesktop(ctx context.Context, s types.DynamicWindowsDesktop) error {
if err := a.action(apidefaults.Namespace, types.KindDynamicWindowsDesktop, types.VerbUpdate); err != nil {
return trace.Wrap(err)
}

existing, err := a.authServer.GetDynamicWindowsDesktops(ctx,
types.DynamicWindowsDesktopFilter{Name: s.GetName()})
if err != nil {
return trace.Wrap(err)
}
if len(existing) == 0 {
return trace.NotFound("no dynamic windows desktops with Name %s", s.GetName())
}

if err := a.checkAccessToDynamicWindowsDesktop(existing[0]); err != nil {
return trace.Wrap(err)
}
if err := a.checkAccessToDynamicWindowsDesktop(s); err != nil {
return trace.Wrap(err)
}
return a.authServer.UpdateDynamicWindowsDesktop(ctx, s)
}

// UpsertDynamicWindowsDesktop updates a dynamic windows desktop resource, creating it if it doesn't exist.
func (a *ServerWithRoles) UpsertDynamicWindowsDesktop(ctx context.Context, s types.DynamicWindowsDesktop) error {
Copy link
Contributor

Choose a reason for hiding this comment

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

Are these be called by humans or by a desktop_service? Should there be an admin actions check?

Comment on lines +350 to +351
select {
case desktops := <-watcher.DynamicWindowsDesktopsC:
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to handle when the watcher is closed?

Suggested change
select {
case desktops := <-watcher.DynamicWindowsDesktopsC:
select {
case <-watcher.Done():
case desktops := <-watcher.DynamicWindowsDesktopsC:

Comment on lines +376 to +378
labels := make(map[string]string)
maps.Copy(labels, dynamicDesktop.GetAllLabels())
labels[types.OriginLabel] = types.OriginDynamic
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
labels := make(map[string]string)
maps.Copy(labels, dynamicDesktop.GetAllLabels())
labels[types.OriginLabel] = types.OriginDynamic
desktopLabels := dynamicDesktop.GetAllLabels()
labels := make(map[string]string, len(desktopLabels)+1)
maps.Copy(labels, desktopLabels)
labels[types.OriginLabel] = types.OriginDynamic


fmt.Printf("labels %q", wd.GetAllLabels())

if err := client.UpsertDynamicWindowsDesktop(ctx, wd); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

This should call CreateDynamicWindowsDesktop unless --force has been provided.

@@ -153,6 +153,7 @@ func (rc *ResourceCommand) Initialize(app *kingpin.Application, config *servicec
types.KindOktaImportRule: rc.createOktaImportRule,
types.KindIntegration: rc.createIntegration,
types.KindWindowsDesktop: rc.createWindowsDesktop,
types.KindDynamicWindowsDesktop: rc.createDynamicWindowsDesktop,
Copy link
Contributor

Choose a reason for hiding this comment

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

Please add an equivalent update handler that calls UpdatDynamicWindowsDesktop when tctl edit is used for this resource.

@@ -882,6 +883,22 @@ func (rc *ResourceCommand) createWindowsDesktop(ctx context.Context, client *aut
return nil
}

func (rc *ResourceCommand) createDynamicWindowsDesktop(ctx context.Context, client *authclient.Client, raw services.UnknownResource) error {
Copy link
Contributor

Choose a reason for hiding this comment

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

Please also update the TestCreateResources and TestEditResources tests for this new resource

@probakowski
Copy link
Contributor Author

It was splitted to smaller PRs, closing this one

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants