From 1c2df213fbc34b04849f2e4c0672e3fc1f285b78 Mon Sep 17 00:00:00 2001 From: Bartosz Leper Date: Thu, 17 Oct 2024 09:27:28 +0200 Subject: [PATCH 01/30] Remove third-party icons (#47557) * Use standard icons * Remove third-party SVG icons --- web/packages/design/src/SVGIcon/AWS.tsx | 53 ----- web/packages/design/src/SVGIcon/Ansible.tsx | 31 --- web/packages/design/src/SVGIcon/Azure.tsx | 84 -------- web/packages/design/src/SVGIcon/Bug.tsx | 31 --- .../design/src/SVGIcon/ChevronRight.tsx | 31 --- web/packages/design/src/SVGIcon/CircleCI.tsx | 31 --- .../design/src/SVGIcon/ExternalLink.tsx | 39 ---- web/packages/design/src/SVGIcon/GCP.tsx | 53 ----- web/packages/design/src/SVGIcon/GitHub.tsx | 35 --- web/packages/design/src/SVGIcon/GitLab.tsx | 48 ----- web/packages/design/src/SVGIcon/Jenkins.tsx | 204 ------------------ .../design/src/SVGIcon/Kubernetes.tsx | 31 --- web/packages/design/src/SVGIcon/Lock.tsx | 31 --- web/packages/design/src/SVGIcon/Okta.tsx | 34 --- .../design/src/SVGIcon/ServerIcon.tsx | 31 --- web/packages/design/src/SVGIcon/Servers.tsx | 31 --- web/packages/design/src/SVGIcon/Spacelift.tsx | 31 --- web/packages/design/src/SVGIcon/index.ts | 15 -- .../src/TopNav/TopNavUserMenu/avatar.png | Bin 8316 -> 0 bytes .../design/src/assets/images/Assets.story.tsx | 8 - .../design/src/assets/images/app-logo.svg | 6 - .../src/assets/images/icons/datadog.svg | 10 - .../src/assets/images/icons/discord.svg | 10 - .../design/src/assets/images/icons/email.svg | 3 - .../src/assets/images/icons/entra-id.svg | 9 - .../design/src/assets/images/icons/jamf.svg | 4 - .../design/src/assets/images/icons/jira.svg | 27 --- .../src/assets/images/icons/mattermost.svg | 11 - .../src/assets/images/icons/msteams.svg | 23 -- .../design/src/assets/images/icons/okta.svg | 10 - .../design/src/assets/images/icons/openai.svg | 1 - .../src/assets/images/icons/opsgenie.svg | 15 -- .../src/assets/images/icons/pagerduty.svg | 4 - .../src/assets/images/icons/servicenow.svg | 3 - .../design/src/assets/images/icons/slack.svg | 17 -- .../src/assets/images/icons/teleport.png | Bin 1153 -> 0 bytes .../design/src/assets/images/kube-logo.svg | 19 -- .../src/assets/images/sample-logo-long.svg | 57 ----- .../src/assets/images/sample-logo-squire.svg | 1 - .../src/assets/images/sec-key-graphic.svg | 1 - .../src/assets/resources/appplication.png | Bin 18476 -> 0 bytes .../design/src/assets/resources/database.png | Bin 10437 -> 0 bytes .../design/src/assets/resources/desktop.png | Bin 12806 -> 0 bytes .../src/assets/resources/kubernetes.png | Bin 14645 -> 0 bytes .../design/src/assets/resources/stack.png | Bin 11100 -> 0 bytes .../src/components/LogoHero/LogoHeroDark.svg | 8 - .../src/components/LogoHero/LogoHeroLight.svg | 8 - 47 files changed, 1099 deletions(-) delete mode 100644 web/packages/design/src/SVGIcon/AWS.tsx delete mode 100644 web/packages/design/src/SVGIcon/Ansible.tsx delete mode 100644 web/packages/design/src/SVGIcon/Azure.tsx delete mode 100644 web/packages/design/src/SVGIcon/Bug.tsx delete mode 100644 web/packages/design/src/SVGIcon/ChevronRight.tsx delete mode 100644 web/packages/design/src/SVGIcon/CircleCI.tsx delete mode 100644 web/packages/design/src/SVGIcon/ExternalLink.tsx delete mode 100644 web/packages/design/src/SVGIcon/GCP.tsx delete mode 100644 web/packages/design/src/SVGIcon/GitHub.tsx delete mode 100644 web/packages/design/src/SVGIcon/GitLab.tsx delete mode 100644 web/packages/design/src/SVGIcon/Jenkins.tsx delete mode 100644 web/packages/design/src/SVGIcon/Kubernetes.tsx delete mode 100644 web/packages/design/src/SVGIcon/Lock.tsx delete mode 100644 web/packages/design/src/SVGIcon/Okta.tsx delete mode 100644 web/packages/design/src/SVGIcon/ServerIcon.tsx delete mode 100644 web/packages/design/src/SVGIcon/Servers.tsx delete mode 100644 web/packages/design/src/SVGIcon/Spacelift.tsx delete mode 100644 web/packages/design/src/TopNav/TopNavUserMenu/avatar.png delete mode 100644 web/packages/design/src/assets/images/app-logo.svg delete mode 100644 web/packages/design/src/assets/images/icons/datadog.svg delete mode 100644 web/packages/design/src/assets/images/icons/discord.svg delete mode 100644 web/packages/design/src/assets/images/icons/email.svg delete mode 100644 web/packages/design/src/assets/images/icons/entra-id.svg delete mode 100644 web/packages/design/src/assets/images/icons/jamf.svg delete mode 100644 web/packages/design/src/assets/images/icons/jira.svg delete mode 100644 web/packages/design/src/assets/images/icons/mattermost.svg delete mode 100644 web/packages/design/src/assets/images/icons/msteams.svg delete mode 100644 web/packages/design/src/assets/images/icons/okta.svg delete mode 100644 web/packages/design/src/assets/images/icons/openai.svg delete mode 100644 web/packages/design/src/assets/images/icons/opsgenie.svg delete mode 100644 web/packages/design/src/assets/images/icons/pagerduty.svg delete mode 100644 web/packages/design/src/assets/images/icons/servicenow.svg delete mode 100644 web/packages/design/src/assets/images/icons/slack.svg delete mode 100644 web/packages/design/src/assets/images/icons/teleport.png delete mode 100644 web/packages/design/src/assets/images/kube-logo.svg delete mode 100644 web/packages/design/src/assets/images/sample-logo-long.svg delete mode 100644 web/packages/design/src/assets/images/sample-logo-squire.svg delete mode 100644 web/packages/design/src/assets/images/sec-key-graphic.svg delete mode 100644 web/packages/design/src/assets/resources/appplication.png delete mode 100644 web/packages/design/src/assets/resources/database.png delete mode 100644 web/packages/design/src/assets/resources/desktop.png delete mode 100644 web/packages/design/src/assets/resources/kubernetes.png delete mode 100644 web/packages/design/src/assets/resources/stack.png delete mode 100644 web/packages/teleport/src/components/LogoHero/LogoHeroDark.svg delete mode 100644 web/packages/teleport/src/components/LogoHero/LogoHeroLight.svg diff --git a/web/packages/design/src/SVGIcon/AWS.tsx b/web/packages/design/src/SVGIcon/AWS.tsx deleted file mode 100644 index 10299e2221092..0000000000000 --- a/web/packages/design/src/SVGIcon/AWS.tsx +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Teleport - * Copyright (C) 2023 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 . - */ - -import React from 'react'; - -import { SVGIcon } from './SVGIcon'; - -import type { SVGIconProps } from './common'; - -export function AWSIcon({ size = 24, fill }: SVGIconProps) { - return ( - - - - - - ); -} diff --git a/web/packages/design/src/SVGIcon/Ansible.tsx b/web/packages/design/src/SVGIcon/Ansible.tsx deleted file mode 100644 index 5f5f2c3982184..0000000000000 --- a/web/packages/design/src/SVGIcon/Ansible.tsx +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Teleport - * Copyright (C) 2023 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 . - */ - -import React from 'react'; - -import { SVGIcon } from './SVGIcon'; - -import type { SVGIconProps } from './common'; - -export function AnsibleIcon({ size = 100, fill }: SVGIconProps) { - return ( - - - - ); -} diff --git a/web/packages/design/src/SVGIcon/Azure.tsx b/web/packages/design/src/SVGIcon/Azure.tsx deleted file mode 100644 index 6ef707b86dc66..0000000000000 --- a/web/packages/design/src/SVGIcon/Azure.tsx +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Teleport - * Copyright (C) 2023 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 . - */ - -import React from 'react'; - -import { SVGIcon } from './SVGIcon'; - -import type { SVGIconProps } from './common'; - -export function AzureIcon({ size = 80, fill }: SVGIconProps) { - return ( - - - - - - - - - - - - - - - - - - - - - - - - ); -} diff --git a/web/packages/design/src/SVGIcon/Bug.tsx b/web/packages/design/src/SVGIcon/Bug.tsx deleted file mode 100644 index ca27faed1fc3a..0000000000000 --- a/web/packages/design/src/SVGIcon/Bug.tsx +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Teleport - * Copyright (C) 2023 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 . - */ - -import React from 'react'; - -import { SVGIcon } from './SVGIcon'; - -import type { SVGIconProps } from './common'; - -export function BugIcon({ size = 32, fill }: SVGIconProps) { - return ( - - - - ); -} diff --git a/web/packages/design/src/SVGIcon/ChevronRight.tsx b/web/packages/design/src/SVGIcon/ChevronRight.tsx deleted file mode 100644 index c0c8eeb52ea06..0000000000000 --- a/web/packages/design/src/SVGIcon/ChevronRight.tsx +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Teleport - * Copyright (C) 2023 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 . - */ - -import React from 'react'; - -import { SVGIcon } from './SVGIcon'; - -import type { SVGIconProps } from './common'; - -export function ChevronRightIcon({ size = 14, fill }: SVGIconProps) { - return ( - - - - ); -} diff --git a/web/packages/design/src/SVGIcon/CircleCI.tsx b/web/packages/design/src/SVGIcon/CircleCI.tsx deleted file mode 100644 index 36a6d56e04d3b..0000000000000 --- a/web/packages/design/src/SVGIcon/CircleCI.tsx +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Teleport - * Copyright (C) 2023 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 . - */ - -import React from 'react'; - -import { SVGIcon } from './SVGIcon'; - -import type { SVGIconProps } from './common'; - -export function CircleCIIcon({ size = 104, fill }: SVGIconProps) { - return ( - - - - ); -} diff --git a/web/packages/design/src/SVGIcon/ExternalLink.tsx b/web/packages/design/src/SVGIcon/ExternalLink.tsx deleted file mode 100644 index 42d87c2849e31..0000000000000 --- a/web/packages/design/src/SVGIcon/ExternalLink.tsx +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Teleport - * Copyright (C) 2023 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 . - */ - -import React from 'react'; - -import { SVGIcon } from './SVGIcon'; - -import type { SVGIconProps } from './common'; - -export function ExternalLinkIcon({ size = 22, fill }: SVGIconProps) { - return ( - - - - - - - - - - - - ); -} diff --git a/web/packages/design/src/SVGIcon/GCP.tsx b/web/packages/design/src/SVGIcon/GCP.tsx deleted file mode 100644 index a85103bb23e04..0000000000000 --- a/web/packages/design/src/SVGIcon/GCP.tsx +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Teleport - * Copyright (C) 2023 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 . - */ - -import React from 'react'; - -import { SVGIcon } from './SVGIcon'; - -import type { SVGIconProps } from './common'; - -export function GCPIcon({ size = 80, fill }: SVGIconProps) { - return ( - - - - - - - - - - - - - - ); -} diff --git a/web/packages/design/src/SVGIcon/GitHub.tsx b/web/packages/design/src/SVGIcon/GitHub.tsx deleted file mode 100644 index f1b786ec9cf98..0000000000000 --- a/web/packages/design/src/SVGIcon/GitHub.tsx +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Teleport - * Copyright (C) 2023 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 . - */ - -import React from 'react'; - -import { SVGIcon } from './SVGIcon'; - -import type { SVGIconProps } from './common'; - -export function GitHubIcon({ size = 98, fill }: SVGIconProps) { - return ( - - - - ); -} diff --git a/web/packages/design/src/SVGIcon/GitLab.tsx b/web/packages/design/src/SVGIcon/GitLab.tsx deleted file mode 100644 index f94c459a2c681..0000000000000 --- a/web/packages/design/src/SVGIcon/GitLab.tsx +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Teleport - * Copyright (C) 2023 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 . - */ - -import React from 'react'; - -import { SVGIcon } from './SVGIcon'; - -import type { SVGIconProps } from './common'; - -export function GitLabIcon({ size = 80, fill }: SVGIconProps) { - return ( - - - - ); -} diff --git a/web/packages/design/src/SVGIcon/Jenkins.tsx b/web/packages/design/src/SVGIcon/Jenkins.tsx deleted file mode 100644 index b77ffc9b794e7..0000000000000 --- a/web/packages/design/src/SVGIcon/Jenkins.tsx +++ /dev/null @@ -1,204 +0,0 @@ -/** - * Teleport - * Copyright (C) 2023 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 . - */ - -import React from 'react'; - -import { SVGIcon } from './SVGIcon'; - -import type { SVGIconProps } from './common'; - -export function JenkinsIcon({ size = 64, fill }: SVGIconProps) { - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); -} diff --git a/web/packages/design/src/SVGIcon/Kubernetes.tsx b/web/packages/design/src/SVGIcon/Kubernetes.tsx deleted file mode 100644 index a054283dfa956..0000000000000 --- a/web/packages/design/src/SVGIcon/Kubernetes.tsx +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Teleport - * Copyright (C) 2023 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 . - */ - -import React from 'react'; - -import { SVGIcon } from './SVGIcon'; - -import type { SVGIconProps } from './common'; - -export function KubernetesIcon({ size = 20, fill }: SVGIconProps) { - return ( - - - - ); -} diff --git a/web/packages/design/src/SVGIcon/Lock.tsx b/web/packages/design/src/SVGIcon/Lock.tsx deleted file mode 100644 index a4ff4f045e755..0000000000000 --- a/web/packages/design/src/SVGIcon/Lock.tsx +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Teleport - * Copyright (C) 2023 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 . - */ - -import React from 'react'; - -import { SVGIcon } from './SVGIcon'; - -import type { SVGIconProps } from './common'; - -export function LockIcon({ size = 13, fill }: SVGIconProps) { - return ( - - - - ); -} diff --git a/web/packages/design/src/SVGIcon/Okta.tsx b/web/packages/design/src/SVGIcon/Okta.tsx deleted file mode 100644 index 3482b359e88a5..0000000000000 --- a/web/packages/design/src/SVGIcon/Okta.tsx +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Teleport - * Copyright (C) 2023 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 . - */ - -import React from 'react'; - -import { SVGIcon } from './SVGIcon'; - -import type { SVGIconProps } from './common'; - -export function OktaIcon({ size = 16, fill }: SVGIconProps) { - return ( - - - - ); -} diff --git a/web/packages/design/src/SVGIcon/ServerIcon.tsx b/web/packages/design/src/SVGIcon/ServerIcon.tsx deleted file mode 100644 index 310682bda1003..0000000000000 --- a/web/packages/design/src/SVGIcon/ServerIcon.tsx +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Teleport - * Copyright (C) 2023 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 . - */ - -import React from 'react'; - -import { SVGIcon } from './SVGIcon'; - -import type { SVGIconProps } from './common'; - -export function ServerIcon({ size = 48, fill }: SVGIconProps) { - return ( - - - - ); -} diff --git a/web/packages/design/src/SVGIcon/Servers.tsx b/web/packages/design/src/SVGIcon/Servers.tsx deleted file mode 100644 index 2095c6aa38bd1..0000000000000 --- a/web/packages/design/src/SVGIcon/Servers.tsx +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Teleport - * Copyright (C) 2023 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 . - */ - -import React from 'react'; - -import { SVGIcon } from './SVGIcon'; - -import type { SVGIconProps } from './common'; - -export function ServersIcon({ size = 20, fill }: SVGIconProps) { - return ( - - - - ); -} diff --git a/web/packages/design/src/SVGIcon/Spacelift.tsx b/web/packages/design/src/SVGIcon/Spacelift.tsx deleted file mode 100644 index 0132c844cbef9..0000000000000 --- a/web/packages/design/src/SVGIcon/Spacelift.tsx +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Teleport - * Copyright (C) 2023 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 . - */ - -import React from 'react'; - -import { SVGIcon } from './SVGIcon'; - -import type { SVGIconProps } from './common'; - -export function SpaceliftIcon({ size = 80, fill }: SVGIconProps) { - return ( - - - - ); -} diff --git a/web/packages/design/src/SVGIcon/index.ts b/web/packages/design/src/SVGIcon/index.ts index 8e78eb016a1f8..279cf38217673 100644 --- a/web/packages/design/src/SVGIcon/index.ts +++ b/web/packages/design/src/SVGIcon/index.ts @@ -16,21 +16,6 @@ * along with this program. If not, see . */ -export { AnsibleIcon } from './Ansible'; -export { AWSIcon } from './AWS'; -export { AzureIcon } from './Azure'; -export { ChevronRightIcon } from './ChevronRight'; -export { CircleCIIcon } from './CircleCI'; -export { ExternalLinkIcon } from './ExternalLink'; -export { GCPIcon } from './GCP'; -export { GitHubIcon } from './GitHub'; -export { GitLabIcon } from './GitLab'; -export { JenkinsIcon } from './Jenkins'; -export { KubernetesIcon } from './Kubernetes'; -export { LockIcon } from './Lock'; -export { OktaIcon } from './Okta'; -export { ServersIcon } from './Servers'; -export { SpaceliftIcon } from './Spacelift'; export { TeleportGearIcon } from './TeleportGearIcon'; export { SVGIcon } from './SVGIcon'; diff --git a/web/packages/design/src/TopNav/TopNavUserMenu/avatar.png b/web/packages/design/src/TopNav/TopNavUserMenu/avatar.png deleted file mode 100644 index c79528ffa1e353875a10bf3be9eef9af8ca3dbe9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8316 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 ( alignItems: 'stretch', }} > - - - - ); diff --git a/web/packages/design/src/assets/images/app-logo.svg b/web/packages/design/src/assets/images/app-logo.svg deleted file mode 100644 index f704706501e22..0000000000000 --- a/web/packages/design/src/assets/images/app-logo.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/web/packages/design/src/assets/images/icons/datadog.svg b/web/packages/design/src/assets/images/icons/datadog.svg deleted file mode 100644 index e851c4436d798..0000000000000 --- a/web/packages/design/src/assets/images/icons/datadog.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/web/packages/design/src/assets/images/icons/discord.svg b/web/packages/design/src/assets/images/icons/discord.svg deleted file mode 100644 index bca8f6ac0863d..0000000000000 --- a/web/packages/design/src/assets/images/icons/discord.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/web/packages/design/src/assets/images/icons/email.svg b/web/packages/design/src/assets/images/icons/email.svg deleted file mode 100644 index a24036a1e929d..0000000000000 --- a/web/packages/design/src/assets/images/icons/email.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/web/packages/design/src/assets/images/icons/entra-id.svg b/web/packages/design/src/assets/images/icons/entra-id.svg deleted file mode 100644 index 0ed35fb73bcf4..0000000000000 --- a/web/packages/design/src/assets/images/icons/entra-id.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/web/packages/design/src/assets/images/icons/jamf.svg b/web/packages/design/src/assets/images/icons/jamf.svg deleted file mode 100644 index 8e3cd2d22ca95..0000000000000 --- a/web/packages/design/src/assets/images/icons/jamf.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/web/packages/design/src/assets/images/icons/jira.svg b/web/packages/design/src/assets/images/icons/jira.svg deleted file mode 100644 index 3ab897e391ebe..0000000000000 --- a/web/packages/design/src/assets/images/icons/jira.svg +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/web/packages/design/src/assets/images/icons/mattermost.svg b/web/packages/design/src/assets/images/icons/mattermost.svg deleted file mode 100644 index 4b0ea191ab04f..0000000000000 --- a/web/packages/design/src/assets/images/icons/mattermost.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/web/packages/design/src/assets/images/icons/msteams.svg b/web/packages/design/src/assets/images/icons/msteams.svg deleted file mode 100644 index 45bfe2d2ba9ee..0000000000000 --- a/web/packages/design/src/assets/images/icons/msteams.svg +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/web/packages/design/src/assets/images/icons/okta.svg b/web/packages/design/src/assets/images/icons/okta.svg deleted file mode 100644 index 0f59179983432..0000000000000 --- a/web/packages/design/src/assets/images/icons/okta.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/web/packages/design/src/assets/images/icons/openai.svg b/web/packages/design/src/assets/images/icons/openai.svg deleted file mode 100644 index e04db75a5bbdc..0000000000000 --- a/web/packages/design/src/assets/images/icons/openai.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/web/packages/design/src/assets/images/icons/opsgenie.svg b/web/packages/design/src/assets/images/icons/opsgenie.svg deleted file mode 100644 index 912e42191c799..0000000000000 --- a/web/packages/design/src/assets/images/icons/opsgenie.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/web/packages/design/src/assets/images/icons/pagerduty.svg b/web/packages/design/src/assets/images/icons/pagerduty.svg deleted file mode 100644 index 99f977df7575b..0000000000000 --- a/web/packages/design/src/assets/images/icons/pagerduty.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/web/packages/design/src/assets/images/icons/servicenow.svg b/web/packages/design/src/assets/images/icons/servicenow.svg deleted file mode 100644 index 58f5dd70f9639..0000000000000 --- a/web/packages/design/src/assets/images/icons/servicenow.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/web/packages/design/src/assets/images/icons/slack.svg b/web/packages/design/src/assets/images/icons/slack.svg deleted file mode 100644 index 832dfed8666db..0000000000000 --- a/web/packages/design/src/assets/images/icons/slack.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/web/packages/design/src/assets/images/icons/teleport.png b/web/packages/design/src/assets/images/icons/teleport.png deleted file mode 100644 index 385275f3bb38f0e95d80f06b1087bbd3f2963909..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1153 zcmV-{1b+L8P)Dt1QQN(?ZQHhO+qP}nwsmaVw(Z^C=lY&^noaU1*-fU;%x`$mf0=xnnx>|v zAnkz%@DX}qEM{RT7GNrdqZMAjC8&Z4N|0moF%xdPHmZZSf~IzeMRD*M&@>rUQ5^gf zG|k86Q5zgbD`-0G|3blhzz!dbrqu2ro7)T9x7&GB#DYV1hFczwD5osopGQI9jw3u$;Qg)Qv{ABq&bv=C2X9TYjcb)83M$W7?H5 z5}Nv=L=Nr;O^a|%77pUOLfdUHa`0);mcmmZeW_&vB8MEP~ z3o~7K9Ocu?rT1V%7=;9mKzDep6-w+bE#HP8&A|)U#l;Ka=b|ST!fPq)!y<>{Tj6=!j%#r<9>GWG4=44&+ZX}TKk^`LhNX}V#nspl#fafRj7HEsiYge? z;1IltMUcIUg!93``Iv_OxF0(qnc+C-Wz2-%b;W)#7HV)dzQI&KGVIDuFj zcVj%fl*SJ@*2VvyKt0N%_~_we#G>|a>9UseaA_q*LfhRf?E&! - - - kube-logo - Created with Sketch. - - - - - - - - - - - - - - \ No newline at end of file diff --git a/web/packages/design/src/assets/images/sample-logo-long.svg b/web/packages/design/src/assets/images/sample-logo-long.svg deleted file mode 100644 index c26150ee59fed..0000000000000 --- a/web/packages/design/src/assets/images/sample-logo-long.svg +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/web/packages/design/src/assets/images/sample-logo-squire.svg b/web/packages/design/src/assets/images/sample-logo-squire.svg deleted file mode 100644 index 140a385ae54cc..0000000000000 --- a/web/packages/design/src/assets/images/sample-logo-squire.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/web/packages/design/src/assets/images/sec-key-graphic.svg b/web/packages/design/src/assets/images/sec-key-graphic.svg deleted file mode 100644 index f447cef468ee3..0000000000000 --- a/web/packages/design/src/assets/images/sec-key-graphic.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/web/packages/design/src/assets/resources/appplication.png b/web/packages/design/src/assets/resources/appplication.png deleted file mode 100644 index b3e7f5e0949cd248fb4da666d01bf3a33de87df1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18476 zcmV)$K#sqOP)bo|NmX^N+5t}aSs;(;K>010I;t!2v|47)x!isEUE?-l|D}J5&aOG zx|Hr$^F^|Dq+Zf0!! zSKrF$jS~P(b%|qP6v~@|DSh%qzjse`Cm-Kj984nXd}+P@Ng%ma-PC_RlN>M?!_n!b zuyUjtc{|5|2$(6Z4tKXpS}%H+R!nZjw6<~U^{)cS&5n&&Am>?FApME_W@9+uQ;d8E zr1GkzP~ou??63HCXzJCTf+?IYg;tRj%HLp`@=pNSpr*Pt_xH6VQy9-2#qiN`0_98$ zC#PFrr)wUX(I-h1OixMJ#W4;Sy?AZB$#1WXTTN`C#KEi|$R-pfzZxJ>28Ptf7y`-5 z0rN1Nnug(scO|SErG=@z;)Pξv9R@nO)=Zh_9?^W_`g#;s|Jpe>63v+D=4MbM(k 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 diff --git a/web/packages/design/src/assets/resources/database.png b/web/packages/design/src/assets/resources/database.png deleted file mode 100644 index fb590e703489af6a51b7fa8c2ef1c0919b1de0a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10437 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 diff --git a/web/packages/design/src/assets/resources/desktop.png b/web/packages/design/src/assets/resources/desktop.png deleted file mode 100644 index 143310160277c9478a936ddf04036ffe47c5b92f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12806 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 diff --git a/web/packages/design/src/assets/resources/stack.png b/web/packages/design/src/assets/resources/stack.png deleted file mode 100644 index c0b898058c4d587b5db71ecbee17fa8ee6f04f42..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11100 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 gXL;2Bw!hwVV6=e*p72+yDRo diff --git a/web/packages/teleport/src/components/LogoHero/LogoHeroDark.svg b/web/packages/teleport/src/components/LogoHero/LogoHeroDark.svg deleted file mode 100644 index a2ed58580c7b9..0000000000000 --- a/web/packages/teleport/src/components/LogoHero/LogoHeroDark.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/web/packages/teleport/src/components/LogoHero/LogoHeroLight.svg b/web/packages/teleport/src/components/LogoHero/LogoHeroLight.svg deleted file mode 100644 index c1a61e63f909d..0000000000000 --- a/web/packages/teleport/src/components/LogoHero/LogoHeroLight.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file From 76080658e0797fb4284107dac8a3b88fcc6a742a Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Thu, 17 Oct 2024 10:53:23 +0100 Subject: [PATCH 02/30] [scankeys] support context cancellation to interrupt keys scan (#47653) This PR allow program cancellation propagation when CTRL+C is invoked during `tsh scan keys`. Signed-off-by: Tiago Silva --- lib/secretsscanner/scanner/scan.go | 4 ++ tool/tsh/common/scan.go | 17 ++++++-- tool/tsh/common/scan_test.go | 69 ++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 tool/tsh/common/scan_test.go diff --git a/lib/secretsscanner/scanner/scan.go b/lib/secretsscanner/scanner/scan.go index 664b6fcef8e67..79de0b89f0552 100644 --- a/lib/secretsscanner/scanner/scan.go +++ b/lib/secretsscanner/scanner/scan.go @@ -113,6 +113,10 @@ func (s *Scanner) findPrivateKeys(ctx context.Context, root, deviceID string, pr logger := s.log.With("root", root) err := filepath.WalkDir(root, func(path string, info fs.DirEntry, err error) error { + // check if the context is done before processing the file. + if ctx.Err() != nil { + return ctx.Err() + } if err != nil { logger.DebugContext(ctx, "error walking directory", "path", path, "error", err) return fs.SkipDir diff --git a/tool/tsh/common/scan.go b/tool/tsh/common/scan.go index 27bd644b80cda..606056adfb3b9 100644 --- a/tool/tsh/common/scan.go +++ b/tool/tsh/common/scan.go @@ -91,11 +91,12 @@ func (c *scanKeysCommand) run(cf *CLIConf) error { return trace.Wrap(err, "device not enrolled") } - fmt.Printf("Device trust credentials found.\nScanning %s.\n", strings.Join(c.dirs, ", ")) + dirs := splitCommaSeparatedSlice(c.dirs) + fmt.Printf("Device trust credentials found.\nScanning %s.\n", strings.Join(dirs, ", ")) scanner, err := secretsscanner.New(secretsscanner.Config{ - Dirs: c.dirs, - SkipPaths: c.skipPaths, + Dirs: dirs, + SkipPaths: splitCommaSeparatedSlice(c.skipPaths), Log: slog.Default(), }) if err != nil { @@ -168,3 +169,13 @@ func collectPrivateKeys(privateKeys []secretsscanner.SSHPrivateKey) []*accessgra } return keys } + +func splitCommaSeparatedSlice(s []string) []string { + var result []string + for _, entry := range s { + for _, split := range strings.Split(entry, ",") { + result = append(result, strings.TrimSpace(split)) + } + } + return result +} diff --git a/tool/tsh/common/scan_test.go b/tool/tsh/common/scan_test.go new file mode 100644 index 0000000000000..01fcc64c14abe --- /dev/null +++ b/tool/tsh/common/scan_test.go @@ -0,0 +1,69 @@ +/* + * 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 . + */ + +package common + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func Test_splitCommaSeparatedSlice(t *testing.T) { + + tests := []struct { + name string + args []string + want []string + }{ + { + name: "empty", + }, + { + name: "single dir", + args: []string{"dir1"}, + want: []string{"dir1"}, + }, + { + name: "multi dir", + args: []string{"dir1", "dir2"}, + want: []string{"dir1", "dir2"}, + }, + { + name: "multi dir comma separated", + args: []string{"dir1,dir2"}, + want: []string{"dir1", "dir2"}, + }, + { + name: "multi dir comma separated spaces", + args: []string{"dir1, dir2"}, + want: []string{"dir1", "dir2"}, + }, + { + name: "multi dir comma separated spaces", + args: []string{"dir1, dir2", "dir3"}, + want: []string{"dir1", "dir2", "dir3"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := splitCommaSeparatedSlice(tt.args) + require.Equal(t, tt.want, got) + }) + } +} From 822c5e13d6d1a710f2f9c7fbaf48066f99050831 Mon Sep 17 00:00:00 2001 From: Paul Gottschling Date: Thu, 17 Oct 2024 08:54:46 -0400 Subject: [PATCH 03/30] Move vale configs into `docs/` (#47575) Organize vale prose linter configurations into the `docs` directory so automated workflows treat them as Teleport documentation changes. Otherwise, vale style changes are classified as code changes, which is not correct. --- .github/workflows/doc-tests.yaml | 1 + .vale.ini => docs/.vale.ini | 2 +- .../vale-styles/3rd-party-products/aws-vs-amazon.yml | 0 .../vale-styles/3rd-party-products/former-names.yml | 0 {.github => docs}/vale-styles/examples/teleport-accounts.yml | 0 {.github => docs}/vale-styles/messaging/capitalization.yml | 0 {.github => docs}/vale-styles/messaging/consistent-terms.yml | 0 {.github => docs}/vale-styles/messaging/edition-names.yml | 0 {.github => docs}/vale-styles/messaging/protocol-products.yml | 0 {.github => docs}/vale-styles/messaging/subjective-terms.yml | 0 {.github => docs}/vale-styles/structure/architecture-h2.yml | 0 {.github => docs}/vale-styles/structure/step-numbering.yml | 0 12 files changed, 2 insertions(+), 1 deletion(-) rename .vale.ini => docs/.vale.ini (78%) rename {.github => docs}/vale-styles/3rd-party-products/aws-vs-amazon.yml (100%) rename {.github => docs}/vale-styles/3rd-party-products/former-names.yml (100%) rename {.github => docs}/vale-styles/examples/teleport-accounts.yml (100%) rename {.github => docs}/vale-styles/messaging/capitalization.yml (100%) rename {.github => docs}/vale-styles/messaging/consistent-terms.yml (100%) rename {.github => docs}/vale-styles/messaging/edition-names.yml (100%) rename {.github => docs}/vale-styles/messaging/protocol-products.yml (100%) rename {.github => docs}/vale-styles/messaging/subjective-terms.yml (100%) rename {.github => docs}/vale-styles/structure/architecture-h2.yml (100%) rename {.github => docs}/vale-styles/structure/step-numbering.yml (100%) diff --git a/.github/workflows/doc-tests.yaml b/.github/workflows/doc-tests.yaml index cc5acd19fe25d..5030bebe367b1 100644 --- a/.github/workflows/doc-tests.yaml +++ b/.github/workflows/doc-tests.yaml @@ -120,4 +120,5 @@ jobs: # changed files. filter_mode: added fail_on_error: true + vale_flags: "--config=docs/.vale.ini" diff --git a/.vale.ini b/docs/.vale.ini similarity index 78% rename from .vale.ini rename to docs/.vale.ini index a780ee1b01f24..9620898c3adba 100644 --- a/.vale.ini +++ b/docs/.vale.ini @@ -1,4 +1,4 @@ -StylesPath = .github/vale-styles +StylesPath = vale-styles MinAlertLevel = suggestion [formats] diff --git a/.github/vale-styles/3rd-party-products/aws-vs-amazon.yml b/docs/vale-styles/3rd-party-products/aws-vs-amazon.yml similarity index 100% rename from .github/vale-styles/3rd-party-products/aws-vs-amazon.yml rename to docs/vale-styles/3rd-party-products/aws-vs-amazon.yml diff --git a/.github/vale-styles/3rd-party-products/former-names.yml b/docs/vale-styles/3rd-party-products/former-names.yml similarity index 100% rename from .github/vale-styles/3rd-party-products/former-names.yml rename to docs/vale-styles/3rd-party-products/former-names.yml diff --git a/.github/vale-styles/examples/teleport-accounts.yml b/docs/vale-styles/examples/teleport-accounts.yml similarity index 100% rename from .github/vale-styles/examples/teleport-accounts.yml rename to docs/vale-styles/examples/teleport-accounts.yml diff --git a/.github/vale-styles/messaging/capitalization.yml b/docs/vale-styles/messaging/capitalization.yml similarity index 100% rename from .github/vale-styles/messaging/capitalization.yml rename to docs/vale-styles/messaging/capitalization.yml diff --git a/.github/vale-styles/messaging/consistent-terms.yml b/docs/vale-styles/messaging/consistent-terms.yml similarity index 100% rename from .github/vale-styles/messaging/consistent-terms.yml rename to docs/vale-styles/messaging/consistent-terms.yml diff --git a/.github/vale-styles/messaging/edition-names.yml b/docs/vale-styles/messaging/edition-names.yml similarity index 100% rename from .github/vale-styles/messaging/edition-names.yml rename to docs/vale-styles/messaging/edition-names.yml diff --git a/.github/vale-styles/messaging/protocol-products.yml b/docs/vale-styles/messaging/protocol-products.yml similarity index 100% rename from .github/vale-styles/messaging/protocol-products.yml rename to docs/vale-styles/messaging/protocol-products.yml diff --git a/.github/vale-styles/messaging/subjective-terms.yml b/docs/vale-styles/messaging/subjective-terms.yml similarity index 100% rename from .github/vale-styles/messaging/subjective-terms.yml rename to docs/vale-styles/messaging/subjective-terms.yml diff --git a/.github/vale-styles/structure/architecture-h2.yml b/docs/vale-styles/structure/architecture-h2.yml similarity index 100% rename from .github/vale-styles/structure/architecture-h2.yml rename to docs/vale-styles/structure/architecture-h2.yml diff --git a/.github/vale-styles/structure/step-numbering.yml b/docs/vale-styles/structure/step-numbering.yml similarity index 100% rename from .github/vale-styles/structure/step-numbering.yml rename to docs/vale-styles/structure/step-numbering.yml From 3e75921c8a6e146f7962b28fcfde575e7ca7d8a4 Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Thu, 17 Oct 2024 14:55:00 +0200 Subject: [PATCH 04/30] Convert `lib/proxy/peer` to slog (#47649) * Convert lib/proxy/peer to slog * . * style compliance with RFD 0154 --- lib/proxy/peer/client.go | 23 +++++++++++------------ lib/proxy/peer/credentials.go | 13 +++++++------ lib/proxy/peer/credentials_test.go | 2 +- lib/proxy/peer/service.go | 4 ++-- lib/service/service.go | 2 +- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/lib/proxy/peer/client.go b/lib/proxy/peer/client.go index 227e60772615f..c00b67006875f 100644 --- a/lib/proxy/peer/client.go +++ b/lib/proxy/peer/client.go @@ -21,6 +21,7 @@ package peer import ( "context" "crypto/tls" + "log/slog" "math/rand/v2" "net" "sync" @@ -29,7 +30,6 @@ import ( "github.com/gravitational/trace" "github.com/jonboulle/clockwork" "github.com/quic-go/quic-go" - "github.com/sirupsen/logrus" "google.golang.org/grpc" "google.golang.org/grpc/connectivity" "google.golang.org/grpc/credentials" @@ -72,7 +72,7 @@ type ClientConfig struct { // TLSCipherSuites optionally contains a list of TLS ciphersuites to use. TLSCipherSuites []uint16 // Log is the proxy client logger. - Log logrus.FieldLogger + Log *slog.Logger // Clock is used to control connection monitoring ticker. Clock clockwork.Clock // GracefulShutdownTimout is used set the graceful shutdown @@ -112,10 +112,10 @@ func noopConnShuffler() connShuffler { // checkAndSetDefaults checks and sets default values func (c *ClientConfig) checkAndSetDefaults() error { if c.Log == nil { - c.Log = logrus.New() + c.Log = slog.Default() } - c.Log = c.Log.WithField( + c.Log = c.Log.With( teleport.ComponentKey, teleport.Component(teleport.ComponentProxyPeer), ) @@ -414,15 +414,14 @@ func (c *Client) sync() { ResourceWatcherConfig: services.ResourceWatcherConfig{ Component: teleport.Component(teleport.ComponentProxyPeer), Client: c.config.AccessPoint, - // TODO(tross): use the configured logger after updating peering to use slog - // Logger: c.config.Logger, + Logger: c.config.Log, }, ProxyDiffer: func(old, new types.Server) bool { return old.GetPeerAddr() != new.GetPeerAddr() }, }) if err != nil { - c.config.Log.Errorf("Error initializing proxy peer watcher: %+v.", err) + c.config.Log.ErrorContext(c.ctx, "error initializing proxy peer watcher", "error", err) return } defer proxyWatcher.Close() @@ -430,14 +429,14 @@ func (c *Client) sync() { for { select { case <-c.ctx.Done(): - c.config.Log.Debug("Stopping peer proxy sync: context done.") + c.config.Log.DebugContext(c.ctx, "stopping peer proxy sync: context done") return case <-proxyWatcher.Done(): - c.config.Log.Debug("Stopping peer proxy sync: proxy watcher done.") + c.config.Log.DebugContext(c.ctx, "stopping peer proxy sync: proxy watcher done") return case proxies := <-proxyWatcher.ProxiesC: if err := c.updateConnections(proxies); err != nil { - c.config.Log.Errorf("Error syncing peer proxies: %+v.", err) + c.config.Log.ErrorContext(c.ctx, "error syncing peer proxies", "error", err) } } } @@ -488,7 +487,7 @@ func (c *Client) updateConnections(proxies []types.Server) error { conn, err := c.connect(id, proxy.GetPeerAddr(), supportsQuic) if err != nil { c.metrics.reportTunnelError(errorProxyPeerTunnelDial) - c.config.Log.Debugf("Error dialing peer proxy %+v at %+v", id, proxy.GetPeerAddr()) + c.config.Log.DebugContext(c.ctx, "error dialing peer proxy", "peer_id", id, "peer_addr", proxy.GetPeerAddr()) errs = append(errs, err) continue } @@ -689,7 +688,7 @@ func (c *Client) getConnections(proxyIDs []string) ([]clientConn, bool, error) { conn, err := c.connect(id, proxy.GetPeerAddr(), supportsQuic) if err != nil { c.metrics.reportTunnelError(errorProxyPeerTunnelDirectDial) - c.config.Log.Debugf("Error direct dialing peer proxy %+v at %+v", id, proxy.GetPeerAddr()) + c.config.Log.DebugContext(c.ctx, "error direct dialing peer proxy", "peer_id", id, "peer_addr", proxy.GetPeerAddr()) errs = append(errs, err) continue } diff --git a/lib/proxy/peer/credentials.go b/lib/proxy/peer/credentials.go index 4767102704eba..5ed14d22ef6ae 100644 --- a/lib/proxy/peer/credentials.go +++ b/lib/proxy/peer/credentials.go @@ -20,10 +20,10 @@ package peer import ( "context" + "log/slog" "net" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "google.golang.org/grpc/credentials" "github.com/gravitational/teleport/api/types" @@ -37,16 +37,16 @@ type clientCredentials struct { credentials.TransportCredentials peerID string peerAddr string - logger logrus.FieldLogger + log *slog.Logger } // newClientCredentials creates new clientCredentials from the given [crendentials.TransportCredentials]. -func newClientCredentials(peerID, peerAddr string, logger logrus.FieldLogger, creds credentials.TransportCredentials) *clientCredentials { +func newClientCredentials(peerID, peerAddr string, log *slog.Logger, creds credentials.TransportCredentials) *clientCredentials { return &clientCredentials{ TransportCredentials: creds, peerID: peerID, peerAddr: peerAddr, - logger: logger, + log: log, } } @@ -73,15 +73,16 @@ func (c *clientCredentials) ClientHandshake(ctx context.Context, laddr string, c return nil, nil, trace.Wrap(err) } - const duplicatePeerMsg = "Detected multiple Proxy Peers with the same public address %q when connecting to Proxy %q which can lead to inconsistent state and problems establishing sessions. For best results ensure that `peer_public_addr` is unique per proxy and not a load balancer." if err := validatePeer(c.peerID, identity); err != nil { - c.logger.Errorf(duplicatePeerMsg, c.peerAddr, c.peerID) + c.log.ErrorContext(ctx, duplicatePeerMsg, "peer_addr", c.peerAddr, "peer_id", c.peerID) return nil, nil, trace.Wrap(err) } return conn, authInfo, nil } +const duplicatePeerMsg = "Detected multiple Proxy Peers with the same public address when connecting to a Proxy which can lead to inconsistent state and problems establishing sessions. For best results ensure that `peer_public_addr` is unique per proxy and not a load balancer." + // getIdentity returns a [tlsca.Identity] that is created from the certificate // presented during the TLS handshake. func getIdentity(authInfo credentials.AuthInfo) (*tlsca.Identity, error) { diff --git a/lib/proxy/peer/credentials_test.go b/lib/proxy/peer/credentials_test.go index d425aa0b08f73..b2862677c9e1c 100644 --- a/lib/proxy/peer/credentials_test.go +++ b/lib/proxy/peer/credentials_test.go @@ -113,7 +113,7 @@ func TestClientCredentials(t *testing.T) { }, } - creds := newClientCredentials(test.expectedPeerID, test.peerAddr, utils.NewLoggerForTests(), newTestCredentials(cert)) + creds := newClientCredentials(test.expectedPeerID, test.peerAddr, utils.NewSlogLoggerForTests(), newTestCredentials(cert)) ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() diff --git a/lib/proxy/peer/service.go b/lib/proxy/peer/service.go index 70610b75120b8..4423892ae04f1 100644 --- a/lib/proxy/peer/service.go +++ b/lib/proxy/peer/service.go @@ -59,7 +59,7 @@ func (s *proxyService) DialNode(stream proto.ProxyService_DialNodeServer) error "src", dial.Source.Addr, "dst", dial.Destination.Addr, ) - log.DebugContext(stream.Context(), "Dial request from peer.") + log.DebugContext(stream.Context(), "dial request from peer") _, clusterName, err := splitServerID(dial.NodeID) if err != nil { @@ -103,7 +103,7 @@ func (s *proxyService) DialNode(stream proto.ProxyService_DialNodeServer) error err = utils.ProxyConn(stream.Context(), streamConn, nodeConn) sent, received := streamConn.Stat() - log.DebugContext(stream.Context(), "Closing dial request from peer.", "sent", sent, "received", received) + log.DebugContext(stream.Context(), "closing dial request from peer", "sent", sent, "received", received) return trace.Wrap(err) } diff --git a/lib/service/service.go b/lib/service/service.go index d012be45b3b7f..0870695cef8fd 100644 --- a/lib/service/service.go +++ b/lib/service/service.go @@ -4347,7 +4347,7 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { TLSCipherSuites: cfg.CipherSuites, GetTLSCertificate: conn.ClientGetCertificate, GetTLSRoots: conn.ClientGetPool, - Log: process.log, + Log: process.logger, Clock: process.Clock, ClusterName: clusterName, QUICTransport: peerQUICTransport, From c1a6e174db58d15e00c5edea6534df1b723214cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Skrz=C4=99tnicki?= Date: Thu, 17 Oct 2024 16:25:00 +0200 Subject: [PATCH 05/30] Don't warn about missing libfido2 for Windows builds. (#47662) --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 0602a372df13c..ca5fa33f3f9e1 100644 --- a/Makefile +++ b/Makefile @@ -361,7 +361,7 @@ binaries: # until we can use this Makefile for native Windows builds. .PHONY: $(BUILDDIR)/tctl $(BUILDDIR)/tctl: - @if [[ -z "$(LIBFIDO2_BUILD_TAG)" ]]; then \ + @if [[ "$(OS)" != "windows" && -z "$(LIBFIDO2_BUILD_TAG)" ]]; then \ echo 'Warning: Building tctl without libfido2. Install libfido2 to have access to MFA.' >&2; \ fi GOOS=$(OS) GOARCH=$(ARCH) $(CGOFLAG) go build -tags "$(PAM_TAG) $(FIPS_TAG) $(LIBFIDO2_BUILD_TAG) $(PIV_BUILD_TAG) $(KUSTOMIZE_NO_DYNAMIC_PLUGIN)" -o $(BUILDDIR)/tctl $(BUILDFLAGS) ./tool/tctl @@ -382,7 +382,7 @@ $(BUILDDIR)/teleport: ensure-webassets bpf-bytecode rdpclient $(BUILDDIR)/tsh: KUBECTL_VERSION ?= $(shell go run ./build.assets/kubectl-version/main.go) $(BUILDDIR)/tsh: KUBECTL_SETVERSION ?= -X k8s.io/component-base/version.gitVersion=$(KUBECTL_VERSION) $(BUILDDIR)/tsh: - @if [[ -z "$(LIBFIDO2_BUILD_TAG)" ]]; then \ + @if [[ "$(OS)" != "windows" && -z "$(LIBFIDO2_BUILD_TAG)" ]]; then \ echo 'Warning: Building tsh without libfido2. Install libfido2 to have access to MFA.' >&2; \ fi GOOS=$(OS) GOARCH=$(ARCH) $(CGOFLAG_TSH) go build -tags "$(FIPS_TAG) $(LIBFIDO2_BUILD_TAG) $(TOUCHID_TAG) $(PIV_BUILD_TAG) $(VNETDAEMON_TAG) $(KUSTOMIZE_NO_DYNAMIC_PLUGIN)" -o $(BUILDDIR)/tsh $(BUILDFLAGS) ./tool/tsh From fdf9e25a1039f0fd8a5dadf0051c59383abd2cb9 Mon Sep 17 00:00:00 2001 From: Paul Gottschling Date: Thu, 17 Oct 2024 10:28:05 -0400 Subject: [PATCH 06/30] Simplify how-to guides (#47586) Closes #38931 Remove "Static" and "Dynamic" configuration branching. Use the dynamic approach unless the guide already shows readers how to use the static approach elsewhere, e.g., using `teleport db configure create`. For example, in the AWS OpenSearch guide, creating a Teleport configuration is required for the guide anyway, so this change uses only the static approach. Ignore edge cases where the branching has some qualities that are unique to a specific guide: - `application-access/cloud-apis/google-cloud.mdx`: includes branching on authentication preference type. - `application-access/guides/connecting-apps.mdx` because it explains syntax differences between the static and dynamic approaches. - `enroll-aws-databases/aws-cross-account.mdx`: branches using three tabs, so editing requires another approach than the one this change applies. This change edits only how-to guides, not conceptual guides, since the goal of a how-to guide is to get the user to an end state as quickly as possible. --- .../device-trust/device-management.mdx | 66 ++------- .../device-trust/enforcing-device-trust.mdx | 47 ++----- .../guides/mfa-for-admin-actions.mdx | 74 ++++------ .../access-controls/guides/passwordless.mdx | 129 +++--------------- .../guides/per-session-mfa.mdx | 49 +------ docs/pages/connect-your-client/web-ui.mdx | 15 -- .../enroll-aws-databases/aws-opensearch.mdx | 56 -------- .../oracle-self-hosted.mdx | 21 +-- .../db-definition-default-dbname.mdx | 26 +--- .../db-definition-self-hosted.mdx | 21 +-- .../auto-user-provisioning/db-definition.mdx | 23 +--- .../enterprise/oidcauthentication.mdx | 52 +++---- .../enterprise/samlauthentication.mdx | 19 --- 13 files changed, 104 insertions(+), 494 deletions(-) diff --git a/docs/pages/admin-guides/access-controls/device-trust/device-management.mdx b/docs/pages/admin-guides/access-controls/device-trust/device-management.mdx index ef200e46dcd68..57f482872f4a5 100644 --- a/docs/pages/admin-guides/access-controls/device-trust/device-management.mdx +++ b/docs/pages/admin-guides/access-controls/device-trust/device-management.mdx @@ -138,11 +138,14 @@ For auto-enrollment to work, the following conditions must be met: integration, like the [Jamf Pro integration](./jamf-integration.mdx). - Auto-enrollment must be enabled in the cluster setting. -Enable auto-enrollment in your cluster settings: +Enable auto-enrollment in your cluster settings. To do so, modify the dynamic +config resource using the following command: - - -Modify the dynamic config resource using `tctl edit cluster_auth_preference`: +```code +$ tctl edit cluster_auth_preference +``` + +Make the following change: ```diff kind: cluster_auth_preference @@ -156,22 +159,7 @@ spec: + auto_enroll: true ``` - - -Edit the Auth Server's `teleport.yaml` file: - -```diff -auth_service: - authentication: - # ... - device_trust: -+ auto_enroll: true -``` - -After saving the changes, restart the Teleport service. - - - +Save and close your editor to apply your changes. Once enabled, users with their device registered in Teleport will have their device enrolled to Teleport in their next login. @@ -244,18 +232,14 @@ To configure `ekcert_allowed_cas`, you must first obtain the CA certificate in PEM format from the manufacturer of the TPM included in your devices. This step varies from manufacturer to manufacturer. -After you obtain the CA certificate in PEM format, there are two ways of -configuring `ekcert_allowed_cas`: +After you obtain the CA certificate in PEM format, modify the dynamic config +resource using the following command: -- Statically using the Teleport configuration file. This is the simplest - option, but is not possible for Teleport Cloud clusters and not recommended - for clusters in a highly available configuration. -- Dynamically using `cluster_auth_preference` resource. This works with all - clusters and is the most flexible. +```code +$ tctl edit cluster_auth_preference +``` - - -Modify the dynamic config resource using `tctl edit cluster_auth_preference`: +Make the following change: ```diff kind: cluster_auth_preference @@ -274,27 +258,7 @@ spec: + -----END CERTIFICATE----- ``` - - -Edit the Auth Server's `teleport.yaml` file and restart: - -```diff -auth_service: - authentication: - ... - device_trust: -+ ekcert_allowed_cas: -+ # The CA can be configured inline within the configuration file: -+ - | -+ -----BEGIN CERTIFICATE----- -+ --snip-- -+ -----END CERTIFICATE----- -+ # Or, it can be configured in the configuration file using a path: -+ - /path/to/my/ekcert-ca.pem -``` - - - +Save and close your editor to apply your changes. ## Troubleshooting diff --git a/docs/pages/admin-guides/access-controls/device-trust/enforcing-device-trust.mdx b/docs/pages/admin-guides/access-controls/device-trust/enforcing-device-trust.mdx index 3bb1fff1e7647..9ac5c819060c2 100644 --- a/docs/pages/admin-guides/access-controls/device-trust/enforcing-device-trust.mdx +++ b/docs/pages/admin-guides/access-controls/device-trust/enforcing-device-trust.mdx @@ -96,10 +96,13 @@ accesses. To enable device mode `required` update your configuration as follows: - - -Create a `cap.yaml` file or get the existing configuration using -`tctl get cluster_auth_preference`: +Edit your cluster authentication preference using the following command: + +```code +$ tctl edit cluster_auth_preference +``` + +Make the following change: ```diff kind: cluster_auth_preference @@ -115,39 +118,11 @@ spec: + mode: "required" # add this line ``` -Update the configuration: - -```code -$ tctl create -f cap.yaml -cluster auth preference has been updated -``` - -You can also edit this configuration directly: - -```code -$ tctl edit cluster_auth_preference -``` - - - -Edit the Auth Server's `teleport.yaml` file and restart all Auth Services: - -```diff -auth_service: - authentication: - type: local - second_factor: "on" - webauthn: - rp_id: (=clusterDefaults.clusterName=) - device_trust: -+ mode: "required" # add this line -``` - - - +Save and close your editor to apply your changes. -Once the config is updated, SSH, Database and Kubernetes access without a trusted device will be forbidden. -For example, SSH access without a trusted device fails with the following error: +Once the config is updated, SSH, Database and Kubernetes access without a +trusted device will be forbidden. For example, SSH access without a trusted +device fails with the following error: ```code $ tsh ssh (=clusterDefaults.nodeIP=) diff --git a/docs/pages/admin-guides/access-controls/guides/mfa-for-admin-actions.mdx b/docs/pages/admin-guides/access-controls/guides/mfa-for-admin-actions.mdx index a3cab5e3e3b5b..809a3b193ba8d 100644 --- a/docs/pages/admin-guides/access-controls/guides/mfa-for-admin-actions.mdx +++ b/docs/pages/admin-guides/access-controls/guides/mfa-for-admin-actions.mdx @@ -56,50 +56,30 @@ WebAuthn is the only form of second factor allowed. for a wider range of cluster configurations. - - - - Edit the `cluster_auth_preference` resource: - - ```code - $ tctl edit cap - ``` - - Update the `cluster_auth_preference` definition to include the following content: - - ```yaml - kind: cluster_auth_preference - version: v2 - metadata: - name: cluster-auth-preference - spec: - type: local - # To make webauthn the only form of second factor allowed, set this field to 'webauthn'. - second_factor: "webauthn" - webauthn: - rp_id: example.com - ``` - - Save and exit the file. `tctl` will update the remote definition: - - ```text - cluster auth preference has been updated - ``` - - - - Edit the Auth Service's `teleport.yaml` file and restart all Auth Service instances: - - ```yaml - # snippet from /etc/teleport.yaml: - auth_service: - authentication: - type: local - # To make webauthn the only form of second factor allowed, set this field to 'webauthn'. - second_factor: "webauthn" - webauthn: - rp_id: example.com - ``` - - - +Edit the `cluster_auth_preference` resource: + +```code +$ tctl edit cap +``` + +Update the `cluster_auth_preference` definition to include the following content: + +```yaml +kind: cluster_auth_preference +version: v2 +metadata: + name: cluster-auth-preference +spec: + type: local + # To make webauthn the only form of second factor allowed, set this field to 'webauthn'. + second_factor: "webauthn" + webauthn: + rp_id: example.com +``` + +Save and exit the file. `tctl` will update the remote definition: + +```text +cluster auth preference has been updated +``` + diff --git a/docs/pages/admin-guides/access-controls/guides/passwordless.mdx b/docs/pages/admin-guides/access-controls/guides/passwordless.mdx index 1ece97815ff4c..a2931cda9ed6c 100644 --- a/docs/pages/admin-guides/access-controls/guides/passwordless.mdx +++ b/docs/pages/admin-guides/access-controls/guides/passwordless.mdx @@ -119,54 +119,17 @@ using `tsh login --proxy=example.com --auth=local` in order to get their first passwordless registration. To enable passwordless by default, add `connector_name: passwordless` to your -cluster configuration: +cluster configuration. - - - - - Auth Server `teleport.yaml` file: - - ```yaml - auth_service: - authentication: - type: local - second_factor: on - webauthn: - rp_id: example.com - connector_name: passwordless # passwordless by default - ``` - - - Create a `cap.yaml` file or get the existing configuration using - `tctl get cluster_auth_preference`: - - ```yaml - kind: cluster_auth_preference - version: v2 - metadata: - name: cluster-auth-preference - spec: - type: local - second_factor: "on" - webauthn: - rp_id: example.com - connector_name: passwordless # passwordless by default - ``` - - Update the configuration: - - ```code - $ tctl create -f cap.yaml - # cluster auth preference has been updated - ``` - - - +Edit your cluster authentication preference configuration using the following +command: + +```code +$ tctl edit cluster_auth_preference +``` - -Create a `cap.yaml` file or get the existing configuration using -`tctl get cluster_auth_preference`: +Ensure that the configuration includes the `connector_name` field as shown +below: ```yaml kind: cluster_auth_preference @@ -181,16 +144,6 @@ spec: connector_name: passwordless # passwordless by default ``` -Update the configuration: - -```code -$ tctl create -f cap.yaml -# cluster auth preference has been updated -``` - - - - ## Troubleshooting ### "Allow passwordless logins" doesn't appear @@ -259,55 +212,15 @@ $ tsh webauthnwin diag ### Disable passwordless If you want to forbid passwordless access to your cluster, add `passwordless: -false` to your configuration: +false` to your configuration. Edit your cluster authentication preference using +the following command: - - - - - Auth Server `teleport.yaml` file: - - ```yaml - # snippet from /etc/teleport.yaml: - auth_service: - authentication: - type: local - second_factor: on - webauthn: - rp_id: example.com - passwordless: false # disable passwordless - ``` - - - Create a `cap.yaml` file or get the existing configuration using - `tctl get cluster_auth_preference`: - - ```yaml - kind: cluster_auth_preference - version: v2 - metadata: - name: cluster-auth-preference - spec: - type: local - second_factor: "on" - webauthn: - rp_id: example.com - passwordless: false # disable passwordless - ``` - - Update the configuration: - - ```code - $ tctl create -f cap.yaml - # cluster auth preference has been updated - ``` - - - +```code +$ tctl edit cluster_auth_preference +``` - -Create a `cap.yaml` file or get the existing configuration using -`tctl get cluster_auth_preference`: +In your editor, ensure that your `cluster_auth_preference` includes a +`passwordless` field similar to the following: ```yaml kind: cluster_auth_preference @@ -322,15 +235,7 @@ spec: passwordless: false # disable passwordless ``` -Update the configuration: - -```code -$ tctl create -f cap.yaml -# cluster auth preference has been updated -``` - - - +Save and close your editor to apply your changes. ### Why did my multi-factor authentication (MFA) device become a passkey? diff --git a/docs/pages/admin-guides/access-controls/guides/per-session-mfa.mdx b/docs/pages/admin-guides/access-controls/guides/per-session-mfa.mdx index e09365dd95da0..c2fa8ed63f9ef 100644 --- a/docs/pages/admin-guides/access-controls/guides/per-session-mfa.mdx +++ b/docs/pages/admin-guides/access-controls/guides/per-session-mfa.mdx @@ -56,51 +56,8 @@ Per-session MFA can be enforced cluster-wide or only for some specific roles. ### Cluster-wide - - - To enforce MFA checks for all roles, edit your cluster authentication -configuration: - - - - -Update `teleport.yaml` on the Auth Server to include the following content: - -```yaml -auth_service: - authentication: - # require per-session MFA cluster-wide - require_session_mfa: yes -``` - - - - -Edit your `cluster_auth_preference` resource: - -```code -$ tctl edit cap -``` - -Ensure that the resource contains the following content: - -```yaml -kind: cluster_auth_preference -metadata: - name: cluster-auth-preference -spec: - require_session_mfa: true -version: v2 -``` - -Apply your changes by saving and closing the file in your editor. - - - - - - +configuration. Edit your `cluster_auth_preference` resource: @@ -121,10 +78,6 @@ version: v2 Apply your changes by saving and closing the file in your editor. - - - - ### Per role To enforce MFA checks for a specific role, update the role to contain: diff --git a/docs/pages/connect-your-client/web-ui.mdx b/docs/pages/connect-your-client/web-ui.mdx index 22b8c6202d1d5..be05318f09d57 100644 --- a/docs/pages/connect-your-client/web-ui.mdx +++ b/docs/pages/connect-your-client/web-ui.mdx @@ -40,9 +40,6 @@ interacted with any Web UI browser tab, either through keyboard input or mouse m To change the default idle timeout of 10 minutes, ask your cluster admin to adjust the `web_idle_timeout` setting in the Auth Service configuration. - - - Use `tctl` to edit the `cluster_networking_config` value: ```code @@ -68,15 +65,3 @@ After you save and exit the editor, `tctl` will update the resource: cluster networking configuration has been updated ``` - - - -Update `/etc/teleport.yaml` in the `auth_service` section and restart the `teleport` daemon. - -```yaml -auth_service: - web_idle_timeout: 10m0s -``` - - - diff --git a/docs/pages/enroll-resources/database-access/enroll-aws-databases/aws-opensearch.mdx b/docs/pages/enroll-resources/database-access/enroll-aws-databases/aws-opensearch.mdx index 5fd5b7cffb2e7..bc4a2f57b5bce 100644 --- a/docs/pages/enroll-resources/database-access/enroll-aws-databases/aws-opensearch.mdx +++ b/docs/pages/enroll-resources/database-access/enroll-aws-databases/aws-opensearch.mdx @@ -184,9 +184,6 @@ Use the token provided by the output of this command in the next step. (!docs/pages/includes/install-linux.mdx!) - - - On the host where you will run the Teleport Database Service, start Teleport with the appropriate configuration. @@ -214,59 +211,6 @@ $ sudo teleport db configure create \ (!docs/pages/includes/start-teleport.mdx service="the Teleport Database Service"!) - - - -Modify your Teleport Database Service static configuration file: - -```yaml -db_service: - enabled: "yes" - databases: - - name: example-opensearch - aws: - account_id: "(=aws.aws_access_key=)" - protocol: opensearch - uri: :443 - static_labels: - env: dev -``` - -Restart the Teleport Database Service for the configuration file changes to take -effect. - - - -Create a dynamic database resource to dynamically register an AWS database -in an external account and proxy connections to it. - -```yaml -kind: db -version: v3 -metadata: - name: "example-opensearch" - description: "Example dynamic database resource" - labels: - env: "dev" -spec: - protocol: "opensearch" - uri: :443 - aws: - account_id: "(=aws.aws_access_key=)" -``` - -Save the configuration to a file like `database.yaml` and create it with `tctl`: - -```code -$ tctl create database.yaml -``` - -For more information about database registration using dynamic database -resources, see: [Dynamic Registration](../guides/dynamic-registration.mdx). - - - - ## Step 4/4. Connect Once the Database Service has started and joined the cluster, you can start accessing Amazon OpenSearch API: diff --git a/docs/pages/enroll-resources/database-access/enroll-self-hosted-databases/oracle-self-hosted.mdx b/docs/pages/enroll-resources/database-access/enroll-self-hosted-databases/oracle-self-hosted.mdx index f66f25e7317a4..17f1aef4d7455 100644 --- a/docs/pages/enroll-resources/database-access/enroll-self-hosted-databases/oracle-self-hosted.mdx +++ b/docs/pages/enroll-resources/database-access/enroll-self-hosted-databases/oracle-self-hosted.mdx @@ -204,10 +204,9 @@ AUDIT ALL STATEMENTS by alice BY access; You must enable auditing for each Teleport user that will be used to connect to Oracle. Additionally you can create a different audit policy for each user. -Configure the Teleport Database Service to pull audit logs from Oracle Audit Trail: +Edit the Database Service configuration you created earlier pull audit logs from +Oracle Audit Trail: - - ```yaml db_service: enabled: "yes" @@ -218,22 +217,6 @@ db_service: oracle: audit_user: "teleport" ``` - - -```yaml -kind: db -version: v3 -metadata: - name: oracle -spec: - protocol: "oracle" - uri: "oracle.example.com:2484" - oracle: - audit_user: "teleport" -``` - - - Teleport doesn't clean up audit trail events from Oracle Audit Trail. Make sure to configure an Oracle Audit Trail cleanup policy to avoid running out of disk space. diff --git a/docs/pages/includes/database-access/auto-user-provisioning/db-definition-default-dbname.mdx b/docs/pages/includes/database-access/auto-user-provisioning/db-definition-default-dbname.mdx index 8db14c3ab953a..f5283bfed938e 100644 --- a/docs/pages/includes/database-access/auto-user-provisioning/db-definition-default-dbname.mdx +++ b/docs/pages/includes/database-access/auto-user-provisioning/db-definition-default-dbname.mdx @@ -1,23 +1,6 @@ {{ default="'teleport'" }} Next, configure the database admin user in the Teleport database configuration: - - -```yaml -db_service: - enabled: "yes" - databases: - - name: "example" - protocol: "{{ protocol }}" - uri: "{{ uri }}" - admin_user: - name: "teleport-admin" - # Optional default database the admin user logs into. Default is - # {{ default }}, if not specified. - # default_database: teleport -``` - - ```yaml kind: db version: v3 @@ -32,11 +15,12 @@ spec: # {{ default }}, if not specified. # default_database: teleport ``` - - - +This example assumes that you have configured the database as a dynamic +resource. If you have configured your database using a static Teleport Database +Service configuration, edit the entry in your `db_service.databases` +configuration. + For auto-discovered cloud databases, the name of the admin user is taken from the `teleport.dev/db-admin` label, and the default database is taken from the `teleport.dev/db-admin-default-database` label. - diff --git a/docs/pages/includes/database-access/auto-user-provisioning/db-definition-self-hosted.mdx b/docs/pages/includes/database-access/auto-user-provisioning/db-definition-self-hosted.mdx index c238a65926a17..84cdb5a0f120c 100644 --- a/docs/pages/includes/database-access/auto-user-provisioning/db-definition-self-hosted.mdx +++ b/docs/pages/includes/database-access/auto-user-provisioning/db-definition-self-hosted.mdx @@ -1,19 +1,5 @@ Next, configure the database admin user in the Teleport database configuration: - - -```yaml -db_service: - enabled: "yes" - databases: - - name: "example" - protocol: "{{ protocol }}" - uri: "{{ uri }}" - admin_user: - name: "teleport-admin" -``` - - ```yaml kind: db version: v3 @@ -25,5 +11,8 @@ spec: admin_user: name: "teleport-admin" ``` - - + +This example assumes that you have configured the database as a dynamic +resource. If you have configured your database using a static Teleport Database +Service configuration, edit the entry in your `db_service.databases` +configuration. diff --git a/docs/pages/includes/database-access/auto-user-provisioning/db-definition.mdx b/docs/pages/includes/database-access/auto-user-provisioning/db-definition.mdx index c2a7749c46055..ac4afded4f7af 100644 --- a/docs/pages/includes/database-access/auto-user-provisioning/db-definition.mdx +++ b/docs/pages/includes/database-access/auto-user-provisioning/db-definition.mdx @@ -1,19 +1,5 @@ Next, configure the database admin user in the Teleport database configuration: - - -```yaml -db_service: - enabled: "yes" - databases: - - name: "example" - protocol: "{{ protocol }}" - uri: "{{ uri }}" - admin_user: - name: "teleport-admin" -``` - - ```yaml kind: db version: v3 @@ -25,10 +11,11 @@ spec: admin_user: name: "teleport-admin" ``` - - - +This example assumes that you have configured the database as a dynamic +resource. If you have configured your database using a static Teleport Database +Service configuration, edit the entry in your `db_service.databases` +configuration. + For auto-discovered cloud databases, the name of the admin user is taken from the `teleport.dev/db-admin` label. - diff --git a/docs/pages/includes/enterprise/oidcauthentication.mdx b/docs/pages/includes/enterprise/oidcauthentication.mdx index 5ecb3a6ce3b3a..136102ea83651 100644 --- a/docs/pages/includes/enterprise/oidcauthentication.mdx +++ b/docs/pages/includes/enterprise/oidcauthentication.mdx @@ -1,39 +1,19 @@ Configure Teleport to use OIDC authentication as the default instead of the local user database. -Follow the instructions for your Teleport edition: - - - - - Update `/etc/teleport.yaml` in the `auth_service` section and restart the `teleport` daemon. - - ```yaml - auth_service: - authentication: - type: oidc - - ``` - - - - - Create a file called `cap.yaml`: - - ```yaml - kind: cluster_auth_preference - metadata: - name: cluster-auth-preference - spec: - type: oidc - version: v2 - ``` - - Create a resource: - - ```code - $ tctl create -f cap.yaml - ``` - - - +Edit your `cluster_auth_preference` resource: + +```code +$ tctl edit cap +``` + +In your editor, ensure that the file contains the following content: + +```yaml +kind: cluster_auth_preference +metadata: + name: cluster-auth-preference +spec: + type: oidc +version: v2 +``` diff --git a/docs/pages/includes/enterprise/samlauthentication.mdx b/docs/pages/includes/enterprise/samlauthentication.mdx index dfabdb7a3ec71..9fcd1e9db5362 100644 --- a/docs/pages/includes/enterprise/samlauthentication.mdx +++ b/docs/pages/includes/enterprise/samlauthentication.mdx @@ -3,11 +3,6 @@ Configure Teleport to use SAML authentication as the default instead of the local user database. -Follow the instructions for your Teleport edition: - - - - Use `tctl` to edit the `cluster_auth_preference` value: ```code @@ -34,20 +29,6 @@ After you save and exit the editor, `tctl` will update the resource: cluster auth preference has been updated ``` - - - -Update `/etc/teleport.yaml` in the `auth_service` section and restart the `teleport` daemon. - -```yaml -auth_service: - authentication: - type: saml -``` - - - - If you need to log in again before configuring your SAML provider, use the flag `--auth=local`. From 333c5a800db4cb9379d5716984759a50eb2654b6 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 17 Oct 2024 09:30:52 -0500 Subject: [PATCH 07/30] Use challenge response when adding MFA device (#47593) This PR changes the requirement for the "Reauthenticate" step when adding an MFA device. Currently, we rely on the number of devices returned when we fetch the devices. However, in the future with SSOasMFA, these devices won't always be enabled. This will allow us to check the backend if any of the available devices are "enabled" and if we've received a valid challenge for a device. if not, we forward the user through the privilege token flow instead. This will be expanded by checking for SSO challenges as well and can now be expanded for any other type of auth. --- .../teleport/src/Account/Account.test.tsx | 6 ++++++ .../ChangePasswordWizard.tsx | 4 +--- .../Account/ManageDevices/useManageDevices.ts | 12 ++++++++++-- web/packages/teleport/src/services/auth/auth.ts | 17 ++++++++++++++++- .../teleport/src/services/auth/makeMfa.ts | 1 + .../teleport/src/services/auth/types.ts | 1 + 6 files changed, 35 insertions(+), 6 deletions(-) diff --git a/web/packages/teleport/src/Account/Account.test.tsx b/web/packages/teleport/src/Account/Account.test.tsx index fb3d4534d651f..6fb23549a0e36 100644 --- a/web/packages/teleport/src/Account/Account.test.tsx +++ b/web/packages/teleport/src/Account/Account.test.tsx @@ -243,6 +243,9 @@ test('adding an MFA device', async () => { const user = userEvent.setup(); const ctx = createTeleportContext(); jest.spyOn(ctx.mfaService, 'fetchDevices').mockResolvedValue([testPasskey]); + jest + .spyOn(auth, 'getChallenge') + .mockResolvedValue({ webauthnPublicKey: null, totpChallenge: true }); jest .spyOn(auth, 'createNewWebAuthnDevice') .mockResolvedValueOnce(dummyCredential); @@ -322,6 +325,9 @@ test('removing an MFA method', async () => { const user = userEvent.setup(); const ctx = createTeleportContext(); jest.spyOn(ctx.mfaService, 'fetchDevices').mockResolvedValue([testMfaMethod]); + jest + .spyOn(auth, 'getChallenge') + .mockResolvedValue({ webauthnPublicKey: null, totpChallenge: false }); jest .spyOn(auth, 'createPrivilegeTokenWithWebauthn') .mockResolvedValueOnce('webauthn-privilege-token'); diff --git a/web/packages/teleport/src/Account/ChangePasswordWizard/ChangePasswordWizard.tsx b/web/packages/teleport/src/Account/ChangePasswordWizard/ChangePasswordWizard.tsx index d5e17e71be03a..458357e83ed54 100644 --- a/web/packages/teleport/src/Account/ChangePasswordWizard/ChangePasswordWizard.tsx +++ b/web/packages/teleport/src/Account/ChangePasswordWizard/ChangePasswordWizard.tsx @@ -22,7 +22,7 @@ import { ButtonPrimary, ButtonSecondary } from 'design/Button'; import Dialog from 'design/Dialog'; import Flex from 'design/Flex'; import { RadioGroup } from 'design/RadioGroup'; -import { StepComponentProps, StepSlider } from 'design/StepSlider'; +import { StepComponentProps, StepSlider, StepHeader } from 'design/StepSlider'; import React, { useState } from 'react'; import FieldInput from 'shared/components/FieldInput'; import Validation, { Validator } from 'shared/components/Validation'; @@ -36,8 +36,6 @@ import { Auth2faType } from 'shared/services'; import Box from 'design/Box'; -import { StepHeader } from 'design/StepSlider'; - import { ChangePasswordReq } from 'teleport/services/auth'; import auth, { MfaChallengeScope } from 'teleport/services/auth/auth'; import { MfaDevice } from 'teleport/services/mfa'; diff --git a/web/packages/teleport/src/Account/ManageDevices/useManageDevices.ts b/web/packages/teleport/src/Account/ManageDevices/useManageDevices.ts index 12bfaf6743f04..95929bf9d9714 100644 --- a/web/packages/teleport/src/Account/ManageDevices/useManageDevices.ts +++ b/web/packages/teleport/src/Account/ManageDevices/useManageDevices.ts @@ -23,6 +23,7 @@ import Ctx from 'teleport/teleportContext'; import cfg from 'teleport/config'; import auth, { DeviceUsage } from 'teleport/services/auth'; import { MfaDevice } from 'teleport/services/mfa'; +import { MfaChallengeScope } from 'teleport/services/auth/auth'; export default function useManageDevices(ctx: Ctx) { const [devices, setDevices] = useState([]); @@ -45,9 +46,16 @@ export default function useManageDevices(ctx: Ctx) { ); } - function onAddDevice(usage: DeviceUsage) { + async function onAddDevice(usage: DeviceUsage) { setNewDeviceUsage(usage); - if (devices.length === 0) { + const response = await auth.getChallenge({ + scope: MfaChallengeScope.MANAGE_DEVICES, + }); + // If the user doesn't receieve any challenges from the backend, that means + // they have no valid devices to be challenged and should instead use a privilege token + // to add a new device. + // TODO (avatus): add SSO challenge here as well when we add SSO for MFA + if (!response.webauthnPublicKey?.challenge && !response.totpChallenge) { createRestrictedTokenAttempt.run(() => auth.createRestrictedPrivilegeToken().then(token => { setToken(token); diff --git a/web/packages/teleport/src/services/auth/auth.ts b/web/packages/teleport/src/services/auth/auth.ts index 5059ac5646e6c..4dec9b2ade0f0 100644 --- a/web/packages/teleport/src/services/auth/auth.ts +++ b/web/packages/teleport/src/services/auth/auth.ts @@ -37,8 +37,8 @@ import { ChangePasswordReq, CreateNewHardwareDeviceRequest, DeviceUsage, + CreateAuthenticateChallengeRequest, } from './types'; -import { CreateAuthenticateChallengeRequest } from './types'; const auth = { checkWebauthnSupport() { @@ -258,6 +258,21 @@ const auth = { return api.post(cfg.api.createPrivilegeTokenPath, { secondFactorToken }); }, + async getChallenge( + req: CreateAuthenticateChallengeRequest, + abortSignal?: AbortSignal + ) { + return api + .post( + cfg.api.mfaAuthnChallengePath, + { + challenge_scope: req.scope, + }, + abortSignal + ) + .then(makeMfaAuthenticateChallenge); + }, + async fetchWebAuthnChallenge( req: CreateAuthenticateChallengeRequest, abortSignal?: AbortSignal diff --git a/web/packages/teleport/src/services/auth/makeMfa.ts b/web/packages/teleport/src/services/auth/makeMfa.ts index 20957e7a773d9..0637967483911 100644 --- a/web/packages/teleport/src/services/auth/makeMfa.ts +++ b/web/packages/teleport/src/services/auth/makeMfa.ts @@ -70,6 +70,7 @@ export function makeMfaAuthenticateChallenge(json): MfaAuthenticateChallenge { } return { + totpChallenge: json.totp_challenge, webauthnPublicKey: webauthnPublicKey, }; } diff --git a/web/packages/teleport/src/services/auth/types.ts b/web/packages/teleport/src/services/auth/types.ts index 078464ed8a52a..11057cd185645 100644 --- a/web/packages/teleport/src/services/auth/types.ts +++ b/web/packages/teleport/src/services/auth/types.ts @@ -33,6 +33,7 @@ export type AuthnChallengeRequest = { }; export type MfaAuthenticateChallenge = { + totpChallenge: boolean; webauthnPublicKey: PublicKeyCredentialRequestOptions; }; From 10fb78449564501d40045abcdda650f9a7c4a62b Mon Sep 17 00:00:00 2001 From: Vadym Popov Date: Thu, 17 Oct 2024 10:36:06 -0400 Subject: [PATCH 08/30] Client AutoUpdate proto structure changes (#47532) * Update client autoupdate proto structure * Replace with reserved * Fix unit tests * Add more info in proto * Rename proto to be aligned RFD namings * Replace enum type for ToolsMode to string --- api/client/webclient/webclient.go | 4 +- .../teleport/autoupdate/v1/autoupdate.pb.go | 228 +++++++++++++----- .../teleport/autoupdate/v1/autoupdate.proto | 23 +- api/types/autoupdate/config.go | 12 + api/types/autoupdate/config_test.go | 27 ++- api/types/autoupdate/version.go | 12 +- api/types/autoupdate/version_test.go | 20 +- lib/cache/cache_test.go | 8 +- lib/services/local/autoupdate_test.go | 48 +++- lib/web/apiserver.go | 4 +- lib/web/apiserver_ping_test.go | 50 +++- tool/tctl/common/collection.go | 4 +- tool/tctl/common/edit_command_test.go | 32 ++- tool/tctl/common/resource_command_test.go | 6 +- 14 files changed, 360 insertions(+), 118 deletions(-) diff --git a/api/client/webclient/webclient.go b/api/client/webclient/webclient.go index d1f11a5a48304..95ae0ea9747c3 100644 --- a/api/client/webclient/webclient.go +++ b/api/client/webclient/webclient.go @@ -334,8 +334,8 @@ type ProxySettings struct { type AutoUpdateSettings struct { // ToolsVersion defines the version of {tsh, tctl} for client auto update. ToolsVersion string `json:"tools_version"` - // ToolsAutoUpdate enables client auto update feature. - ToolsAutoUpdate bool `json:"tools_auto_update"` + // ToolsMode defines mode client auto update feature `enabled|disabled`. + ToolsMode string `json:"tools_mode"` } // KubeProxySettings is kubernetes proxy settings diff --git a/api/gen/proto/go/teleport/autoupdate/v1/autoupdate.pb.go b/api/gen/proto/go/teleport/autoupdate/v1/autoupdate.pb.go index 2b608e5b5b472..be298bcc51b60 100644 --- a/api/gen/proto/go/teleport/autoupdate/v1/autoupdate.pb.go +++ b/api/gen/proto/go/teleport/autoupdate/v1/autoupdate.pb.go @@ -120,8 +120,7 @@ type AutoUpdateConfigSpec struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // ToolsAutoupdate encodes the feature flag to enable/disable tools autoupdates. - ToolsAutoupdate bool `protobuf:"varint,1,opt,name=tools_autoupdate,json=toolsAutoupdate,proto3" json:"tools_autoupdate,omitempty"` + Tools *AutoUpdateConfigSpecTools `protobuf:"bytes,2,opt,name=tools,proto3" json:"tools,omitempty"` } func (x *AutoUpdateConfigSpec) Reset() { @@ -154,11 +153,58 @@ func (*AutoUpdateConfigSpec) Descriptor() ([]byte, []int) { return file_teleport_autoupdate_v1_autoupdate_proto_rawDescGZIP(), []int{1} } -func (x *AutoUpdateConfigSpec) GetToolsAutoupdate() bool { +func (x *AutoUpdateConfigSpec) GetTools() *AutoUpdateConfigSpecTools { if x != nil { - return x.ToolsAutoupdate + return x.Tools } - return false + return nil +} + +// AutoUpdateConfigSpecTools encodes the parameters for client tools auto updates. +type AutoUpdateConfigSpecTools struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Mode defines state of the client tools auto update. + Mode string `protobuf:"bytes,1,opt,name=mode,proto3" json:"mode,omitempty"` +} + +func (x *AutoUpdateConfigSpecTools) Reset() { + *x = AutoUpdateConfigSpecTools{} + mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AutoUpdateConfigSpecTools) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AutoUpdateConfigSpecTools) ProtoMessage() {} + +func (x *AutoUpdateConfigSpecTools) ProtoReflect() protoreflect.Message { + mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AutoUpdateConfigSpecTools.ProtoReflect.Descriptor instead. +func (*AutoUpdateConfigSpecTools) Descriptor() ([]byte, []int) { + return file_teleport_autoupdate_v1_autoupdate_proto_rawDescGZIP(), []int{2} +} + +func (x *AutoUpdateConfigSpecTools) GetMode() string { + if x != nil { + return x.Mode + } + return "" } // AutoUpdateVersion is a resource singleton with version required for @@ -177,7 +223,7 @@ type AutoUpdateVersion struct { func (x *AutoUpdateVersion) Reset() { *x = AutoUpdateVersion{} - mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[2] + mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -189,7 +235,7 @@ func (x *AutoUpdateVersion) String() string { func (*AutoUpdateVersion) ProtoMessage() {} func (x *AutoUpdateVersion) ProtoReflect() protoreflect.Message { - mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[2] + mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -202,7 +248,7 @@ func (x *AutoUpdateVersion) ProtoReflect() protoreflect.Message { // Deprecated: Use AutoUpdateVersion.ProtoReflect.Descriptor instead. func (*AutoUpdateVersion) Descriptor() ([]byte, []int) { - return file_teleport_autoupdate_v1_autoupdate_proto_rawDescGZIP(), []int{2} + return file_teleport_autoupdate_v1_autoupdate_proto_rawDescGZIP(), []int{3} } func (x *AutoUpdateVersion) GetKind() string { @@ -246,13 +292,12 @@ type AutoUpdateVersionSpec struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // ToolsVersion is the semantic version required for tools autoupdates. - ToolsVersion string `protobuf:"bytes,1,opt,name=tools_version,json=toolsVersion,proto3" json:"tools_version,omitempty"` + Tools *AutoUpdateVersionSpecTools `protobuf:"bytes,2,opt,name=tools,proto3" json:"tools,omitempty"` } func (x *AutoUpdateVersionSpec) Reset() { *x = AutoUpdateVersionSpec{} - mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[3] + mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -264,7 +309,7 @@ func (x *AutoUpdateVersionSpec) String() string { func (*AutoUpdateVersionSpec) ProtoMessage() {} func (x *AutoUpdateVersionSpec) ProtoReflect() protoreflect.Message { - mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[3] + mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -277,12 +322,60 @@ func (x *AutoUpdateVersionSpec) ProtoReflect() protoreflect.Message { // Deprecated: Use AutoUpdateVersionSpec.ProtoReflect.Descriptor instead. func (*AutoUpdateVersionSpec) Descriptor() ([]byte, []int) { - return file_teleport_autoupdate_v1_autoupdate_proto_rawDescGZIP(), []int{3} + return file_teleport_autoupdate_v1_autoupdate_proto_rawDescGZIP(), []int{4} +} + +func (x *AutoUpdateVersionSpec) GetTools() *AutoUpdateVersionSpecTools { + if x != nil { + return x.Tools + } + return nil +} + +// AutoUpdateVersionSpecTools encodes the parameters for client tools auto updates. +type AutoUpdateVersionSpecTools struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // TargetVersion specifies the semantic version required for tools to establish a connection with the cluster. + // Client tools after connection to the cluster going to be updated to this version automatically. + TargetVersion string `protobuf:"bytes,1,opt,name=target_version,json=targetVersion,proto3" json:"target_version,omitempty"` +} + +func (x *AutoUpdateVersionSpecTools) Reset() { + *x = AutoUpdateVersionSpecTools{} + mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AutoUpdateVersionSpecTools) String() string { + return protoimpl.X.MessageStringOf(x) } -func (x *AutoUpdateVersionSpec) GetToolsVersion() string { +func (*AutoUpdateVersionSpecTools) ProtoMessage() {} + +func (x *AutoUpdateVersionSpecTools) ProtoReflect() protoreflect.Message { + mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[5] if x != nil { - return x.ToolsVersion + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AutoUpdateVersionSpecTools.ProtoReflect.Descriptor instead. +func (*AutoUpdateVersionSpecTools) Descriptor() ([]byte, []int) { + return file_teleport_autoupdate_v1_autoupdate_proto_rawDescGZIP(), []int{5} +} + +func (x *AutoUpdateVersionSpecTools) GetTargetVersion() string { + if x != nil { + return x.TargetVersion } return "" } @@ -309,35 +402,50 @@ var file_teleport_autoupdate_v1_autoupdate_proto_rawDesc = []byte{ 0x73, 0x70, 0x65, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x61, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x22, 0x41, + 0x6e, 0x66, 0x69, 0x67, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x22, 0x77, 0x0a, 0x14, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x53, 0x70, 0x65, 0x63, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x5f, - 0x61, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0f, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x41, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x22, 0xd9, 0x01, 0x0a, 0x11, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, - 0x75, 0x62, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, - 0x75, 0x62, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x12, 0x38, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x68, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x41, 0x0a, 0x04, 0x73, 0x70, - 0x65, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x2e, 0x61, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x22, 0x3c, 0x0a, - 0x15, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x5f, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x74, - 0x6f, 0x6f, 0x6c, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x56, 0x5a, 0x54, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, - 0x6f, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x61, 0x75, 0x74, 0x6f, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x69, 0x67, 0x53, 0x70, 0x65, 0x63, 0x12, 0x47, 0x0a, 0x05, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2e, 0x61, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, + 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, + 0x70, 0x65, 0x63, 0x54, 0x6f, 0x6f, 0x6c, 0x73, 0x52, 0x05, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x4a, + 0x04, 0x08, 0x01, 0x10, 0x02, 0x52, 0x10, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x5f, 0x61, 0x75, 0x74, + 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x22, 0x2f, 0x0a, 0x19, 0x41, 0x75, 0x74, 0x6f, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x70, 0x65, 0x63, 0x54, + 0x6f, 0x6f, 0x6c, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0xd9, 0x01, 0x0a, 0x11, 0x41, 0x75, 0x74, + 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, + 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, + 0x6e, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x75, 0x62, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x18, 0x0a, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x12, 0x41, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x61, 0x75, 0x74, 0x6f, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, + 0x73, 0x70, 0x65, 0x63, 0x22, 0x76, 0x0a, 0x15, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x12, 0x48, 0x0a, + 0x05, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x61, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x54, 0x6f, 0x6f, 0x6c, 0x73, + 0x52, 0x05, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x52, 0x0d, 0x74, + 0x6f, 0x6f, 0x6c, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x43, 0x0a, 0x1a, + 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x53, 0x70, 0x65, 0x63, 0x54, 0x6f, 0x6f, 0x6c, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0d, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x42, 0x56, 0x5a, 0x54, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, + 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2f, 0x61, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x61, + 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -352,24 +460,28 @@ func file_teleport_autoupdate_v1_autoupdate_proto_rawDescGZIP() []byte { return file_teleport_autoupdate_v1_autoupdate_proto_rawDescData } -var file_teleport_autoupdate_v1_autoupdate_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_teleport_autoupdate_v1_autoupdate_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_teleport_autoupdate_v1_autoupdate_proto_goTypes = []any{ - (*AutoUpdateConfig)(nil), // 0: teleport.autoupdate.v1.AutoUpdateConfig - (*AutoUpdateConfigSpec)(nil), // 1: teleport.autoupdate.v1.AutoUpdateConfigSpec - (*AutoUpdateVersion)(nil), // 2: teleport.autoupdate.v1.AutoUpdateVersion - (*AutoUpdateVersionSpec)(nil), // 3: teleport.autoupdate.v1.AutoUpdateVersionSpec - (*v1.Metadata)(nil), // 4: teleport.header.v1.Metadata + (*AutoUpdateConfig)(nil), // 0: teleport.autoupdate.v1.AutoUpdateConfig + (*AutoUpdateConfigSpec)(nil), // 1: teleport.autoupdate.v1.AutoUpdateConfigSpec + (*AutoUpdateConfigSpecTools)(nil), // 2: teleport.autoupdate.v1.AutoUpdateConfigSpecTools + (*AutoUpdateVersion)(nil), // 3: teleport.autoupdate.v1.AutoUpdateVersion + (*AutoUpdateVersionSpec)(nil), // 4: teleport.autoupdate.v1.AutoUpdateVersionSpec + (*AutoUpdateVersionSpecTools)(nil), // 5: teleport.autoupdate.v1.AutoUpdateVersionSpecTools + (*v1.Metadata)(nil), // 6: teleport.header.v1.Metadata } var file_teleport_autoupdate_v1_autoupdate_proto_depIdxs = []int32{ - 4, // 0: teleport.autoupdate.v1.AutoUpdateConfig.metadata:type_name -> teleport.header.v1.Metadata + 6, // 0: teleport.autoupdate.v1.AutoUpdateConfig.metadata:type_name -> teleport.header.v1.Metadata 1, // 1: teleport.autoupdate.v1.AutoUpdateConfig.spec:type_name -> teleport.autoupdate.v1.AutoUpdateConfigSpec - 4, // 2: teleport.autoupdate.v1.AutoUpdateVersion.metadata:type_name -> teleport.header.v1.Metadata - 3, // 3: teleport.autoupdate.v1.AutoUpdateVersion.spec:type_name -> teleport.autoupdate.v1.AutoUpdateVersionSpec - 4, // [4:4] is the sub-list for method output_type - 4, // [4:4] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name + 2, // 2: teleport.autoupdate.v1.AutoUpdateConfigSpec.tools:type_name -> teleport.autoupdate.v1.AutoUpdateConfigSpecTools + 6, // 3: teleport.autoupdate.v1.AutoUpdateVersion.metadata:type_name -> teleport.header.v1.Metadata + 4, // 4: teleport.autoupdate.v1.AutoUpdateVersion.spec:type_name -> teleport.autoupdate.v1.AutoUpdateVersionSpec + 5, // 5: teleport.autoupdate.v1.AutoUpdateVersionSpec.tools:type_name -> teleport.autoupdate.v1.AutoUpdateVersionSpecTools + 6, // [6:6] is the sub-list for method output_type + 6, // [6:6] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name } func init() { file_teleport_autoupdate_v1_autoupdate_proto_init() } @@ -383,7 +495,7 @@ func file_teleport_autoupdate_v1_autoupdate_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_teleport_autoupdate_v1_autoupdate_proto_rawDesc, NumEnums: 0, - NumMessages: 4, + NumMessages: 6, NumExtensions: 0, NumServices: 0, }, diff --git a/api/proto/teleport/autoupdate/v1/autoupdate.proto b/api/proto/teleport/autoupdate/v1/autoupdate.proto index 1987407fe7d81..b4e557549b316 100644 --- a/api/proto/teleport/autoupdate/v1/autoupdate.proto +++ b/api/proto/teleport/autoupdate/v1/autoupdate.proto @@ -33,8 +33,15 @@ message AutoUpdateConfig { // AutoUpdateConfigSpec encodes the parameters of the autoupdate config object. message AutoUpdateConfigSpec { - // ToolsAutoupdate encodes the feature flag to enable/disable tools autoupdates. - bool tools_autoupdate = 1; + reserved 1; + reserved "tools_autoupdate"; // ToolsAutoupdate is replaced by tools.mode. + AutoUpdateConfigSpecTools tools = 2; +} + +// AutoUpdateConfigSpecTools encodes the parameters for client tools auto updates. +message AutoUpdateConfigSpecTools { + // Mode defines state of the client tools auto update. + string mode = 1; } // AutoUpdateVersion is a resource singleton with version required for @@ -50,6 +57,14 @@ message AutoUpdateVersion { // AutoUpdateVersionSpec encodes the parameters of the autoupdate versions. message AutoUpdateVersionSpec { - // ToolsVersion is the semantic version required for tools autoupdates. - string tools_version = 1; + reserved 1; + reserved "tools_version"; // ToolsVersion is replaced by tools.target_version. + AutoUpdateVersionSpecTools tools = 2; +} + +// AutoUpdateVersionSpecTools encodes the parameters for client tools auto updates. +message AutoUpdateVersionSpecTools { + // TargetVersion specifies the semantic version required for tools to establish a connection with the cluster. + // Client tools after connection to the cluster going to be updated to this version automatically. + string target_version = 1; } diff --git a/api/types/autoupdate/config.go b/api/types/autoupdate/config.go index 5be3db89fd0c5..d61c35eccf0c2 100644 --- a/api/types/autoupdate/config.go +++ b/api/types/autoupdate/config.go @@ -26,6 +26,13 @@ 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{ @@ -58,6 +65,11 @@ func ValidateAutoUpdateConfig(c *autoupdate.AutoUpdateConfig) error { if c.Spec == nil { 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") + } + } return nil } diff --git a/api/types/autoupdate/config_test.go b/api/types/autoupdate/config_test.go index 2ee33dc5bf2b3..443d6f246fa56 100644 --- a/api/types/autoupdate/config_test.go +++ b/api/types/autoupdate/config_test.go @@ -41,7 +41,9 @@ func TestNewAutoUpdateConfig(t *testing.T) { { name: "success tools autoupdate disabled", spec: &autoupdate.AutoUpdateConfigSpec{ - ToolsAutoupdate: false, + Tools: &autoupdate.AutoUpdateConfigSpecTools{ + Mode: ToolsUpdateModeDisabled, + }, }, assertErr: func(t *testing.T, err error, a ...any) { require.NoError(t, err) @@ -53,14 +55,18 @@ func TestNewAutoUpdateConfig(t *testing.T) { Name: types.MetaNameAutoUpdateConfig, }, Spec: &autoupdate.AutoUpdateConfigSpec{ - ToolsAutoupdate: false, + Tools: &autoupdate.AutoUpdateConfigSpecTools{ + Mode: ToolsUpdateModeDisabled, + }, }, }, }, { name: "success tools autoupdate enabled", spec: &autoupdate.AutoUpdateConfigSpec{ - ToolsAutoupdate: true, + Tools: &autoupdate.AutoUpdateConfigSpecTools{ + Mode: ToolsUpdateModeEnabled, + }, }, assertErr: func(t *testing.T, err error, a ...any) { require.NoError(t, err) @@ -72,7 +78,9 @@ func TestNewAutoUpdateConfig(t *testing.T) { Name: types.MetaNameAutoUpdateConfig, }, Spec: &autoupdate.AutoUpdateConfigSpec{ - ToolsAutoupdate: true, + Tools: &autoupdate.AutoUpdateConfigSpecTools{ + Mode: ToolsUpdateModeEnabled, + }, }, }, }, @@ -83,6 +91,17 @@ func TestNewAutoUpdateConfig(t *testing.T) { require.ErrorContains(t, err, "Spec is nil") }, }, + { + name: "invalid tools mode", + spec: &autoupdate.AutoUpdateConfigSpec{ + Tools: &autoupdate.AutoUpdateConfigSpecTools{ + Mode: "invalid-mode", + }, + }, + assertErr: func(t *testing.T, err error, a ...any) { + require.ErrorContains(t, err, "ToolsMode is not valid") + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/api/types/autoupdate/version.go b/api/types/autoupdate/version.go index 088171a072ae3..ad2d12f265949 100644 --- a/api/types/autoupdate/version.go +++ b/api/types/autoupdate/version.go @@ -60,11 +60,13 @@ func ValidateAutoUpdateVersion(v *autoupdate.AutoUpdateVersion) error { return trace.BadParameter("Spec is nil") } - if v.Spec.ToolsVersion == "" { - return trace.BadParameter("ToolsVersion is unset") - } - if _, err := semver.NewVersion(v.Spec.ToolsVersion); err != nil { - return trace.BadParameter("ToolsVersion is not a valid semantic version") + if v.Spec.Tools != nil { + if v.Spec.Tools.TargetVersion == "" { + return trace.BadParameter("TargetVersion is unset") + } + if _, err := semver.NewVersion(v.Spec.Tools.TargetVersion); err != nil { + return trace.BadParameter("TargetVersion is not a valid semantic version") + } } return nil diff --git a/api/types/autoupdate/version_test.go b/api/types/autoupdate/version_test.go index 5fe4f167a037e..70790a204b219 100644 --- a/api/types/autoupdate/version_test.go +++ b/api/types/autoupdate/version_test.go @@ -41,7 +41,9 @@ func TestNewAutoUpdateVersion(t *testing.T) { { name: "success tools autoupdate version", spec: &autoupdate.AutoUpdateVersionSpec{ - ToolsVersion: "1.2.3-dev", + Tools: &autoupdate.AutoUpdateVersionSpecTools{ + TargetVersion: "1.2.3-dev", + }, }, assertErr: func(t *testing.T, err error, a ...any) { require.NoError(t, err) @@ -53,26 +55,32 @@ func TestNewAutoUpdateVersion(t *testing.T) { Name: types.MetaNameAutoUpdateVersion, }, Spec: &autoupdate.AutoUpdateVersionSpec{ - ToolsVersion: "1.2.3-dev", + Tools: &autoupdate.AutoUpdateVersionSpecTools{ + TargetVersion: "1.2.3-dev", + }, }, }, }, { name: "invalid empty tools version", spec: &autoupdate.AutoUpdateVersionSpec{ - ToolsVersion: "", + Tools: &autoupdate.AutoUpdateVersionSpecTools{ + TargetVersion: "", + }, }, assertErr: func(t *testing.T, err error, a ...any) { - require.ErrorContains(t, err, "ToolsVersion is unset") + require.ErrorContains(t, err, "TargetVersion is unset") }, }, { name: "invalid semantic tools version", spec: &autoupdate.AutoUpdateVersionSpec{ - ToolsVersion: "17-0-0", + Tools: &autoupdate.AutoUpdateVersionSpecTools{ + TargetVersion: "17-0-0", + }, }, assertErr: func(t *testing.T, err error, a ...any) { - require.ErrorContains(t, err, "ToolsVersion is not a valid semantic version") + require.ErrorContains(t, err, "TargetVersion is not a valid semantic version") }, }, { diff --git a/lib/cache/cache_test.go b/lib/cache/cache_test.go index 7a9546bc1762d..6961d1c2e1b12 100644 --- a/lib/cache/cache_test.go +++ b/lib/cache/cache_test.go @@ -4003,7 +4003,9 @@ func newAutoUpdateConfig(t *testing.T) *autoupdate.AutoUpdateConfig { t.Helper() r, err := update.NewAutoUpdateConfig(&autoupdate.AutoUpdateConfigSpec{ - ToolsAutoupdate: true, + Tools: &autoupdate.AutoUpdateConfigSpecTools{ + Mode: update.ToolsUpdateModeEnabled, + }, }) require.NoError(t, err) return r @@ -4013,7 +4015,9 @@ func newAutoUpdateVersion(t *testing.T) *autoupdate.AutoUpdateVersion { t.Helper() r, err := update.NewAutoUpdateVersion(&autoupdate.AutoUpdateVersionSpec{ - ToolsVersion: "1.2.3", + Tools: &autoupdate.AutoUpdateVersionSpecTools{ + TargetVersion: "1.2.3", + }, }) require.NoError(t, err) return r diff --git a/lib/services/local/autoupdate_test.go b/lib/services/local/autoupdate_test.go index 77e13937ac47a..ae858d65cc47c 100644 --- a/lib/services/local/autoupdate_test.go +++ b/lib/services/local/autoupdate_test.go @@ -51,7 +51,11 @@ func TestAutoUpdateServiceConfigCRUD(t *testing.T) { Kind: types.KindAutoUpdateConfig, Version: types.V1, Metadata: &headerv1.Metadata{Name: types.MetaNameAutoUpdateConfig}, - Spec: &autoupdatev1pb.AutoUpdateConfigSpec{ToolsAutoupdate: true}, + Spec: &autoupdatev1pb.AutoUpdateConfigSpec{ + Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{ + Mode: autoupdate.ToolsUpdateModeEnabled, + }, + }, } created, err := service.CreateAutoUpdateConfig(ctx, config) @@ -72,10 +76,12 @@ func TestAutoUpdateServiceConfigCRUD(t *testing.T) { require.Empty(t, diff) require.Equal(t, created.GetMetadata().GetRevision(), got.GetMetadata().GetRevision()) - config.Spec.ToolsAutoupdate = false + config.Spec.Tools = &autoupdatev1pb.AutoUpdateConfigSpecTools{ + Mode: autoupdate.ToolsUpdateModeDisabled, + } updated, err := service.UpdateAutoUpdateConfig(ctx, config) require.NoError(t, err) - require.NotEqual(t, got.GetSpec().GetToolsAutoupdate(), updated.GetSpec().GetToolsAutoupdate()) + require.NotEqual(t, got.GetSpec().GetTools(), updated.GetSpec().GetTools()) _, err = service.UpsertAutoUpdateConfig(ctx, config) require.NoError(t, err) @@ -107,7 +113,11 @@ func TestAutoUpdateServiceVersionCRUD(t *testing.T) { Kind: types.KindAutoUpdateVersion, Version: types.V1, Metadata: &headerv1.Metadata{Name: types.MetaNameAutoUpdateVersion}, - Spec: &autoupdatev1pb.AutoUpdateVersionSpec{ToolsVersion: "1.2.3"}, + Spec: &autoupdatev1pb.AutoUpdateVersionSpec{ + Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{ + TargetVersion: "1.2.3", + }, + }, } created, err := service.CreateAutoUpdateVersion(ctx, version) @@ -128,10 +138,12 @@ func TestAutoUpdateServiceVersionCRUD(t *testing.T) { require.Empty(t, diff) require.Equal(t, created.GetMetadata().GetRevision(), got.GetMetadata().GetRevision()) - version.Spec.ToolsVersion = "3.2.1" + version.Spec.Tools = &autoupdatev1pb.AutoUpdateVersionSpecTools{ + TargetVersion: "3.2.1", + } updated, err := service.UpdateAutoUpdateVersion(ctx, version) require.NoError(t, err) - require.NotEqual(t, got.GetSpec().GetToolsVersion(), updated.GetSpec().GetToolsVersion()) + require.NotEqual(t, got.GetSpec().GetTools().GetTargetVersion(), updated.GetSpec().GetTools().GetTargetVersion()) _, err = service.UpsertAutoUpdateVersion(ctx, version) require.NoError(t, err) @@ -163,7 +175,11 @@ func TestAutoUpdateServiceInvalidNameCreate(t *testing.T) { Kind: types.KindAutoUpdateConfig, Version: types.V1, Metadata: &headerv1.Metadata{Name: "invalid-auto-update-config-name"}, - Spec: &autoupdatev1pb.AutoUpdateConfigSpec{ToolsAutoupdate: true}, + Spec: &autoupdatev1pb.AutoUpdateConfigSpec{ + Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{ + Mode: autoupdate.ToolsUpdateModeEnabled, + }, + }, } createdConfig, err := service.CreateAutoUpdateConfig(ctx, config) @@ -174,7 +190,11 @@ func TestAutoUpdateServiceInvalidNameCreate(t *testing.T) { Kind: types.KindAutoUpdateVersion, Version: types.V1, Metadata: &headerv1.Metadata{Name: "invalid-auto-update-version-name"}, - Spec: &autoupdatev1pb.AutoUpdateVersionSpec{ToolsVersion: "1.2.3"}, + Spec: &autoupdatev1pb.AutoUpdateVersionSpec{ + Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{ + TargetVersion: "1.2.3", + }, + }, } createdVersion, err := service.CreateAutoUpdateVersion(ctx, version) @@ -196,7 +216,11 @@ func TestAutoUpdateServiceInvalidNameUpdate(t *testing.T) { ctx := context.Background() // Validate the config update restriction. - config, err := autoupdate.NewAutoUpdateConfig(&autoupdatev1pb.AutoUpdateConfigSpec{ToolsAutoupdate: true}) + config, err := autoupdate.NewAutoUpdateConfig(&autoupdatev1pb.AutoUpdateConfigSpec{ + Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{ + Mode: autoupdate.ToolsUpdateModeEnabled, + }, + }) require.NoError(t, err) createdConfig, err := service.UpsertAutoUpdateConfig(ctx, config) @@ -209,7 +233,11 @@ func TestAutoUpdateServiceInvalidNameUpdate(t *testing.T) { require.Nil(t, createdConfig) // Validate the version update restriction. - version, err := autoupdate.NewAutoUpdateVersion(&autoupdatev1pb.AutoUpdateVersionSpec{ToolsVersion: "1.2.3"}) + version, err := autoupdate.NewAutoUpdateVersion(&autoupdatev1pb.AutoUpdateVersionSpec{ + Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{ + TargetVersion: "1.2.3", + }, + }) require.NoError(t, err) createdVersion, err := service.UpsertAutoUpdateVersion(ctx, version) diff --git a/lib/web/apiserver.go b/lib/web/apiserver.go index 8404d66d6ce2f..1c4c24a10e214 100644 --- a/lib/web/apiserver.go +++ b/lib/web/apiserver.go @@ -1545,7 +1545,7 @@ func (h *Handler) find(w http.ResponseWriter, r *http.Request, p httprouter.Para if err != nil && !trace.IsNotFound(err) && !trace.IsNotImplemented(err) { h.logger.ErrorContext(r.Context(), "failed to receive AutoUpdateConfig", "error", err) } else if err == nil { - response.AutoUpdate.ToolsAutoUpdate = autoUpdateConfig.GetSpec().GetToolsAutoupdate() + response.AutoUpdate.ToolsMode = autoUpdateConfig.GetSpec().GetTools().GetMode() } autoUpdateVersion, err := h.cfg.AccessPoint.GetAutoUpdateVersion(r.Context()) @@ -1553,7 +1553,7 @@ func (h *Handler) find(w http.ResponseWriter, r *http.Request, p httprouter.Para if err != nil && !trace.IsNotFound(err) && !trace.IsNotImplemented(err) { h.logger.ErrorContext(r.Context(), "failed to receive AutoUpdateVersion", "error", err) } else if err == nil { - response.AutoUpdate.ToolsVersion = autoUpdateVersion.GetSpec().GetToolsVersion() + response.AutoUpdate.ToolsVersion = autoUpdateVersion.GetSpec().GetTools().GetTargetVersion() } return response, nil diff --git a/lib/web/apiserver_ping_test.go b/lib/web/apiserver_ping_test.go index e14017d0ff135..6e51b57305595 100644 --- a/lib/web/apiserver_ping_test.go +++ b/lib/web/apiserver_ping_test.go @@ -306,28 +306,52 @@ func TestPing_autoUpdateResources(t *testing.T) { expected: webclient.AutoUpdateSettings{}, }, { - name: "enable auto update", - config: &autoupdatev1pb.AutoUpdateConfigSpec{ToolsAutoupdate: true}, - expected: webclient.AutoUpdateSettings{ToolsAutoUpdate: true}, + name: "enable auto update", + config: &autoupdatev1pb.AutoUpdateConfigSpec{ + Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{ + Mode: autoupdate.ToolsUpdateModeEnabled, + }, + }, + expected: webclient.AutoUpdateSettings{ToolsMode: "enabled"}, cleanup: true, }, { - name: "set auto update version", - version: &autoupdatev1pb.AutoUpdateVersionSpec{ToolsVersion: "1.2.3"}, + name: "set auto update version", + version: &autoupdatev1pb.AutoUpdateVersionSpec{ + Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{ + TargetVersion: "1.2.3", + }, + }, expected: webclient.AutoUpdateSettings{ToolsVersion: "1.2.3"}, cleanup: true, }, { - name: "enable auto update and set version", - config: &autoupdatev1pb.AutoUpdateConfigSpec{ToolsAutoupdate: true}, - version: &autoupdatev1pb.AutoUpdateVersionSpec{ToolsVersion: "1.2.3"}, - expected: webclient.AutoUpdateSettings{ToolsAutoUpdate: true, ToolsVersion: "1.2.3"}, + name: "enable auto update and set version", + config: &autoupdatev1pb.AutoUpdateConfigSpec{ + Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{ + Mode: autoupdate.ToolsUpdateModeEnabled, + }, + }, + version: &autoupdatev1pb.AutoUpdateVersionSpec{ + Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{ + TargetVersion: "1.2.3", + }, + }, + expected: webclient.AutoUpdateSettings{ToolsMode: "enabled", ToolsVersion: "1.2.3"}, }, { - name: "modify auto update config and version", - config: &autoupdatev1pb.AutoUpdateConfigSpec{ToolsAutoupdate: false}, - version: &autoupdatev1pb.AutoUpdateVersionSpec{ToolsVersion: "3.2.1"}, - expected: webclient.AutoUpdateSettings{ToolsAutoUpdate: false, ToolsVersion: "3.2.1"}, + name: "modify auto update config and version", + config: &autoupdatev1pb.AutoUpdateConfigSpec{ + Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{ + Mode: autoupdate.ToolsUpdateModeDisabled, + }, + }, + version: &autoupdatev1pb.AutoUpdateVersionSpec{ + Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{ + TargetVersion: "3.2.1", + }, + }, + expected: webclient.AutoUpdateSettings{ToolsMode: "disabled", ToolsVersion: "3.2.1"}, }, } for _, tc := range tests { diff --git a/tool/tctl/common/collection.go b/tool/tctl/common/collection.go index e3ae81f763945..5719344fe4bd3 100644 --- a/tool/tctl/common/collection.go +++ b/tool/tctl/common/collection.go @@ -1851,7 +1851,7 @@ func (c *autoUpdateConfigCollection) writeText(w io.Writer, verbose bool) error t := asciitable.MakeTable([]string{"Name", "Tools AutoUpdate Enabled"}) t.AddRow([]string{ c.config.GetMetadata().GetName(), - fmt.Sprintf("%v", c.config.GetSpec().GetToolsAutoupdate()), + fmt.Sprintf("%v", c.config.GetSpec().GetTools().GetMode()), }) _, err := t.AsBuffer().WriteTo(w) return trace.Wrap(err) @@ -1869,7 +1869,7 @@ func (c *autoUpdateVersionCollection) writeText(w io.Writer, verbose bool) error t := asciitable.MakeTable([]string{"Name", "Tools AutoUpdate Version"}) t.AddRow([]string{ c.version.GetMetadata().GetName(), - fmt.Sprintf("%v", c.version.GetSpec().GetToolsVersion()), + fmt.Sprintf("%v", c.version.GetSpec().GetTools().TargetVersion), }) _, err := t.AsBuffer().WriteTo(w) return trace.Wrap(err) diff --git a/tool/tctl/common/edit_command_test.go b/tool/tctl/common/edit_command_test.go index 42f9dee0043ed..9ce3a89d7bf85 100644 --- a/tool/tctl/common/edit_command_test.go +++ b/tool/tctl/common/edit_command_test.go @@ -555,10 +555,18 @@ func testEditStaticHostUser(t *testing.T, clt *authclient.Client) { func testEditAutoUpdateConfig(t *testing.T, clt *authclient.Client) { ctx := context.Background() - expected, err := autoupdate.NewAutoUpdateConfig(&autoupdatev1pb.AutoUpdateConfigSpec{ToolsAutoupdate: true}) + expected, err := autoupdate.NewAutoUpdateConfig(&autoupdatev1pb.AutoUpdateConfigSpec{ + Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{ + Mode: autoupdate.ToolsUpdateModeEnabled, + }, + }) require.NoError(t, err) - initial, err := autoupdate.NewAutoUpdateConfig(&autoupdatev1pb.AutoUpdateConfigSpec{ToolsAutoupdate: false}) + initial, err := autoupdate.NewAutoUpdateConfig(&autoupdatev1pb.AutoUpdateConfigSpec{ + Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{ + Mode: autoupdate.ToolsUpdateModeDisabled, + }, + }) require.NoError(t, err) serviceClient := autoupdatev1pb.NewAutoUpdateServiceClient(clt.GetConnection()) @@ -581,18 +589,26 @@ func testEditAutoUpdateConfig(t *testing.T, clt *authclient.Client) { actual, err := clt.GetAutoUpdateConfig(ctx) require.NoError(t, err, "failed to get autoupdate config after edit") - assert.NotEqual(t, initial.GetSpec().GetToolsAutoupdate(), actual.GetSpec().GetToolsAutoupdate(), + assert.NotEqual(t, initial.GetSpec().GetTools().Mode, actual.GetSpec().GetTools().GetMode(), "tools_autoupdate should have been modified by edit") - assert.Equal(t, expected.GetSpec().GetToolsAutoupdate(), actual.GetSpec().GetToolsAutoupdate()) + assert.Equal(t, expected.GetSpec().GetTools().GetMode(), actual.GetSpec().GetTools().GetMode()) } func testEditAutoUpdateVersion(t *testing.T, clt *authclient.Client) { ctx := context.Background() - expected, err := autoupdate.NewAutoUpdateVersion(&autoupdatev1pb.AutoUpdateVersionSpec{ToolsVersion: "3.2.1"}) + expected, err := autoupdate.NewAutoUpdateVersion(&autoupdatev1pb.AutoUpdateVersionSpec{ + Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{ + TargetVersion: "3.2.1", + }, + }) require.NoError(t, err) - initial, err := autoupdate.NewAutoUpdateVersion(&autoupdatev1pb.AutoUpdateVersionSpec{ToolsVersion: "1.2.3"}) + initial, err := autoupdate.NewAutoUpdateVersion(&autoupdatev1pb.AutoUpdateVersionSpec{ + Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{ + TargetVersion: "1.2.3", + }, + }) require.NoError(t, err) serviceClient := autoupdatev1pb.NewAutoUpdateServiceClient(clt.GetConnection()) @@ -615,7 +631,7 @@ func testEditAutoUpdateVersion(t *testing.T, clt *authclient.Client) { actual, err := clt.GetAutoUpdateVersion(ctx) require.NoError(t, err, "failed to get autoupdate version after edit") - assert.NotEqual(t, initial.GetSpec().GetToolsVersion(), actual.GetSpec().GetToolsVersion(), + assert.NotEqual(t, initial.GetSpec().GetTools().GetTargetVersion(), actual.GetSpec().GetTools().GetTargetVersion(), "tools_autoupdate should have been modified by edit") - assert.Equal(t, expected.GetSpec().GetToolsVersion(), actual.GetSpec().GetToolsVersion()) + assert.Equal(t, expected.GetSpec().GetTools().GetTargetVersion(), actual.GetSpec().GetTools().GetTargetVersion()) } diff --git a/tool/tctl/common/resource_command_test.go b/tool/tctl/common/resource_command_test.go index 1eb4b6ee34bfd..dca834a2e6e7f 100644 --- a/tool/tctl/common/resource_command_test.go +++ b/tool/tctl/common/resource_command_test.go @@ -2293,7 +2293,8 @@ metadata: name: autoupdate-config revision: 3a43b44a-201e-4d7f-aef1-ae2f6d9811ed spec: - tools_autoupdate: true + tools: + mode: enabled version: v1 ` _, err := runResourceCommand(t, clt, []string{"get", types.KindAutoUpdateConfig, "--format=json"}) @@ -2328,7 +2329,8 @@ metadata: name: autoupdate-version revision: 3a43b44a-201e-4d7f-aef1-ae2f6d9811ed spec: - tools_version: 1.2.3 + tools: + target_version: 1.2.3 version: v1 ` _, err := runResourceCommand(t, clt, []string{"get", types.KindAutoUpdateVersion, "--format=json"}) From 775044bb75519010bc73a6176722e763d8a03547 Mon Sep 17 00:00:00 2001 From: Alan Parra Date: Thu, 17 Oct 2024 12:07:37 -0300 Subject: [PATCH 09/30] Check bounds of tpm.EKs() slice before indexing (#47664) --- lib/tpm/tpm.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/tpm/tpm.go b/lib/tpm/tpm.go index b720df596a822..6175efdedb018 100644 --- a/lib/tpm/tpm.go +++ b/lib/tpm/tpm.go @@ -125,6 +125,11 @@ func QueryWithTPM( if err != nil { return nil, trace.Wrap(err, "querying EKs") } + // Be a good citizen and check the slice bounds. This is not expected to + // happen. + if len(eks) == 0 { + return nil, trace.BadParameter("no endorsement keys found in tpm") + } // The first EK returned by `go-attestation` will be an RSA based EK key or // EK cert. On Windows, ECC certs may also be returned following this. At From 22edf09261f6c78ed6dd59b9202e4b2fb0cf452c Mon Sep 17 00:00:00 2001 From: Brian Joerger Date: Thu, 17 Oct 2024 10:35:18 -0700 Subject: [PATCH 10/30] feat: SSO MFA - Add `MFAToken` to SSO Callback flow (#47648) * Add MFA token to SSO callback response. * Add MFA token to auth response validation endpoints. * Cleanup. --- lib/auth/authclient/clt.go | 6 ++++++ lib/auth/authclient/http_client.go | 6 ++++++ lib/web/apiserver.go | 5 +++++ 3 files changed, 17 insertions(+) diff --git a/lib/auth/authclient/clt.go b/lib/auth/authclient/clt.go index 02c00a7502e23..005a44cdda8a1 100644 --- a/lib/auth/authclient/clt.go +++ b/lib/auth/authclient/clt.go @@ -896,6 +896,8 @@ type OIDCAuthResponse struct { // HostSigners is a list of signing host public keys // trusted by proxy, used in console login HostSigners []types.CertAuthority `json:"host_signers"` + // MFAToken is an SSO MFA token. + MFAToken string `json:"mfa_token"` } // OIDCAuthRequest is an OIDC auth request that supports standard json marshaling. @@ -941,6 +943,8 @@ type SAMLAuthResponse struct { // HostSigners is a list of signing host public keys // trusted by proxy, used in console login HostSigners []types.CertAuthority `json:"host_signers"` + // MFAToken is an SSO MFA token. + MFAToken string `json:"mfa_token"` } // SAMLAuthRequest is a SAML auth request that supports standard json marshaling. @@ -1485,6 +1489,8 @@ type SSHLoginResponse struct { HostSigners []TrustedCerts `json:"host_signers"` // SAMLSingleLogoutEnabled is whether SAML SLO (single logout) is enabled for the SAML auth connector being used, if applicable. SAMLSingleLogoutEnabled bool `json:"samlSingleLogoutEnabled"` + // MFAToken is an SSO MFA token. + MFAToken string `json:"mfa_token"` } // TrustedCerts contains host certificates, it preserves backwards compatibility diff --git a/lib/auth/authclient/http_client.go b/lib/auth/authclient/http_client.go index 271f6d94008fe..f2a9f696fc36f 100644 --- a/lib/auth/authclient/http_client.go +++ b/lib/auth/authclient/http_client.go @@ -638,6 +638,8 @@ type OIDCAuthRawResponse struct { // HostSigners is a list of signing host public keys // trusted by proxy, used in console login HostSigners []json.RawMessage `json:"host_signers"` + // MFAToken is an SSO MFA token. + MFAToken string `json:"mfa_token"` } // ValidateOIDCAuthCallback validates OIDC auth callback returned from redirect @@ -658,6 +660,7 @@ func (c *HTTPClient) ValidateOIDCAuthCallback(ctx context.Context, q url.Values) Cert: rawResponse.Cert, Req: rawResponse.Req, TLSCert: rawResponse.TLSCert, + MFAToken: rawResponse.MFAToken, } if len(rawResponse.Session) != 0 { session, err := services.UnmarshalWebSession(rawResponse.Session) @@ -707,6 +710,8 @@ type SAMLAuthRawResponse struct { HostSigners []json.RawMessage `json:"host_signers"` // TLSCert is TLS certificate authority certificate TLSCert []byte `json:"tls_cert,omitempty"` + // MFAToken is an SSO MFA token. + MFAToken string `json:"mfa_token"` } // ValidateSAMLResponse validates response returned by SAML identity provider @@ -729,6 +734,7 @@ func (c *HTTPClient) ValidateSAMLResponse(ctx context.Context, samlResponse, con Cert: rawResponse.Cert, Req: rawResponse.Req, TLSCert: rawResponse.TLSCert, + MFAToken: rawResponse.MFAToken, } if len(rawResponse.Session) != 0 { session, err := services.UnmarshalWebSession(rawResponse.Session) diff --git a/lib/web/apiserver.go b/lib/web/apiserver.go index 1c4c24a10e214..8e1e4ba43f691 100644 --- a/lib/web/apiserver.go +++ b/lib/web/apiserver.go @@ -2184,6 +2184,8 @@ type AuthParams struct { // FIPS mode means Teleport started in a FedRAMP/FIPS 140-2 compliant // configuration. FIPS bool + // MFAToken is an SSO MFA token. + MFAToken string } // ConstructSSHResponse creates a special SSH response for SSH login method @@ -2198,6 +2200,7 @@ func ConstructSSHResponse(response AuthParams) (*url.URL, error) { Cert: response.Cert, TLSCert: response.TLSCert, HostSigners: authclient.AuthoritiesToTrustedCerts(response.HostSigners), + MFAToken: response.MFAToken, } out, err := json.Marshal(consoleResponse) if err != nil { @@ -5018,6 +5021,8 @@ type SSOCallbackResponse struct { // ClientRedirectURL is the URL to redirect back to on completion of // the SSO login process. ClientRedirectURL string + // MFAToken is an SSO MFA token. + MFAToken string } // SSOSetWebSessionAndRedirectURL validates the CSRF token in the response From 0043313659bcb06257d583d17ead8a99846b003f Mon Sep 17 00:00:00 2001 From: Alan Parra Date: Thu, 17 Oct 2024 18:00:40 -0300 Subject: [PATCH 11/30] chore: Update e/ reference (#47682) --- e | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e b/e index 5a1b7ff1b8aa0..ad2d7f191845b 160000 --- a/e +++ b/e @@ -1 +1 @@ -Subproject commit 5a1b7ff1b8aa0579e171efe9b607d742ebf8e5bb +Subproject commit ad2d7f191845b4964c1e2baa211dceb87b6ac105 From 17586ed5bce656608a2a691ea2b403814049e8a8 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 17 Oct 2024 16:45:00 -0500 Subject: [PATCH 12/30] Validate redirect URL origin during app authentication (#47640) --- lib/web/app/redirect.go | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/web/app/redirect.go b/lib/web/app/redirect.go index 9aba5cca7bc8b..48f959478bee9 100644 --- a/lib/web/app/redirect.go +++ b/lib/web/app/redirect.go @@ -103,8 +103,9 @@ const appRedirectHTML = ` Teleport Redirection Service