Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion integration/autoupdate/tools/updater_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,10 @@ func TestUpdate(t *testing.T) {

// Verify that the installed version is equal to requested one.
cmd := exec.CommandContext(ctx, tctlPath, "version")
var stderr bytes.Buffer
cmd.Stderr = &stderr
out, err := cmd.Output()
require.NoError(t, err)
require.NoError(t, err, stderr.String())

matches := pattern.FindStringSubmatch(string(out))
require.Len(t, matches, 2)
Expand Down
11 changes: 6 additions & 5 deletions tool/tctl/common/client/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import (
"github.com/gravitational/teleport/lib/service/servicecfg"
"github.com/gravitational/teleport/lib/utils"
"github.com/gravitational/teleport/tool/common"
tctlcfg "github.com/gravitational/teleport/tool/tctl/common/config"
"github.com/gravitational/teleport/tool/tctl/common/config"
)

// InitFunc initiates connection to auth service, makes ping request and return the client instance.
Expand All @@ -47,9 +47,10 @@ import (
type InitFunc func(ctx context.Context) (client *authclient.Client, close func(context.Context), err error)

// GetInitFunc wraps lazy loading auth init function for commands which requires the auth client.
func GetInitFunc(ccf tctlcfg.GlobalCLIFlags, cfg *servicecfg.Config) InitFunc {
func GetInitFunc(cfg *servicecfg.Config, getAuthFn config.LoadAuthFunc) InitFunc {
return func(ctx context.Context) (*authclient.Client, func(context.Context), error) {
clientConfig, err := tctlcfg.ApplyConfig(&ccf, cfg)
// Lazy load auth configuration
clientConfig, err := getAuthFn(cfg)
if err != nil {
return nil, nil, trace.Wrap(err)
}
Expand All @@ -70,7 +71,7 @@ func GetInitFunc(ccf tctlcfg.GlobalCLIFlags, cfg *servicecfg.Config) InitFunc {
dialer, err := reversetunnelclient.NewTunnelAuthDialer(reversetunnelclient.TunnelAuthDialerConfig{
Resolver: resolver,
ClientConfig: clientConfig.SSH,
Log: cfg.Logger,
Log: clientConfig.Log,
InsecureSkipTLSVerify: clientConfig.Insecure,
GetClusterCAs: apiclient.ClusterCAsFromCertPool(clientConfig.TLS.RootCAs),
})
Expand All @@ -87,7 +88,7 @@ func GetInitFunc(ccf tctlcfg.GlobalCLIFlags, cfg *servicecfg.Config) InitFunc {
}
fmt.Fprintf(os.Stderr,
"ERROR: Cannot connect to the auth server. Is the auth server running on %q?\n",
cfg.AuthServerAddresses()[0].Addr)
clientConfig.AuthServers[0].Addr)
return nil, nil, trace.NewAggregate(&common.ExitCodeError{Code: 1}, err)
}

Expand Down
80 changes: 43 additions & 37 deletions tool/tctl/common/config/global.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,14 @@ type GlobalCLIFlags struct {
Insecure bool
}

type LoadAuthFunc func(cfg *servicecfg.Config) (*authclient.Config, error)

// ApplyConfig takes configuration values from the config file and applies them
// to 'servicecfg.Config' object.
//
// The returned authclient.Config has the credentials needed to dial the auth
// The returned LoadAuthFunc can be used to create the credentials needed to dial the auth
// server.
func ApplyConfig(ccf *GlobalCLIFlags, cfg *servicecfg.Config) (*authclient.Config, error) {
func ApplyConfig(ccf *GlobalCLIFlags, cfg *servicecfg.Config) (LoadAuthFunc, error) {
ctx := context.TODO()
// --debug flag
if ccf.Debug {
Expand Down Expand Up @@ -132,8 +134,11 @@ func ApplyConfig(ccf *GlobalCLIFlags, cfg *servicecfg.Config) (*authclient.Confi
}
authConfig, err := LoadConfigFromProfile(ccf, cfg)
if err == nil {
return authConfig, nil
return func(_ *servicecfg.Config) (*authclient.Config, error) {
return authConfig, nil
}, nil
}

if !trace.IsNotFound(err) {
return nil, trace.Wrap(err)
} else if runtime.GOOS == constants.WindowsOS {
Expand All @@ -158,40 +163,41 @@ func ApplyConfig(ccf *GlobalCLIFlags, cfg *servicecfg.Config) (*authclient.Confi
}
}

authConfig := new(authclient.Config)
// read the host UUID only in case the identity was not provided,
// because it will be used for reading local auth server identity
cfg.HostUUID, err = hostid.ReadFile(cfg.DataDir)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
return nil, trace.Wrap(err, "Could not load Teleport host UUID file at %s. "+
"Please make sure that a Teleport Auth Service instance is running on this host prior to using tctl or provide credentials by logging in with tsh first.",
filepath.Join(cfg.DataDir, hostid.FileName))
} else if errors.Is(err, fs.ErrPermission) {
return nil, trace.Wrap(err, "Teleport does not have permission to read Teleport host UUID file at %s. "+
"Ensure that you are running as a user with appropriate permissions or provide credentials by logging in with tsh first.",
filepath.Join(cfg.DataDir, hostid.FileName))
return func(cfg *servicecfg.Config) (*authclient.Config, error) {
authConfig := new(authclient.Config)
// read the host UUID only in case the identity was not provided,
// because it will be used for reading local auth server identity
cfg.HostUUID, err = hostid.ReadFile(cfg.DataDir)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
return nil, trace.Wrap(err, "Could not load Teleport host UUID file at %s. "+
"Please make sure that a Teleport Auth Service instance is running on this host prior to using tctl or provide credentials by logging in with tsh first.",
filepath.Join(cfg.DataDir, hostid.FileName))
} else if errors.Is(err, fs.ErrPermission) {
return nil, trace.Wrap(err, "Teleport does not have permission to read Teleport host UUID file at %s. "+
"Ensure that you are running as a user with appropriate permissions or provide credentials by logging in with tsh first.",
filepath.Join(cfg.DataDir, hostid.FileName))
}
return nil, trace.Wrap(err)
}
return nil, trace.Wrap(err)
}
identity, err := storage.ReadLocalIdentity(filepath.Join(cfg.DataDir, teleport.ComponentProcess), state.IdentityID{Role: types.RoleAdmin, HostUUID: cfg.HostUUID})
if err != nil {
// The "admin" identity is not present? This means the tctl is running
// NOT on the auth server
if trace.IsNotFound(err) {
return nil, trace.AccessDenied("tctl must be used on an Auth Service host or provided with credentials by logging in with tsh first.")
identity, err := storage.ReadLocalIdentity(filepath.Join(cfg.DataDir, teleport.ComponentProcess), state.IdentityID{Role: types.RoleAdmin, HostUUID: cfg.HostUUID})
if err != nil {
// The "admin" identity is not present? This means the tctl is running
// NOT on the auth server
if trace.IsNotFound(err) {
return nil, trace.AccessDenied("tctl must be used on an Auth Service host or provided with credentials by logging in with tsh first.")
}
return nil, trace.Wrap(err)
}
return nil, trace.Wrap(err)
}
authConfig.TLS, err = identity.TLSConfig(cfg.CipherSuites)
if err != nil {
return nil, trace.Wrap(err)
}
authConfig.TLS.InsecureSkipVerify = ccf.Insecure
authConfig.Insecure = ccf.Insecure
authConfig.AuthServers = cfg.AuthServerAddresses()
authConfig.Log = cfg.Logger
authConfig.DialOpts = append(authConfig.DialOpts, metadata.WithUserAgentFromTeleportComponent(teleport.ComponentTCTL))

return authConfig, nil
authConfig.TLS, err = identity.TLSConfig(cfg.CipherSuites)
if err != nil {
return nil, trace.Wrap(err)
}
authConfig.TLS.InsecureSkipVerify = ccf.Insecure
authConfig.Insecure = ccf.Insecure
authConfig.AuthServers = cfg.AuthServerAddresses()
authConfig.Log = cfg.Logger
authConfig.DialOpts = append(authConfig.DialOpts, metadata.WithUserAgentFromTeleportComponent(teleport.ComponentTCTL))
return authConfig, nil
}, nil
}
8 changes: 7 additions & 1 deletion tool/tctl/common/tctl.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,13 @@ func TryRun(ctx context.Context, commands []CLICommand, args []string) error {

cfg.Debug = ccf.Debug

clientFunc := commonclient.GetInitFunc(ccf, cfg)
getAuthFn, err := tctlcfg.ApplyConfig(&ccf, cfg)
if err != nil {
return trace.Wrap(err)
}

clientFunc := commonclient.GetInitFunc(cfg, getAuthFn)

// Execute whatever is selected.
for _, c := range commands {
match, err := c.TryRun(ctx, selectedCmd, clientFunc)
Expand Down
Loading