From 3c79bae775552c5a3d9b6d9e1fba190719de9f07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Cie=C5=9Blak?= Date: Wed, 23 Oct 2024 16:02:45 +0200 Subject: [PATCH 01/12] Turn off HTTPS of Vite's dev server in Connect (#47849) --- .../teleterm/electron.vite.config.mts | 26 ------------------- .../src/mainProcess/windowsManager.ts | 2 +- 2 files changed, 1 insertion(+), 27 deletions(-) diff --git a/web/packages/teleterm/electron.vite.config.mts b/web/packages/teleterm/electron.vite.config.mts index 42a7d81eb44d6..926fc20b307cb 100644 --- a/web/packages/teleterm/electron.vite.config.mts +++ b/web/packages/teleterm/electron.vite.config.mts @@ -17,7 +17,6 @@ */ import path from 'node:path'; -import { existsSync, readFileSync } from 'node:fs'; import { defineConfig, externalizeDepsPlugin, UserConfig } from 'electron-vite'; @@ -120,31 +119,6 @@ const config = defineConfig(env => { }, }; - if (env.mode === 'development') { - if (process.env.VITE_HTTPS_KEY && process.env.VITE_HTTPS_CERT) { - config.renderer.server.https = { - key: readFileSync(process.env.VITE_HTTPS_KEY), - cert: readFileSync(process.env.VITE_HTTPS_CERT), - }; - } else { - const certsDirectory = path.resolve(rootDirectory, 'web/certs'); - - if (!existsSync(certsDirectory)) { - throw new Error( - 'Could not find SSL certificates. Please follow web/README.md to generate certificates.' - ); - } - - const keyPath = path.resolve(certsDirectory, 'server.key'); - const certPath = path.resolve(certsDirectory, 'server.crt'); - - config.renderer.server.https = { - key: readFileSync(keyPath), - cert: readFileSync(certPath), - }; - } - } - return config; }); diff --git a/web/packages/teleterm/src/mainProcess/windowsManager.ts b/web/packages/teleterm/src/mainProcess/windowsManager.ts index daa0935122c4c..513e31573400c 100644 --- a/web/packages/teleterm/src/mainProcess/windowsManager.ts +++ b/web/packages/teleterm/src/mainProcess/windowsManager.ts @@ -362,7 +362,7 @@ export class WindowsManager { * */ function getWindowUrl(isDev: boolean): string { if (isDev) { - return 'https://localhost:8080/'; + return 'http://localhost:8080/'; } // The returned URL is percent-encoded. From 7a44000c54b8a1972d1591d427db0c47a4986d1b Mon Sep 17 00:00:00 2001 From: Marco Dinis Date: Wed, 23 Oct 2024 15:19:47 +0100 Subject: [PATCH 02/12] Test plan: remove single EC2 enrollment (#47852) Single EC2 enrollment was using EICE which is no longer available. --- .github/ISSUE_TEMPLATE/webtestplan.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/webtestplan.md b/.github/ISSUE_TEMPLATE/webtestplan.md index 2f905c9f3ec68..bcdac7b26219d 100644 --- a/.github/ISSUE_TEMPLATE/webtestplan.md +++ b/.github/ISSUE_TEMPLATE/webtestplan.md @@ -175,11 +175,14 @@ All actions should require re-authn with a webauthn device. Use Discover Wizard to enroll new resources and access them: -- [ ] SSH Server (teleport service, singular EC2, SSM agent) +- [ ] SSH Server using Teleport Service - [ ] Self-Hosted PostgreSQL and Mongo -- [ ] AWS RDS (singular RDS, auto discover with ECS) - [ ] Kubernetes -- [ ] AWS EKS cluster +- [ ] Using an AWS OIDC Integration + - [ ] EC2 Auto Enrollment (SSM) + - [ ] RDS flow: single database + - [ ] RDS flow: Auto Enrollment (by VPC) + - [ ] EKS Clusters - [ ] Non-guided cards link out to correct docs #### Access Lists From 0c3b807db6a058b2a32dd407f46cb49aafff2147 Mon Sep 17 00:00:00 2001 From: Hugo Shaka Date: Wed, 23 Oct 2024 13:38:17 -0400 Subject: [PATCH 03/12] Add autoupdate agent type validations (#47831) * Add autoupdate agent validations * Add AutoUpdateAgentRollout constants * Fix autoupdate API licenses Teleport's `api/` and `integrations/` should be Apache-licensed. Only the main teleport process should be licenses under AGPLv3. * address feedback --- api/types/autoupdate/config.go | 74 +++++++++----- api/types/autoupdate/config_test.go | 148 ++++++++++++++++++++++++--- api/types/autoupdate/constants.go | 46 +++++++++ api/types/autoupdate/rollout.go | 76 ++++++++++++++ api/types/autoupdate/rollout_test.go | 145 ++++++++++++++++++++++++++ api/types/autoupdate/utils.go | 68 ++++++++++++ api/types/autoupdate/version.go | 50 +++++---- api/types/autoupdate/version_test.go | 119 +++++++++++++++++---- api/types/constants.go | 6 ++ 9 files changed, 651 insertions(+), 81 deletions(-) create mode 100644 api/types/autoupdate/constants.go create mode 100644 api/types/autoupdate/rollout.go create mode 100644 api/types/autoupdate/rollout_test.go create mode 100644 api/types/autoupdate/utils.go diff --git a/api/types/autoupdate/config.go b/api/types/autoupdate/config.go index d61c35eccf0c2..32ae056195b64 100644 --- a/api/types/autoupdate/config.go +++ b/api/types/autoupdate/config.go @@ -1,24 +1,24 @@ /* - * Teleport - * Copyright (C) 2024 Gravitational, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ +Copyright 2024 Gravitational, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package autoupdate import ( + "time" + "github.com/gravitational/trace" "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1" @@ -26,13 +26,6 @@ import ( "github.com/gravitational/teleport/api/types" ) -const ( - // ToolsUpdateModeEnabled enables client tools automatic updates. - ToolsUpdateModeEnabled = "enabled" - // ToolsUpdateModeDisabled disables client tools automatic updates. - ToolsUpdateModeDisabled = "disabled" -) - // NewAutoUpdateConfig creates a new auto update configuration resource. func NewAutoUpdateConfig(spec *autoupdate.AutoUpdateConfigSpec) (*autoupdate.AutoUpdateConfig, error) { config := &autoupdate.AutoUpdateConfig{ @@ -66,10 +59,41 @@ func ValidateAutoUpdateConfig(c *autoupdate.AutoUpdateConfig) error { return trace.BadParameter("Spec is nil") } if c.Spec.Tools != nil { - if c.Spec.Tools.Mode != ToolsUpdateModeDisabled && c.Spec.Tools.Mode != ToolsUpdateModeEnabled { - return trace.BadParameter("ToolsMode is not valid") + if err := checkToolsMode(c.Spec.Tools.Mode); err != nil { + return trace.Wrap(err, "validating spec.tools.mode") + } + } + if c.Spec.Agents != nil { + if err := checkAgentsMode(c.Spec.Agents.Mode); err != nil { + return trace.Wrap(err, "validating spec.agents.mode") + } + if err := checkAgentsStrategy(c.Spec.Agents.Strategy); err != nil { + return trace.Wrap(err, "validating spec.agents.strategy") } + + windowDuration := c.Spec.Agents.MaintenanceWindowDuration.AsDuration() + if c.Spec.Agents.Strategy == AgentsStrategyHaltOnError && windowDuration != 0 { + return trace.BadParameter("spec.agents.maintenance_window_duration must be zero when the strategy is %q", c.Spec.Agents.Strategy) + } + if c.Spec.Agents.Strategy == AgentsStrategyTimeBased && windowDuration < 10*time.Minute { + return trace.BadParameter("spec.agents.maintenance_window_duration must be greater than 10 minutes when the strategy is %q", c.Spec.Agents.Strategy) + } + + if err := checkAgentSchedules(c.Spec.Agents.Schedules); err != nil { + return trace.Wrap(err, "validating spec.agents.schedules") + } + } return nil } + +func checkAgentSchedules(schedules *autoupdate.AgentAutoUpdateSchedules) error { + // TODO: change this logic when we implement group support. + // Currently we reject any non-nil schedule + // When we'll implement schedule support, we'll treat an empty schedule as the default schedule. + if schedules == nil { + return nil + } + return trace.NotImplemented("agent schedules are not implemented yet") +} diff --git a/api/types/autoupdate/config_test.go b/api/types/autoupdate/config_test.go index 443d6f246fa56..f6b6a87aa6bd8 100644 --- a/api/types/autoupdate/config_test.go +++ b/api/types/autoupdate/config_test.go @@ -1,29 +1,29 @@ /* - * Teleport - * Copyright (C) 2024 Gravitational, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ +Copyright 2024 Gravitational, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package autoupdate import ( "testing" + "time" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/require" "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/durationpb" "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1" headerv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1" @@ -99,7 +99,121 @@ func TestNewAutoUpdateConfig(t *testing.T) { }, }, assertErr: func(t *testing.T, err error, a ...any) { - require.ErrorContains(t, err, "ToolsMode is not valid") + require.ErrorContains(t, err, "unsupported tools mode: \"invalid-mode\"") + }, + }, + { + name: "invalid agents mode", + spec: &autoupdate.AutoUpdateConfigSpec{ + Agents: &autoupdate.AutoUpdateConfigSpecAgents{ + Mode: "invalid-mode", + Strategy: AgentsStrategyHaltOnError, + }, + }, + assertErr: func(t *testing.T, err error, a ...any) { + require.ErrorContains(t, err, "unsupported agents mode: \"invalid-mode\"") + }, + }, + { + name: "invalid agents strategy", + spec: &autoupdate.AutoUpdateConfigSpec{ + Agents: &autoupdate.AutoUpdateConfigSpecAgents{ + Mode: AgentsUpdateModeEnabled, + Strategy: "invalid-strategy", + }, + }, + assertErr: func(t *testing.T, err error, a ...any) { + require.ErrorContains(t, err, "unsupported agents strategy: \"invalid-strategy\"") + }, + }, + { + name: "invalid agents non-nil maintenance window with halt-on-error", + spec: &autoupdate.AutoUpdateConfigSpec{ + Agents: &autoupdate.AutoUpdateConfigSpecAgents{ + Mode: AgentsUpdateModeEnabled, + Strategy: AgentsStrategyHaltOnError, + MaintenanceWindowDuration: durationpb.New(time.Hour), + }, + }, + assertErr: func(t *testing.T, err error, a ...any) { + require.ErrorContains(t, err, "maintenance_window_duration must be zero") + }, + }, + { + name: "invalid agents nil maintenance window with time-based strategy", + spec: &autoupdate.AutoUpdateConfigSpec{ + Agents: &autoupdate.AutoUpdateConfigSpecAgents{ + Mode: AgentsUpdateModeEnabled, + Strategy: AgentsStrategyTimeBased, + }, + }, + assertErr: func(t *testing.T, err error, a ...any) { + require.ErrorContains(t, err, "maintenance_window_duration must be greater than 10 minutes") + }, + }, + { + name: "invalid agents short maintenance window", + spec: &autoupdate.AutoUpdateConfigSpec{ + Agents: &autoupdate.AutoUpdateConfigSpecAgents{ + Mode: AgentsUpdateModeEnabled, + Strategy: AgentsStrategyTimeBased, + MaintenanceWindowDuration: durationpb.New(time.Minute), + }, + }, + assertErr: func(t *testing.T, err error, a ...any) { + require.ErrorContains(t, err, "maintenance_window_duration must be greater than 10 minutes") + }, + }, + { + name: "success agents autoupdate halt-on-failure", + spec: &autoupdate.AutoUpdateConfigSpec{ + Agents: &autoupdate.AutoUpdateConfigSpecAgents{ + Mode: AgentsUpdateModeEnabled, + Strategy: AgentsStrategyHaltOnError, + }, + }, + assertErr: func(t *testing.T, err error, a ...any) { + require.NoError(t, err) + }, + want: &autoupdate.AutoUpdateConfig{ + Kind: types.KindAutoUpdateConfig, + Version: types.V1, + Metadata: &headerv1.Metadata{ + Name: types.MetaNameAutoUpdateConfig, + }, + Spec: &autoupdate.AutoUpdateConfigSpec{ + Agents: &autoupdate.AutoUpdateConfigSpecAgents{ + Mode: AgentsUpdateModeEnabled, + Strategy: AgentsStrategyHaltOnError, + }, + }, + }, + }, + { + name: "success agents autoupdate time-based", + spec: &autoupdate.AutoUpdateConfigSpec{ + Agents: &autoupdate.AutoUpdateConfigSpecAgents{ + Mode: AgentsUpdateModeEnabled, + Strategy: AgentsStrategyTimeBased, + MaintenanceWindowDuration: durationpb.New(time.Hour), + }, + }, + assertErr: func(t *testing.T, err error, a ...any) { + require.NoError(t, err) + }, + want: &autoupdate.AutoUpdateConfig{ + Kind: types.KindAutoUpdateConfig, + Version: types.V1, + Metadata: &headerv1.Metadata{ + Name: types.MetaNameAutoUpdateConfig, + }, + Spec: &autoupdate.AutoUpdateConfigSpec{ + Agents: &autoupdate.AutoUpdateConfigSpecAgents{ + Mode: AgentsUpdateModeEnabled, + Strategy: AgentsStrategyTimeBased, + MaintenanceWindowDuration: durationpb.New(time.Hour), + }, + }, }, }, } diff --git a/api/types/autoupdate/constants.go b/api/types/autoupdate/constants.go new file mode 100644 index 0000000000000..deed5168fb21f --- /dev/null +++ b/api/types/autoupdate/constants.go @@ -0,0 +1,46 @@ +/* +Copyright 2024 Gravitational, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package autoupdate + +const ( + // ToolsUpdateModeEnabled enables client tools automatic updates. + ToolsUpdateModeEnabled = "enabled" + // ToolsUpdateModeDisabled disables client tools automatic updates. + ToolsUpdateModeDisabled = "disabled" + + // AgentsUpdateModeEnabled enabled agent automatic updates. + AgentsUpdateModeEnabled = "enabled" + // AgentsUpdateModeDisabled disables agent automatic updates. + AgentsUpdateModeDisabled = "disabled" + // AgentsUpdateModeSuspended temporarily suspends agent automatic updates. + AgentsUpdateModeSuspended = "suspended" + + // AgentsScheduleRegular is the regular agent update schedule. + AgentsScheduleRegular = "regular" + // AgentsScheduleImmediate is the immediate agent update schedule. + // Every agent must update immediately if it's not already running the target version. + // This can be used to recover agents in case of major incident or actively exploited vulnerability. + AgentsScheduleImmediate = "immediate" + + // AgentsStrategyHaltOnError is the agent update strategy that updates groups sequentially + // according to their order in the schedule. The previous groups must succeed. + AgentsStrategyHaltOnError = "halt-on-error" + // AgentsStrategyTimeBased is the agent update strategy that updates groups solely based on their + // maintenance window. There is no dependency between groups. Agents won't be instructed to update + // if the window is over. + AgentsStrategyTimeBased = "time-based" +) diff --git a/api/types/autoupdate/rollout.go b/api/types/autoupdate/rollout.go new file mode 100644 index 0000000000000..814d71313d3ed --- /dev/null +++ b/api/types/autoupdate/rollout.go @@ -0,0 +1,76 @@ +/* +Copyright 2024 Gravitational, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package autoupdate + +import ( + "github.com/gravitational/trace" + + "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1" + headerv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1" + "github.com/gravitational/teleport/api/types" +) + +// NewAutoUpdateAgentRollout creates a new auto update version resource. +func NewAutoUpdateAgentRollout(spec *autoupdate.AutoUpdateAgentRolloutSpec) (*autoupdate.AutoUpdateAgentRollout, error) { + version := &autoupdate.AutoUpdateAgentRollout{ + Kind: types.KindAutoUpdateAgentRollout, + Version: types.V1, + Metadata: &headerv1.Metadata{ + Name: types.MetaNameAutoUpdateAgentRollout, + }, + Spec: spec, + } + if err := ValidateAutoUpdateAgentRollout(version); err != nil { + return nil, trace.Wrap(err) + } + + return version, nil +} + +// ValidateAutoUpdateAgentRollout checks that required parameters are set +// for the specified AutoUpdateAgentRollout. +func ValidateAutoUpdateAgentRollout(v *autoupdate.AutoUpdateAgentRollout) error { + if v == nil { + return trace.BadParameter("AutoUpdateAgentRollout is nil") + } + if v.Metadata == nil { + return trace.BadParameter("Metadata is nil") + } + if v.Metadata.Name != types.MetaNameAutoUpdateAgentRollout { + return trace.BadParameter("Name is not valid") + } + if v.Spec == nil { + return trace.BadParameter("Spec is nil") + } + if err := checkVersion(v.Spec.StartVersion); err != nil { + return trace.Wrap(err, "validating spec.start_version") + } + if err := checkVersion(v.Spec.TargetVersion); err != nil { + return trace.Wrap(err, "validating spec.target_version") + } + if err := checkAgentsMode(v.Spec.AutoupdateMode); err != nil { + return trace.Wrap(err, "validating spec.autoupdate_mode") + } + if err := checkScheduleName(v.Spec.Schedule); err != nil { + return trace.Wrap(err, "validating spec.schedule") + } + if err := checkAgentsStrategy(v.Spec.Strategy); err != nil { + return trace.Wrap(err, "validating spec.strategy") + } + + return nil +} diff --git a/api/types/autoupdate/rollout_test.go b/api/types/autoupdate/rollout_test.go new file mode 100644 index 0000000000000..cce4dc8495d83 --- /dev/null +++ b/api/types/autoupdate/rollout_test.go @@ -0,0 +1,145 @@ +/* +Copyright 2024 Gravitational, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package autoupdate + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/testing/protocmp" + + "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1" + headerv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1" + "github.com/gravitational/teleport/api/types" +) + +// TestNewAutoUpdateConfig verifies validation for AutoUpdateConfig resource. +func TestNewAutoUpdateAgentRollout(t *testing.T) { + tests := []struct { + name string + spec *autoupdate.AutoUpdateAgentRolloutSpec + want *autoupdate.AutoUpdateAgentRollout + assertErr func(*testing.T, error, ...any) + }{ + { + name: "success valid rollout", + spec: &autoupdate.AutoUpdateAgentRolloutSpec{ + StartVersion: "1.2.3", + TargetVersion: "2.3.4-dev", + Schedule: AgentsScheduleRegular, + AutoupdateMode: AgentsUpdateModeEnabled, + Strategy: AgentsStrategyHaltOnError, + }, + assertErr: func(t *testing.T, err error, a ...any) { + require.NoError(t, err) + }, + want: &autoupdate.AutoUpdateAgentRollout{ + Kind: types.KindAutoUpdateAgentRollout, + Version: types.V1, + Metadata: &headerv1.Metadata{ + Name: types.MetaNameAutoUpdateAgentRollout, + }, + Spec: &autoupdate.AutoUpdateAgentRolloutSpec{ + StartVersion: "1.2.3", + TargetVersion: "2.3.4-dev", + Schedule: AgentsScheduleRegular, + AutoupdateMode: AgentsUpdateModeEnabled, + Strategy: AgentsStrategyHaltOnError, + }, + }, + }, + { + name: "missing spec", + spec: nil, + assertErr: func(t *testing.T, err error, a ...any) { + require.ErrorContains(t, err, "Spec is nil") + }, + }, + { + name: "missing start version", + spec: &autoupdate.AutoUpdateAgentRolloutSpec{ + TargetVersion: "2.3.4-dev", + Schedule: AgentsScheduleRegular, + AutoupdateMode: AgentsUpdateModeEnabled, + Strategy: AgentsStrategyHaltOnError, + }, + assertErr: func(t *testing.T, err error, a ...any) { + require.ErrorContains(t, err, "start_version\n\tversion is unset") + }, + }, + { + name: "invalid target version", + spec: &autoupdate.AutoUpdateAgentRolloutSpec{ + StartVersion: "1.2.3", + TargetVersion: "2-3-4", + Schedule: AgentsScheduleRegular, + AutoupdateMode: AgentsUpdateModeEnabled, + Strategy: AgentsStrategyHaltOnError, + }, + assertErr: func(t *testing.T, err error, a ...any) { + require.ErrorContains(t, err, "target_version\n\tversion \"2-3-4\" is not a valid semantic version") + }, + }, + { + name: "invalid autoupdate mode", + spec: &autoupdate.AutoUpdateAgentRolloutSpec{ + StartVersion: "1.2.3", + TargetVersion: "2.3.4-dev", + Schedule: AgentsScheduleRegular, + AutoupdateMode: "invalid-mode", + Strategy: AgentsStrategyHaltOnError, + }, + assertErr: func(t *testing.T, err error, a ...any) { + require.ErrorContains(t, err, "unsupported agents mode: \"invalid-mode\"") + }, + }, + { + name: "invalid schedule name", + spec: &autoupdate.AutoUpdateAgentRolloutSpec{ + StartVersion: "1.2.3", + TargetVersion: "2.3.4-dev", + Schedule: "invalid-schedule", + AutoupdateMode: AgentsUpdateModeEnabled, + Strategy: AgentsStrategyHaltOnError, + }, + assertErr: func(t *testing.T, err error, a ...any) { + require.ErrorContains(t, err, "unsupported schedule type: \"invalid-schedule\"") + }, + }, + { + name: "invalid strategy", + spec: &autoupdate.AutoUpdateAgentRolloutSpec{ + StartVersion: "1.2.3", + TargetVersion: "2.3.4-dev", + Schedule: AgentsScheduleRegular, + AutoupdateMode: AgentsUpdateModeEnabled, + Strategy: "invalid-strategy", + }, + assertErr: func(t *testing.T, err error, a ...any) { + require.ErrorContains(t, err, "unsupported agents strategy: \"invalid-strategy\"") + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := NewAutoUpdateAgentRollout(tt.spec) + tt.assertErr(t, err) + require.Empty(t, cmp.Diff(got, tt.want, protocmp.Transform())) + }) + } +} diff --git a/api/types/autoupdate/utils.go b/api/types/autoupdate/utils.go new file mode 100644 index 0000000000000..4772ff8a94411 --- /dev/null +++ b/api/types/autoupdate/utils.go @@ -0,0 +1,68 @@ +/* +Copyright 2024 Gravitational, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package autoupdate + +import ( + "github.com/coreos/go-semver/semver" + "github.com/gravitational/trace" +) + +func checkVersion(version string) error { + if version == "" { + return trace.BadParameter("version is unset") + } + if _, err := semver.NewVersion(version); err != nil { + return trace.BadParameter("version %q is not a valid semantic version", version) + } + return nil +} + +func checkAgentsMode(mode string) error { + switch mode { + case AgentsUpdateModeEnabled, AgentsUpdateModeDisabled, AgentsUpdateModeSuspended: + return nil + default: + return trace.BadParameter("unsupported agents mode: %q", mode) + } +} + +func checkToolsMode(mode string) error { + switch mode { + case ToolsUpdateModeEnabled, ToolsUpdateModeDisabled: + return nil + default: + return trace.BadParameter("unsupported tools mode: %q", mode) + } +} + +func checkScheduleName(schedule string) error { + switch schedule { + case AgentsScheduleRegular, AgentsScheduleImmediate: + return nil + default: + return trace.BadParameter("unsupported schedule type: %q", schedule) + } +} + +func checkAgentsStrategy(strategy string) error { + switch strategy { + case AgentsStrategyHaltOnError, AgentsStrategyTimeBased: + return nil + default: + return trace.BadParameter("unsupported agents strategy: %q", strategy) + } +} diff --git a/api/types/autoupdate/version.go b/api/types/autoupdate/version.go index ad2d12f265949..4bfe14c53fc1f 100644 --- a/api/types/autoupdate/version.go +++ b/api/types/autoupdate/version.go @@ -1,25 +1,22 @@ /* - * Teleport - * Copyright (C) 2024 Gravitational, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ +Copyright 2024 Gravitational, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package autoupdate import ( - "github.com/coreos/go-semver/semver" "github.com/gravitational/trace" "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1" @@ -61,11 +58,22 @@ func ValidateAutoUpdateVersion(v *autoupdate.AutoUpdateVersion) error { } if v.Spec.Tools != nil { - if v.Spec.Tools.TargetVersion == "" { - return trace.BadParameter("TargetVersion is unset") + if err := checkVersion(v.Spec.Tools.TargetVersion); err != nil { + return trace.Wrap(err, "validating spec.tools.target_version") + } + } + if v.Spec.Agents != nil { + if err := checkVersion(v.Spec.Agents.StartVersion); err != nil { + return trace.Wrap(err, "validating spec.agents.start_version") + } + if err := checkVersion(v.Spec.Agents.TargetVersion); err != nil { + return trace.Wrap(err, "validating spec.agents.target_version") + } + if err := checkAgentsMode(v.Spec.Agents.Mode); err != nil { + return trace.Wrap(err, "validating spec.agents.mode") } - if _, err := semver.NewVersion(v.Spec.Tools.TargetVersion); err != nil { - return trace.BadParameter("TargetVersion is not a valid semantic version") + if err := checkScheduleName(v.Spec.Agents.Schedule); err != nil { + return trace.Wrap(err, "validating spec.agents.schedule") } } diff --git a/api/types/autoupdate/version_test.go b/api/types/autoupdate/version_test.go index 70790a204b219..a59a4f6fe6c22 100644 --- a/api/types/autoupdate/version_test.go +++ b/api/types/autoupdate/version_test.go @@ -1,20 +1,18 @@ /* - * Teleport - * Copyright (C) 2024 Gravitational, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ +Copyright 2024 Gravitational, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package autoupdate @@ -69,7 +67,7 @@ func TestNewAutoUpdateVersion(t *testing.T) { }, }, assertErr: func(t *testing.T, err error, a ...any) { - require.ErrorContains(t, err, "TargetVersion is unset") + require.ErrorContains(t, err, "target_version\n\tversion is unset") }, }, { @@ -80,7 +78,7 @@ func TestNewAutoUpdateVersion(t *testing.T) { }, }, assertErr: func(t *testing.T, err error, a ...any) { - require.ErrorContains(t, err, "TargetVersion is not a valid semantic version") + require.ErrorContains(t, err, "target_version\n\tversion \"17-0-0\" is not a valid semantic version") }, }, { @@ -90,6 +88,91 @@ func TestNewAutoUpdateVersion(t *testing.T) { require.ErrorContains(t, err, "Spec is nil") }, }, + { + name: "success agents autoupdate version", + spec: &autoupdate.AutoUpdateVersionSpec{ + Agents: &autoupdate.AutoUpdateVersionSpecAgents{ + StartVersion: "1.2.3-dev.1", + TargetVersion: "1.2.3-dev.2", + Schedule: AgentsScheduleRegular, + Mode: AgentsUpdateModeEnabled, + }, + }, + assertErr: func(t *testing.T, err error, a ...any) { + require.NoError(t, err) + }, + want: &autoupdate.AutoUpdateVersion{ + Kind: types.KindAutoUpdateVersion, + Version: types.V1, + Metadata: &headerv1.Metadata{ + Name: types.MetaNameAutoUpdateVersion, + }, + Spec: &autoupdate.AutoUpdateVersionSpec{ + Agents: &autoupdate.AutoUpdateVersionSpecAgents{ + StartVersion: "1.2.3-dev.1", + TargetVersion: "1.2.3-dev.2", + Schedule: AgentsScheduleRegular, + Mode: AgentsUpdateModeEnabled, + }, + }, + }, + }, + { + name: "invalid empty agents start version", + spec: &autoupdate.AutoUpdateVersionSpec{ + Agents: &autoupdate.AutoUpdateVersionSpecAgents{ + StartVersion: "", + TargetVersion: "1.2.3", + Mode: AgentsUpdateModeEnabled, + Schedule: AgentsScheduleRegular, + }, + }, + assertErr: func(t *testing.T, err error, a ...any) { + require.ErrorContains(t, err, "start_version\n\tversion is unset") + }, + }, + { + name: "invalid empty agents target version", + spec: &autoupdate.AutoUpdateVersionSpec{ + Agents: &autoupdate.AutoUpdateVersionSpecAgents{ + StartVersion: "1.2.3-dev", + TargetVersion: "", + Mode: AgentsUpdateModeEnabled, + Schedule: AgentsScheduleRegular, + }, + }, + assertErr: func(t *testing.T, err error, a ...any) { + require.ErrorContains(t, err, "target_version\n\tversion is unset") + }, + }, + { + name: "invalid semantic agents start version", + spec: &autoupdate.AutoUpdateVersionSpec{ + Agents: &autoupdate.AutoUpdateVersionSpecAgents{ + StartVersion: "17-0-0", + TargetVersion: "1.2.3", + Mode: AgentsUpdateModeEnabled, + Schedule: AgentsScheduleRegular, + }, + }, + assertErr: func(t *testing.T, err error, a ...any) { + require.ErrorContains(t, err, "start_version\n\tversion \"17-0-0\" is not a valid semantic version") + }, + }, + { + name: "invalid semantic agents target version", + spec: &autoupdate.AutoUpdateVersionSpec{ + Agents: &autoupdate.AutoUpdateVersionSpecAgents{ + StartVersion: "1.2.3", + TargetVersion: "17-0-0", + Mode: AgentsUpdateModeEnabled, + Schedule: AgentsScheduleRegular, + }, + }, + assertErr: func(t *testing.T, err error, a ...any) { + require.ErrorContains(t, err, "target_version\n\tversion \"17-0-0\" is not a valid semantic version") + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/api/types/constants.go b/api/types/constants.go index 87c0335586bf6..b50b91c480aad 100644 --- a/api/types/constants.go +++ b/api/types/constants.go @@ -331,12 +331,18 @@ const ( // KindAutoUpdateVersion is the resource with autoupdate versions. KindAutoUpdateVersion = "autoupdate_version" + // KindAutoUpdateAgentRollout is the resource that controls and tracks agent rollouts. + KindAutoUpdateAgentRollout = "autoupdate_agent_rollout" + // MetaNameAutoUpdateConfig is the name of a configuration resource for autoupdate config. MetaNameAutoUpdateConfig = "autoupdate-config" // MetaNameAutoUpdateVersion is the name of a resource for autoupdate version. MetaNameAutoUpdateVersion = "autoupdate-version" + // MetaNameAutoUpdateAgentRollout is the name of the autoupdate agent rollout resource. + MetaNameAutoUpdateAgentRollout = "autoupdate-agent-rollout" + // KindClusterAuditConfig is the resource that holds cluster audit configuration. KindClusterAuditConfig = "cluster_audit_config" From 5de066049c50302e1634291c5b62a859850c97de Mon Sep 17 00:00:00 2001 From: Gavin Frazar Date: Wed, 23 Oct 2024 11:15:56 -0700 Subject: [PATCH 04/12] use AWS ECS service per VPC for DB service (#47842) Originally, single db enrollment deployed a DB service for an entire AWS region. This behavior was recently changed to deploy the DB service for a specific VPC in a region. So now the AWS ECS service must be named after the VPC it is deployed to for single db enrollment. --- .../AutoDeploy/AutoDeploy.test.tsx | 2 +- .../DeployService/AutoDeploy/AutoDeploy.tsx | 36 ++++++++----------- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/web/packages/teleport/src/Discover/Database/DeployService/AutoDeploy/AutoDeploy.test.tsx b/web/packages/teleport/src/Discover/Database/DeployService/AutoDeploy/AutoDeploy.test.tsx index 93aaf318946f4..97fc6d979408b 100644 --- a/web/packages/teleport/src/Discover/Database/DeployService/AutoDeploy/AutoDeploy.test.tsx +++ b/web/packages/teleport/src/Discover/Database/DeployService/AutoDeploy/AutoDeploy.test.tsx @@ -240,7 +240,7 @@ function getMockedContexts() { }; jest - .spyOn(integrationService, 'deployAwsOidcService') + .spyOn(integrationService, 'deployDatabaseServices') .mockResolvedValue('dashboard-url'); jest.spyOn(teleCtx.databaseService, 'fetchDatabases').mockResolvedValue({ diff --git a/web/packages/teleport/src/Discover/Database/DeployService/AutoDeploy/AutoDeploy.tsx b/web/packages/teleport/src/Discover/Database/DeployService/AutoDeploy/AutoDeploy.tsx index 48c013f4e7359..51f2137f4f4d1 100644 --- a/web/packages/teleport/src/Discover/Database/DeployService/AutoDeploy/AutoDeploy.tsx +++ b/web/packages/teleport/src/Discover/Database/DeployService/AutoDeploy/AutoDeploy.tsx @@ -126,22 +126,24 @@ export function AutoDeploy({ toggleDeployMethod }: DeployServiceProp) { agentMeta.awsIntegration.spec.roleArn ); + const deployment = { + region: dbMeta.awsRegion, + accountId: awsAccountId, + taskRoleArn, + deployments: [ + { + vpcId: dbMeta.awsVpcId, + subnetIds: selectedSubnetIds, + securityGroups: selectedSecurityGroups, + }, + ], + }; + if (wantAutoDiscover) { setAttempt({ status: 'processing' }); integrationService - .deployDatabaseServices(integrationName, { - region: dbMeta.awsRegion, - accountId: awsAccountId, - taskRoleArn, - deployments: [ - { - vpcId: dbMeta.awsVpcId, - subnetIds: selectedSubnetIds, - securityGroups: selectedSecurityGroups, - }, - ], - }) + .deployDatabaseServices(integrationName, deployment) .then(url => { setAttempt({ status: 'success' }); setSvcDeployedAwsUrl(url); @@ -155,15 +157,7 @@ export function AutoDeploy({ toggleDeployMethod }: DeployServiceProp) { } else { setAttempt({ status: 'processing' }); integrationService - .deployAwsOidcService(integrationName, { - deploymentMode: 'database-service', - region: dbMeta.awsRegion, - subnetIds: selectedSubnetIds, - taskRoleArn, - securityGroups: selectedSecurityGroups, - vpcId: dbMeta.awsVpcId, - accountId: awsAccountId, - }) + .deployDatabaseServices(integrationName, deployment) // The user is still technically in the "processing" // state, because after this call succeeds, we will // start pinging for the newly registered db From d391ccc792c35fdf41ff24b425aeb67d4a8db956 Mon Sep 17 00:00:00 2001 From: Przemko Robakowski Date: Wed, 23 Oct 2024 20:19:44 +0200 Subject: [PATCH 05/12] tctl support for DynamicWindowsDesktop (#46988) * Add DynamicWindowsDesktop to proto * Add resource matchers to Windows desktop service config * Implement API and backend for DynamicWindowsDesktop * tctl support for DynamicWindowsDesktop * Fix imports * Remove unused methods * move rpc to separate server * rework api and grpc more towards 153-style * e * remove dynamic windows from paginated resource * add tests * add tests * update client * Update api/proto/teleport/legacy/types/types.proto Co-authored-by: rosstimothy <39066650+rosstimothy@users.noreply.github.com> * lint * gci * cleanup * cleanup * use generic service * cleanup * cleanup * cleanup * cleanup * cleanup * gci * add admin action checks * move service * add service test * add tests * gci * review comments * review comments * review comments * review comments * review comments * review comments --------- Co-authored-by: rosstimothy <39066650+rosstimothy@users.noreply.github.com> --- tool/tctl/common/collection.go | 29 ++++++++ tool/tctl/common/edit_command_test.go | 36 ++++++++++ tool/tctl/common/resource_command.go | 81 +++++++++++++++++++++++ tool/tctl/common/resource_command_test.go | 33 +++++++++ 4 files changed, 179 insertions(+) diff --git a/tool/tctl/common/collection.go b/tool/tctl/common/collection.go index 5719344fe4bd3..1afe9f0c7bca9 100644 --- a/tool/tctl/common/collection.go +++ b/tool/tctl/common/collection.go @@ -866,6 +866,35 @@ func (c *windowsDesktopCollection) writeJSON(w io.Writer) error { return utils.WriteJSONArray(w, c.desktops) } +type dynamicWindowsDesktopCollection struct { + desktops []types.DynamicWindowsDesktop +} + +func (c *dynamicWindowsDesktopCollection) resources() (r []types.Resource) { + r = make([]types.Resource, 0, len(c.desktops)) + for _, resource := range c.desktops { + r = append(r, resource) + } + return r +} + +func (c *dynamicWindowsDesktopCollection) writeText(w io.Writer, verbose bool) error { + var rows [][]string + for _, d := range c.desktops { + labels := common.FormatLabels(d.GetAllLabels(), verbose) + rows = append(rows, []string{d.GetName(), d.GetAddr(), d.GetDomain(), labels}) + } + headers := []string{"Name", "Address", "AD Domain", "Labels"} + var t asciitable.Table + if verbose { + t = asciitable.MakeTable(headers, rows...) + } else { + t = asciitable.MakeTableWithTruncatedColumn(headers, rows, "Labels") + } + _, err := t.AsBuffer().WriteTo(w) + return trace.Wrap(err) +} + type tokenCollection struct { tokens []types.ProvisionToken } diff --git a/tool/tctl/common/edit_command_test.go b/tool/tctl/common/edit_command_test.go index 9ce3a89d7bf85..c0ffbf342a485 100644 --- a/tool/tctl/common/edit_command_test.go +++ b/tool/tctl/common/edit_command_test.go @@ -91,6 +91,10 @@ func TestEditResources(t *testing.T) { kind: types.KindAutoUpdateVersion, edit: testEditAutoUpdateVersion, }, + { + kind: types.KindDynamicWindowsDesktop, + edit: testEditDynamicWindowsDesktop, + }, } for _, test := range tests { @@ -635,3 +639,35 @@ func testEditAutoUpdateVersion(t *testing.T, clt *authclient.Client) { "tools_autoupdate should have been modified by edit") assert.Equal(t, expected.GetSpec().GetTools().GetTargetVersion(), actual.GetSpec().GetTools().GetTargetVersion()) } + +func testEditDynamicWindowsDesktop(t *testing.T, clt *authclient.Client) { + ctx := context.Background() + + expected, err := types.NewDynamicWindowsDesktopV1("test", nil, types.DynamicWindowsDesktopSpecV1{ + Addr: "test", + }) + require.NoError(t, err) + created, err := clt.DynamicDesktopClient().CreateDynamicWindowsDesktop(ctx, expected) + require.NoError(t, err) + + editor := func(name string) error { + f, err := os.Create(name) + if err != nil { + return trace.Wrap(err, "opening file to edit") + } + + expected.SetRevision(created.GetRevision()) + expected.Spec.Addr = "test2" + + collection := &dynamicWindowsDesktopCollection{desktops: []types.DynamicWindowsDesktop{expected}} + return trace.NewAggregate(writeYAML(collection, f), f.Close()) + } + + _, err = runEditCommand(t, clt, []string{"edit", "dynamic_windows_desktop/test"}, withEditor(editor)) + require.NoError(t, err) + + actual, err := clt.DynamicDesktopClient().GetDynamicWindowsDesktop(ctx, expected.GetName()) + require.NoError(t, err) + expected.SetRevision(actual.GetRevision()) + require.Empty(t, cmp.Diff(expected, actual, protocmp.Transform())) +} diff --git a/tool/tctl/common/resource_command.go b/tool/tctl/common/resource_command.go index 7975a80a7298f..dd558e01f7319 100644 --- a/tool/tctl/common/resource_command.go +++ b/tool/tctl/common/resource_command.go @@ -156,6 +156,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, types.KindAccessList: rc.createAccessList, types.KindDiscoveryConfig: rc.createDiscoveryConfig, types.KindAuditQuery: rc.createAuditQuery, @@ -193,6 +194,7 @@ func (rc *ResourceCommand) Initialize(app *kingpin.Application, config *servicec types.KindUserTask: rc.updateUserTask, types.KindAutoUpdateConfig: rc.updateAutoUpdateConfig, types.KindAutoUpdateVersion: rc.updateAutoUpdateVersion, + types.KindDynamicWindowsDesktop: rc.updateDynamicWindowsDesktop, } rc.config = config @@ -891,6 +893,45 @@ 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 { + wd, err := services.UnmarshalDynamicWindowsDesktop(raw.Raw) + if err != nil { + return trace.Wrap(err) + } + dynamicDesktopClient := client.DynamicDesktopClient() + if _, err := dynamicDesktopClient.CreateDynamicWindowsDesktop(ctx, wd); err != nil { + if trace.IsAlreadyExists(err) { + if !rc.force { + return trace.AlreadyExists("application %q already exists", wd.GetName()) + } + if _, err := dynamicDesktopClient.UpdateDynamicWindowsDesktop(ctx, wd); err != nil { + return trace.Wrap(err) + } + fmt.Printf("dynamic windows desktop %q has been updated\n", wd.GetName()) + return nil + } + return trace.Wrap(err) + } + + fmt.Printf("dynamic windows desktop %q has been updated\n", wd.GetName()) + return nil +} + +func (rc *ResourceCommand) updateDynamicWindowsDesktop(ctx context.Context, client *authclient.Client, raw services.UnknownResource) error { + wd, err := services.UnmarshalDynamicWindowsDesktop(raw.Raw) + if err != nil { + return trace.Wrap(err) + } + + dynamicDesktopClient := client.DynamicDesktopClient() + if _, err := dynamicDesktopClient.UpdateDynamicWindowsDesktop(ctx, wd); err != nil { + return trace.Wrap(err) + } + + fmt.Printf("dynamic windows desktop %q has been updated\n", wd.GetName()) + return nil +} + func (rc *ResourceCommand) createAppServer(ctx context.Context, client *authclient.Client, raw services.UnknownResource) error { appServer, err := services.UnmarshalAppServer(raw.Raw) if err != nil { @@ -1688,6 +1729,11 @@ func (rc *ResourceCommand) Delete(ctx context.Context, client *authclient.Client return trace.Wrap(err) } fmt.Printf("windows desktop service %q has been deleted\n", rc.ref.Name) + case types.KindDynamicWindowsDesktop: + if err = client.DynamicDesktopClient().DeleteDynamicWindowsDesktop(ctx, rc.ref.Name); err != nil { + return trace.Wrap(err) + } + fmt.Printf("dynamic windows desktop %q has been deleted\n", rc.ref.Name) case types.KindWindowsDesktop: desktops, err := client.GetWindowsDesktops(ctx, types.WindowsDesktopFilter{Name: rc.ref.Name}) @@ -2461,6 +2507,41 @@ func (rc *ResourceCommand) getCollection(ctx context.Context, client *authclient return nil, trace.NotFound("Windows desktop %q not found", rc.ref.Name) } return &windowsDesktopCollection{desktops: out}, nil + case types.KindDynamicWindowsDesktop: + dynamicDesktopClient := client.DynamicDesktopClient() + if rc.ref.Name != "" { + desktop, err := dynamicDesktopClient.GetDynamicWindowsDesktop(ctx, rc.ref.Name) + if err != nil { + return nil, trace.Wrap(err) + } + return &dynamicWindowsDesktopCollection{ + desktops: []types.DynamicWindowsDesktop{desktop}, + }, nil + } + + pageToken := "" + desktops := make([]types.DynamicWindowsDesktop, 0, 100) + for { + d, next, err := dynamicDesktopClient.ListDynamicWindowsDesktop(ctx, 100, pageToken) + if err != nil { + return nil, trace.Wrap(err) + } + if rc.ref.Name == "" { + desktops = append(desktops, d...) + } else { + for _, desktop := range desktops { + if desktop.GetName() == rc.ref.Name { + desktops = append(desktops, desktop) + } + } + } + pageToken = next + if next == "" { + break + } + } + + return &dynamicWindowsDesktopCollection{desktops}, nil case types.KindToken: if rc.ref.Name == "" { tokens, err := client.GetTokens(ctx) diff --git a/tool/tctl/common/resource_command_test.go b/tool/tctl/common/resource_command_test.go index dca834a2e6e7f..cd7b8f7fc5de4 100644 --- a/tool/tctl/common/resource_command_test.go +++ b/tool/tctl/common/resource_command_test.go @@ -1419,6 +1419,10 @@ func TestCreateResources(t *testing.T) { kind: types.KindAutoUpdateVersion, create: testCreateAutoUpdateVersion, }, + { + kind: types.KindDynamicWindowsDesktop, + create: testCreateDynamicWindowsDesktop, + }, } for _, test := range tests { @@ -2359,6 +2363,35 @@ version: v1 )) } +func testCreateDynamicWindowsDesktop(t *testing.T, clt *authclient.Client) { + const resourceYAML = `kind: dynamic_windows_desktop +metadata: + name: test + revision: 3a43b44a-201e-4d7f-aef1-ae2f6d9811ed +spec: + addr: test +version: v1 +` + + // Create the resource. + resourceYAMLPath := filepath.Join(t.TempDir(), "resource.yaml") + require.NoError(t, os.WriteFile(resourceYAMLPath, []byte(resourceYAML), 0644)) + _, err := runResourceCommand(t, clt, []string{"create", resourceYAMLPath}) + require.NoError(t, err) + + // Get the resource + buf, err := runResourceCommand(t, clt, []string{"get", types.KindDynamicWindowsDesktop, "--format=json"}) + require.NoError(t, err) + resources := mustDecodeJSON[[]types.DynamicWindowsDesktopV1](t, buf) + require.Len(t, resources, 1) + + var expected types.DynamicWindowsDesktopV1 + require.NoError(t, yaml.Unmarshal([]byte(resourceYAML), &expected)) + expected.SetRevision(resources[0].GetRevision()) + + require.Empty(t, cmp.Diff([]types.DynamicWindowsDesktopV1{expected}, resources, protocmp.Transform())) +} + func TestPluginResourceWrapper(t *testing.T) { tests := []struct { name string From 7a9f3de56928b9a9842f17cc77f883678de0f03e Mon Sep 17 00:00:00 2001 From: Sakshyam Shah Date: Wed, 23 Oct 2024 14:28:23 -0400 Subject: [PATCH 06/12] aws identity center plugin config dependency (#47844) * update PluginAWSICSettings proto message: - add access_list_default_owners - add saml_idp_service_provider_name * identity center plugin deps * add OriginAWSIdentityCenter commentary --- api/types/common/constants.go | 6 ++++++ api/types/plugin.go | 3 +++ api/types/saml_idp_service_provider.go | 2 +- api/types/saml_idp_service_provider_test.go | 9 +++++++++ api/types/samlsp/samlsp.go | 3 +++ 5 files changed, 22 insertions(+), 1 deletion(-) diff --git a/api/types/common/constants.go b/api/types/common/constants.go index 0c9b541e19bbc..431393e706fe8 100644 --- a/api/types/common/constants.go +++ b/api/types/common/constants.go @@ -69,6 +69,11 @@ const ( // OriginEntraID indicates that the resource was imported // from the Entra ID directory. OriginEntraID = "entra-id" + + // OriginAWSIdentityCenter indicates that the resource was + // imported from the AWS Identity Center or created from + // the AWS Identity Center plugin. + OriginAWSIdentityCenter = "aws-identity-center" ) // OriginValues lists all possible origin values. @@ -82,4 +87,5 @@ var OriginValues = []string{ OriginSCIM, OriginDiscoveryKubernetes, OriginEntraID, + OriginAWSIdentityCenter, } diff --git a/api/types/plugin.go b/api/types/plugin.go index 2645c7b39f0d7..c598856960bfa 100644 --- a/api/types/plugin.go +++ b/api/types/plugin.go @@ -42,6 +42,7 @@ var AllPluginTypes = []PluginType{ PluginTypeEntraID, PluginTypeSCIM, PluginTypeDatadog, + PluginTypeAWSIdentityCenter, } const ( @@ -75,6 +76,8 @@ const ( PluginTypeSCIM = "scim" // PluginTypeDatadog indicates the Datadog Incident Management plugin PluginTypeDatadog = "datadog" + // PluginTypeAWSIdentityCenter indicates AWS Identity Center plugin + PluginTypeAWSIdentityCenter = "aws-identity-center" ) // PluginSubkind represents the type of the plugin, e.g., access request, MDM etc. diff --git a/api/types/saml_idp_service_provider.go b/api/types/saml_idp_service_provider.go index 0a274e0fec54c..f04a7434585a2 100644 --- a/api/types/saml_idp_service_provider.go +++ b/api/types/saml_idp_service_provider.go @@ -387,7 +387,7 @@ func (am *SAMLAttributeMapping) CheckAndSetDefaults() error { // preset can be either empty or one of the supported type. func (s *SAMLIdPServiceProviderV1) checkAndSetPresetDefaults(preset string) bool { switch preset { - case "", samlsp.Unspecified: + case "", samlsp.Unspecified, samlsp.AWSIdentityCenter: return true case samlsp.GCPWorkforce: if s.GetRelayState() == "" { diff --git a/api/types/saml_idp_service_provider_test.go b/api/types/saml_idp_service_provider_test.go index 1b00aeebd78da..dd70d78b9e971 100644 --- a/api/types/saml_idp_service_provider_test.go +++ b/api/types/saml_idp_service_provider_test.go @@ -196,6 +196,15 @@ func TestNewSAMLIdPServiceProvider(t *testing.T) { errAssertion: require.NoError, preset: samlsp.Unspecified, }, + { + name: "aws-identity-center preset", + entityDescriptor: "", + entityID: "IAMShowcase", + acsURL: acsURL, + expectedEntityID: "IAMShowcase", + errAssertion: require.NoError, + preset: samlsp.AWSIdentityCenter, + }, { name: "unsupported preset value", entityDescriptor: "", diff --git a/api/types/samlsp/samlsp.go b/api/types/samlsp/samlsp.go index fa552cc75c00f..1a94cc81c49d2 100644 --- a/api/types/samlsp/samlsp.go +++ b/api/types/samlsp/samlsp.go @@ -23,6 +23,9 @@ const ( // Unspecified preset type is used in the Web UI to denote a generic SAML service // provider preset. Unspecified = "unspecified" + // AWSIdentityCenter is a SAML service provider preset name for AWS + // Identity Center. + AWSIdentityCenter = "aws-identity-center" ) const ( From 67de9ce55527a24536d1dba0acf6487ad0650789 Mon Sep 17 00:00:00 2001 From: Paul Gottschling Date: Wed, 23 Oct 2024 15:10:15 -0400 Subject: [PATCH 07/12] Edit the docs release plan (#47814) Add an item to ensure that redirects only exist in the default version of the docs. This is the version that we use to build the site map, and links from the product and other Teleport sites only refer to unversioned `/docs/` paths. --- .github/ISSUE_TEMPLATE/test-plan-docs.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/test-plan-docs.md b/.github/ISSUE_TEMPLATE/test-plan-docs.md index 604ca810153db..e31d0b73cfd51 100644 --- a/.github/ISSUE_TEMPLATE/test-plan-docs.md +++ b/.github/ISSUE_TEMPLATE/test-plan-docs.md @@ -36,11 +36,15 @@ to determine the rollout date. git submodule add https://github.com/gravitational/teleport content/.x ``` -## Is the docs site up to date with the new release? +## Is the docs site content up to date with the new release? - [ ] Verify that Teleport version variables are correct and reflect the upcoming release. Check `docs/config.json` for this. +- [ ] Ensure that redirects (as configured in `docs/config.json`) only exist for + the default version of the docs site, and have been removed from other + versions. + - [ ] Remove version warnings in the docs that mention a version we no longer support _except_ for the last EOL version. E.g., if we no longer support version 10, remove messages saying "You need at least version n to use this From c1c1427b67e4833d67d6af7309a8b5f990c8ef6a Mon Sep 17 00:00:00 2001 From: rosstimothy <39066650+rosstimothy@users.noreply.github.com> Date: Wed, 23 Oct 2024 19:21:31 +0000 Subject: [PATCH 08/12] Add tsh command to resolve a single host (#47689) `tsh resolve` allows identifying a single host either directly by hostname, or via custom search/predicate expression from a matched proxy template. The main use case is to provide a simple command for users that wish to use `Match exec` in their SSH config. Today, all matching in SSH config must be done via DNS and requires users to add some Teleport specific suffix/prefix, or use a wildcard entry to invoke a tsh proxy command. For example, the SSH config generated today via `tsh config` is the following: ``` # Common flags for all local.dev hosts Host *.cluster-name proxy.example.com UserKnownHostsFile "/Users/tim/.tsh/known_hosts" IdentityFile "/Users/tim/.tsh/keys/proxy.example.com/tim" CertificateFile "/Users/tim/.tsh/keys/proxy.example.com/tim-ssh/cluster-name-cert.pub" # Flags for all local.dev hosts except the proxy Host *.cluster-name Port 3022 !proxy.example.com ProxyCommand "tsh" proxy ssh --cluster=cluster-name --proxy=proxy.example.com:443 %r@%h:%p ``` This allows connections to the Teleport SSH service without using tsh directly via `ssh foo.cluster-name`. However, when migrating to Teleport, that requires the user to alter their existing workflow to include the cluster-name suffix. To remedy this, users can now augment their SSH config to utilize `Match exec` instead of globbing on the cluster name suffix with the following: ``` Match exec "tsh resolve -q %h" ProxyCommand "tsh" proxy ssh --cluster=cluster-name --proxy=proxy.example.com:443 %r@%h:%p ``` By default tsh resolve will output the matching host, if one was found, but if the `-q` flag provided like in the example above the output will be silenced. If no matches are found, or multiple matches are found, `tsh resolve` will exit with a non-zero exit code as per the `Match exec` requirements. If and only if a single host is resolved will `tsh resolve` exit with a zero exit code. There are performance concerns that need to be taken into account before users adopt this in their SSH config. First, this may cause `tsh resolve` to be invoked on any SSH request. For example, even doing git pull/git push may now first require `tsh resolve` to exit with a non-zero exit code before interact with the git remote. Additionally, when `tsh resolve` finds a match, any connections to the node will require _two_ connections to the cluster and _two_ ListUnifiedResourcesRequests to resolve the host since the invocation of `tsh resolve` and `tsh proxy ssh` or `tbot proxy ssh` do not share any resources. For the reasons mentioned above, the SSH configuration generated by `tsh config` was not updated to include this new command. If users want to opt into this behavior they must acknowledge the latency concerns by manually editting the config. --- tool/tsh/common/tsh.go | 106 ++++++++++++++++++++ tool/tsh/common/tsh_test.go | 191 ++++++++++++++++++++++++++++++++++++ 2 files changed, 297 insertions(+) diff --git a/tool/tsh/common/tsh.go b/tool/tsh/common/tsh.go index b02091d75a8a1..d7573c092a9d2 100644 --- a/tool/tsh/common/tsh.go +++ b/tool/tsh/common/tsh.go @@ -802,6 +802,11 @@ func Run(ctx context.Context, args []string, opts ...CliOption) error { ssh.Flag("no-resume", "Disable SSH connection resumption").Envar(noResumeEnvVar).BoolVar(&cf.DisableSSHResumption) ssh.Flag("relogin", "Permit performing an authentication attempt on a failed command").Default("true").BoolVar(&cf.Relogin) + resolve := app.Command("resolve", "Resolves an SSH host.") + resolve.Arg("host", "Remote hostname to resolve").Required().StringVar(&cf.UserHost) + resolve.Flag("quiet", "Quiet mode").Short('q').BoolVar(&cf.Quiet) + resolve.Flag("format", defaults.FormatFlagDescription(defaults.DefaultFormats...)).Short('f').Default(teleport.Text).EnumVar(&cf.Format, defaults.DefaultFormats...) + // Daemon service for teleterm client daemon := app.Command("daemon", "Daemon is the tsh daemon service.").Hidden() daemonStart := daemon.Command("start", "Starts tsh daemon service.").Hidden() @@ -1361,6 +1366,18 @@ func Run(ctx context.Context, args []string, opts ...CliOption) error { err = onVersion(&cf) case ssh.FullCommand(): err = onSSH(&cf) + case resolve.FullCommand(): + err = onResolve(&cf) + // If quiet was specified for this command and + // an error occurred, exit with a non-zero exit + // code without emitting any other messaging. + // In this case, the command was likely invoked + // via a Match exec block from an SSH config and + // if no matches were found, we should not add + // additional spam to stderr. + if err != nil && cf.Quiet { + err = trace.Wrap(&common.ExitCodeError{Code: 1}) + } case latencySSH.FullCommand(): err = onSSHLatency(&cf) case benchSSH.FullCommand(): @@ -3595,6 +3612,95 @@ func runLocalCommand(hostLogin string, command []string) error { return cmd.Run() } +// onResolve executes `tsh resolve`, a command that +// attempts to resolve a single host from a provided +// hostname. The host information provided may be +// interpolated by proxy templates and converted +// from a hostname into a fuzzy search, or predicate query. +// Errors are returned if unable to connect to the cluster, +// no matching hosts were found, or multiple matching hosts +// were found. This is primarily meant to be used as a command +// for a match exec block in an SSH config. +func onResolve(cf *CLIConf) error { + tc, err := makeClient(cf) + if err != nil { + return trace.Wrap(err) + } + + req := proto.ListUnifiedResourcesRequest{ + Kinds: []string{types.KindNode}, + Labels: tc.Labels, + SearchKeywords: tc.SearchKeywords, + PredicateExpression: tc.PredicateExpression, + UseSearchAsRoles: tc.UseSearchAsRoles, + SortBy: types.SortBy{Field: types.ResourceKind}, + // Limit to 2 so we can check for an ambiguous result + Limit: 2, + } + + // If no search criteria were explicitly provided, then match exclusively + // on the hostname of the server. Otherwise, this would end up listing + // the first two servers that the user has access to and yield unexpected results. + if len(tc.Labels) == 0 && len(tc.SearchKeywords) == 0 && tc.PredicateExpression == "" { + req.PredicateExpression = fmt.Sprintf(`name == "%s"`, tc.Host) + } + + // Only enable the re-authentication behavior if not invoked with `-q`. When + // in quiet mode, this command is likely being invoked via ssh and + // the login prompt will not be able to be presented to users anyway. + executor := client.RetryWithRelogin + if cf.Quiet { + executor = func(ctx context.Context, teleportClient *client.TeleportClient, f func() error, option ...client.RetryWithReloginOption) error { + return f() + } + } + + var page []*types.EnrichedResource + if err := executor(cf.Context, tc, func() error { + clt, err := tc.ConnectToCluster(cf.Context) + if err != nil { + return trace.Wrap(err) + } + + defer clt.Close() + + page, _, err = apiclient.GetUnifiedResourcePage(cf.Context, clt.AuthClient, &req) + if err != nil { + return trace.Wrap(err) + } + + return nil + }); err != nil { + return trace.Wrap(err) + } + + switch len(page) { + case 1: + case 0: + return trace.NotFound("no matching hosts found") + default: + return trace.BadParameter("multiple matching hosts found") + } + + if cf.Quiet { + return nil + } + + format := strings.ToLower(cf.Format) + switch format { + case teleport.Text, "": + printNodesAsText(cf.Stdout(), []types.Server{page[0].ResourceWithLabels.(types.Server)}, true) + case teleport.JSON: + utils.WriteJSON(cf.Stdout(), page[0].ResourceWithLabels) + case teleport.YAML: + utils.WriteYAML(cf.Stdout(), page[0].ResourceWithLabels) + default: + return trace.BadParameter("unsupported format %q", cf.Format) + } + + return nil +} + // onSSH executes 'tsh ssh' command func onSSH(cf *CLIConf) error { tc, err := makeClient(cf) diff --git a/tool/tsh/common/tsh_test.go b/tool/tsh/common/tsh_test.go index 0cc5e07fc9aab..e7662c5dd03ec 100644 --- a/tool/tsh/common/tsh_test.go +++ b/tool/tsh/common/tsh_test.go @@ -4012,6 +4012,25 @@ func setCopyStdout(stdout io.Writer) CliOption { func setHomePath(path string) CliOption { return func(cf *CLIConf) error { cf.HomePath = path + // The TSHConfig is populated prior to applying any options, and + // if the home directory is being overridden, then any proxy templates + // and aliases that may have been loaded from the default home directory + // should be returned to default to avoid altering tests run from machines + // that may have a tsh config file present in the home directory. + cf.TSHConfig = client.TSHConfig{} + return nil + } +} + +func setTSHConfig(cfg client.TSHConfig) CliOption { + return func(cf *CLIConf) error { + for _, template := range cfg.ProxyTemplates { + if err := template.Check(); err != nil { + return err + } + } + + cf.TSHConfig = cfg return nil } } @@ -6527,3 +6546,175 @@ func TestRolesToString(t *testing.T) { }) } } + +// TestResolve tests that host resolution works for various inputs and +// that proxy templates are respected. +func TestResolve(t *testing.T) { + modules.SetTestModules(t, &modules.TestModules{TestBuildType: modules.BuildEnterprise}) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + accessRoleName := "access" + sshHostname := "test-ssh-server" + + accessUser, err := types.NewUser(accessRoleName) + require.NoError(t, err) + accessUser.SetRoles([]string{accessRoleName}) + + user, err := user.Current() + require.NoError(t, err) + accessUser.SetLogins([]string{user.Username}) + + traits := map[string][]string{ + constants.TraitLogins: {user.Username}, + } + accessUser.SetTraits(traits) + + connector := mockConnector(t) + rootServerOpts := []testserver.TestServerOptFunc{ + testserver.WithBootstrap(connector, accessUser), + testserver.WithHostname(sshHostname), + testserver.WithClusterName(t, "root"), + testserver.WithSSHPublicAddrs("127.0.0.1:0"), + testserver.WithConfig(func(cfg *servicecfg.Config) { + cfg.SSH.Enabled = true + cfg.SSH.PublicAddrs = []utils.NetAddr{cfg.SSH.Addr} + cfg.SSH.DisableCreateHostUser = true + cfg.SSH.Labels = map[string]string{ + "animal": "llama", + "env": "dev", + } + }), + } + rootServer := testserver.MakeTestServer(t, rootServerOpts...) + + node := testserver.MakeTestServer(t, + testserver.WithConfig(func(cfg *servicecfg.Config) { + cfg.SetAuthServerAddresses(rootServer.Config.AuthServerAddresses()) + cfg.Hostname = "second-node" + cfg.Auth.Enabled = false + cfg.Proxy.Enabled = false + cfg.SSH.Enabled = true + cfg.SSH.DisableCreateHostUser = true + cfg.SSH.Labels = map[string]string{ + "animal": "shark", + "env": "dev", + } + })) + + rootProxyAddr, err := rootServer.ProxyWebAddr() + require.NoError(t, err) + + require.EventuallyWithT(t, func(t *assert.CollectT) { + found, err := rootServer.GetAuthServer().GetNodes(ctx, apidefaults.Namespace) + if !assert.NoError(t, err) || !assert.Len(t, found, 2) { + return + } + }, 10*time.Second, 100*time.Millisecond) + + tmpHomePath := t.TempDir() + rootAuth := rootServer.GetAuthServer() + + err = Run(ctx, []string{ + "login", + "--insecure", + "--proxy", rootProxyAddr.String(), + "--user", user.Username, + }, setHomePath(tmpHomePath), setMockSSOLogin(rootAuth, accessUser, connector.GetName())) + require.NoError(t, err) + + tests := []struct { + name string + hostname string + quiet bool + assertion require.ErrorAssertionFunc + }{ + { + name: "resolved without using templates", + hostname: sshHostname, + assertion: require.NoError, + }, + { + name: "resolved via predicate from template", + hostname: "2.3.4.5", + assertion: require.NoError, + }, + { + name: "resolved via search from template", + hostname: "llama.example.com:3023", + assertion: require.NoError, + }, + { + name: "no matching host", + hostname: "asdf", + assertion: func(tt require.TestingT, err error, i ...interface{}) { + require.Error(tt, err, i...) + require.ErrorContains(tt, err, "no matching hosts", i...) + }, + }, + { + name: "quiet prevents output", + hostname: node.Config.Hostname, + quiet: true, + assertion: require.NoError, + }, + { + name: "multiple matching hosts", + hostname: "dev.example.com", + assertion: func(tt require.TestingT, err error, i ...interface{}) { + require.Error(tt, err, i...) + require.ErrorContains(tt, err, "multiple matching hosts", i...) + }, + }, + } + + for _, test := range tests { + test := test + ctx := context.Background() + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + stdout := &output{buf: bytes.Buffer{}} + stderr := &output{buf: bytes.Buffer{}} + + args := []string{"resolve"} + if test.quiet { + args = append(args, "-q") + } + args = append(args, test.hostname) + + err := Run(ctx, args, + setHomePath(tmpHomePath), + setTSHConfig(client.TSHConfig{ + ProxyTemplates: client.ProxyTemplates{ + { + Template: `^([0-9\.]+):\d+$`, + Query: `labels["animal"] == "llama"`, + }, + { + Template: `^(.*).example.com:\d+$`, + Search: "$1", + }, + }, + }), + func(conf *CLIConf) error { + conf.overrideStdin = &bytes.Buffer{} + conf.OverrideStdout = stdout + conf.overrideStderr = stderr + return nil + }, + ) + + test.assertion(t, err) + if err != nil { + return + } + + if test.quiet { + require.Empty(t, stdout.String()) + } else { + require.Contains(t, stdout.String(), sshHostname) + } + }) + } +} From 795337f8142fdfdfcfb7720c4bd7476a988b5f72 Mon Sep 17 00:00:00 2001 From: Steven Martin Date: Wed, 23 Oct 2024 15:32:21 -0400 Subject: [PATCH 09/12] docs: remove deny in impersonation (#47847) --- .../access-controls/guides/impersonation.mdx | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/docs/pages/admin-guides/access-controls/guides/impersonation.mdx b/docs/pages/admin-guides/access-controls/guides/impersonation.mdx index 2896bbdfb5444..8916069032136 100644 --- a/docs/pages/admin-guides/access-controls/guides/impersonation.mdx +++ b/docs/pages/admin-guides/access-controls/guides/impersonation.mdx @@ -86,11 +86,6 @@ spec: users: ['jenkins'] roles: ['jenkins'] - # The deny section uses the identical format as the 'allow' section. - # The deny rules always override allow rules. - deny: - node_labels: - '*': '*' ``` Create the `role` resource: @@ -207,12 +202,6 @@ spec: where: > equals(impersonate_role.metadata.labels["group"], "security") && equals(impersonate_user.metadata.labels["group"], "security") - - # The deny section uses the identical format as the 'allow' section. - # The deny rules always override allow rules. - deny: - node_labels: - '*': '*' ``` Create the resources: @@ -285,12 +274,6 @@ spec: where: > contains(user.spec.traits["group"], impersonate_role.metadata.labels["group"]) && contains(user.spec.traits["group"], impersonate_user.metadata.labels["group"]) - - # The deny section uses the identical format as the 'allow' section. - # The deny rules always override allow rules. - deny: - node_labels: - '*': '*' ``` While user traits typically come from an external identity provider, we can test From f651ab19cd70b93ac8f7f020db8983e2425aa9c7 Mon Sep 17 00:00:00 2001 From: Przemko Robakowski Date: Wed, 23 Oct 2024 22:33:58 +0200 Subject: [PATCH 10/12] Create WindowsDesktops for matching DynamicWindowsDesktops (#46991) * Add DynamicWindowsDesktop to proto * Add resource matchers to Windows desktop service config * Implement API and backend for DynamicWindowsDesktop * Cache and watcher support for DynamicWindowsDesktop * Create WindowsDesktops for matching DynamicWindowsDesktops * Fix imports * fix test * lint * review comments * move rpc to separate server * rework api and grpc more towards 153-style * e * remove dynamic windows from paginated resource * add tests * add tests * Update api/proto/teleport/legacy/types/types.proto Co-authored-by: rosstimothy <39066650+rosstimothy@users.noreply.github.com> * lint * gci * cleanup * cleanup * use generic service * cleanup * cleanup * cleanup * cleanup * cleanup * gci * rework cache * rework cache * add admin action checks * move service * add service test * gci * update discovery * add discovery test * review comments * review comments * review comments * update interfaces * gci * gci * review comments * review comments * fix loggers * fix tctl --------- Co-authored-by: rosstimothy <39066650+rosstimothy@users.noreply.github.com> --- api/client/dynamicwindows/dynamicwindows.go | 2 +- lib/auth/authclient/clt.go | 3 + lib/srv/desktop/discovery.go | 90 +++++++++++++ lib/srv/desktop/discovery_test.go | 133 ++++++++++++++++++++ lib/srv/desktop/windows_server.go | 4 + tool/tctl/common/resource_command.go | 2 +- 6 files changed, 232 insertions(+), 2 deletions(-) diff --git a/api/client/dynamicwindows/dynamicwindows.go b/api/client/dynamicwindows/dynamicwindows.go index 6c158a39e4243..32ba1762f1aed 100644 --- a/api/client/dynamicwindows/dynamicwindows.go +++ b/api/client/dynamicwindows/dynamicwindows.go @@ -46,7 +46,7 @@ func (c *Client) GetDynamicWindowsDesktop(ctx context.Context, name string) (typ return desktop, trace.Wrap(err) } -func (c *Client) ListDynamicWindowsDesktop(ctx context.Context, pageSize int, pageToken string) ([]types.DynamicWindowsDesktop, string, error) { +func (c *Client) ListDynamicWindowsDesktops(ctx context.Context, pageSize int, pageToken string) ([]types.DynamicWindowsDesktop, string, error) { resp, err := c.grpcClient.ListDynamicWindowsDesktops(ctx, &dynamicwindows.ListDynamicWindowsDesktopsRequest{ PageSize: int32(pageSize), PageToken: pageToken, diff --git a/lib/auth/authclient/clt.go b/lib/auth/authclient/clt.go index 005a44cdda8a1..4d3bdabd846b7 100644 --- a/lib/auth/authclient/clt.go +++ b/lib/auth/authclient/clt.go @@ -33,6 +33,7 @@ import ( "github.com/gravitational/teleport/api/client" "github.com/gravitational/teleport/api/client/crownjewel" "github.com/gravitational/teleport/api/client/databaseobject" + "github.com/gravitational/teleport/api/client/dynamicwindows" "github.com/gravitational/teleport/api/client/externalauditstorage" "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/client/secreport" @@ -1600,6 +1601,8 @@ type ClientI interface { types.WebSessionsGetter types.WebTokensGetter + DynamicDesktopClient() *dynamicwindows.Client + // TrustClient returns a client to the Trust service. TrustClient() trustpb.TrustServiceClient diff --git a/lib/srv/desktop/discovery.go b/lib/srv/desktop/discovery.go index cc7dd71daa6d2..c44bcc0a9cabb 100644 --- a/lib/srv/desktop/discovery.go +++ b/lib/srv/desktop/discovery.go @@ -24,6 +24,7 @@ import ( "errors" "fmt" "log/slog" + "maps" "net" "net/netip" "strings" @@ -32,6 +33,7 @@ import ( "github.com/go-ldap/ldap/v3" "github.com/gravitational/trace" + "github.com/gravitational/teleport" apidefaults "github.com/gravitational/teleport/api/defaults" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/auth/windows" @@ -306,3 +308,91 @@ func (s *WindowsService) ldapEntryToWindowsDesktop(ctx context.Context, entry *l desktop.SetExpiry(s.cfg.Clock.Now().UTC().Add(apidefaults.ServerAnnounceTTL * 3)) return desktop, nil } + +// startDynamicReconciler starts resource watcher and reconciler that registers/unregisters Windows desktops +// according to the up-to-date list of dynamic Windows desktops resources. +func (s *WindowsService) startDynamicReconciler(ctx context.Context) (*services.DynamicWindowsDesktopWatcher, error) { + if len(s.cfg.ResourceMatchers) == 0 { + s.cfg.Logger.DebugContext(ctx, "Not starting dynamic desktop resource watcher.") + return nil, nil + } + s.cfg.Logger.DebugContext(ctx, "Starting dynamic desktop resource watcher.") + dynamicDesktopClient := s.cfg.AuthClient.DynamicDesktopClient() + watcher, err := services.NewDynamicWindowsDesktopWatcher(ctx, services.DynamicWindowsDesktopWatcherConfig{ + DynamicWindowsDesktopGetter: dynamicDesktopClient, + ResourceWatcherConfig: services.ResourceWatcherConfig{ + Component: teleport.ComponentWindowsDesktop, + Client: s.cfg.AccessPoint, + }, + }) + if err != nil { + return nil, trace.Wrap(err) + } + + currentResources := make(map[string]types.WindowsDesktop) + var newResources map[string]types.WindowsDesktop + + reconciler, err := services.NewReconciler(services.ReconcilerConfig[types.WindowsDesktop]{ + Matcher: func(desktop types.WindowsDesktop) bool { + return services.MatchResourceLabels(s.cfg.ResourceMatchers, desktop.GetAllLabels()) + }, + GetCurrentResources: func() map[string]types.WindowsDesktop { + return currentResources + }, + GetNewResources: func() map[string]types.WindowsDesktop { + return newResources + }, + OnCreate: s.upsertDesktop, + OnUpdate: s.updateDesktop, + OnDelete: s.deleteDesktop, + }) + if err != nil { + return nil, trace.Wrap(err) + } + go func() { + defer s.cfg.Logger.DebugContext(ctx, "DynamicWindowsDesktop resource watcher done.") + defer watcher.Close() + for { + select { + case desktops := <-watcher.DynamicWindowsDesktopsC: + newResources = make(map[string]types.WindowsDesktop) + for _, dynamicDesktop := range desktops { + desktop, err := s.toWindowsDesktop(dynamicDesktop) + if err != nil { + s.cfg.Logger.WarnContext(ctx, "Can't create desktop resource", "error", err) + continue + } + newResources[dynamicDesktop.GetName()] = desktop + } + if err := reconciler.Reconcile(ctx); err != nil { + s.cfg.Logger.WarnContext(ctx, "Reconciliation failed, will retry", "error", err) + continue + } + currentResources = newResources + case <-watcher.Done(): + return + case <-ctx.Done(): + return + } + } + }() + return watcher, nil +} + +func (s *WindowsService) toWindowsDesktop(dynamicDesktop types.DynamicWindowsDesktop) (*types.WindowsDesktopV3, error) { + width, height := dynamicDesktop.GetScreenSize() + desktopLabels := dynamicDesktop.GetAllLabels() + labels := make(map[string]string, len(desktopLabels)+1) + maps.Copy(labels, desktopLabels) + labels[types.OriginLabel] = types.OriginDynamic + return types.NewWindowsDesktopV3(dynamicDesktop.GetName(), labels, types.WindowsDesktopSpecV3{ + Addr: dynamicDesktop.GetAddr(), + Domain: dynamicDesktop.GetDomain(), + HostID: s.cfg.Heartbeat.HostUUID, + NonAD: dynamicDesktop.NonAD(), + ScreenSize: &types.Resolution{ + Width: width, + Height: height, + }, + }) +} diff --git a/lib/srv/desktop/discovery_test.go b/lib/srv/desktop/discovery_test.go index 2a35918d264b4..fc188f75ce1d6 100644 --- a/lib/srv/desktop/discovery_test.go +++ b/lib/srv/desktop/discovery_test.go @@ -33,7 +33,9 @@ import ( "github.com/stretchr/testify/require" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/auth" "github.com/gravitational/teleport/lib/auth/windows" + "github.com/gravitational/teleport/lib/services" logutils "github.com/gravitational/teleport/lib/utils/log" ) @@ -169,3 +171,134 @@ func TestDNSErrors(t *testing.T) { require.Less(t, time.Since(start), dnsQueryTimeout-1*time.Second) require.Error(t, err) } + +func TestDynamicWindowsDiscovery(t *testing.T) { + t.Parallel() + authServer, err := auth.NewTestAuthServer(auth.TestAuthServerConfig{ + ClusterName: "test", + Dir: t.TempDir(), + }) + require.NoError(t, err) + t.Cleanup(func() { + require.NoError(t, authServer.Close()) + }) + + tlsServer, err := authServer.NewTestTLSServer() + require.NoError(t, err) + t.Cleanup(func() { + require.NoError(t, tlsServer.Close()) + }) + + client, err := tlsServer.NewClient(auth.TestServerID(types.RoleWindowsDesktop, "test-host-id")) + require.NoError(t, err) + t.Cleanup(func() { + require.NoError(t, client.Close()) + }) + + dynamicWindowsClient := client.DynamicDesktopClient() + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + for _, testCase := range []struct { + name string + labels map[string]string + expected int + }{ + { + name: "no labels", + expected: 0, + }, + { + name: "no matching labels", + labels: map[string]string{"xyz": "abc"}, + expected: 0, + }, + { + name: "matching labels", + labels: map[string]string{"foo": "bar"}, + expected: 1, + }, + { + name: "matching wildcard labels", + labels: map[string]string{"abc": "abc"}, + expected: 1, + }, + } { + t.Run(testCase.name, func(t *testing.T) { + s := &WindowsService{ + cfg: WindowsServiceConfig{ + Heartbeat: HeartbeatConfig{ + HostUUID: "1234", + }, + Logger: slog.New(logutils.NewSlogTextHandler(io.Discard, logutils.SlogTextHandlerConfig{})), + Clock: clockwork.NewFakeClock(), + AuthClient: client, + AccessPoint: client, + ResourceMatchers: []services.ResourceMatcher{{ + Labels: types.Labels{ + "foo": {"bar"}, + }, + }, { + Labels: types.Labels{ + "abc": {"*"}, + }, + }}, + }, + dnsResolver: &net.Resolver{ + PreferGo: true, + Dial: func(ctx context.Context, network, address string) (net.Conn, error) { + return nil, errors.New("this resolver always fails") + }, + }, + } + reconciler, err := s.startDynamicReconciler(ctx) + require.NoError(t, err) + t.Cleanup(func() { + reconciler.Close() + require.NoError(t, authServer.AuthServer.DeleteAllWindowsDesktops(ctx)) + require.NoError(t, authServer.AuthServer.DeleteAllDynamicWindowsDesktops(ctx)) + }) + + desktop, err := types.NewDynamicWindowsDesktopV1("test", testCase.labels, types.DynamicWindowsDesktopSpecV1{ + Addr: "addr", + }) + require.NoError(t, err) + + _, err = dynamicWindowsClient.CreateDynamicWindowsDesktop(ctx, desktop) + require.NoError(t, err) + + time.Sleep(10 * time.Millisecond) + + desktops, err := client.GetWindowsDesktops(ctx, types.WindowsDesktopFilter{}) + require.NoError(t, err) + require.Len(t, desktops, testCase.expected) + if testCase.expected > 0 { + require.Equal(t, desktop.GetName(), desktops[0].GetName()) + require.Equal(t, desktop.GetAddr(), desktops[0].GetAddr()) + } + + desktop.Spec.Addr = "addr2" + _, err = dynamicWindowsClient.UpdateDynamicWindowsDesktop(ctx, desktop) + require.NoError(t, err) + + time.Sleep(10 * time.Millisecond) + desktops, err = client.GetWindowsDesktops(ctx, types.WindowsDesktopFilter{}) + require.NoError(t, err) + require.Len(t, desktops, testCase.expected) + if testCase.expected > 0 { + require.Equal(t, desktop.GetName(), desktops[0].GetName()) + require.Equal(t, desktop.GetAddr(), desktops[0].GetAddr()) + } + + require.NoError(t, dynamicWindowsClient.DeleteDynamicWindowsDesktop(ctx, "test")) + + time.Sleep(10 * time.Millisecond) + + desktops, err = client.GetWindowsDesktops(ctx, types.WindowsDesktopFilter{}) + require.NoError(t, err) + require.Empty(t, desktops) + }) + + } +} diff --git a/lib/srv/desktop/windows_server.go b/lib/srv/desktop/windows_server.go index 28aaee2a48483..791f477861666 100644 --- a/lib/srv/desktop/windows_server.go +++ b/lib/srv/desktop/windows_server.go @@ -411,6 +411,10 @@ func NewWindowsService(cfg WindowsServiceConfig) (*WindowsService, error) { return nil, trace.Wrap(err) } + if _, err := s.startDynamicReconciler(ctx); err != nil { + return nil, trace.Wrap(err) + } + if len(s.cfg.DiscoveryBaseDN) > 0 { if err := s.startDesktopDiscovery(); err != nil { return nil, trace.Wrap(err) diff --git a/tool/tctl/common/resource_command.go b/tool/tctl/common/resource_command.go index dd558e01f7319..c37e8805581e6 100644 --- a/tool/tctl/common/resource_command.go +++ b/tool/tctl/common/resource_command.go @@ -2522,7 +2522,7 @@ func (rc *ResourceCommand) getCollection(ctx context.Context, client *authclient pageToken := "" desktops := make([]types.DynamicWindowsDesktop, 0, 100) for { - d, next, err := dynamicDesktopClient.ListDynamicWindowsDesktop(ctx, 100, pageToken) + d, next, err := dynamicDesktopClient.ListDynamicWindowsDesktops(ctx, 100, pageToken) if err != nil { return nil, trace.Wrap(err) } From cca0f1914cf690c9b4e62ca148fa9041eaeacadd Mon Sep 17 00:00:00 2001 From: Hugo Shaka Date: Wed, 23 Oct 2024 16:59:26 -0400 Subject: [PATCH 11/12] bump e to fix ent builds since #47840 (#47873) --- e | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e b/e index e4f948c672214..b5db15651a8d4 160000 --- a/e +++ b/e @@ -1 +1 @@ -Subproject commit e4f948c6722147398b9abb2fe1075456443c4dde +Subproject commit b5db15651a8d4590601e470d3e86cc3698e011b2 From b1bc41f60e1e2eb153acdec9960d51bda5066cc2 Mon Sep 17 00:00:00 2001 From: "teleport-post-release-automation[bot]" <128860004+teleport-post-release-automation[bot]@users.noreply.github.com> Date: Wed, 23 Oct 2024 22:16:58 +0000 Subject: [PATCH 12/12] [auto] Update AMI IDs for 16.4.6 (#47865) Co-authored-by: GitHub Co-authored-by: Zac Bergquist --- assets/aws/Makefile | 2 +- examples/aws/terraform/AMIS.md | 204 +++++++++--------- .../terraform/ha-autoscale-cluster/README.md | 2 +- .../aws/terraform/starter-cluster/README.md | 2 +- 4 files changed, 105 insertions(+), 105 deletions(-) diff --git a/assets/aws/Makefile b/assets/aws/Makefile index f78a0cdd49186..e65933213f99b 100644 --- a/assets/aws/Makefile +++ b/assets/aws/Makefile @@ -2,7 +2,7 @@ # This must be a _released_ version of Teleport, i.e. one which has binaries # available for download on https://goteleport.com/download # Unreleased versions will fail to build. -TELEPORT_VERSION ?= 16.4.3 +TELEPORT_VERSION ?= 16.4.6 # Teleport UID is the UID of a non-privileged 'teleport' user TELEPORT_UID ?= 1007 diff --git a/examples/aws/terraform/AMIS.md b/examples/aws/terraform/AMIS.md index 1b775003751a4..87ccac2e7b33e 100644 --- a/examples/aws/terraform/AMIS.md +++ b/examples/aws/terraform/AMIS.md @@ -6,116 +6,116 @@ This list is updated when new AMI versions are released. ### OSS ``` -# ap-northeast-1 v16.4.3 arm64 OSS: ami-03a99db2fa775900b -# ap-northeast-1 v16.4.3 x86_64 OSS: ami-068026fd823473990 -# ap-northeast-2 v16.4.3 arm64 OSS: ami-063cb5eff8ee93ae9 -# ap-northeast-2 v16.4.3 x86_64 OSS: ami-0b5b1fd8897ecd033 -# ap-northeast-3 v16.4.3 arm64 OSS: ami-0785a98abf53559f0 -# ap-northeast-3 v16.4.3 x86_64 OSS: ami-034ce21eb596f461d -# ap-south-1 v16.4.3 arm64 OSS: ami-05adbaaa445ab4f3a -# ap-south-1 v16.4.3 x86_64 OSS: ami-04c6a2b4dec6c67c8 -# ap-southeast-1 v16.4.3 arm64 OSS: ami-0858dbe717d962d28 -# ap-southeast-1 v16.4.3 x86_64 OSS: ami-09b59d463eb157e24 -# ap-southeast-2 v16.4.3 arm64 OSS: ami-06e224a773717fd38 -# ap-southeast-2 v16.4.3 x86_64 OSS: ami-022784e5407820ded -# ca-central-1 v16.4.3 arm64 OSS: ami-02c6ed3bc0c9d824e -# ca-central-1 v16.4.3 x86_64 OSS: ami-05bc6e07efc2afffd -# eu-central-1 v16.4.3 arm64 OSS: ami-0af7ec44d5b1f6c92 -# eu-central-1 v16.4.3 x86_64 OSS: ami-03fffa5ced60deb24 -# eu-north-1 v16.4.3 arm64 OSS: ami-0fbf10c3cec893e64 -# eu-north-1 v16.4.3 x86_64 OSS: ami-0dd3359279b92a8ea -# eu-west-1 v16.4.3 arm64 OSS: ami-0b43e18f381fd3ce2 -# eu-west-1 v16.4.3 x86_64 OSS: ami-04bc718116bd8d8ba -# eu-west-2 v16.4.3 arm64 OSS: ami-08b85aee5566b6d38 -# eu-west-2 v16.4.3 x86_64 OSS: ami-08f0d8bdc46fd4e96 -# eu-west-3 v16.4.3 arm64 OSS: ami-0c7c33c805040b4f5 -# eu-west-3 v16.4.3 x86_64 OSS: ami-05f3a559217d578ab -# sa-east-1 v16.4.3 arm64 OSS: ami-04cda1b5f71cf0adf -# sa-east-1 v16.4.3 x86_64 OSS: ami-05a836867a9c59a8e -# us-east-1 v16.4.3 arm64 OSS: ami-0509993561cc232d9 -# us-east-1 v16.4.3 x86_64 OSS: ami-099176631d6e3001f -# us-east-2 v16.4.3 arm64 OSS: ami-05911a676387e6359 -# us-east-2 v16.4.3 x86_64 OSS: ami-0acb2c68a8a6bd47b -# us-west-1 v16.4.3 arm64 OSS: ami-0931a3203ab46db22 -# us-west-1 v16.4.3 x86_64 OSS: ami-015c7af6995403f1c -# us-west-2 v16.4.3 arm64 OSS: ami-08fc0e0bf4374c62b -# us-west-2 v16.4.3 x86_64 OSS: ami-0ebe4df1270296286 +# ap-northeast-1 v16.4.6 arm64 OSS: ami-0307b5460949a2d09 +# ap-northeast-1 v16.4.6 x86_64 OSS: ami-0dfab44a54daa6774 +# ap-northeast-2 v16.4.6 arm64 OSS: ami-0e55fd0db3d73dac8 +# ap-northeast-2 v16.4.6 x86_64 OSS: ami-0b8383b49bc5da0a3 +# ap-northeast-3 v16.4.6 arm64 OSS: ami-028c94128392137d3 +# ap-northeast-3 v16.4.6 x86_64 OSS: ami-0d125ae89b4371a18 +# ap-south-1 v16.4.6 arm64 OSS: ami-044c55ce57c90a643 +# ap-south-1 v16.4.6 x86_64 OSS: ami-0abbfcf08a8e28c0e +# ap-southeast-1 v16.4.6 arm64 OSS: ami-0e49484dca4bb8fb4 +# ap-southeast-1 v16.4.6 x86_64 OSS: ami-0d23cc1ba5cc1ebea +# ap-southeast-2 v16.4.6 arm64 OSS: ami-07ebd854df12f216a +# ap-southeast-2 v16.4.6 x86_64 OSS: ami-0c22e4641198f8bec +# ca-central-1 v16.4.6 arm64 OSS: ami-0c143a08dcf5ca3fd +# ca-central-1 v16.4.6 x86_64 OSS: ami-0c16191afc3fb654a +# eu-central-1 v16.4.6 arm64 OSS: ami-0551d4f5aeede709e +# eu-central-1 v16.4.6 x86_64 OSS: ami-0cd3c4404ebac1b6d +# eu-north-1 v16.4.6 arm64 OSS: ami-001a29d66d3f99d0c +# eu-north-1 v16.4.6 x86_64 OSS: ami-099742a52e0243c12 +# eu-west-1 v16.4.6 arm64 OSS: ami-0f056146565210169 +# eu-west-1 v16.4.6 x86_64 OSS: ami-0bb1098e9061f3286 +# eu-west-2 v16.4.6 arm64 OSS: ami-0c41c8a9e6623cbeb +# eu-west-2 v16.4.6 x86_64 OSS: ami-0ef2785710a394477 +# eu-west-3 v16.4.6 arm64 OSS: ami-01f68dd1652a01299 +# eu-west-3 v16.4.6 x86_64 OSS: ami-062cc20058012a2ac +# sa-east-1 v16.4.6 arm64 OSS: ami-0448cf45899c4109b +# sa-east-1 v16.4.6 x86_64 OSS: ami-00153f4b099207ba6 +# us-east-1 v16.4.6 arm64 OSS: ami-023856580215a2b43 +# us-east-1 v16.4.6 x86_64 OSS: ami-06cc987e0952a0378 +# us-east-2 v16.4.6 arm64 OSS: ami-0291a0a11bdd06c8d +# us-east-2 v16.4.6 x86_64 OSS: ami-05435267916884c4e +# us-west-1 v16.4.6 arm64 OSS: ami-06cf14c2b9bfa2fc3 +# us-west-1 v16.4.6 x86_64 OSS: ami-0646577553c25d2fb +# us-west-2 v16.4.6 arm64 OSS: ami-04b773c97239cdc04 +# us-west-2 v16.4.6 x86_64 OSS: ami-015f89025eb9ab2dd ``` ### Enterprise ``` -# ap-northeast-1 v16.4.3 arm64 Enterprise: ami-0587524cb092763b8 -# ap-northeast-1 v16.4.3 x86_64 Enterprise: ami-03d76c92521d9b178 -# ap-northeast-2 v16.4.3 arm64 Enterprise: ami-087a2c13eb63f133a -# ap-northeast-2 v16.4.3 x86_64 Enterprise: ami-030048fc4753d7456 -# ap-northeast-3 v16.4.3 arm64 Enterprise: ami-0d88ac92fc0f7c1f1 -# ap-northeast-3 v16.4.3 x86_64 Enterprise: ami-02edeee2926921c43 -# ap-south-1 v16.4.3 arm64 Enterprise: ami-0fa8621c5aac4fdd1 -# ap-south-1 v16.4.3 x86_64 Enterprise: ami-09d7e5e25b07aff41 -# ap-southeast-1 v16.4.3 arm64 Enterprise: ami-07a6b4a9e00672cfa -# ap-southeast-1 v16.4.3 x86_64 Enterprise: ami-0cad3b30fd7786746 -# ap-southeast-2 v16.4.3 arm64 Enterprise: ami-0ee80dba1cfc25ddb -# ap-southeast-2 v16.4.3 x86_64 Enterprise: ami-015871b65daf94360 -# ca-central-1 v16.4.3 arm64 Enterprise: ami-0096116bef0c781eb -# ca-central-1 v16.4.3 x86_64 Enterprise: ami-0d4217deec2fa79af -# eu-central-1 v16.4.3 arm64 Enterprise: ami-03d1da8c34a30b131 -# eu-central-1 v16.4.3 x86_64 Enterprise: ami-018d51199ce555b06 -# eu-north-1 v16.4.3 arm64 Enterprise: ami-0186b55009355474e -# eu-north-1 v16.4.3 x86_64 Enterprise: ami-009e80af1651b105a -# eu-west-1 v16.4.3 arm64 Enterprise: ami-035fab6a83d3de6b4 -# eu-west-1 v16.4.3 x86_64 Enterprise: ami-0404e446cd95923d5 -# eu-west-2 v16.4.3 arm64 Enterprise: ami-086099148dcbca910 -# eu-west-2 v16.4.3 x86_64 Enterprise: ami-0cfedd8ec032b41a9 -# eu-west-3 v16.4.3 arm64 Enterprise: ami-0a05a917e47722280 -# eu-west-3 v16.4.3 x86_64 Enterprise: ami-0d670841a9b05347f -# sa-east-1 v16.4.3 arm64 Enterprise: ami-00b649f81437accf3 -# sa-east-1 v16.4.3 x86_64 Enterprise: ami-04a90f47e22ec6b94 -# us-east-1 v16.4.3 arm64 Enterprise: ami-0964d07c4f1970766 -# us-east-1 v16.4.3 x86_64 Enterprise: ami-02a8e3fa00529a543 -# us-east-2 v16.4.3 arm64 Enterprise: ami-0f29f06b24b96c7fb -# us-east-2 v16.4.3 x86_64 Enterprise: ami-012ff8f25038b5e68 -# us-west-1 v16.4.3 arm64 Enterprise: ami-02638c230b9ddeb0f -# us-west-1 v16.4.3 x86_64 Enterprise: ami-0082e714b7e481911 -# us-west-2 v16.4.3 arm64 Enterprise: ami-04fb6f48cbe7ea880 -# us-west-2 v16.4.3 x86_64 Enterprise: ami-01af4cd28ddaaca88 +# ap-northeast-1 v16.4.6 arm64 Enterprise: ami-05f137e5b9201f996 +# ap-northeast-1 v16.4.6 x86_64 Enterprise: ami-03212c4ccbf6eca85 +# ap-northeast-2 v16.4.6 arm64 Enterprise: ami-0924720d520fe8a11 +# ap-northeast-2 v16.4.6 x86_64 Enterprise: ami-0fb3f151c13f316f2 +# ap-northeast-3 v16.4.6 arm64 Enterprise: ami-04d7f1e045968880c +# ap-northeast-3 v16.4.6 x86_64 Enterprise: ami-0a2f75caf6781d4f8 +# ap-south-1 v16.4.6 arm64 Enterprise: ami-0f9d46726c638e2d4 +# ap-south-1 v16.4.6 x86_64 Enterprise: ami-072c1bda7163cd497 +# ap-southeast-1 v16.4.6 arm64 Enterprise: ami-011f33a197bfe7e44 +# ap-southeast-1 v16.4.6 x86_64 Enterprise: ami-0fbc3bdaa3047315e +# ap-southeast-2 v16.4.6 arm64 Enterprise: ami-058180b9c6d8cc471 +# ap-southeast-2 v16.4.6 x86_64 Enterprise: ami-0aa1b5b87840a1156 +# ca-central-1 v16.4.6 arm64 Enterprise: ami-05aadbead2b91af95 +# ca-central-1 v16.4.6 x86_64 Enterprise: ami-0d08f955e2eb4cce2 +# eu-central-1 v16.4.6 arm64 Enterprise: ami-068077fc8087e409c +# eu-central-1 v16.4.6 x86_64 Enterprise: ami-0ec63c9cf4ec58ef1 +# eu-north-1 v16.4.6 arm64 Enterprise: ami-04116b37191302aec +# eu-north-1 v16.4.6 x86_64 Enterprise: ami-0ff60c42661fd7ea6 +# eu-west-1 v16.4.6 arm64 Enterprise: ami-0b8f5a17eeecef1a2 +# eu-west-1 v16.4.6 x86_64 Enterprise: ami-05f6c12ade128ff2f +# eu-west-2 v16.4.6 arm64 Enterprise: ami-0d23fa319e42572c1 +# eu-west-2 v16.4.6 x86_64 Enterprise: ami-0e6fb58e541932cf6 +# eu-west-3 v16.4.6 arm64 Enterprise: ami-0567bafa2392fe6da +# eu-west-3 v16.4.6 x86_64 Enterprise: ami-01e39ed7e04cc3dd6 +# sa-east-1 v16.4.6 arm64 Enterprise: ami-08ba21a1168c24241 +# sa-east-1 v16.4.6 x86_64 Enterprise: ami-0b23038d8ed82c2a3 +# us-east-1 v16.4.6 arm64 Enterprise: ami-0e7d540c0ed15a0cc +# us-east-1 v16.4.6 x86_64 Enterprise: ami-050d0ef0234295033 +# us-east-2 v16.4.6 arm64 Enterprise: ami-0c94bb5cd6d326f4a +# us-east-2 v16.4.6 x86_64 Enterprise: ami-04fa696ef2b274943 +# us-west-1 v16.4.6 arm64 Enterprise: ami-0b43f032b82b354f0 +# us-west-1 v16.4.6 x86_64 Enterprise: ami-0f927c0e8705a2aaa +# us-west-2 v16.4.6 arm64 Enterprise: ami-0379aa56700286ccd +# us-west-2 v16.4.6 x86_64 Enterprise: ami-0606b81e192fa074d ``` ### Enterprise FIPS ``` -# ap-northeast-1 v16.4.3 arm64 Enterprise FIPS: ami-019cf60b62d5a10d7 -# ap-northeast-1 v16.4.3 x86_64 Enterprise FIPS: ami-059f7cfa3b78b78bb -# ap-northeast-2 v16.4.3 arm64 Enterprise FIPS: ami-0686e3978afbe194e -# ap-northeast-2 v16.4.3 x86_64 Enterprise FIPS: ami-09c75e412314e2285 -# ap-northeast-3 v16.4.3 arm64 Enterprise FIPS: ami-0cd8f2c41cf6ab3a6 -# ap-northeast-3 v16.4.3 x86_64 Enterprise FIPS: ami-0d7e615231cb9a07a -# ap-south-1 v16.4.3 arm64 Enterprise FIPS: ami-052410bcc79298775 -# ap-south-1 v16.4.3 x86_64 Enterprise FIPS: ami-0c34957c4e88fc5cb -# ap-southeast-1 v16.4.3 arm64 Enterprise FIPS: ami-009a131295b547ba4 -# ap-southeast-1 v16.4.3 x86_64 Enterprise FIPS: ami-0ae06abb3a918f1b0 -# ap-southeast-2 v16.4.3 arm64 Enterprise FIPS: ami-08c3056bb1b60a845 -# ap-southeast-2 v16.4.3 x86_64 Enterprise FIPS: ami-0406318e9822c396c -# ca-central-1 v16.4.3 arm64 Enterprise FIPS: ami-0a3156111e029549a -# ca-central-1 v16.4.3 x86_64 Enterprise FIPS: ami-0d3c0c6ed2542c23f -# eu-central-1 v16.4.3 arm64 Enterprise FIPS: ami-0f687501df4213855 -# eu-central-1 v16.4.3 x86_64 Enterprise FIPS: ami-065c83aee094e9d84 -# eu-north-1 v16.4.3 arm64 Enterprise FIPS: ami-08ca656487c08696d -# eu-north-1 v16.4.3 x86_64 Enterprise FIPS: ami-0e0458bbd101b2afd -# eu-west-1 v16.4.3 arm64 Enterprise FIPS: ami-038a541f0f3bb079b -# eu-west-1 v16.4.3 x86_64 Enterprise FIPS: ami-0e3895fc4988402eb -# eu-west-2 v16.4.3 arm64 Enterprise FIPS: ami-0d37fd8143a442afb -# eu-west-2 v16.4.3 x86_64 Enterprise FIPS: ami-03e00869abccc5778 -# eu-west-3 v16.4.3 arm64 Enterprise FIPS: ami-07454b7d4f108d56b -# eu-west-3 v16.4.3 x86_64 Enterprise FIPS: ami-0eafe00629a7e0ef0 -# sa-east-1 v16.4.3 arm64 Enterprise FIPS: ami-067f2d87167d38e61 -# sa-east-1 v16.4.3 x86_64 Enterprise FIPS: ami-0f5e7d1cae7105731 -# us-east-1 v16.4.3 arm64 Enterprise FIPS: ami-09af1d781d706b43c -# us-east-1 v16.4.3 x86_64 Enterprise FIPS: ami-0beb13e821cf7c941 -# us-east-2 v16.4.3 arm64 Enterprise FIPS: ami-016941078434174d1 -# us-east-2 v16.4.3 x86_64 Enterprise FIPS: ami-0a49d29be30d77240 -# us-west-1 v16.4.3 arm64 Enterprise FIPS: ami-00e936b6a86e30520 -# us-west-1 v16.4.3 x86_64 Enterprise FIPS: ami-0ae4df0bf4ea14c19 -# us-west-2 v16.4.3 arm64 Enterprise FIPS: ami-005e1ffa94a45bf74 -# us-west-2 v16.4.3 x86_64 Enterprise FIPS: ami-0b1e72b0e4fe47181 +# ap-northeast-1 v16.4.6 arm64 Enterprise FIPS: ami-03222719a3da72577 +# ap-northeast-1 v16.4.6 x86_64 Enterprise FIPS: ami-03b67fc78f15b9cb1 +# ap-northeast-2 v16.4.6 arm64 Enterprise FIPS: ami-07b1142c8d0a97142 +# ap-northeast-2 v16.4.6 x86_64 Enterprise FIPS: ami-0f5200769c8d1d528 +# ap-northeast-3 v16.4.6 arm64 Enterprise FIPS: ami-0c735142ffa7fe311 +# ap-northeast-3 v16.4.6 x86_64 Enterprise FIPS: ami-01f76396df08b2ea4 +# ap-south-1 v16.4.6 arm64 Enterprise FIPS: ami-0b151dab5123a1cde +# ap-south-1 v16.4.6 x86_64 Enterprise FIPS: ami-0a548663132b1fbcc +# ap-southeast-1 v16.4.6 arm64 Enterprise FIPS: ami-0ee14bd0e530b9802 +# ap-southeast-1 v16.4.6 x86_64 Enterprise FIPS: ami-02605938b8431df73 +# ap-southeast-2 v16.4.6 arm64 Enterprise FIPS: ami-0e1ab9036f0d741ee +# ap-southeast-2 v16.4.6 x86_64 Enterprise FIPS: ami-0da24aa1dca222d28 +# ca-central-1 v16.4.6 arm64 Enterprise FIPS: ami-0168f83e25bab435d +# ca-central-1 v16.4.6 x86_64 Enterprise FIPS: ami-0dd0f6a87b55bf122 +# eu-central-1 v16.4.6 arm64 Enterprise FIPS: ami-0be0fd9618583374b +# eu-central-1 v16.4.6 x86_64 Enterprise FIPS: ami-0c87fd696f03d07be +# eu-north-1 v16.4.6 arm64 Enterprise FIPS: ami-0165c744dd34b0367 +# eu-north-1 v16.4.6 x86_64 Enterprise FIPS: ami-0fc4ea5c96fe4b2a1 +# eu-west-1 v16.4.6 arm64 Enterprise FIPS: ami-064921542d826b418 +# eu-west-1 v16.4.6 x86_64 Enterprise FIPS: ami-03291e366b650c3bb +# eu-west-2 v16.4.6 arm64 Enterprise FIPS: ami-0913a3fa04526977b +# eu-west-2 v16.4.6 x86_64 Enterprise FIPS: ami-0f303317a7395c1da +# eu-west-3 v16.4.6 arm64 Enterprise FIPS: ami-0403b9e814a93e163 +# eu-west-3 v16.4.6 x86_64 Enterprise FIPS: ami-0550f4d59db15f7d7 +# sa-east-1 v16.4.6 arm64 Enterprise FIPS: ami-04479f0cc8407de74 +# sa-east-1 v16.4.6 x86_64 Enterprise FIPS: ami-0ae361c5388c2e620 +# us-east-1 v16.4.6 arm64 Enterprise FIPS: ami-0d6a7504bf4c12471 +# us-east-1 v16.4.6 x86_64 Enterprise FIPS: ami-0184537bbd4a91c80 +# us-east-2 v16.4.6 arm64 Enterprise FIPS: ami-0ba3bdc20ee3911eb +# us-east-2 v16.4.6 x86_64 Enterprise FIPS: ami-0deebe84e68d2ca33 +# us-west-1 v16.4.6 arm64 Enterprise FIPS: ami-04f75a751ff454fe0 +# us-west-1 v16.4.6 x86_64 Enterprise FIPS: ami-0dc241b1b82dcb44b +# us-west-2 v16.4.6 arm64 Enterprise FIPS: ami-0bcc7e8ade2f96862 +# us-west-2 v16.4.6 x86_64 Enterprise FIPS: ami-0e396f7cd99e781ae ``` diff --git a/examples/aws/terraform/ha-autoscale-cluster/README.md b/examples/aws/terraform/ha-autoscale-cluster/README.md index eceed030de64f..fff741d817e46 100644 --- a/examples/aws/terraform/ha-autoscale-cluster/README.md +++ b/examples/aws/terraform/ha-autoscale-cluster/README.md @@ -46,7 +46,7 @@ export TF_VAR_cluster_name="teleport.example.com" # OSS: aws ec2 describe-images --owners 146628656107 --filters 'Name=name,Values=teleport-oss-*' # Enterprise: aws ec2 describe-images --owners 146628656107 --filters 'Name=name,Values=teleport-ent-*' # FIPS 140-2 images are also available for Enterprise customers, look for '-fips' on the end of the AMI's name -export TF_VAR_ami_name="teleport-ent-16.4.3-arm64" +export TF_VAR_ami_name="teleport-ent-16.4.6-arm64" # Instance types used for authentication server auto scaling group # This should match to the AMI instance architecture type, ARM or x86 diff --git a/examples/aws/terraform/starter-cluster/README.md b/examples/aws/terraform/starter-cluster/README.md index f8ca9b8a6f1dc..5a7030487d616 100644 --- a/examples/aws/terraform/starter-cluster/README.md +++ b/examples/aws/terraform/starter-cluster/README.md @@ -98,7 +98,7 @@ TF_VAR_license_path ?= "/path/to/license" # OSS: aws ec2 describe-images --owners 146628656107 --filters 'Name=name,Values=teleport-oss-*' # Enterprise: aws ec2 describe-images --owners 146628656107 --filters 'Name=name,Values=teleport-ent-*' # FIPS 140-2 images are also available for Enterprise customers, look for '-fips' on the end of the AMI's name -TF_VAR_ami_name ?= "teleport-ent-16.4.3-arm64" +TF_VAR_ami_name ?= "teleport-ent-16.4.6-arm64" # Route 53 hosted zone to use, must be a root zone registered in AWS, e.g. example.com TF_VAR_route53_zone ?= "example.com"