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

Adds support for loading tctl creds from ~/.tsh profile #4678

Merged
merged 1 commit into from
Nov 10, 2020
Merged

Conversation

klizhentas
Copy link
Contributor

This commit fixes #4439

Please take a look at teleport.e counterpart

lib/client/keyagent.go Show resolved Hide resolved
lib/client/keystore_test.go Outdated Show resolved Hide resolved
lib/client/keystore_test.go Show resolved Hide resolved
lib/client/keystore_test.go Show resolved Hide resolved
lib/client/keystore_test.go Outdated Show resolved Hide resolved
lib/client/keystore_test.go Outdated Show resolved Hide resolved
lib/client/keystore_test.go Outdated Show resolved Hide resolved
lib/client/keystore_test.go Outdated Show resolved Hide resolved
tool/tctl/common/tctl.go Outdated Show resolved Hide resolved
tool/tctl/main.go Show resolved Hide resolved
@a-palchikov
Copy link
Contributor

Passing a specific configuration function via an exported API could be confusing.
How about factoring the act of loading the identity into an API:

type IdentityLoader func(*GlobalCLIFlags, *service.Config) (*AuthServiceClientConfig, error)

which could be part of the flags struct:

// GlobalCLIFlags keeps the CLI flags that apply to all tctl commands
type GlobalCLIFlags struct {
	...
	// IdentityLoader specifies the function to load the identity information
	IdentityLoader IdentityLoader
	...
}

And then multiple implementations could be provided:
[tool/tctl/common/tctl.go]

app.Flag("identity", "Path to the identity file exported with 'tctl auth sign'").
	Short('i').
	StringVar(&identityFilePath)
// ...
if identityFilePath != "" {
	ccf.IdentityLoader = loadIdentityFromFile(identityFilePath)
} else {
	ccf.IdentityLoader = loadIdentityFromHostUUID
}
// configure all commands with Teleport configuration (they share 'cfg')
clientConfig, err := applyConfig(&ccf, cfg)
if err != nil {
	utils.FatalError(err)
}
// ...

OSS implementations:

func loadIdentityFromFile(path string) IdentityLoader {
	return func(_ *GlobalCLIFlags, cfg *service.Config) (*AuthServiceClientConfig, error) {
		key, err := common.LoadIdentity(path)
		if err != nil {
			return nil, trace.Wrap(err)
		}
		authConfig := new(AuthServiceClientConfig)
		authConfig.TLS, err = key.ClientTLSConfig(cfg.CipherSuites)
		if err != nil {
			return nil, trace.Wrap(err)
		}
		authConfig.SSH, err = key.ClientSSHConfig()
		if err != nil {
			return nil, trace.Wrap(err)
		}
		return authConfig, nil
	}
}

func loadIdentityFromHostUUID(_ *GlobalCLIFlags, cfg *service.Config) (authConfig *AuthServiceClientConfig, err error) {
	// 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 = utils.ReadHostUUID(cfg.DataDir)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	identity, err := auth.ReadLocalIdentity(filepath.Join(cfg.DataDir, teleport.ComponentProcess), auth.IdentityID{Role: teleport.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 either used on the auth server or provided with the identity file via --identity flag")
		}
		return nil, trace.Wrap(err)
	}
	tls, err := identity.TLSConfig(cfg.CipherSuites)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	return &AuthServiceClientConfig{
		TLS: tls,
	}, nil
}

and pass them from corresponding main:

var ccf common.GlobalCLIFlags
ccf.IdentityLoader = loadIdentityCustom // override when necessary
common.Run(commands, ccf)

It is mostly the same but will make the read code better imho.

tool/tctl/common/tctl.go Outdated Show resolved Hide resolved
@benarent
Copy link
Contributor

benarent commented Nov 2, 2020

We should review our profile support after this is complete, #3089 to make tctl CLI UX as smooth as possible.

@webvictim
Copy link
Contributor

webvictim commented Nov 2, 2020

I built this from source and it seems to work, it's very slow though (8 seconds between pressing enter and getting a response):

$ time ~/go/src/github.com/gravitational/teleport/e/build/tctl status
Cluster  gus.cloud.gravitational.io                                              
Version  5.0.0-beta.8                                                            
User CA  never updated                                                           
Host CA  never updated                                                           
CA pin   sha256:dec0a2e0ded908af3e5ba8f124f1d44aeb8a316ce99df3ef0339a9e5951f723d 
~/go/src/github.com/gravitational/teleport/e/build/tctl status  0.08s user 0.02s system 1% cpu 8.147 total
$ time ~/go/src/github.com/gravitational/teleport/e/build/tctl -d status
DEBU             Debug logging has been enabled. common/tctl.go:303
DEBU [KEYSTORE]  Returning SSH certificate "/home/gus/.tsh/keys/gus.cloud.gravitational.io/admin-cert.pub" valid until "2020-11-03 01:29:45 -0400 AST", TLS certificate "/home/gus/.tsh/keys/gus.cloud.gravitational.io/admin-x509.pem" valid until "2020-11-03 05:29:45 +0000 UTC". client/keystore.go:277
DEBU             Found active profile: {https   gus.cloud.gravitational.io:443   false  } admin. tctl/profile.go:43
DEBU [KEYSTORE]  Returning SSH certificate "/home/gus/.tsh/keys/gus.cloud.gravitational.io/admin-cert.pub" valid until "2020-11-03 01:29:45 -0400 AST", TLS certificate "/home/gus/.tsh/keys/gus.cloud.gravitational.io/admin-x509.pem" valid until "2020-11-03 05:29:45 +0000 UTC". client/keystore.go:277
DEBU             Setting auth server to web proxy gus.cloud.gravitational.io:443. tctl/profile.go:75
DEBU             Connecting to auth servers: [{gus.cloud.gravitational.io:443 tcp }]. common/tctl.go:181
DEBU [CLIENT]    HTTPS client init(proxyAddr=gus.cloud.gravitational.io:443, insecure=false) client/weblogin.go:307
DEBU             Attempting to connect using reverse tunnel address gus.cloud.gravitational.io:3024. common/tctl.go:214
DEBU [HTTP:PROX] No valid environment variables found. proxy/proxy.go:222
DEBU [HTTP:PROX] No proxy set in environment, returning direct dialer. proxy/proxy.go:137
DEBU [CLIENT]    Validated host gus.cloud.gravitational.io:3024. client/keyagent.go:99
DEBU [AUTH]      GRPC(CLIENT): keep alive 1m0s count: 3. auth/clt.go:319
DEBU [HTTP:PROX] No valid environment variables found. proxy/proxy.go:222
DEBU [HTTP:PROX] No proxy set in environment, returning direct dialer. proxy/proxy.go:137
DEBU [CLIENT]    Validated host gus.cloud.gravitational.io:3024. client/keyagent.go:99
Cluster  gus.cloud.gravitational.io                                                        
Version  5.0.0-beta.8                                                                      
User CA  never updated, update_servers: Jan  1 00:00:00 UTC, complete: Jan  1 00:00:00 UTC 
Host CA  never updated, update_servers: Jan  1 00:00:00 UTC, complete: Jan  1 00:00:00 UTC 
CA pin   sha256:dec0a2e0ded908af3e5ba8f124f1d44aeb8a316ce99df3ef0339a9e5951f723d           
Remote clusters

~/go/src/github.com/gravitational/teleport/e/build/tctl -d status  0.09s user 0.02s system 1% cpu 8.227 total

My ping to cloud is 94ms, I don't think it should take this long?

[root@ip-10-0-0-239 ~]# ping <home IP>
PING <home IP> 56(84) bytes of data.
64 bytes from <home IP>: icmp_seq=1 ttl=35 time=93.9 ms
64 bytes from <home IP>: icmp_seq=2 ttl=35 time=94.0 ms
64 bytes from <home IP>: icmp_seq=3 ttl=35 time=93.7 ms
64 bytes from <home IP>: icmp_seq=4 ttl=35 time=94.4 ms
64 bytes from <home IP>: icmp_seq=5 ttl=35 time=93.6 ms

@klizhentas
Copy link
Contributor Author

Strange, takes 1 second for me.

time ./e/build/tctl status
Cluster  tea.cloud.gravitational.io                                              
Version  5.0.0-beta.9                                                            
User CA  never updated                                                           
Host CA  never updated                                                           
CA pin   sha256:904c2dad75a290cf4b7debf316ddfc12402e77fce452d71bc67dfd602a5a090f 

real	0m1.085s
user	0m0.268s
sys	0m0.095s

@webvictim
Copy link
Contributor

Weird, I ran it in a Docker container and it was quick. It seems to be quicker locally now as well. Must have been a glitch,

@russjones
Copy link
Contributor

@a-palchikov I see what you're saying refactoring of the identity loading code, but this will be deleted in a few weeks once 5.1.0 lands and OSS and Enterprise both support RBAC. I'm in-favor of leaving as-is till then, what do you think?

lib/client/keystore_test.go Outdated Show resolved Hide resolved
lib/client/keystore_test.go Outdated Show resolved Hide resolved
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

tctl.e client should use tsh credentials to perform admin actions.
5 participants