Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add version incompatibility warning to Connect #51832

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
237 changes: 208 additions & 29 deletions gen/proto/go/teleport/lib/teleterm/v1/auth_settings.pb.go

Large diffs are not rendered by default.

151 changes: 150 additions & 1 deletion gen/proto/ts/teleport/lib/teleterm/v1/auth_settings_pb.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

68 changes: 54 additions & 14 deletions lib/client/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -4321,7 +4321,7 @@ func (tc *TeleportClient) Ping(ctx context.Context) (*webclient.PingResponse, er

// Verify server->client and client->server compatibility.
if tc.CheckVersions {
warning, err := getClientIncompatibilityWarning(versions{
warning, err := getClientIncompatibilityWarning(Versions{
MinClient: pr.MinClientVersion,
Client: teleport.Version,
Server: pr.ServerVersion,
Expand Down Expand Up @@ -4353,35 +4353,73 @@ func (tc *TeleportClient) Ping(ctx context.Context) (*webclient.PingResponse, er
return pr, nil
}

type versions struct {
type Versions struct {
MinClient string
Client string
Server string
}

func getClientIncompatibilityWarning(versions versions) (string, error) {
if !utils.MeetsMinVersion(versions.Client, versions.MinClient) {
return fmt.Sprintf(`
WARNING
Detected potentially incompatible client and server versions.
Minimum client version supported by the server is %v but you are using %v.
Please upgrade tsh to %v or newer or use the --skip-version-check flag to bypass this check.
Future versions of tsh will fail when incompatible versions are detected.
// ClientVersionStatus describes the compatibility status of the client version when compared
// against the version of the server.
type ClientVersionStatus int

`,
versions.MinClient, versions.Client, versions.MinClient), nil
const (
ClientVersionCompatUnspecified ClientVersionStatus = iota
// ClientVersionOK means that the client is on the same major version as the server or at most one
// major version behind.
// For example, the server is on v17.1.0 and the client is on v16.0.4.
ClientVersionOK
// ClientVersionTooOld means that the client is at least two major versions behind the server.
// For example, the server is on v19.3.2 and the client is on v17.4.1.
ClientVersionTooOld
// ClientVersionTooNew means that the client is at least one major version ahead of the server.
// For example, the server is on v18.2.1 and the client is on v19.0.0.
ClientVersionTooNew
)

func GetClientVersionStatus(versions Versions) (ClientVersionStatus, error) {
if !utils.MeetsMinVersion(versions.Client, versions.MinClient) {
return ClientVersionTooOld, nil
}

clientMajorVersion, err := utils.MajorSemver(versions.Client)
if err != nil {
return "", trace.Wrap(err)
return ClientVersionCompatUnspecified, trace.Wrap(err)
}
serverMajorVersion, err := utils.MajorSemver(versions.Server)
if err != nil {
return "", trace.Wrap(err)
return ClientVersionCompatUnspecified, trace.Wrap(err)
}

if !utils.MeetsMaxVersion(clientMajorVersion, serverMajorVersion) {
return ClientVersionTooNew, nil
}

return ClientVersionOK, nil
}

func getClientIncompatibilityWarning(versions Versions) (string, error) {
clientVersionStatus, err := GetClientVersionStatus(versions)
if err != nil {
return "", trace.Wrap(err)
}

switch clientVersionStatus {
case ClientVersionTooOld:
return fmt.Sprintf(`
WARNING
Detected potentially incompatible client and server versions.
Minimum client version supported by the server is %v but you are using %v.
Please upgrade tsh to %v or newer or use the --skip-version-check flag to bypass this check.
Future versions of tsh will fail when incompatible versions are detected.

`,
versions.MinClient, versions.Client, versions.MinClient), nil
case ClientVersionTooNew:
serverMajorVersion, err := utils.MajorSemver(versions.Server)
if err != nil {
return "", trace.Wrap(err)
}
serverVersionWithWildcards, err := utils.MajorSemverWithWildcards(serverMajorVersion)
if err != nil {
return "", trace.Wrap(err)
Expand All @@ -4395,6 +4433,8 @@ Future versions of tsh will fail when incompatible versions are detected.

`,
serverVersionWithWildcards, versions.Client, serverVersionWithWildcards), nil
case ClientVersionCompatUnspecified:
return "", trace.Errorf("got unknown compatibility status")
}

return "", nil
Expand Down
2 changes: 1 addition & 1 deletion lib/client/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1563,7 +1563,7 @@ Future versions of tsh will fail when incompatible versions are detected.
minClientVersion, err := semver.NewVersion(test.serverVersion)
require.NoError(t, err)
minClientVersion.Major = minClientVersion.Major - 1
warning, err := getClientIncompatibilityWarning(versions{
warning, err := getClientIncompatibilityWarning(Versions{
MinClient: minClientVersion.String(),
Client: test.clientVersion,
Server: test.serverVersion,
Expand Down
37 changes: 36 additions & 1 deletion lib/teleterm/apiserver/handler/handler_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ import (

"github.com/gravitational/trace"

"github.com/gravitational/teleport"
api "github.com/gravitational/teleport/gen/proto/go/teleport/lib/teleterm/v1"
"github.com/gravitational/teleport/lib/client"
)

// Login logs in a user to a cluster
Expand Down Expand Up @@ -116,7 +118,7 @@ func (s *Handler) GetAuthSettings(ctx context.Context, req *api.GetAuthSettingsR
return nil, trace.Wrap(err)
}

preferences, err := cluster.SyncAuthPreference(ctx)
preferences, pr, err := cluster.SyncAuthPreference(ctx)
if err != nil {
return nil, trace.Wrap(err)
}
Expand All @@ -136,6 +138,24 @@ func (s *Handler) GetAuthSettings(ctx context.Context, req *api.GetAuthSettingsR
})
}

versions := client.Versions{
MinClient: pr.MinClientVersion,
Client: teleport.Version,
Server: pr.ServerVersion,
}

clientVersionStatus, err := client.GetClientVersionStatus(versions)
if err != nil {
return nil, trace.Wrap(err)
}

result.ClientVersionStatus = libclientVersionStatusToAPIVersionStatus(clientVersionStatus)
result.Versions = &api.Versions{
MinClient: versions.MinClient,
Client: versions.Client,
Server: versions.Server,
}

return result, nil
}

Expand All @@ -150,3 +170,18 @@ func (s *Handler) StartHeadlessWatcher(_ context.Context, req *api.StartHeadless

return &api.StartHeadlessWatcherResponse{}, nil
}

func libclientVersionStatusToAPIVersionStatus(vs client.ClientVersionStatus) api.ClientVersionStatus {
switch vs {
case client.ClientVersionOK:
return api.ClientVersionStatus_CLIENT_VERSION_STATUS_OK

case client.ClientVersionTooOld:
return api.ClientVersionStatus_CLIENT_VERSION_STATUS_TOO_OLD

case client.ClientVersionTooNew:
return api.ClientVersionStatus_CLIENT_VERSION_STATUS_TOO_NEW
}

return api.ClientVersionStatus_CLIENT_VERSION_STATUS_COMPAT_UNSPECIFIED
}
Loading