diff --git a/assets/install-scripts/install.sh b/assets/install-scripts/install.sh
deleted file mode 100755
index 949bb1bcc4d8f..0000000000000
--- a/assets/install-scripts/install.sh
+++ /dev/null
@@ -1,430 +0,0 @@
-#!/bin/bash
-# Copyright 2022 Gravitational, Inc
-
-# This script detects the current Linux distribution and installs Teleport
-# through its package manager, if supported, or downloading a tarball otherwise.
-# We'll download Teleport from the official website and checksum it to make sure it was properly
-# downloaded before executing.
-
-# The script is wrapped inside a function to protect against the connection being interrupted
-# in the middle of the stream.
-
-# For more download options, head to https://goteleport.com/download/
-
-set -euo pipefail
-
-# download uses curl or wget to download a teleport binary
-download() {
- URL=$1
- TMP_PATH=$2
-
- echo "Downloading $URL"
- if type curl &>/dev/null; then
- set -x
- # shellcheck disable=SC2086
- $SUDO $CURL -o "$TMP_PATH" "$URL"
- else
- set -x
- # shellcheck disable=SC2086
- $SUDO $CURL -O "$TMP_PATH" "$URL"
- fi
- set +x
-}
-
-install_via_apt_get() {
- echo "Installing Teleport v$TELEPORT_VERSION via apt-get"
- add_apt_key
- set -x
- $SUDO apt-get install -y "teleport$TELEPORT_SUFFIX=$TELEPORT_VERSION"
- set +x
- if [ "$TELEPORT_EDITION" = "cloud" ]; then
- set -x
- $SUDO apt-get install -y teleport-ent-updater
- set +x
- fi
-}
-
-add_apt_key() {
- APT_REPO_ID=$ID
- APT_REPO_VERSION_CODENAME=$VERSION_CODENAME
- IS_LEGACY=0
-
- # check if we must use legacy .asc key
- case "$ID" in
- ubuntu | pop | neon | zorin)
- if ! expr "$VERSION_ID" : "2.*" >/dev/null; then
- IS_LEGACY=1
- fi
- ;;
- debian | raspbian)
- if [ "$VERSION_ID" -lt 11 ]; then
- IS_LEGACY=1
- fi
- ;;
- linuxmint | parrot)
- if [ "$VERSION_ID" -lt 5 ]; then
- IS_LEGACY=1
- fi
- ;;
- elementary)
- if [ "$VERSION_ID" -lt 6 ]; then
- IS_LEGACY=1
- fi
- ;;
- kali)
- YEAR="$(echo "$VERSION_ID" | cut -f1 -d.)"
- if [ "$YEAR" -lt 2021 ]; then
- IS_LEGACY=1
- fi
- ;;
- esac
-
- if [[ "$IS_LEGACY" == 0 ]]; then
- # set APT_REPO_ID if necessary
- case "$ID" in
- linuxmint | kali | elementary | pop | raspbian | neon | zorin | parrot)
- APT_REPO_ID=$ID_LIKE
- ;;
- esac
-
- # set APT_REPO_VERSION_CODENAME if necessary
- case "$ID" in
- linuxmint | elementary | pop | neon | zorin)
- APT_REPO_VERSION_CODENAME=$UBUNTU_CODENAME
- ;;
- kali)
- APT_REPO_VERSION_CODENAME="bullseye"
- ;;
- parrot)
- APT_REPO_VERSION_CODENAME="buster"
- ;;
- esac
- fi
-
- echo "Downloading Teleport's PGP public key..."
- TEMP_DIR=$(mktemp -d -t teleport-XXXXXXXXXX)
- MAJOR=$(echo "$TELEPORT_VERSION" | cut -f1 -d.)
- TELEPORT_REPO=""
-
- CHANNEL="stable/v${MAJOR}"
- if [ "$TELEPORT_EDITION" = "cloud" ]; then
- CHANNEL="stable/cloud"
- fi
-
- if [[ "$IS_LEGACY" == 1 ]]; then
- if ! type gpg >/dev/null; then
- echo "Installing gnupg"
- set -x
- $SUDO apt-get update
- $SUDO apt-get install -y gnupg
- set +x
- fi
- TMP_KEY="$TEMP_DIR/teleport-pubkey.asc"
- download "https://deb.releases.teleport.dev/teleport-pubkey.asc" "$TMP_KEY"
- set -x
- $SUDO apt-key add "$TMP_KEY"
- set +x
- TELEPORT_REPO="deb https://apt.releases.teleport.dev/${APT_REPO_ID?} ${APT_REPO_VERSION_CODENAME?} ${CHANNEL}"
- else
- TMP_KEY="$TEMP_DIR/teleport-pubkey.gpg"
- download "https://apt.releases.teleport.dev/gpg" "$TMP_KEY"
- set -x
- $SUDO mkdir -p /etc/apt/keyrings
- $SUDO cp "$TMP_KEY" /etc/apt/keyrings/teleport-archive-keyring.asc
- set +x
- TELEPORT_REPO="deb [signed-by=/etc/apt/keyrings/teleport-archive-keyring.asc] https://apt.releases.teleport.dev/${APT_REPO_ID?} ${APT_REPO_VERSION_CODENAME?} ${CHANNEL}"
- fi
-
- set -x
- echo "$TELEPORT_REPO" | $SUDO tee /etc/apt/sources.list.d/teleport.list >/dev/null
- set +x
-
- set -x
- $SUDO apt-get update
- set +x
-}
-
-# $1 is the value of the $ID path segment in the YUM repo URL. In
-# /etc/os-release, this is either the value of $ID or $ID_LIKE.
-install_via_yum() {
- # shellcheck source=/dev/null
- source /etc/os-release
-
- # Get the major version from the version ID.
- VERSION_ID=$(echo "$VERSION_ID" | grep -Eo "^[0-9]+")
- TELEPORT_MAJOR_VERSION="v$(echo "$TELEPORT_VERSION" | grep -Eo "^[0-9]+")"
-
- CHANNEL="stable/${TELEPORT_MAJOR_VERSION}"
- if [ "$TELEPORT_EDITION" = "cloud" ]; then
- CHANNEL="stable/cloud"
- fi
-
- if type dnf &>/dev/null; then
- echo "Installing Teleport v$TELEPORT_VERSION through dnf"
- $SUDO dnf install -y 'dnf-command(config-manager)'
- $SUDO dnf config-manager --add-repo "$(rpm --eval "https://yum.releases.teleport.dev/$1/$VERSION_ID/Teleport/%{_arch}/$CHANNEL/teleport-yum.repo")"
- $SUDO dnf install -y "teleport$TELEPORT_SUFFIX-$TELEPORT_VERSION"
-
- if [ "$TELEPORT_EDITION" = "cloud" ]; then
- $SUDO dnf install -y teleport-ent-updater
- fi
-
- else
- echo "Installing Teleport v$TELEPORT_VERSION through yum"
- $SUDO yum install -y yum-utils
- $SUDO yum-config-manager --add-repo "$(rpm --eval "https://yum.releases.teleport.dev/$1/$VERSION_ID/Teleport/%{_arch}/$CHANNEL/teleport-yum.repo")"
- $SUDO yum install -y "teleport$TELEPORT_SUFFIX-$TELEPORT_VERSION"
-
- if [ "$TELEPORT_EDITION" = "cloud" ]; then
- $SUDO yum install -y teleport-ent-updater
- fi
- fi
- set +x
-}
-
-install_via_zypper() {
- # shellcheck source=/dev/null
- source /etc/os-release
-
- # Get the major version from the version ID.
- VERSION_ID=$(echo "$VERSION_ID" | grep -Eo "^[0-9]+")
- TELEPORT_MAJOR_VERSION="v$(echo "$TELEPORT_VERSION" | grep -Eo "^[0-9]+")"
-
- CHANNEL="stable/${TELEPORT_MAJOR_VERSION}"
- if [ "$TELEPORT_EDITION" = "cloud" ]; then
- CHANNEL="stable/cloud"
- fi
-
- $SUDO rpm --import https://zypper.releases.teleport.dev/gpg
- $SUDO zypper addrepo --refresh --repo "$(rpm --eval "https://zypper.releases.teleport.dev/$ID/$VERSION_ID/Teleport/%{_arch}/$CHANNEL/teleport-zypper.repo")"
- $SUDO zypper --gpg-auto-import-keys refresh teleport
- $SUDO zypper install -y "teleport$TELEPORT_SUFFIX"
-
- if [ "$TELEPORT_EDITION" = "cloud" ]; then
- $SUDO zypper install -y teleport-ent-updater
- fi
-
- set +x
-}
-
-
-# download .tar.gz file via curl/wget, unzip it and run the install script
-install_via_curl() {
- TEMP_DIR=$(mktemp -d -t teleport-XXXXXXXXXX)
-
- TELEPORT_FILENAME="teleport$TELEPORT_SUFFIX-v$TELEPORT_VERSION-linux-$ARCH-bin.tar.gz"
- URL="https://cdn.teleport.dev/${TELEPORT_FILENAME}"
- download "${URL}" "${TEMP_DIR}/${TELEPORT_FILENAME}"
-
- TMP_CHECKSUM="${TEMP_DIR}/${TELEPORT_FILENAME}.sha256"
- download "${URL}.sha256" "$TMP_CHECKSUM"
-
- set -x
- cd "$TEMP_DIR"
- # shellcheck disable=SC2086
- $SUDO $SHA_COMMAND -c "$TMP_CHECKSUM"
- cd -
-
- $SUDO tar -xzf "${TEMP_DIR}/${TELEPORT_FILENAME}" -C "$TEMP_DIR"
- $SUDO "$TEMP_DIR/teleport/install"
- set +x
-}
-
-# wrap script in a function so a partially downloaded script
-# doesn't execute
-install_teleport() {
- # exit if not on Linux
- if [[ $(uname) != "Linux" ]]; then
- echo "ERROR: This script works only for Linux. Please go to the downloads page to find the proper installation method for your operating system:"
- echo "https://goteleport.com/download/"
- exit 1
- fi
-
- KERNEL_VERSION=$(uname -r)
- MIN_VERSION="2.6.23"
- if [ $MIN_VERSION != "$(echo -e "$MIN_VERSION\n$KERNEL_VERSION" | sort -V | head -n1)" ]; then
- echo "ERROR: Teleport requires Linux kernel version $MIN_VERSION+"
- exit 1
- fi
-
- # check if can run as admin either by running as root or by
- # having 'sudo' or 'doas' installed
- IS_ROOT=""
- SUDO=""
- if [ "$(id -u)" = 0 ]; then
- # running as root, no need for sudo/doas
- IS_ROOT="YES"
- SUDO=""
- elif type sudo &>/dev/null; then
- SUDO="sudo"
- elif type doas &>/dev/null; then
- SUDO="doas"
- fi
-
- if [ -z "$SUDO" ] && [ -z "$IS_ROOT" ]; then
- echo "ERROR: The installer requires a way to run commands as root."
- echo "Either run this script as root or install sudo/doas."
- exit 1
- fi
-
- # require curl/wget
- CURL=""
- if type curl &>/dev/null; then
- CURL="curl -fL"
- elif type wget &>/dev/null; then
- CURL="wget"
- fi
- if [ -z "$CURL" ]; then
- echo "ERROR: This script requires either curl or wget in order to download files. Please install one of them and try again."
- exit 1
- fi
-
- # require shasum/sha256sum
- SHA_COMMAND=""
- if type shasum &>/dev/null; then
- SHA_COMMAND="shasum -a 256"
- elif type sha256sum &>/dev/null; then
- SHA_COMMAND="sha256sum"
- else
- echo "ERROR: This script requires sha256sum or shasum to validate the download. Please install it and try again."
- exit 1
- fi
-
- # detect distro
- OS_RELEASE=/etc/os-release
- ID=""
- ID_LIKE=""
- VERSION_CODENAME=""
- UBUNTU_CODENAME=""
- if [[ -f "$OS_RELEASE" ]]; then
- # shellcheck source=/dev/null
- . $OS_RELEASE
- fi
- # Some $ID_LIKE values include multiple distro names in an arbitrary order, so
- # evaluate the first one.
- ID_LIKE="${ID_LIKE%% *}"
-
- # detect architecture
- ARCH=""
- case $(uname -m) in
- x86_64)
- ARCH="amd64"
- ;;
- i386)
- ARCH="386"
- ;;
- armv7l)
- ARCH="arm"
- ;;
- aarch64)
- ARCH="arm64"
- ;;
- **)
- echo "ERROR: Your system's architecture isn't officially supported or couldn't be determined."
- echo "Please refer to the installation guide for more information:"
- echo "https://goteleport.com/docs/installation/"
- exit 1
- ;;
- esac
-
- # select install method based on distribution
- # if ID is debian derivate, run apt-get
- case "$ID" in
- debian | ubuntu | kali | linuxmint | pop | raspbian | neon | zorin | parrot | elementary)
- install_via_apt_get
- ;;
- # if ID is amazon Linux 2/RHEL/etc, run yum
- centos | rhel | amzn)
- install_via_yum "$ID"
- ;;
- sles)
- install_via_zypper
- ;;
- *)
- # before downloading manually, double check if we didn't miss any debian or
- # rh/fedora derived distros using the ID_LIKE var.
- case "${ID_LIKE}" in
- ubuntu | debian)
- install_via_apt_get
- ;;
- centos | fedora | rhel)
- # There is no repository for "fedora", and there is no difference
- # between the repositories for "centos" and "rhel", so pick an arbitrary
- # one.
- install_via_yum rhel
- ;;
- *)
- if [ "$TELEPORT_EDITION" = "cloud" ]; then
- echo "The system does not support a package manager, which is required for Teleport Enterprise Cloud."
- exit 1
- fi
-
- # if ID and ID_LIKE didn't return a supported distro, download through curl
- echo "There is no officially supported package for your package manager. Downloading and installing Teleport via curl."
- install_via_curl
- ;;
- esac
- ;;
- esac
-
- GREEN='\033[0;32m'
- COLOR_OFF='\033[0m'
-
- echo ""
- echo -e "${GREEN}$(teleport version) installed successfully!${COLOR_OFF}"
- echo ""
- echo "The following commands are now available:"
- if type teleport &>/dev/null; then
- echo " teleport - The daemon that runs the Auth Service, Proxy Service, and other Teleport services."
- fi
- if type tsh &>/dev/null; then
- echo " tsh - A tool that lets end users interact with Teleport."
- fi
- if type tctl &>/dev/null; then
- echo " tctl - An administrative tool that can configure the Teleport Auth Service."
- fi
- if type tbot &>/dev/null; then
- echo " tbot - Teleport Machine ID client."
- fi
- if type fdpass-teleport &>/dev/null; then
- echo " fdpass-teleport - Teleport Machine ID client."
- fi
- if type teleport-update &>/dev/null; then
- echo " teleport-update - Teleport auto-update agent."
- fi
-}
-
-# The suffix is "-ent" if we are installing a commercial edition of Teleport and
-# empty for Teleport Community Edition.
-TELEPORT_SUFFIX=""
-TELEPORT_VERSION=""
-TELEPORT_EDITION=""
-if [ $# -ge 1 ] && [ -n "$1" ]; then
- TELEPORT_VERSION=$1
-else
- echo "ERROR: Please provide the version you want to install (e.g., 10.1.9)."
- exit 1
-fi
-
-if ! echo "$1" | grep -qE "[0-9]+\.[0-9]+\.[0-9]+"; then
- echo "ERROR: The first parameter must be a version number, e.g., 10.1.9."
- exit 1
-fi
-
-if [ $# -ge 2 ] && [ -n "$2" ]; then
- TELEPORT_EDITION=$2
-
- case $TELEPORT_EDITION in
- enterprise | cloud)
- TELEPORT_SUFFIX="-ent"
- ;;
- # An empty edition defaults to OSS.
- oss | "" )
- ;;
- *)
- echo 'ERROR: The second parameter must be "oss", "cloud", or "enterprise".'
- exit 1
- ;;
- esac
-fi
-install_teleport
diff --git a/assets/install-scripts/install.sh b/assets/install-scripts/install.sh
new file mode 120000
index 0000000000000..c41183ab5d100
--- /dev/null
+++ b/assets/install-scripts/install.sh
@@ -0,0 +1 @@
+../../lib/web/scripts/install/install.sh
\ No newline at end of file
diff --git a/lib/srv/server/installer/defaultinstallers.go b/lib/srv/server/installer/defaultinstallers.go
index c5c2642903bb8..f9bb4dbf77b9f 100644
--- a/lib/srv/server/installer/defaultinstallers.go
+++ b/lib/srv/server/installer/defaultinstallers.go
@@ -40,7 +40,7 @@ func oneoffScriptToDefaultInstaller() *types.InstallerV1 {
}
script, err := oneoff.BuildScript(oneoff.OneOffScriptParams{
- TeleportArgs: strings.Join(argsList, " "),
+ EntrypointArgs: strings.Join(argsList, " "),
SuccessMessage: "Teleport is installed and running.",
TeleportCommandPrefix: oneoff.PrefixSUDO,
})
diff --git a/lib/web/apiserver.go b/lib/web/apiserver.go
index 95ca84e410619..009083aa9dede 100644
--- a/lib/web/apiserver.go
+++ b/lib/web/apiserver.go
@@ -894,6 +894,11 @@ func (h *Handler) bindDefaultEndpoints() {
h.GET("/webapi/tokens", h.WithAuth(h.getTokens))
h.DELETE("/webapi/tokens", h.WithAuth(h.deleteToken))
+ // install script, the ':token' wildcard is a hack to make the router happy and support
+ // the token-less route "/scripts/install.sh".
+ // h.installScriptHandle Will reject any unknown sub-route.
+ h.GET("/scripts/:token", h.WithHighLimiter(h.installScriptHandle))
+
// join scripts
h.GET("/scripts/:token/install-node.sh", h.WithLimiter(h.getNodeJoinScriptHandle))
h.GET("/scripts/:token/install-app.sh", h.WithLimiter(h.getAppJoinScriptHandle))
diff --git a/lib/web/apiserver_test.go b/lib/web/apiserver_test.go
index ca6f1272e1763..a9d93db9559a0 100644
--- a/lib/web/apiserver_test.go
+++ b/lib/web/apiserver_test.go
@@ -8538,9 +8538,9 @@ func createProxy(ctx context.Context, t *testing.T, proxyID string, node *regula
},
)
handler.handler.cfg.ProxyKubeAddr = utils.FromAddr(kubeProxyAddr)
+ handler.handler.cfg.PublicProxyAddr = webServer.Listener.Addr().String()
url, err := url.Parse("https://" + webServer.Listener.Addr().String())
require.NoError(t, err)
- handler.handler.cfg.PublicProxyAddr = url.String()
return &testProxy{
clock: clock,
diff --git a/lib/web/autoupdate_common.go b/lib/web/autoupdate_common.go
index 8e2d817c78c46..0daaadaec02ce 100644
--- a/lib/web/autoupdate_common.go
+++ b/lib/web/autoupdate_common.go
@@ -42,7 +42,7 @@ func (h *Handler) autoUpdateAgentVersion(ctx context.Context, group, updaterUUID
rollout, err := h.cfg.AccessPoint.GetAutoUpdateAgentRollout(ctx)
if err != nil {
// Fallback to channels if there is no autoupdate_agent_rollout.
- if trace.IsNotFound(err) {
+ if trace.IsNotFound(err) || trace.IsNotImplemented(err) {
return getVersionFromChannel(ctx, h.cfg.AutomaticUpgradesChannels, group)
}
// Something is broken, we don't want to fallback to channels, this would be harmful.
@@ -77,7 +77,7 @@ func (h *Handler) autoUpdateAgentShouldUpdate(ctx context.Context, group, update
rollout, err := h.cfg.AccessPoint.GetAutoUpdateAgentRollout(ctx)
if err != nil {
// Fallback to channels if there is no autoupdate_agent_rollout.
- if trace.IsNotFound(err) {
+ if trace.IsNotFound(err) || trace.IsNotImplemented(err) {
// Updaters using the RFD184 API are not aware of maintenance windows
// like RFD109 updaters are. To have both updaters adopt the same behavior
// we must do the CMC window lookup for them.
diff --git a/lib/web/integrations_awsoidc.go b/lib/web/integrations_awsoidc.go
index aa8019b330f49..ff98c9ed8f1dd 100644
--- a/lib/web/integrations_awsoidc.go
+++ b/lib/web/integrations_awsoidc.go
@@ -600,7 +600,7 @@ func (h *Handler) awsOIDCConfigureDeployServiceIAM(w http.ResponseWriter, r *htt
fmt.Sprintf("--aws-account-id=%s", shsprintf.EscapeDefaultContext(awsAccountID)),
}
script, err := oneoff.BuildScript(oneoff.OneOffScriptParams{
- TeleportArgs: strings.Join(argsList, " "),
+ EntrypointArgs: strings.Join(argsList, " "),
SuccessMessage: "Success! You can now go back to the Teleport Web UI to complete the database enrollment.",
})
if err != nil {
@@ -633,7 +633,7 @@ func (h *Handler) awsOIDCConfigureAWSAppAccessIAM(w http.ResponseWriter, r *http
fmt.Sprintf("--role=%s", shsprintf.EscapeDefaultContext(role)),
}
script, err := oneoff.BuildScript(oneoff.OneOffScriptParams{
- TeleportArgs: strings.Join(argsList, " "),
+ EntrypointArgs: strings.Join(argsList, " "),
SuccessMessage: "Success! You can now go back to the Teleport Web UI to use AWS App Access.",
})
if err != nil {
@@ -704,7 +704,7 @@ func (h *Handler) awsOIDCConfigureEC2SSMIAM(w http.ResponseWriter, r *http.Reque
fmt.Sprintf("--aws-account-id=%s", shsprintf.EscapeDefaultContext(awsAccountID)),
}
script, err := oneoff.BuildScript(oneoff.OneOffScriptParams{
- TeleportArgs: strings.Join(argsList, " "),
+ EntrypointArgs: strings.Join(argsList, " "),
SuccessMessage: "Success! You can now go back to the Teleport Web UI to finish the EC2 auto discover set up.",
})
if err != nil {
@@ -745,7 +745,7 @@ func (h *Handler) awsOIDCConfigureEKSIAM(w http.ResponseWriter, r *http.Request,
fmt.Sprintf("--aws-account-id=%s", shsprintf.EscapeDefaultContext(awsAccountID)),
}
script, err := oneoff.BuildScript(oneoff.OneOffScriptParams{
- TeleportArgs: strings.Join(argsList, " "),
+ EntrypointArgs: strings.Join(argsList, " "),
SuccessMessage: "Success! You can now go back to the Teleport Web UI to complete the EKS enrollment.",
})
if err != nil {
@@ -1252,7 +1252,7 @@ func (h *Handler) awsOIDCConfigureIdP(w http.ResponseWriter, r *http.Request, p
}
script, err := oneoff.BuildScript(oneoff.OneOffScriptParams{
- TeleportArgs: strings.Join(argsList, " "),
+ EntrypointArgs: strings.Join(argsList, " "),
SuccessMessage: "Success! You can now go back to the Teleport Web UI to use the integration with AWS.",
})
if err != nil {
@@ -1293,7 +1293,7 @@ func (h *Handler) awsOIDCConfigureListDatabasesIAM(w http.ResponseWriter, r *htt
fmt.Sprintf("--aws-account-id=%s", shsprintf.EscapeDefaultContext(awsAccountID)),
}
script, err := oneoff.BuildScript(oneoff.OneOffScriptParams{
- TeleportArgs: strings.Join(argsList, " "),
+ EntrypointArgs: strings.Join(argsList, " "),
SuccessMessage: "Success! You can now go back to the Teleport Web UI to complete the Database enrollment.",
})
if err != nil {
@@ -1339,7 +1339,7 @@ func (h *Handler) awsAccessGraphOIDCSync(w http.ResponseWriter, r *http.Request,
fmt.Sprintf("--aws-account-id=%s", shsprintf.EscapeDefaultContext(awsAccountID)),
}
script, err := oneoff.BuildScript(oneoff.OneOffScriptParams{
- TeleportArgs: strings.Join(argsList, " "),
+ EntrypointArgs: strings.Join(argsList, " "),
SuccessMessage: "Success! You can now go back to the Teleport Web UI to complete the Access Graph AWS Sync enrollment.",
})
if err != nil {
diff --git a/lib/web/integrations_awsoidc_test.go b/lib/web/integrations_awsoidc_test.go
index 8e1c099bf5575..babdf4d56ed3d 100644
--- a/lib/web/integrations_awsoidc_test.go
+++ b/lib/web/integrations_awsoidc_test.go
@@ -173,7 +173,7 @@ func TestBuildDeployServiceConfigureIAMScript(t *testing.T) {
}
require.Contains(t, string(resp.Bytes()),
- fmt.Sprintf("teleportArgs='%s'\n", tc.expectedTeleportArgs),
+ fmt.Sprintf("entrypointArgs='%s'\n", tc.expectedTeleportArgs),
)
})
}
@@ -304,7 +304,7 @@ func TestBuildEC2SSMIAMScript(t *testing.T) {
}
require.Contains(t, string(resp.Bytes()),
- fmt.Sprintf("teleportArgs='%s'\n", tc.expectedTeleportArgs),
+ fmt.Sprintf("entrypointArgs='%s'\n", tc.expectedTeleportArgs),
)
})
}
@@ -379,7 +379,7 @@ func TestBuildAWSAppAccessConfigureIAMScript(t *testing.T) {
}
require.Contains(t, string(resp.Bytes()),
- fmt.Sprintf("teleportArgs='%s'\n", tc.expectedTeleportArgs),
+ fmt.Sprintf("entrypointArgs='%s'\n", tc.expectedTeleportArgs),
)
})
}
@@ -482,7 +482,7 @@ func TestBuildEKSConfigureIAMScript(t *testing.T) {
}
require.Contains(t, string(resp.Bytes()),
- fmt.Sprintf("teleportArgs='%s'\n", tc.expectedTeleportArgs),
+ fmt.Sprintf("entrypointArgs='%s'\n", tc.expectedTeleportArgs),
)
})
}
@@ -614,7 +614,7 @@ func TestBuildAWSOIDCIdPConfigureScript(t *testing.T) {
}
require.Contains(t, string(resp.Bytes()),
- fmt.Sprintf("teleportArgs='%s'\n", tc.expectedTeleportArgs),
+ fmt.Sprintf("entrypointArgs='%s'\n", tc.expectedTeleportArgs),
)
})
}
@@ -717,7 +717,7 @@ func TestBuildListDatabasesConfigureIAMScript(t *testing.T) {
}
require.Contains(t, string(resp.Bytes()),
- fmt.Sprintf("teleportArgs='%s'\n", tc.expectedTeleportArgs),
+ fmt.Sprintf("entrypointArgs='%s'\n", tc.expectedTeleportArgs),
)
})
}
diff --git a/lib/web/integrations_azureoidc.go b/lib/web/integrations_azureoidc.go
index 0ce8d624a79f1..3a1dd654550e5 100644
--- a/lib/web/integrations_azureoidc.go
+++ b/lib/web/integrations_azureoidc.go
@@ -66,7 +66,7 @@ func (h *Handler) azureOIDCConfigure(w http.ResponseWriter, r *http.Request, p h
}
script, err := oneoff.BuildScript(oneoff.OneOffScriptParams{
- TeleportArgs: strings.Join(argsList, " "),
+ EntrypointArgs: strings.Join(argsList, " "),
SuccessMessage: "Success! You can now go back to the Teleport Web UI to use the integration with Azure.",
})
if err != nil {
diff --git a/lib/web/integrations_azureoidc_test.go b/lib/web/integrations_azureoidc_test.go
index cbdadad4ef433..6e0f72c6d3136 100644
--- a/lib/web/integrations_azureoidc_test.go
+++ b/lib/web/integrations_azureoidc_test.go
@@ -97,7 +97,7 @@ func TestAzureOIDCConfigureScript(t *testing.T) {
}
require.Contains(t, string(resp.Bytes()),
- fmt.Sprintf("teleportArgs='%s'\n", tc.expectedTeleportArgs),
+ fmt.Sprintf("entrypointArgs='%s'\n", tc.expectedTeleportArgs),
)
})
}
diff --git a/lib/web/integrations_samlidp.go b/lib/web/integrations_samlidp.go
index 0ea1e0b1d67d3..eda5dac78a265 100644
--- a/lib/web/integrations_samlidp.go
+++ b/lib/web/integrations_samlidp.go
@@ -56,7 +56,7 @@ func (h *Handler) gcpWorkforceConfigScript(w http.ResponseWriter, r *http.Reques
fmt.Sprintf("--idp-metadata-url=%s", shsprintf.EscapeDefaultContext(samlIdPMetadataURL)),
}
script, err := oneoff.BuildScript(oneoff.OneOffScriptParams{
- TeleportArgs: strings.Join(argsList, " "),
+ EntrypointArgs: strings.Join(argsList, " "),
SuccessMessage: "Success! You can now go back to the Teleport Web UI to complete enrolling this workforce pool to Teleport SAML Identity Provider.",
})
if err != nil {
diff --git a/lib/web/scripts.go b/lib/web/scripts.go
new file mode 100644
index 0000000000000..d5e89bec59e1d
--- /dev/null
+++ b/lib/web/scripts.go
@@ -0,0 +1,154 @@
+/*
+ * Teleport
+ * Copyright (C) 2025 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 web
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+ "os"
+
+ "github.com/gravitational/trace"
+ "github.com/julienschmidt/httprouter"
+
+ "github.com/gravitational/teleport"
+ "github.com/gravitational/teleport/api/types"
+ "github.com/gravitational/teleport/lib/modules"
+ "github.com/gravitational/teleport/lib/utils/teleportassets"
+ "github.com/gravitational/teleport/lib/web/scripts"
+)
+
+// installScriptHandle handles calls for "/scripts/install.sh" and responds with a bash script installing Teleport
+// by downloading and running `teleport-update`. This installation script does not start the agent, join it,
+// or configure its services. This is handled by the "/scripts/:token/install-*.sh" scripts.
+func (h *Handler) installScriptHandle(w http.ResponseWriter, r *http.Request, params httprouter.Params) (any, error) {
+ // This is a hack because the router is not allowing us to register "/scripts/install.sh", so we use
+ // the parameter ":token" to match the script name.
+ // Currently, only "install.sh" is supported.
+ if params.ByName("token") != "install.sh" {
+ return nil, trace.NotFound(`Route not found, query "/scripts/install.sh" for the install-only script, or "/scripts/:token/install-node.sh" for the install + join script.`)
+ }
+
+ // TODO(hugoShaka): cache function
+ opts, err := h.installScriptOptions(r.Context())
+ if err != nil {
+ return nil, trace.Wrap(err, "Failed to build install script options")
+ }
+
+ script, err := scripts.GetInstallScript(r.Context(), opts)
+ if err != nil {
+ h.logger.WarnContext(r.Context(), "Failed to get install script", "error", err)
+ return nil, trace.Wrap(err, "getting script")
+ }
+
+ w.WriteHeader(http.StatusOK)
+ if _, err := fmt.Fprintln(w, script); err != nil {
+ h.logger.WarnContext(r.Context(), "Failed to write install script", "error", err)
+ }
+
+ return nil, nil
+}
+
+// installScriptOptions computes the agent installation options based on the proxy configuration and the cluster status.
+// This includes:
+// - the type of automatic updates
+// - the desired version
+// - the proxy address (used for updates).
+// - the Teleport artifact name and CDN
+func (h *Handler) installScriptOptions(ctx context.Context) (scripts.InstallScriptOptions, error) {
+ const defaultGroup, defaultUpdater = "", ""
+
+ version, err := h.autoUpdateAgentVersion(ctx, defaultGroup, defaultUpdater)
+ if err != nil {
+ h.logger.WarnContext(ctx, "Failed to get intended agent version", "error", err)
+ version = teleport.Version
+ }
+
+ // if there's a rollout, we do new autoupdates
+ _, rolloutErr := h.cfg.AccessPoint.GetAutoUpdateAgentRollout(ctx)
+ if rolloutErr != nil && !(trace.IsNotFound(rolloutErr) || trace.IsNotImplemented(rolloutErr)) {
+ h.logger.WarnContext(ctx, "Failed to get rollout", "error", rolloutErr)
+ return scripts.InstallScriptOptions{}, trace.Wrap(err, "failed to check the autoupdate agent rollout state")
+ }
+
+ var autoupdateStyle scripts.AutoupdateStyle
+ switch {
+ case rolloutErr == nil:
+ autoupdateStyle = scripts.UpdaterBinaryAutoupdate
+ case automaticUpgrades(h.clusterFeatures):
+ autoupdateStyle = scripts.PackageManagerAutoupdate
+ default:
+ autoupdateStyle = scripts.NoAutoupdate
+ }
+
+ var teleportFlavor string
+ switch modules.GetModules().BuildType() {
+ case modules.BuildEnterprise:
+ teleportFlavor = types.PackageNameEnt
+ case modules.BuildOSS, modules.BuildCommunity:
+ teleportFlavor = types.PackageNameOSS
+ default:
+ h.logger.WarnContext(ctx, "Unknown built type, defaulting to the 'teleport' package.", "type", modules.GetModules().BuildType())
+ teleportFlavor = types.PackageNameOSS
+ }
+
+ cdnBaseURL, err := getCDNBaseURL()
+ if err != nil {
+ h.logger.WarnContext(ctx, "Failed to get CDN base URL", "error", err)
+ return scripts.InstallScriptOptions{}, trace.Wrap(err)
+ }
+
+ return scripts.InstallScriptOptions{
+ AutoupdateStyle: autoupdateStyle,
+ TeleportVersion: version,
+ CDNBaseURL: cdnBaseURL,
+ ProxyAddr: h.PublicProxyAddr(),
+ TeleportFlavor: teleportFlavor,
+ FIPS: modules.IsBoringBinary(),
+ }, nil
+
+}
+
+// EnvVarCDNBaseURL is the environment variable that allows users to override the Teleport base CDN url used in the installation script.
+// Setting this value is required for testing (make production builds install from the dev CDN, and vice versa).
+// As we (the Teleport company) don't distribute AGPL binaries, this must be set when using a Teleport OSS build.
+// Example values:
+// - "https://cdn.teleport.dev" (prod)
+// - "https://cdn.cloud.gravitational.io" (dev builds/staging)
+const EnvVarCDNBaseURL = "TELEPORT_CDN_BASE_URL"
+
+func getCDNBaseURL() (string, error) {
+ // If the user explicitly overrides the CDN base URL, we use it.
+ if override := os.Getenv(EnvVarCDNBaseURL); override != "" {
+ return override, nil
+ }
+
+ // If this is an AGPL build, we don't want to automatically install binaries distributed under a more restrictive
+ // license so we error and ask the user set the CDN URL, either to:
+ // - the official Teleport CDN if they agree with the community license and meet its requirements
+ // - a custom CDN where they can store their own AGPL binaries
+ if modules.GetModules().BuildType() == modules.BuildOSS {
+ return "", trace.BadParameter(
+ "This proxy is licensed under AGPL but CDN binaries are licensed under the more restrictive Community license. "+
+ "You can set TELEPORT_CDN_BASE_URL to a custom CDN, or to %q if you are OK with using the Community Edition license.",
+ teleportassets.CDNBaseURL())
+ }
+
+ return teleportassets.CDNBaseURL(), nil
+}
diff --git a/lib/web/scripts/install.go b/lib/web/scripts/install.go
new file mode 100644
index 0000000000000..25a8970e58563
--- /dev/null
+++ b/lib/web/scripts/install.go
@@ -0,0 +1,167 @@
+/*
+ * Teleport
+ * Copyright (C) 2025 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 scripts
+
+import (
+ "context"
+ _ "embed"
+ "net/url"
+ "strings"
+
+ "github.com/google/safetext/shsprintf"
+ "github.com/gravitational/trace"
+
+ "github.com/gravitational/teleport/lib/web/scripts/oneoff"
+)
+
+// AutoupdateStyle represents the kind of autoupdate mechanism the script should use.
+type AutoupdateStyle int
+
+const (
+ // NoAutoupdate means the installed Teleport should not autoupdate.
+ NoAutoupdate AutoupdateStyle = iota
+ // PackageManagerAutoupdate means the installed Teleport should update via a script triggering package manager
+ // updates. The script lives in the 'teleport-ent-update' package and was our original attempt at automatic updates.
+ // See RFD-109 for more details: https://github.com/gravitational/teleport/blob/master/rfd/0109-cloud-agent-upgrades.md
+ PackageManagerAutoupdate
+ // UpdaterBinaryAutoupdate means the installed Teleport should update via the teleport-update binary.
+ // This update style does not depend on any package manager (although it has a system dependency to wake up the
+ // updater).
+ // See RFD-184 for more details: https://github.com/gravitational/teleport/blob/master/rfd/0184-agent-auto-updates.md
+ UpdaterBinaryAutoupdate
+)
+
+// InstallScriptOptions contains the Teleport installation options used to generate installation scripts.
+type InstallScriptOptions struct {
+ AutoupdateStyle AutoupdateStyle
+ // TeleportVersion that should be installed. Without the leading "v".
+ TeleportVersion string
+ // CDNBaseURL is the URL of the CDN hosting teleport tarballs.
+ // If left empty, the 'teleport-update' installer will pick the one to use.
+ // For example: "https://cdn.example.com"
+ CDNBaseURL string
+ // ProxyAddr is the address of the Teleport Proxy service that will be used
+ // by the updater to fetch the desired version. Teleport Addrs are
+ // 'hostname:port' (no scheme nor path).
+ ProxyAddr string
+ // TeleportFlavor is the name of the Teleport artifact fetched from the CDN.
+ // Common values are "teleport" and "teleport-ent".
+ TeleportFlavor string
+ // FIPS represents if the installed Teleport version should use Teleport
+ // binaries built for FIPS compliance.
+ FIPS bool
+}
+
+// Check validates that the minimal options are set.
+func (o *InstallScriptOptions) Check() error {
+ switch o.AutoupdateStyle {
+ case NoAutoupdate, PackageManagerAutoupdate:
+ return nil
+ case UpdaterBinaryAutoupdate:
+ // We'll do the checks later.
+ default:
+ return trace.BadParameter("unsupported autoupdate style: %v", o.AutoupdateStyle)
+ }
+ if o.ProxyAddr == "" {
+ return trace.BadParameter("Proxy address is required")
+ }
+
+ if o.TeleportVersion == "" {
+ return trace.BadParameter("Teleport version is required")
+ }
+
+ if o.TeleportFlavor == "" {
+ return trace.BadParameter("Teleport flavor is required")
+ }
+
+ if o.CDNBaseURL != "" {
+ url, err := url.Parse(o.CDNBaseURL)
+ if err != nil {
+ return trace.Wrap(err, "failed to parse CDN base URL")
+ }
+ if url.Scheme != "https" {
+ return trace.BadParameter("CDNBaseURL's scheme must be 'https://'")
+ }
+ }
+ return nil
+}
+
+// oneOffParams returns the oneoff.OneOffScriptParams that will install Teleport
+// using the oneoff.sh script to download and execute 'teleport-update'.
+func (o *InstallScriptOptions) oneOffParams() (params oneoff.OneOffScriptParams) {
+ // We add the leading v if it's not here
+ version := o.TeleportVersion
+ if o.TeleportVersion[0] != 'v' {
+ version = "v" + o.TeleportVersion
+ }
+
+ args := []string{"enable", "--proxy", shsprintf.EscapeDefaultContext(o.ProxyAddr)}
+ if o.CDNBaseURL != "" {
+ args = append(args, "--base-url", shsprintf.EscapeDefaultContext(o.CDNBaseURL))
+ }
+
+ return oneoff.OneOffScriptParams{
+ Entrypoint: "teleport-update",
+ EntrypointArgs: strings.Join(args, " "),
+ CDNBaseURL: o.CDNBaseURL,
+ TeleportVersion: version,
+ TeleportFlavor: o.TeleportFlavor,
+ SuccessMessage: "Teleport successfully installed.",
+ TeleportFIPS: o.FIPS,
+ }
+}
+
+// GetInstallScript returns a Teleport installation script.
+// This script only installs Teleport, it does not start the agent, join it, nor configure its services.
+// See the InstallNodeBashScript if you need a more complete setup.
+func GetInstallScript(ctx context.Context, opts InstallScriptOptions) (string, error) {
+ switch opts.AutoupdateStyle {
+ case NoAutoupdate, PackageManagerAutoupdate:
+ return getLegacyInstallScript(ctx, opts)
+ case UpdaterBinaryAutoupdate:
+ return getUpdaterInstallScript(ctx, opts)
+ default:
+ return "", trace.BadParameter("unsupported autoupdate style: %v", opts.AutoupdateStyle)
+ }
+}
+
+//go:embed install/install.sh
+var legacyInstallScript string
+
+// getLegacyInstallScript returns the installation script that we have been serving at
+// "https://cdn.teleport.dev/install.sh". This script installs teleport via package manager
+// or by unpacking the tarball. Its usage should be phased out in favor of the updater-based
+// installation script served by getUpdaterInstallScript.
+func getLegacyInstallScript(ctx context.Context, opts InstallScriptOptions) (string, error) {
+ return legacyInstallScript, nil
+}
+
+// getUpdaterInstallScript returns an installation script that downloads teleport-update
+// and uses it to install a self-updating version of Teleport.
+// This installation script is based on the oneoff.sh script and will become the standard
+// way of installing Teleport.
+func getUpdaterInstallScript(ctx context.Context, opts InstallScriptOptions) (string, error) {
+ if err := opts.Check(); err != nil {
+ return "", trace.Wrap(err, "invalid install script parameters")
+ }
+
+ scriptParams := opts.oneOffParams()
+
+ return oneoff.BuildScript(scriptParams)
+}
diff --git a/lib/web/scripts/install/install.sh b/lib/web/scripts/install/install.sh
new file mode 100755
index 0000000000000..52d3da00e4f63
--- /dev/null
+++ b/lib/web/scripts/install/install.sh
@@ -0,0 +1,430 @@
+#!/bin/bash
+# Copyright 2022 Gravitational, Inc
+
+# This script detects the current Linux distribution and installs Teleport
+# through its package manager, if supported, or downloading a tarball otherwise.
+# We'll download Teleport from the official website and checksum it to make sure it was properly
+# downloaded before executing.
+
+# The script is wrapped inside a function to protect against the connection being interrupted
+# in the middle of the stream.
+
+# For more download options, head to https://goteleport.com/download/
+
+set -euo pipefail
+
+# download uses curl or wget to download a teleport binary
+download() {
+ URL=$1
+ TMP_PATH=$2
+
+ echo "Downloading $URL"
+ if type curl &>/dev/null; then
+ set -x
+ # shellcheck disable=SC2086
+ $SUDO $CURL -o "$TMP_PATH" "$URL"
+ else
+ set -x
+ # shellcheck disable=SC2086
+ $SUDO $CURL -O "$TMP_PATH" "$URL"
+ fi
+ set +x
+}
+
+install_via_apt_get() {
+ echo "Installing Teleport v$TELEPORT_VERSION via apt-get"
+ add_apt_key
+ set -x
+ $SUDO apt-get install -y "teleport$TELEPORT_SUFFIX=$TELEPORT_VERSION"
+ set +x
+ if [ "$TELEPORT_EDITION" = "cloud" ]; then
+ set -x
+ $SUDO apt-get install -y teleport-ent-updater
+ set +x
+ fi
+}
+
+add_apt_key() {
+ APT_REPO_ID=$ID
+ APT_REPO_VERSION_CODENAME=$VERSION_CODENAME
+ IS_LEGACY=0
+
+ # check if we must use legacy .asc key
+ case "$ID" in
+ ubuntu | pop | neon | zorin)
+ if ! expr "$VERSION_ID" : "2.*" >/dev/null; then
+ IS_LEGACY=1
+ fi
+ ;;
+ debian | raspbian)
+ if [ "$VERSION_ID" -lt 11 ]; then
+ IS_LEGACY=1
+ fi
+ ;;
+ linuxmint | parrot)
+ if [ "$VERSION_ID" -lt 5 ]; then
+ IS_LEGACY=1
+ fi
+ ;;
+ elementary)
+ if [ "$VERSION_ID" -lt 6 ]; then
+ IS_LEGACY=1
+ fi
+ ;;
+ kali)
+ YEAR="$(echo "$VERSION_ID" | cut -f1 -d.)"
+ if [ "$YEAR" -lt 2021 ]; then
+ IS_LEGACY=1
+ fi
+ ;;
+ esac
+
+ if [[ "$IS_LEGACY" == 0 ]]; then
+ # set APT_REPO_ID if necessary
+ case "$ID" in
+ linuxmint | kali | elementary | pop | raspbian | neon | zorin | parrot)
+ APT_REPO_ID=$ID_LIKE
+ ;;
+ esac
+
+ # set APT_REPO_VERSION_CODENAME if necessary
+ case "$ID" in
+ linuxmint | elementary | pop | neon | zorin)
+ APT_REPO_VERSION_CODENAME=$UBUNTU_CODENAME
+ ;;
+ kali)
+ APT_REPO_VERSION_CODENAME="bullseye"
+ ;;
+ parrot)
+ APT_REPO_VERSION_CODENAME="buster"
+ ;;
+ esac
+ fi
+
+ echo "Downloading Teleport's PGP public key..."
+ TEMP_DIR=$(mktemp -d -t teleport-XXXXXXXXXX)
+ MAJOR=$(echo "$TELEPORT_VERSION" | cut -f1 -d.)
+ TELEPORT_REPO=""
+
+ CHANNEL="stable/v${MAJOR}"
+ if [ "$TELEPORT_EDITION" = "cloud" ]; then
+ CHANNEL="stable/cloud"
+ fi
+
+ if [[ "$IS_LEGACY" == 1 ]]; then
+ if ! type gpg >/dev/null; then
+ echo "Installing gnupg"
+ set -x
+ $SUDO apt-get update
+ $SUDO apt-get install -y gnupg
+ set +x
+ fi
+ TMP_KEY="$TEMP_DIR/teleport-pubkey.asc"
+ download "https://deb.releases.teleport.dev/teleport-pubkey.asc" "$TMP_KEY"
+ set -x
+ $SUDO apt-key add "$TMP_KEY"
+ set +x
+ TELEPORT_REPO="deb https://apt.releases.teleport.dev/${APT_REPO_ID?} ${APT_REPO_VERSION_CODENAME?} ${CHANNEL}"
+ else
+ TMP_KEY="$TEMP_DIR/teleport-pubkey.gpg"
+ download "https://apt.releases.teleport.dev/gpg" "$TMP_KEY"
+ set -x
+ $SUDO mkdir -p /etc/apt/keyrings
+ $SUDO cp "$TMP_KEY" /etc/apt/keyrings/teleport-archive-keyring.asc
+ set +x
+ TELEPORT_REPO="deb [signed-by=/etc/apt/keyrings/teleport-archive-keyring.asc] https://apt.releases.teleport.dev/${APT_REPO_ID?} ${APT_REPO_VERSION_CODENAME?} ${CHANNEL}"
+ fi
+
+ set -x
+ echo "$TELEPORT_REPO" | $SUDO tee /etc/apt/sources.list.d/teleport.list >/dev/null
+ set +x
+
+ set -x
+ $SUDO apt-get update
+ set +x
+}
+
+# $1 is the value of the $ID path segment in the YUM repo URL. In
+# /etc/os-release, this is either the value of $ID or $ID_LIKE.
+install_via_yum() {
+ # shellcheck source=/dev/null
+ source /etc/os-release
+
+ # Get the major version from the version ID.
+ VERSION_ID=$(echo "$VERSION_ID" | grep -Eo "^[0-9]+")
+ TELEPORT_MAJOR_VERSION="v$(echo "$TELEPORT_VERSION" | grep -Eo "^[0-9]+")"
+
+ CHANNEL="stable/${TELEPORT_MAJOR_VERSION}"
+ if [ "$TELEPORT_EDITION" = "cloud" ]; then
+ CHANNEL="stable/cloud"
+ fi
+
+ if type dnf &>/dev/null; then
+ echo "Installing Teleport v$TELEPORT_VERSION through dnf"
+ $SUDO dnf install -y 'dnf-command(config-manager)'
+ $SUDO dnf config-manager --add-repo "$(rpm --eval "https://yum.releases.teleport.dev/$1/$VERSION_ID/Teleport/%{_arch}/$CHANNEL/teleport-yum.repo")"
+ $SUDO dnf install -y "teleport$TELEPORT_SUFFIX-$TELEPORT_VERSION"
+
+ if [ "$TELEPORT_EDITION" = "cloud" ]; then
+ $SUDO dnf install -y teleport-ent-updater
+ fi
+
+ else
+ echo "Installing Teleport v$TELEPORT_VERSION through yum"
+ $SUDO yum install -y yum-utils
+ $SUDO yum-config-manager --add-repo "$(rpm --eval "https://yum.releases.teleport.dev/$1/$VERSION_ID/Teleport/%{_arch}/$CHANNEL/teleport-yum.repo")"
+ $SUDO yum install -y "teleport$TELEPORT_SUFFIX-$TELEPORT_VERSION"
+
+ if [ "$TELEPORT_EDITION" = "cloud" ]; then
+ $SUDO yum install -y teleport-ent-updater
+ fi
+ fi
+ set +x
+}
+
+install_via_zypper() {
+ # shellcheck source=/dev/null
+ source /etc/os-release
+
+ # Get the major version from the version ID.
+ VERSION_ID=$(echo "$VERSION_ID" | grep -Eo "^[0-9]+")
+ TELEPORT_MAJOR_VERSION="v$(echo "$TELEPORT_VERSION" | grep -Eo "^[0-9]+")"
+
+ CHANNEL="stable/${TELEPORT_MAJOR_VERSION}"
+ if [ "$TELEPORT_EDITION" = "cloud" ]; then
+ CHANNEL="stable/cloud"
+ fi
+
+ $SUDO rpm --import https://zypper.releases.teleport.dev/gpg
+ $SUDO zypper addrepo --refresh --repo "$(rpm --eval "https://zypper.releases.teleport.dev/$ID/$VERSION_ID/Teleport/%{_arch}/$CHANNEL/teleport-zypper.repo")"
+ $SUDO zypper --gpg-auto-import-keys refresh teleport
+ $SUDO zypper install -y "teleport$TELEPORT_SUFFIX"
+
+ if [ "$TELEPORT_EDITION" = "cloud" ]; then
+ $SUDO zypper install -y teleport-ent-updater
+ fi
+
+ set +x
+}
+
+
+# download .tar.gz file via curl/wget, unzip it and run the install script
+install_via_curl() {
+ TEMP_DIR=$(mktemp -d -t teleport-XXXXXXXXXX)
+
+ TELEPORT_FILENAME="teleport$TELEPORT_SUFFIX-v$TELEPORT_VERSION-linux-$ARCH-bin.tar.gz"
+ URL="https://cdn.teleport.dev/${TELEPORT_FILENAME}"
+ download "${URL}" "${TEMP_DIR}/${TELEPORT_FILENAME}"
+
+ TMP_CHECKSUM="${TEMP_DIR}/${TELEPORT_FILENAME}.sha256"
+ download "${URL}.sha256" "$TMP_CHECKSUM"
+
+ set -x
+ cd "$TEMP_DIR"
+ # shellcheck disable=SC2086
+ $SUDO $SHA_COMMAND -c "$TMP_CHECKSUM"
+ cd -
+
+ $SUDO tar -xzf "${TEMP_DIR}/${TELEPORT_FILENAME}" -C "$TEMP_DIR"
+ $SUDO "$TEMP_DIR/teleport/install"
+ set +x
+}
+
+# wrap script in a function so a partially downloaded script
+# doesn't execute
+install_teleport() {
+ # exit if not on Linux
+ if [[ $(uname) != "Linux" ]]; then
+ echo "ERROR: This script works only for Linux. Please go to the downloads page to find the proper installation method for your operating system:"
+ echo "https://goteleport.com/download/"
+ exit 1
+ fi
+
+ KERNEL_VERSION=$(uname -r)
+ MIN_VERSION="2.6.23"
+ if [ $MIN_VERSION != "$(echo -e "$MIN_VERSION\n$KERNEL_VERSION" | sort -V | head -n1)" ]; then
+ echo "ERROR: Teleport requires Linux kernel version $MIN_VERSION+"
+ exit 1
+ fi
+
+ # check if can run as admin either by running as root or by
+ # having 'sudo' or 'doas' installed
+ IS_ROOT=""
+ SUDO=""
+ if [ "$(id -u)" = 0 ]; then
+ # running as root, no need for sudo/doas
+ IS_ROOT="YES"
+ SUDO=""
+ elif type sudo &>/dev/null; then
+ SUDO="sudo"
+ elif type doas &>/dev/null; then
+ SUDO="doas"
+ fi
+
+ if [ -z "$SUDO" ] && [ -z "$IS_ROOT" ]; then
+ echo "ERROR: The installer requires a way to run commands as root."
+ echo "Either run this script as root or install sudo/doas."
+ exit 1
+ fi
+
+ # require curl/wget
+ CURL=""
+ if type curl &>/dev/null; then
+ CURL="curl -fL"
+ elif type wget &>/dev/null; then
+ CURL="wget"
+ fi
+ if [ -z "$CURL" ]; then
+ echo "ERROR: This script requires either curl or wget in order to download files. Please install one of them and try again."
+ exit 1
+ fi
+
+ # require shasum/sha256sum
+ SHA_COMMAND=""
+ if type shasum &>/dev/null; then
+ SHA_COMMAND="shasum -a 256"
+ elif type sha256sum &>/dev/null; then
+ SHA_COMMAND="sha256sum"
+ else
+ echo "ERROR: This script requires sha256sum or shasum to validate the download. Please install it and try again."
+ exit 1
+ fi
+
+ # detect distro
+ OS_RELEASE=/etc/os-release
+ ID=""
+ ID_LIKE=""
+ VERSION_CODENAME=""
+ UBUNTU_CODENAME=""
+ if [[ -f "$OS_RELEASE" ]]; then
+ # shellcheck source=/dev/null
+ . $OS_RELEASE
+ fi
+ # Some $ID_LIKE values include multiple distro names in an arbitrary order, so
+ # evaluate the first one.
+ ID_LIKE="${ID_LIKE%% *}"
+
+ # detect architecture
+ ARCH=""
+ case $(uname -m) in
+ x86_64)
+ ARCH="amd64"
+ ;;
+ i386)
+ ARCH="386"
+ ;;
+ armv7l)
+ ARCH="arm"
+ ;;
+ aarch64)
+ ARCH="arm64"
+ ;;
+ **)
+ echo "ERROR: Your system's architecture isn't officially supported or couldn't be determined."
+ echo "Please refer to the installation guide for more information:"
+ echo "https://goteleport.com/docs/installation/"
+ exit 1
+ ;;
+ esac
+
+ # select install method based on distribution
+ # if ID is debian derivate, run apt-get
+ case "$ID" in
+ debian | ubuntu | kali | linuxmint | pop | raspbian | neon | zorin | parrot | elementary)
+ install_via_apt_get
+ ;;
+ # if ID is amazon Linux 2/RHEL/etc, run yum
+ centos | rhel | amzn)
+ install_via_yum "$ID"
+ ;;
+ sles)
+ install_via_zypper
+ ;;
+ *)
+ # before downloading manually, double check if we didn't miss any debian or
+ # rh/fedora derived distros using the ID_LIKE var.
+ case "${ID_LIKE}" in
+ ubuntu | debian)
+ install_via_apt_get
+ ;;
+ centos | fedora | rhel)
+ # There is no repository for "fedora", and there is no difference
+ # between the repositories for "centos" and "rhel", so pick an arbitrary
+ # one.
+ install_via_yum rhel
+ ;;
+ *)
+ if [ "$TELEPORT_EDITION" = "cloud" ]; then
+ echo "The system does not support a package manager, which is required for Teleport Enterprise Cloud."
+ exit 1
+ fi
+
+ # if ID and ID_LIKE didn't return a supported distro, download through curl
+ echo "There is no officially supported package for your package manager. Downloading and installing Teleport via curl."
+ install_via_curl
+ ;;
+ esac
+ ;;
+ esac
+
+ GREEN='\033[0;32m'
+ COLOR_OFF='\033[0m'
+
+ echo ""
+ echo -e "${GREEN}$(teleport version) installed successfully!${COLOR_OFF}"
+ echo ""
+ echo "The following commands are now available:"
+ if type teleport &>/dev/null; then
+ echo " teleport - The daemon that runs the Auth Service, Proxy Service, and other Teleport services."
+ fi
+ if type tsh &>/dev/null; then
+ echo " tsh - A tool that lets end users interact with Teleport."
+ fi
+ if type tctl &>/dev/null; then
+ echo " tctl - An administrative tool that can configure the Teleport Auth Service."
+ fi
+ if type tbot &>/dev/null; then
+ echo " tbot - Teleport Machine ID client."
+ fi
+ if type fdpass-teleport &>/dev/null; then
+ echo " fdpass-teleport - Teleport Machine ID client."
+ fi
+ if type teleport-update &>/dev/null; then
+ echo " teleport-update - Teleport auto-update agent."
+ fi
+}
+
+# The suffix is "-ent" if we are installing a commercial edition of Teleport and
+# empty for Teleport Community Edition.
+TELEPORT_SUFFIX=""
+TELEPORT_VERSION=""
+TELEPORT_EDITION=""
+if [ $# -ge 1 ] && [ -n "$1" ]; then
+ TELEPORT_VERSION=$1
+else
+ echo "ERROR: Please provide the version you want to install (e.g., 10.1.9)."
+ exit 1
+fi
+
+if ! echo "$1" | grep -qE "[0-9]+\.[0-9]+\.[0-9]+"; then
+ echo "ERROR: The first parameter must be a version number, e.g., 10.1.9."
+ exit 1
+fi
+
+if [ $# -ge 2 ] && [ -n "$2" ]; then
+ TELEPORT_EDITION=$2
+
+ case $TELEPORT_EDITION in
+ enterprise | cloud)
+ TELEPORT_SUFFIX="-ent"
+ ;;
+ # An empty edition defaults to OSS.
+ oss | "" )
+ ;;
+ *)
+ echo 'ERROR: The second parameter must be "oss", "cloud", or "enterprise".'
+ exit 1
+ ;;
+ esac
+fi
+install_teleport
diff --git a/lib/web/scripts/install_test.go b/lib/web/scripts/install_test.go
new file mode 100644
index 0000000000000..5606858d4aac8
--- /dev/null
+++ b/lib/web/scripts/install_test.go
@@ -0,0 +1,138 @@
+/*
+ * Teleport
+ * Copyright (C) 2025 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 scripts
+
+import (
+ "context"
+ "fmt"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "github.com/gravitational/teleport/api/types"
+ "github.com/gravitational/teleport/lib/utils/teleportassets"
+)
+
+func TestGetInstallScript(t *testing.T) {
+ ctx := context.Background()
+ testVersion := "1.2.3"
+ testProxyAddr := "proxy.example.com:443"
+
+ tests := []struct {
+ name string
+ opts InstallScriptOptions
+ assertFn func(t *testing.T, script string)
+ }{
+ {
+ name: "Legacy install, no autoupdate",
+ opts: InstallScriptOptions{AutoupdateStyle: NoAutoupdate},
+ assertFn: func(t *testing.T, script string) {
+ require.Equal(t, legacyInstallScript, script)
+ },
+ },
+ {
+ name: "Legacy install, package manager autoupdate",
+ opts: InstallScriptOptions{AutoupdateStyle: NoAutoupdate},
+ assertFn: func(t *testing.T, script string) {
+ require.Equal(t, legacyInstallScript, script)
+ },
+ },
+ {
+ name: "Oneoff install",
+ opts: InstallScriptOptions{
+ AutoupdateStyle: UpdaterBinaryAutoupdate,
+ TeleportVersion: testVersion,
+ ProxyAddr: testProxyAddr,
+ TeleportFlavor: types.PackageNameOSS,
+ },
+ assertFn: func(t *testing.T, script string) {
+ require.Contains(t, script, "entrypoint='teleport-update'")
+ require.Contains(t, script, fmt.Sprintf("teleportVersion='v%s'", testVersion))
+ require.Contains(t, script, fmt.Sprintf("teleportFlavor='%s'", types.PackageNameOSS))
+ require.Contains(t, script, fmt.Sprintf("cdnBaseURL='%s'", teleportassets.CDNBaseURL()))
+ require.Contains(t, script, fmt.Sprintf("entrypointArgs='enable --proxy %s'", testProxyAddr))
+ require.Contains(t, script, "packageSuffix='bin.tar.gz'")
+ },
+ },
+ {
+ name: "Oneoff install custom CDN",
+ opts: InstallScriptOptions{
+ AutoupdateStyle: UpdaterBinaryAutoupdate,
+ TeleportVersion: testVersion,
+ ProxyAddr: testProxyAddr,
+ TeleportFlavor: types.PackageNameOSS,
+ CDNBaseURL: "https://cdn.example.com",
+ },
+ assertFn: func(t *testing.T, script string) {
+ require.Contains(t, script, "entrypoint='teleport-update'")
+ require.Contains(t, script, fmt.Sprintf("teleportVersion='v%s'", testVersion))
+ require.Contains(t, script, fmt.Sprintf("teleportFlavor='%s'", types.PackageNameOSS))
+ require.Contains(t, script, "cdnBaseURL='https://cdn.example.com'")
+ require.Contains(t, script, fmt.Sprintf("entrypointArgs='enable --proxy %s --base-url %s'", testProxyAddr, "https://cdn.example.com"))
+ require.Contains(t, script, "packageSuffix='bin.tar.gz'")
+ },
+ },
+ {
+ name: "Oneoff enterprise install",
+ opts: InstallScriptOptions{
+ AutoupdateStyle: UpdaterBinaryAutoupdate,
+ TeleportVersion: testVersion,
+ ProxyAddr: testProxyAddr,
+ TeleportFlavor: types.PackageNameEnt,
+ },
+ assertFn: func(t *testing.T, script string) {
+ require.Contains(t, script, "entrypoint='teleport-update'")
+ require.Contains(t, script, fmt.Sprintf("teleportVersion='v%s'", testVersion))
+ require.Contains(t, script, fmt.Sprintf("teleportFlavor='%s'", types.PackageNameEnt))
+ require.Contains(t, script, fmt.Sprintf("cdnBaseURL='%s'", teleportassets.CDNBaseURL()))
+ require.Contains(t, script, fmt.Sprintf("entrypointArgs='enable --proxy %s'", testProxyAddr))
+ require.Contains(t, script, "packageSuffix='bin.tar.gz'")
+ },
+ },
+ {
+ name: "Oneoff enterprise FIPS install",
+ opts: InstallScriptOptions{
+ AutoupdateStyle: UpdaterBinaryAutoupdate,
+ TeleportVersion: testVersion,
+ ProxyAddr: testProxyAddr,
+ TeleportFlavor: types.PackageNameEnt,
+ FIPS: true,
+ },
+ assertFn: func(t *testing.T, script string) {
+ require.Contains(t, script, "entrypoint='teleport-update'")
+ require.Contains(t, script, fmt.Sprintf("teleportVersion='v%s'", testVersion))
+ require.Contains(t, script, fmt.Sprintf("teleportFlavor='%s'", types.PackageNameEnt))
+ require.Contains(t, script, fmt.Sprintf("cdnBaseURL='%s'", teleportassets.CDNBaseURL()))
+ require.Contains(t, script, fmt.Sprintf("entrypointArgs='enable --proxy %s'", testProxyAddr))
+ require.Contains(t, script, "packageSuffix='fips-bin.tar.gz'")
+ },
+ },
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ // Sanity check, test input should be legal.
+ require.NoError(t, test.opts.Check())
+
+ // Test execution.
+ result, err := GetInstallScript(ctx, test.opts)
+ require.NoError(t, err)
+ test.assertFn(t, result)
+ })
+ }
+}
diff --git a/lib/web/scripts/oneoff/oneoff.go b/lib/web/scripts/oneoff/oneoff.go
index 5d12c2c938289..794749b1abc02 100644
--- a/lib/web/scripts/oneoff/oneoff.go
+++ b/lib/web/scripts/oneoff/oneoff.go
@@ -22,6 +22,7 @@ import (
"bytes"
_ "embed"
"slices"
+ "strings"
"text/template"
"github.com/gravitational/trace"
@@ -63,9 +64,12 @@ type OneOffScriptParams struct {
// Used for testing.
binSudo string
- // TeleportArgs is the arguments to pass to the teleport binary.
+ // Entrypoint is the name of the binary from the teleport package. Defaults to "teleport", but can be set to
+ // other binaries such as "teleport-update" or "tbot".
+ Entrypoint string
+ // EntrypointArgs is the arguments to pass to the Entrypoint binary.
// Eg, 'version'
- TeleportArgs string
+ EntrypointArgs string
// BinUname is the binary used to get OS name and Architecture of the host.
// Defaults to `uname`.
@@ -88,16 +92,23 @@ type OneOffScriptParams struct {
// - teleport-ent
TeleportFlavor string
+ // TeleportFIPS represents if the script should install a FIPS build of Teleport.
+ TeleportFIPS bool
+
// SuccessMessage is a message shown to the user after the one off is completed.
SuccessMessage string
}
// CheckAndSetDefaults checks if the required params ara present.
func (p *OneOffScriptParams) CheckAndSetDefaults() error {
- if p.TeleportArgs == "" {
+ if p.EntrypointArgs == "" {
return trace.BadParameter("missing teleport args")
}
+ if p.Entrypoint == "" {
+ p.Entrypoint = "teleport"
+ }
+
if p.BinUname == "" {
p.BinUname = binUname
}
@@ -117,6 +128,7 @@ func (p *OneOffScriptParams) CheckAndSetDefaults() error {
if p.CDNBaseURL == "" {
p.CDNBaseURL = teleportassets.CDNBaseURL()
}
+ p.CDNBaseURL = strings.TrimRight(p.CDNBaseURL, "/")
if p.TeleportFlavor == "" {
p.TeleportFlavor = types.PackageNameOSS
diff --git a/lib/web/scripts/oneoff/oneoff.sh b/lib/web/scripts/oneoff/oneoff.sh
index 912e4d6ab3368..eaa15841be18b 100644
--- a/lib/web/scripts/oneoff/oneoff.sh
+++ b/lib/web/scripts/oneoff/oneoff.sh
@@ -5,7 +5,10 @@ cdnBaseURL='{{.CDNBaseURL}}'
teleportVersion='{{.TeleportVersion}}'
teleportFlavor='{{.TeleportFlavor}}' # teleport or teleport-ent
successMessage='{{.SuccessMessage}}'
-teleportArgs='{{.TeleportArgs}}'
+entrypointArgs='{{.EntrypointArgs}}'
+entrypoint='{{.Entrypoint}}'
+packageSuffix='{{ if .TeleportFIPS }}fips-{{ end }}bin.tar.gz'
+fips='{{ if .TeleportFIPS }}true{{ end }}'
# shellcheck disable=all
# Use $HOME or / as base dir
@@ -17,20 +20,24 @@ ARCH=$({{.BinUname}} -m)
trap 'rm -rf -- "$tempDir"' EXIT
teleportTarballName() {
- if [ ${OS} = "Darwin" ]; then
- echo ${teleportFlavor}-${teleportVersion}-darwin-universal-bin.tar.gz
+ if [ "${OS}" = "Darwin" ]; then
+ if [ "$fips" = "true"]; then
+ echo "FIPS version of Teleport is not compatible with MacOS. Please run this script in a Linux machine."
+ return 1
+ fi
+ echo "${teleportFlavor}-${teleportVersion}-darwin-universal-${packageSuffix}"
return 0
fi;
- if [ ${OS} != "Linux" ]; then
+ if [ "${OS}" != "Linux" ]; then
echo "Only MacOS and Linux are supported." >&2
return 1
fi;
- if [ ${ARCH} = "armv7l" ]; then echo "${teleportFlavor}-${teleportVersion}-linux-arm-bin.tar.gz"
- elif [ ${ARCH} = "aarch64" ]; then echo "${teleportFlavor}-${teleportVersion}-linux-arm64-bin.tar.gz"
- elif [ ${ARCH} = "x86_64" ]; then echo "${teleportFlavor}-${teleportVersion}-linux-amd64-bin.tar.gz"
- elif [ ${ARCH} = "i686" ]; then echo "${teleportFlavor}-${teleportVersion}-linux-386-bin.tar.gz"
+ if [ ${ARCH} = "armv7l" ]; then echo "${teleportFlavor}-${teleportVersion}-linux-arm-${packageSuffix}"
+ elif [ ${ARCH} = "aarch64" ]; then echo "${teleportFlavor}-${teleportVersion}-linux-arm64-${packageSuffix}"
+ elif [ ${ARCH} = "x86_64" ]; then echo "${teleportFlavor}-${teleportVersion}-linux-amd64-${packageSuffix}"
+ elif [ ${ARCH} = "i686" ]; then echo "${teleportFlavor}-${teleportVersion}-linux-386-${packageSuffix}"
else
echo "Invalid Linux architecture ${ARCH}." >&2
return 1
@@ -40,12 +47,12 @@ teleportTarballName() {
main() {
tarballName=$(teleportTarballName)
echo "Downloading from ${cdnBaseURL}/${tarballName} and extracting teleport to ${tempDir} ..."
- curl --show-error --fail --location ${cdnBaseURL}/${tarballName} | tar xzf - -C ${tempDir} ${teleportFlavor}/teleport
+ curl --show-error --fail --location "${cdnBaseURL}/${tarballName}" | tar xzf - -C "${tempDir}" "${teleportFlavor}/${entrypoint}"
- mkdir -p ${tempDir}/bin
- mv ${tempDir}/${teleportFlavor}/teleport ${tempDir}/bin/teleport
- echo "> ${tempDir}/bin/teleport ${teleportArgs} $@"
- {{.TeleportCommandPrefix}} ${tempDir}/bin/teleport ${teleportArgs} $@ && echo $successMessage
+ mkdir -p "${tempDir}/bin"
+ mv "${tempDir}/${teleportFlavor}/${entrypoint}" "${tempDir}/bin/${entrypoint}"
+ echo "> ${tempDir}/bin/${entrypoint} ${entrypointArgs} $@"
+ {{.TeleportCommandPrefix}} "${tempDir}/bin/${entrypoint}" ${entrypointArgs} $@ && echo "$successMessage"
}
main $@
diff --git a/lib/web/scripts/oneoff/oneoff_test.go b/lib/web/scripts/oneoff/oneoff_test.go
index 963f7d2392f1d..c6da7d96e2e21 100644
--- a/lib/web/scripts/oneoff/oneoff_test.go
+++ b/lib/web/scripts/oneoff/oneoff_test.go
@@ -69,7 +69,7 @@ func TestOneOffScript(t *testing.T) {
BinMktemp: mktempMock.Path,
CDNBaseURL: "dummyURL",
TeleportVersion: "v13.1.0",
- TeleportArgs: "version",
+ EntrypointArgs: "version",
})
require.NoError(t, err)
@@ -99,7 +99,7 @@ func TestOneOffScript(t *testing.T) {
BinMktemp: mktempMock.Path,
CDNBaseURL: testServer.URL,
TeleportVersion: "v13.1.0",
- TeleportArgs: "version",
+ EntrypointArgs: "version",
SuccessMessage: "Test was a success.",
})
require.NoError(t, err)
@@ -156,7 +156,7 @@ func TestOneOffScript(t *testing.T) {
BinMktemp: mktempMock.Path,
CDNBaseURL: testServer.URL,
TeleportVersion: "v13.1.0",
- TeleportArgs: "version",
+ EntrypointArgs: "version",
SuccessMessage: "Test was a success.",
TeleportCommandPrefix: "sudo",
binSudo: sudoMock.Path,
@@ -215,7 +215,7 @@ func TestOneOffScript(t *testing.T) {
BinUname: unameMock.Path,
BinMktemp: mktempMock.Path,
CDNBaseURL: testServer.URL,
- TeleportArgs: "help",
+ EntrypointArgs: "help",
TeleportVersion: "v13.1.0",
SuccessMessage: "Test was a success.",
})
@@ -293,7 +293,7 @@ func TestOneOffScript(t *testing.T) {
BinMktemp: mktempMock.Path,
CDNBaseURL: "dummyURL",
TeleportVersion: "v13.1.0",
- TeleportArgs: "version",
+ EntrypointArgs: "version",
SuccessMessage: "Test was a success.",
TeleportFlavor: "../not-teleport",
})
@@ -306,7 +306,7 @@ func TestOneOffScript(t *testing.T) {
BinMktemp: mktempMock.Path,
CDNBaseURL: "dummyURL",
TeleportVersion: "v13.1.0",
- TeleportArgs: "version",
+ EntrypointArgs: "version",
SuccessMessage: "Test was a success.",
TeleportFlavor: "teleport",
TeleportCommandPrefix: "rm -rf thing",
@@ -343,7 +343,7 @@ func TestOneOffScript(t *testing.T) {
BinMktemp: mktempMock.Path,
CDNBaseURL: testServer.URL,
TeleportVersion: "v13.1.0",
- TeleportArgs: "version",
+ EntrypointArgs: "version",
SuccessMessage: "Test was a success.",
})
require.NoError(t, err)
diff --git a/tool/tctl/common/plugin/entraid.go b/tool/tctl/common/plugin/entraid.go
index c537be2680da8..b175b11312665 100644
--- a/tool/tctl/common/plugin/entraid.go
+++ b/tool/tctl/common/plugin/entraid.go
@@ -399,7 +399,7 @@ func buildScript(proxyPublicAddr string, entraCfg entraArgs) (string, error) {
}
script, err := oneoff.BuildScript(oneoff.OneOffScriptParams{
- TeleportArgs: strings.Join(argsList, " "),
+ EntrypointArgs: strings.Join(argsList, " "),
SuccessMessage: "Success! You can now go back to the Teleport Web UI to use the integration with Azure.",
})
if err != nil {