diff --git a/build.assets/Makefile b/build.assets/Makefile index ff62520fac4..f70bca5d945 100644 --- a/build.assets/Makefile +++ b/build.assets/Makefile @@ -51,6 +51,7 @@ docs: bbox # .PHONY:run-docs run-docs: bbox + @echo -e "\n\n----> LIVE EDIT HERE: http://localhost:6600/admin-guide/\n" docker run $(DOCKERFLAGS) -ti $(NOROOT) -e HOME=$(SRCDIR)/build.assets -p 6600:6600 -w $(SRCDIR) $(BBOX) mkdocs serve -a 0.0.0.0:6600 # diff --git a/docs/admin-guide.md b/docs/admin-guide.md index 665130401b7..216255653cc 100644 --- a/docs/admin-guide.md +++ b/docs/admin-guide.md @@ -131,7 +131,12 @@ Lets cover some of these flags in more detail: [Teleport Architecture](architecture.md) document. * `--advertise-ip` flag can be used when Teleport nodes are running behind NAT and - their externally routable IP cannot be automatically determined. + their externally routable IP cannot be automatically determined. + For example, assume that a host "foo" can be reached via `10.0.0.10` but there is + no `A` DNS record for "foo", so you cannot connect to it via `tsh ssh foo`. If + you start teleport on "foo" with `--advertise-ip=10.0.0.10`, it will automatically + tell Teleport proxy to use that IP when someone tries to connect + to "foo". This is also useful when connecting to Teleport nodes using their labels. * `--nodename` flag lets you assign an alternative name the node which can be used by clients to login. By default it's equal to the value returned by `hostname` @@ -162,6 +167,10 @@ teleport: # by default it's equal to hostname nodename: graviton + # Data directory where Teleport keeps its data, like keys/users for + # authentication (if using the default BoltDB back-end) + data_dir: /var/lib/teleport + # one-time invitation token used to join a cluster. it is not used on # subsequent starts auth_token: xxxx-token-xxxx @@ -192,7 +201,6 @@ teleport: # backend if you want to run Teleport in HA configuration. storage: type: bolt - data_dir: /var/lib/teleport # This section configures the 'auth service': auth_service: diff --git a/fixtures/local-cluster.yaml b/fixtures/local-cluster.yaml new file mode 100644 index 00000000000..cd06df3f542 --- /dev/null +++ b/fixtures/local-cluster.yaml @@ -0,0 +1,16 @@ +# Simple config file with just a few customizations (with comments) +teleport: + nodename: localhost + # auth token allows easy adding of other nodes. pass this value + # as --token when starting nodes. + auth_token: OhwiZ2ainushemith1oquiex + log: + output: stderr + severity: INFO +auth_service: + enabled: yes + cluster_name: teleport.local +ssh_service: + enabled: yes +proxy_service: + enabled: yes diff --git a/fixtures/tmp-cluster.yaml b/fixtures/tmp-cluster.yaml new file mode 100644 index 00000000000..3c9e30f9d14 --- /dev/null +++ b/fixtures/tmp-cluster.yaml @@ -0,0 +1,22 @@ +# This config file creates a single node teleport cluster with all +# its data in /tmp +# +# All listening ports are changed as well +# +teleport: + nodename: tmp.localhost + auth_token: Tex7siequoo9eew4Eitoo1ni + data_dir: /tmp/teleport + log: + output: stderr + severity: INFO +auth_service: + enabled: yes + cluster_name: teleport.tmp + listen_addr: 0.0.0.0:5010 +ssh_service: + enabled: yes + listen_addr: 0.0.0.0:5011 +proxy_service: + listen_addr: 0.0.0.0:5012 + enabled: yes diff --git a/integration/helpers.go b/integration/helpers.go index bfdc15a99b6..e1b5a2f9f47 100644 --- a/integration/helpers.go +++ b/integration/helpers.go @@ -197,6 +197,7 @@ func (i *TeleInstance) Create(trustedSecrets []*InstanceSecrets, enableSSH bool, return err } tconf := service.MakeDefaultConfig() + tconf.DataDir = dataDir tconf.Console = console tconf.Auth.DomainName = i.Secrets.SiteName tconf.Auth.Authorities = append(tconf.Auth.Authorities, i.Secrets.GetCAs()...) @@ -221,9 +222,8 @@ func (i *TeleInstance) Create(trustedSecrets []*InstanceSecrets, enableSSH bool, tconf.Proxy.SSHAddr.Addr = net.JoinHostPort(i.Hostname, i.GetPortProxy()) tconf.Proxy.WebAddr.Addr = net.JoinHostPort(i.Hostname, i.GetPortWeb()) tconf.Proxy.DisableWebUI = true - tconf.AuthServers[0].Addr = tconf.Auth.SSHAddr.Addr - tconf.ConfigureBolt(dataDir) - tconf.DataDir = dataDir + tconf.AuthServers = append(tconf.AuthServers, tconf.Auth.SSHAddr) + tconf.ConfigureBolt() tconf.Keygen = testauthority.New() i.Config = tconf i.Process, err = service.NewTeleport(tconf) diff --git a/lib/auth/apiserver.go b/lib/auth/apiserver.go index 27cbaeedc2b..7f9cba08ca8 100644 --- a/lib/auth/apiserver.go +++ b/lib/auth/apiserver.go @@ -689,6 +689,7 @@ func (s *APIServer) createUserWithToken(w http.ResponseWriter, r *http.Request, } sess, err := s.a.CreateUserWithToken(req.Token, req.Password, req.HOTPToken) if err != nil { + log.Error(err) return nil, trace.Wrap(err) } return sess, nil diff --git a/lib/auth/tun.go b/lib/auth/tun.go index 3714c0d60df..3de5ae6a212 100644 --- a/lib/auth/tun.go +++ b/lib/auth/tun.go @@ -578,7 +578,7 @@ func NewTunClient(purpose string, for _, o := range opts { o(tc) } - log.Infof("newTunClient(%s)", purpose) + log.Infof("newTunClient(%s) with auth: %v", purpose, authServers) clt, err := NewClient("http://stub:0", tc.Dial) if err != nil { @@ -645,7 +645,7 @@ func (c *TunClient) GetAgent() (AgentCloser, error) { // Dial dials to Auth server's HTTP API over SSH tunnel func (c *TunClient) Dial(network, address string) (net.Conn, error) { - log.Infof("TunClient[%s].Dial(%v, %v)", c.purpose, network, address) + log.Infof("TunClient[%s].Dial()", c.purpose) client, err := c.getClient() if err != nil { return nil, trace.Wrap(err) @@ -762,6 +762,7 @@ func (c *TunClient) getClient() (client *ssh.Client, err error) { if len(authServers) == 0 { return nil, trace.Errorf("all auth servers are offline") } + log.Infof("tunClient(%s).authServers: %v", c.purpose, authServers) // try to connect to the 1st one who will pick up: for _, authServer := range authServers { diff --git a/lib/config/config_test.go b/lib/config/config_test.go index 9cd5d65dd3f..cb61599e445 100644 --- a/lib/config/config_test.go +++ b/lib/config/config_test.go @@ -98,7 +98,7 @@ func (s *ConfigTestSuite) TestSampleConfig(c *check.C) { // validate a couple of values: c.Assert(fc.Limits.MaxUsers, check.Equals, defaults.LimiterMaxConcurrentUsers) - c.Assert(fc.Global.Storage.DirName, check.Equals, defaults.DataDir) + c.Assert(fc.Global.DataDir, check.Equals, defaults.DataDir) c.Assert(fc.Logger.Severity, check.Equals, "INFO") } @@ -134,7 +134,7 @@ func (s *ConfigTestSuite) TestConfigReading(c *check.C) { c.Assert(conf.Logger.Output, check.Equals, "stderr") c.Assert(conf.Logger.Severity, check.Equals, "INFO") c.Assert(conf.Storage.Type, check.Equals, "bolt") - c.Assert(conf.Storage.DirName, check.Equals, "/var/lib/teleport") + c.Assert(conf.DataDir, check.Equals, "/path/to/data") c.Assert(conf.Auth.Enabled(), check.Equals, true) c.Assert(conf.Auth.ListenAddress, check.Equals, "tcp://auth") c.Assert(conf.SSH.Configured(), check.Equals, true) @@ -330,6 +330,7 @@ func makeConfigFixture() string { // common config: conf.NodeName = NodeName + conf.DataDir = "/path/to/data" conf.AuthServers = AuthServers conf.Limits.MaxConnections = 100 conf.Limits.MaxUsers = 5 @@ -337,7 +338,6 @@ func makeConfigFixture() string { conf.Logger.Output = "stderr" conf.Logger.Severity = "INFO" conf.Storage.Type = "bolt" - conf.Storage.DirName = "/var/lib/teleport" // auth service: conf.Auth.EnabledFlag = "Yeah" diff --git a/lib/config/configuration.go b/lib/config/configuration.go index 4e5503339fc..c689ac2e71e 100644 --- a/lib/config/configuration.go +++ b/lib/config/configuration.go @@ -71,7 +71,7 @@ type CommandLineFlags struct { // readConfigFile reads /etc/teleport.yaml (or whatever is passed via --config flag) // and overrides values in 'cfg' structure -func readConfigFile(cliConfigPath string) (*FileConfig, error) { +func ReadConfigFile(cliConfigPath string) (*FileConfig, error) { configFilePath := defaults.ConfigFilePath // --config tells us to use a specific conf. file: if cliConfigPath != "" { @@ -126,25 +126,30 @@ func ApplyFileConfig(fc *FileConfig, cfg *service.Config) error { if err != nil { return trace.Errorf("cannot parse auth server address: '%v'", as) } - cfg.AuthServers = append(cfg.AuthServers, *addr) + cfg.AuthServers = []utils.NetAddr{*addr} } } cfg.ApplyToken(fc.AuthToken) cfg.Auth.DomainName = fc.Auth.DomainName + if fc.Global.DataDir != "" { + cfg.DataDir = fc.Global.DataDir + } + if fc.Storage.Type == "" { + fc.Storage.Type = teleport.BoltBackendType + } // configure storage: switch fc.Storage.Type { case teleport.BoltBackendType: - cfg.ConfigureBolt(fc.Storage.DirName) + cfg.ConfigureBolt() case teleport.ETCDBackendType: - if err := cfg.ConfigureETCD( - fc.Storage.DirName, etcdbk.Config{ - Nodes: fc.Storage.Peers, - Key: fc.Storage.Prefix, - TLSKeyFile: fc.Storage.TLSKeyFile, - TLSCertFile: fc.Storage.TLSCertFile, - TLSCAFile: fc.Storage.TLSCAFile, - }); err != nil { + if err := cfg.ConfigureETCD(etcdbk.Config{ + Nodes: fc.Storage.Peers, + Key: fc.Storage.Prefix, + TLSKeyFile: fc.Storage.TLSKeyFile, + TLSCertFile: fc.Storage.TLSCertFile, + TLSCAFile: fc.Storage.TLSCAFile, + }); err != nil { return trace.Wrap(err) } case "": @@ -268,6 +273,7 @@ func ApplyFileConfig(fc *FileConfig, cfg *service.Config) error { return trace.Wrap(err) } cfg.Auth.SSHAddr = *addr + cfg.AuthServers = append(cfg.AuthServers, *addr) } for _, authority := range fc.Auth.Authorities { ca, err := authority.Parse() @@ -325,7 +331,7 @@ func applyString(src string, target *string) bool { // with CLI commands taking precedence func Configure(clf *CommandLineFlags, cfg *service.Config) error { // load /etc/teleport.yaml and apply it's values: - fileConf, err := readConfigFile(clf.ConfigFile) + fileConf, err := ReadConfigFile(clf.ConfigFile) if err != nil { return trace.Wrap(err) } diff --git a/lib/config/fileconf.go b/lib/config/fileconf.go index 8e9848d7115..1b682cef405 100644 --- a/lib/config/fileconf.go +++ b/lib/config/fileconf.go @@ -198,7 +198,7 @@ func MakeSampleFileConfig() (fc *FileConfig) { g.AuthServers = []string{defaults.AuthListenAddr().Addr} g.Limits.MaxConnections = defaults.LimiterMaxConnections g.Limits.MaxUsers = defaults.LimiterMaxConcurrentUsers - g.Storage.DirName = defaults.DataDir + g.DataDir = defaults.DataDir g.Storage.Type = conf.Auth.RecordsBackend.Type g.PIDFile = "/var/run/teleport.pid" @@ -259,7 +259,6 @@ func MakeAuthPeerFileConfig(domainName string, token string) (fc *FileConfig) { g.AuthServers = []string{""} g.Limits.MaxConnections = defaults.LimiterMaxConnections g.Limits.MaxUsers = defaults.LimiterMaxConcurrentUsers - g.Storage.DirName = defaults.DataDir g.Storage.Type = teleport.ETCDBackendType g.Storage.Prefix = defaults.ETCDPrefix g.Storage.Peers = []string{"insert ETCD peers addresses here"} @@ -318,8 +317,6 @@ type Log struct { type StorageBackend struct { // Type can be "bolt" or "etcd" Type string `yaml:"type,omitempty"` - // DirName is valid only for bolt - DirName string `yaml:"data_dir,omitempty"` // Peers is a lsit of etcd peers, valid only for etcd Peers []string `yaml:"peers,omitempty"` // Prefix is etcd key prefix, valid only for etcd @@ -342,6 +339,7 @@ type Global struct { Logger Log `yaml:"log,omitempty"` Storage StorageBackend `yaml:"storage,omitempty"` AdvertiseIP net.IP `yaml:"advertise_ip,omitempty"` + DataDir string `yaml:"data_dir,omitempty"` // Keys holds the list of SSH key/cert pairs used by all services // Each service (like proxy, auth, node) can find the key it needs diff --git a/lib/service/cfg.go b/lib/service/cfg.go index 5f70558dd58..6199baba67d 100644 --- a/lib/service/cfg.go +++ b/lib/service/cfg.go @@ -122,22 +122,22 @@ func (cfg *Config) ApplyToken(token string) bool { } // ConfigureBolt configures Bolt back-ends with a data dir. -func (cfg *Config) ConfigureBolt(dataDir string) { +func (cfg *Config) ConfigureBolt() { a := &cfg.Auth if a.EventsBackend.Type == teleport.BoltBackendType { - a.EventsBackend.Params = boltParams(dataDir, defaults.EventsBoltFile) + a.EventsBackend.Params = boltParams(cfg.DataDir, defaults.EventsBoltFile) } if a.KeysBackend.Type == teleport.BoltBackendType { - a.KeysBackend.Params = boltParams(dataDir, defaults.KeysBoltFile) + a.KeysBackend.Params = boltParams(cfg.DataDir, defaults.KeysBoltFile) } if a.RecordsBackend.Type == teleport.BoltBackendType { - a.RecordsBackend.Params = boltParams(dataDir, defaults.RecordsBoltFile) + a.RecordsBackend.Params = boltParams(cfg.DataDir, defaults.RecordsBoltFile) } } // ConfigureETCD configures ETCD backend (still uses BoltDB for some cases) -func (cfg *Config) ConfigureETCD(dataDir string, etcdCfg etcdbk.Config) error { +func (cfg *Config) ConfigureETCD(etcdCfg etcdbk.Config) error { a := &cfg.Auth params, err := etcdParams(etcdCfg) @@ -149,10 +149,10 @@ func (cfg *Config) ConfigureETCD(dataDir string, etcdCfg etcdbk.Config) error { // We can't store records and events in ETCD a.EventsBackend.Type = teleport.BoltBackendType - a.EventsBackend.Params = boltParams(dataDir, defaults.EventsBoltFile) + a.EventsBackend.Params = boltParams(cfg.DataDir, defaults.EventsBoltFile) a.RecordsBackend.Type = teleport.BoltBackendType - a.RecordsBackend.Params = boltParams(dataDir, defaults.RecordsBoltFile) + a.RecordsBackend.Params = boltParams(cfg.DataDir, defaults.RecordsBoltFile) return nil } @@ -313,9 +313,6 @@ func ApplyDefaults(cfg *Config) { // global defaults cfg.Hostname = hostname cfg.DataDir = defaults.DataDir - if cfg.Auth.Enabled { - cfg.AuthServers = []utils.NetAddr{cfg.Auth.SSHAddr} - } cfg.Console = os.Stdout } diff --git a/lib/service/cfg_test.go b/lib/service/cfg_test.go index af184538417..67878d17120 100644 --- a/lib/service/cfg_test.go +++ b/lib/service/cfg_test.go @@ -54,11 +54,9 @@ func (s *ConfigSuite) TestDefaultConfig(c *C) { if len(config.Hostname) < 2 { c.Error("default hostname wasn't properly set") } - c.Assert(config.AuthServers, DeepEquals, []utils.NetAddr{localAuthAddr}) // auth section auth := config.Auth - c.Assert(config.AuthServers, DeepEquals, []utils.NetAddr{auth.SSHAddr}) c.Assert(auth.SSHAddr, DeepEquals, localAuthAddr) c.Assert(auth.Limiter.MaxConnections, Equals, int64(defaults.LimiterMaxConnections)) c.Assert(auth.Limiter.MaxNumberOfUsers, Equals, defaults.LimiterMaxConcurrentUsers) diff --git a/tool/tctl/main.go b/tool/tctl/main.go index 4e1d4ae5342..791f7a1c03c 100644 --- a/tool/tctl/main.go +++ b/tool/tctl/main.go @@ -26,9 +26,11 @@ import ( "strings" "time" + "github.com/Sirupsen/logrus" "github.com/gravitational/teleport" "github.com/gravitational/teleport/lib/auth" "github.com/gravitational/teleport/lib/auth/native" + "github.com/gravitational/teleport/lib/config" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/service" "github.com/gravitational/teleport/lib/services" @@ -43,7 +45,8 @@ import ( ) type CLIConfig struct { - Debug bool + Debug bool + ConfigFile string } type UserCommand struct { @@ -113,6 +116,9 @@ func main() { app.Flag("debug", "Enable verbose logging to stderr"). Short('d'). BoolVar(&ccf.Debug) + app.Flag("config", fmt.Sprintf("Path to a configuration file [%v]", defaults.ConfigFilePath)). + Short('c'). + ExistingFileVar(&ccf.ConfigFile) // commands: ver := app.Command("version", "Print the version.") @@ -120,7 +126,7 @@ func main() { // user add command: users := app.Command("users", "Manage users logins") - userAdd := users.Command("add", "Generates an invitation token and prints the signup URL for setting up 2nd factor auth.") + userAdd := users.Command("add", "Generate an invitation token and print the signup URL") userAdd.Arg("login", "Teleport user login").Required().StringVar(&cmdUsers.login) userAdd.Arg("local-logins", "Local UNIX users this account can log in as [login]"). Default("").StringVar(&cmdUsers.allowedLogins) @@ -128,7 +134,7 @@ func main() { userAdd.Alias(AddUserHelp) // list users command - userList := users.Command("ls", "Lists all user accounts") + userList := users.Command("ls", "List all user accounts") // delete user command userDelete := users.Command("del", "Deletes user accounts") @@ -137,13 +143,13 @@ func main() { // add node command nodes := app.Command("nodes", "Issue invites for other nodes to join the cluster") - nodeAdd := nodes.Command("add", "Generates an invitation token. Use it to add a new node to the Teleport cluster") + nodeAdd := nodes.Command("add", "Generate an invitation token. Use it to add a new node to the Teleport cluster") nodeAdd.Flag("roles", "Comma-separated list of roles for the new node to assume [node]").Default("node").StringVar(&cmdNodes.roles) nodeAdd.Flag("ttl", "Time to live for a generated token").DurationVar(&cmdNodes.ttl) nodeAdd.Flag("count", "add count tokens and output JSON with the list").Hidden().Default("1").IntVar(&cmdNodes.count) nodeAdd.Flag("format", "output format, 'text' or 'json'").Hidden().Default("text").StringVar(&cmdNodes.format) nodeAdd.Alias(AddNodeHelp) - nodeList := nodes.Command("ls", "Lists all active SSH nodes within the cluster") + nodeList := nodes.Command("ls", "List all active SSH nodes within the cluster") nodeList.Alias(ListNodesHelp) // operations on invitation tokens @@ -153,16 +159,16 @@ func main() { tokenDel.Arg("token", "Token to delete").StringVar(&cmdTokens.token) // operations with authorities - auth := app.Command("authorities", "Operations with user and host certificate authorities").Hidden() - auth.Flag("type", "authority type, 'user' or 'host'").Default(string(services.UserCA)).StringVar(&cmdAuth.authType) - authList := auth.Command("ls", "List trusted user certificate authorities") - authExport := auth.Command("export", "Export concatenated keys to standard output") - authExport.Flag("private-keys", "if set, will print private keys").BoolVar(&cmdAuth.exportPrivateKeys) + auth := app.Command("auth", "Operations with user and host certificate authorities").Hidden() + auth.Flag("type", "authority type, 'user' or 'host'").StringVar(&cmdAuth.authType) + authList := auth.Command("ls", "List trusted certificate authorities (CAs)") + authExport := auth.Command("export", "Export CA keys to standard output") + authExport.Flag("keys", "if set, will print private keys").BoolVar(&cmdAuth.exportPrivateKeys) authExport.Flag("fingerprint", "filter authority by fingerprint").StringVar(&cmdAuth.exportAuthorityFingerprint) - authGenerate := auth.Command("gen", "Generate new OpenSSH keypair") - authGenerate.Flag("pub-key", "path to the public key to write").Required().StringVar(&cmdAuth.genPubPath) - authGenerate.Flag("priv-key", "path to the private key to write").Required().StringVar(&cmdAuth.genPrivPath) + authGenerate := auth.Command("gen", "Generate a new SSH keypair") + authGenerate.Flag("pub-key", "path to the public key").Required().StringVar(&cmdAuth.genPubPath) + authGenerate.Flag("priv-key", "path to the private key").Required().StringVar(&cmdAuth.genPrivPath) authGenAndSign := auth.Command("gencert", "Generate OpenSSH keys and certificate for a joining teleport proxy, node or auth server").Hidden() authGenAndSign.Flag("priv-key", "path to the private key to write").Required().StringVar(&cmdAuth.genPrivPath) @@ -172,17 +178,17 @@ func main() { authGenAndSign.Flag("domain", "cluster certificate authority domain name").Required().StringVar(&cmdAuth.genAuthorityDomain) // operations with reverse tunnels - reverseTunnels := app.Command("rts", "Operations with reverse tunnels").Hidden() - reverseTunnelsList := reverseTunnels.Command("ls", "List reverse tunnels").Hidden() - reverseTunnelsDelete := reverseTunnels.Command("del", "Deletes reverse tunnels").Hidden() - reverseTunnelsDelete.Arg("domain", "Comma-separated list of reverse tunnels to delete"). + reverseTunnels := app.Command("tunnels", "Operations on reverse tunnels clusters").Hidden() + reverseTunnelsList := reverseTunnels.Command("ls", "List tunnels").Hidden() + reverseTunnelsDelete := reverseTunnels.Command("del", "Delete a tunnel").Hidden() + reverseTunnelsDelete.Arg("name", "Tunnels to delete"). Required().StringVar(&cmdReverseTunnel.domainNames) - reverseTunnelsUpsert := reverseTunnels.Command("upsert", "Update or add a new reverse tunnel").Hidden() - reverseTunnelsUpsert.Arg("domain", "Domain name of the reverse tunnel"). + reverseTunnelsUpsert := reverseTunnels.Command("add", "Create a new reverse tunnel").Hidden() + reverseTunnelsUpsert.Arg("name", "Name of the tunnel"). Required().StringVar(&cmdReverseTunnel.domainNames) - reverseTunnelsUpsert.Arg("addrs", "Comma-separated list of dial addresses for reverse tunnels to dial"). + reverseTunnelsUpsert.Arg("addrs", "Comma-separated list of tunnels"). Required().SetValue(&cmdReverseTunnel.dialAddrs) - reverseTunnelsUpsert.Flag("ttl", "Optional TTL (time to live) for reverse tunnel").DurationVar(&cmdReverseTunnel.ttl) + reverseTunnelsUpsert.Flag("ttl", "Optional TTL (time to live) for the tunnel").DurationVar(&cmdReverseTunnel.ttl) // parse CLI commands+flags: command, err := app.Parse(os.Args[1:]) @@ -190,11 +196,7 @@ func main() { utils.FatalError(err) } - // --debug flag - if ccf.Debug { - utils.InitLoggerDebug() - } - + applyConfig(&ccf, cfg) validateConfig(cfg) // some commands do not need a connection to client @@ -411,17 +413,29 @@ func (u *NodeCommand) ListActive(client *auth.TunClient) error { // ListAuthorities shows list of user authorities we trust func (a *AuthCommand) ListAuthorities(client *auth.TunClient) error { - authType := services.CertAuthType(a.authType) - if err := authType.Check(); err != nil { - return trace.Wrap(err) + // by default show authorities of both types: + authTypes := []services.CertAuthType{ + services.UserCA, + services.HostCA, } - authorities, err := client.GetCertAuthorities(authType, false) - if err != nil { - return trace.Wrap(err) + // but if there was a --type switch, only select those: + if a.authType != "" { + authTypes = []services.CertAuthType{services.CertAuthType(a.authType)} + if err := authTypes[0].Check(); err != nil { + return trace.Wrap(err) + } + } + authorities := make([]*services.CertAuthority, 0) + for _, t := range authTypes { + ats, err := client.GetCertAuthorities(t, false) + if err != nil { + return trace.Wrap(err) + } + authorities = append(authorities, ats...) } view := func() string { t := goterm.NewTable(0, 10, 5, ' ', 0) - printHeader(t, []string{"Type", "Authority Domain", "Fingerprint", "Restricted to logins"}) + printHeader(t, []string{"Type", "Cluster Name", "Fingerprint", "Allowed Logins"}) if len(authorities) == 0 { return t.String() } @@ -431,7 +445,11 @@ func (a *AuthCommand) ListAuthorities(client *auth.TunClient) error { if err != nil { fingerprint = fmt.Sprintf("