mirror of
https://github.com/gravitational/teleport
synced 2024-10-21 01:34:01 +00:00
Migrated 'users' and 'nodes' CLI commands to the new model
This commit is contained in:
parent
8ad748b86d
commit
432cb34c91
|
@ -155,7 +155,7 @@ type serverCollection struct {
|
|||
|
||||
func (s *serverCollection) writeText(w io.Writer) error {
|
||||
t := goterm.NewTable(0, 10, 5, ' ', 0)
|
||||
printHeader(t, []string{"Hostname", "Name", "Address", "Labels"})
|
||||
printHeader(t, []string{"Hostname", "UUID", "Address", "Labels"})
|
||||
if len(s.servers) == 0 {
|
||||
_, err := io.WriteString(w, t.String())
|
||||
return trace.Wrap(err)
|
||||
|
|
|
@ -23,8 +23,10 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gravitational/kingpin"
|
||||
"github.com/gravitational/teleport"
|
||||
"github.com/gravitational/teleport/lib/auth"
|
||||
"github.com/gravitational/teleport/lib/defaults"
|
||||
"github.com/gravitational/teleport/lib/service"
|
||||
"github.com/gravitational/trace"
|
||||
)
|
||||
|
@ -44,6 +46,42 @@ type NodeCommand struct {
|
|||
ttl time.Duration
|
||||
// namespace is node namespace
|
||||
namespace string
|
||||
|
||||
// CLI subcommands (clauses)
|
||||
nodeAdd *kingpin.CmdClause
|
||||
nodeList *kingpin.CmdClause
|
||||
}
|
||||
|
||||
// Initialize allows NodeCommand to plug itself into the CLI parser
|
||||
func (c *NodeCommand) Initialize(app *kingpin.Application, config *service.Config) {
|
||||
c.config = config
|
||||
|
||||
// add node command
|
||||
nodes := app.Command("nodes", "Issue invites for other nodes to join the cluster")
|
||||
c.nodeAdd = nodes.Command("add", "Generate a node invitation token")
|
||||
c.nodeAdd.Flag("roles", "Comma-separated list of roles for the new node to assume [node]").Default("node").StringVar(&c.roles)
|
||||
c.nodeAdd.Flag("ttl", "Time to live for a generated token").Default(defaults.ProvisioningTokenTTL.String()).DurationVar(&c.ttl)
|
||||
c.nodeAdd.Flag("count", "add count tokens and output JSON with the list").Hidden().Default("1").IntVar(&c.count)
|
||||
c.nodeAdd.Flag("format", "output format, 'text' or 'json'").Hidden().Default("text").StringVar(&c.format)
|
||||
c.nodeAdd.Alias(AddNodeHelp)
|
||||
|
||||
c.nodeList = nodes.Command("ls", "List all active SSH nodes within the cluster")
|
||||
c.nodeList.Flag("namespace", "Namespace of the nodes").Default(defaults.Namespace).StringVar(&c.namespace)
|
||||
c.nodeList.Alias(ListNodesHelp)
|
||||
}
|
||||
|
||||
// TryRun takes the CLI command as an argument (like "nodes ls") and executes it.
|
||||
func (c *NodeCommand) TryRun(cmd string, client *auth.TunClient) (match bool, err error) {
|
||||
switch cmd {
|
||||
case c.nodeAdd.FullCommand():
|
||||
err = c.Invite(client)
|
||||
case c.nodeList.FullCommand():
|
||||
err = c.ListActive(client)
|
||||
|
||||
default:
|
||||
return false, nil
|
||||
}
|
||||
return true, trace.Wrap(err)
|
||||
}
|
||||
|
||||
// Invite generates a token which can be used to add another SSH node
|
||||
|
@ -83,7 +121,6 @@ func (u *NodeCommand) Invite(client *auth.TunClient) error {
|
|||
}
|
||||
fmt.Printf(" - This invitation token will expire in %d minutes\n", int(u.ttl.Minutes()))
|
||||
fmt.Printf(" - %v must be reachable from the new node, see --advertise-ip server flag\n", authServers[0].GetAddr())
|
||||
fmt.Printf(` - For tokens of type "trustedcluster", tctl needs to be used to create a TrustedCluster resource. See the Admin Guide for more details.`)
|
||||
} else {
|
||||
out, err := json.Marshal(tokens)
|
||||
if err != nil {
|
||||
|
|
|
@ -31,16 +31,98 @@ import (
|
|||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/buger/goterm"
|
||||
"github.com/gravitational/kingpin"
|
||||
"github.com/gravitational/trace"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
type CLIConfig struct {
|
||||
// GlobalCLIFlags keeps the CLI flags that apply to all tctl commands
|
||||
type GlobalCLIFlags struct {
|
||||
Debug bool
|
||||
ConfigFile string
|
||||
ConfigString string
|
||||
}
|
||||
|
||||
// CLICommand interface must be implemented by every CLI command
|
||||
//
|
||||
// This allows OSS and Enterprise Teleport editions to plug their own
|
||||
// implementations of different CLI commands into the common execution
|
||||
// framework
|
||||
//
|
||||
type CLICommand interface {
|
||||
// Initialize allows a caller-defined command to plug itself into CLI
|
||||
// argument parsing
|
||||
Initialize(*kingpin.Application, *service.Config)
|
||||
|
||||
// TryRun is executed after the CLI parsing is done. The command must
|
||||
// determine if selectedCommand belongs to it and return match=true
|
||||
TryRun(selectedCommand string, c *auth.TunClient) (match bool, err error)
|
||||
}
|
||||
|
||||
func Run2(distribution string, commands []CLICommand) {
|
||||
utils.InitLogger(utils.LoggingForCLI, logrus.WarnLevel)
|
||||
|
||||
// app is the command line parser
|
||||
app := utils.InitCLIParser("tctl", GlobalHelpString)
|
||||
|
||||
// cfg (teleport auth server configuration) is going to be shared by all
|
||||
// commands
|
||||
cfg := service.MakeDefaultConfig()
|
||||
|
||||
// each command will add itself to the CLI parser:
|
||||
for i := range commands {
|
||||
commands[i].Initialize(app, cfg)
|
||||
}
|
||||
|
||||
// these global flags apply to all commands
|
||||
var ccf GlobalCLIFlags
|
||||
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)
|
||||
app.Flag("config-string",
|
||||
"Base64 encoded configuration string").Hidden().Envar(defaults.ConfigEnvar).StringVar(&ccf.ConfigString)
|
||||
|
||||
// "version" command is always available:
|
||||
ver := app.Command("version", "Print the version.")
|
||||
app.HelpFlag.Short('h')
|
||||
|
||||
// parse CLI commands+flags:
|
||||
selectedCmd, err := app.Parse(os.Args[1:])
|
||||
if err != nil {
|
||||
utils.FatalError(err)
|
||||
}
|
||||
|
||||
// "version" command?
|
||||
if selectedCmd == ver.FullCommand() {
|
||||
utils.PrintVersion(distribution)
|
||||
return
|
||||
}
|
||||
|
||||
// configure all commands with Teleport configuration (they share 'cfg')
|
||||
applyConfig(&ccf, cfg)
|
||||
|
||||
// connect to the auth sever:
|
||||
client, err := connectToAuthService(cfg)
|
||||
if err != nil {
|
||||
utils.FatalError(err)
|
||||
}
|
||||
|
||||
// execute whatever is selected:
|
||||
var match bool
|
||||
for _, c := range commands {
|
||||
match, err = c.TryRun(selectedCmd, client)
|
||||
if err != nil {
|
||||
utils.FatalError(err)
|
||||
}
|
||||
if match {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run() is the same as 'make'. It helps to share the code between different
|
||||
// "distributions" like OSS or Enterprise
|
||||
//
|
||||
|
@ -51,8 +133,6 @@ func Run(distribution string) {
|
|||
|
||||
// generate default tctl configuration:
|
||||
cfg := service.MakeDefaultConfig()
|
||||
cmdUsers := UserCommand{config: cfg}
|
||||
cmdNodes := NodeCommand{config: cfg}
|
||||
cmdAuth := AuthCommand{config: cfg}
|
||||
cmdTokens := TokenCommand{config: cfg}
|
||||
cmdGet := GetCommand{config: cfg}
|
||||
|
@ -60,7 +140,7 @@ func Run(distribution string) {
|
|||
cmdDelete := DeleteCommand{config: cfg}
|
||||
|
||||
// define global flags:
|
||||
var ccf CLIConfig
|
||||
var ccf GlobalCLIFlags
|
||||
app.Flag("debug", "Enable verbose logging to stderr").
|
||||
Short('d').
|
||||
BoolVar(&ccf.Debug)
|
||||
|
@ -74,20 +154,6 @@ func Run(distribution string) {
|
|||
ver := app.Command("version", "Print the version.")
|
||||
app.HelpFlag.Short('h')
|
||||
|
||||
// user add command:
|
||||
users := app.Command("users", "Manage users logins")
|
||||
|
||||
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)
|
||||
userAdd.Alias(AddUserHelp)
|
||||
|
||||
userUpdate := users.Command("update", "Update properties for existing user").Hidden()
|
||||
userUpdate.Arg("login", "Teleport user login").Required().StringVar(&cmdUsers.login)
|
||||
userUpdate.Flag("set-roles", "Roles to assign to this user").
|
||||
Default("").StringVar(&cmdUsers.roles)
|
||||
|
||||
delete := app.Command("del", "Delete resources").Hidden()
|
||||
delete.Arg("resource", "Resource to delete").SetValue(&cmdDelete.ref)
|
||||
|
||||
|
@ -102,26 +168,6 @@ func Run(distribution string) {
|
|||
create := app.Command("create", "Create or update a resource").Hidden()
|
||||
create.Flag("filename", "resource definition file").Short('f').StringVar(&cmdCreate.filename)
|
||||
|
||||
// list users command
|
||||
userList := users.Command("ls", "List all user accounts")
|
||||
|
||||
// delete user command
|
||||
userDelete := users.Command("del", "Deletes user accounts")
|
||||
userDelete.Arg("logins", "Comma-separated list of user logins to delete").
|
||||
Required().StringVar(&cmdUsers.login)
|
||||
|
||||
// add node command
|
||||
nodes := app.Command("nodes", "Issue invites for other nodes to join the 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").Default(defaults.ProvisioningTokenTTL.String()).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", "List all active SSH nodes within the cluster")
|
||||
nodeList.Flag("namespace", "Namespace of the nodes").Default(defaults.Namespace).StringVar(&cmdNodes.namespace)
|
||||
nodeList.Alias(ListNodesHelp)
|
||||
|
||||
// operations on invitation tokens
|
||||
tokens := app.Command("tokens", "List or revoke invitation tokens")
|
||||
tokenList := tokens.Command("ls", "List node and user invitation tokens")
|
||||
|
@ -184,18 +230,7 @@ func Run(distribution string) {
|
|||
err = cmdCreate.Create(client)
|
||||
case delete.FullCommand():
|
||||
err = cmdDelete.Delete(client)
|
||||
case userAdd.FullCommand():
|
||||
err = cmdUsers.Add(client)
|
||||
case userList.FullCommand():
|
||||
err = cmdUsers.List(client)
|
||||
case userUpdate.FullCommand():
|
||||
err = cmdUsers.Update(client)
|
||||
case userDelete.FullCommand():
|
||||
err = cmdUsers.Delete(client)
|
||||
case nodeAdd.FullCommand():
|
||||
err = cmdNodes.Invite(client)
|
||||
case nodeList.FullCommand():
|
||||
err = cmdNodes.ListActive(client)
|
||||
|
||||
case authExport.FullCommand():
|
||||
err = cmdAuth.ExportAuthorities(client)
|
||||
case tokenList.FullCommand():
|
||||
|
@ -256,7 +291,7 @@ func connectToAuthService(cfg *service.Config) (client *auth.TunClient, err erro
|
|||
|
||||
// applyConfig takes configuration values from the config file and applies
|
||||
// them to 'service.Config' object
|
||||
func applyConfig(ccf *CLIConfig, cfg *service.Config) error {
|
||||
func applyConfig(ccf *GlobalCLIFlags, cfg *service.Config) error {
|
||||
// load /etc/teleport.yaml and apply it's values:
|
||||
fileConf, err := config.ReadConfigFile(ccf.ConfigFile)
|
||||
if err != nil {
|
||||
|
|
|
@ -21,7 +21,7 @@ const (
|
|||
AddUserHelp = `Notes:
|
||||
|
||||
1. tctl will generate a signup token and give you a URL to share with a user.
|
||||
He will have to configure the mandatory 2nd facto auth and select a password.
|
||||
A user will have to complete account creation by visiting the URL.
|
||||
|
||||
2. A Teleport user account is not the same as a local UNIX users on SSH nodes.
|
||||
You must assign a list of allowed local users for every Teleport login.
|
||||
|
@ -30,7 +30,7 @@ Examples:
|
|||
|
||||
> tctl users add joe admin,nginx
|
||||
|
||||
This creates a Teleport identity 'joe' who can login as 'admin' or 'nginx'
|
||||
This creates a Teleport account 'joe' who can login as 'admin' or 'nginx'
|
||||
to any SSH node connected to this auth server.
|
||||
|
||||
> tctl users add joe
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gravitational/kingpin"
|
||||
"github.com/gravitational/teleport/lib/auth"
|
||||
"github.com/gravitational/teleport/lib/defaults"
|
||||
"github.com/gravitational/teleport/lib/service"
|
||||
|
@ -32,12 +33,58 @@ import (
|
|||
)
|
||||
|
||||
// UserCommand implements `tctl users` set of commands
|
||||
// It implements CLICommand interface
|
||||
type UserCommand struct {
|
||||
config *service.Config
|
||||
login string
|
||||
allowedLogins string
|
||||
roles string
|
||||
identities []string
|
||||
|
||||
userAdd *kingpin.CmdClause
|
||||
userUpdate *kingpin.CmdClause
|
||||
userList *kingpin.CmdClause
|
||||
userDelete *kingpin.CmdClause
|
||||
}
|
||||
|
||||
// Initialize allows UserCommand to plug itself into the CLI parser
|
||||
func (u *UserCommand) Initialize(app *kingpin.Application, config *service.Config) {
|
||||
u.config = config
|
||||
users := app.Command("users", "Manage user accounts")
|
||||
|
||||
u.userAdd = users.Command("add", "Generate a user invitation token")
|
||||
u.userAdd.Arg("account", "Teleport user account name").Required().StringVar(&u.login)
|
||||
u.userAdd.Arg("local-logins", "Local UNIX users this account can log in as [login]").
|
||||
Default("").StringVar(&u.allowedLogins)
|
||||
u.userAdd.Alias(AddUserHelp)
|
||||
|
||||
u.userUpdate = users.Command("update", "Update properties for existing user").Hidden()
|
||||
u.userUpdate.Arg("login", "Teleport user login").Required().StringVar(&u.login)
|
||||
u.userUpdate.Flag("set-roles", "Roles to assign to this user").
|
||||
Default("").StringVar(&u.roles)
|
||||
|
||||
u.userList = users.Command("ls", "List all user accounts")
|
||||
|
||||
u.userDelete = users.Command("rm", "Deletes user accounts").Alias("del")
|
||||
u.userDelete.Arg("logins", "Comma-separated list of user logins to delete").
|
||||
Required().StringVar(&u.login)
|
||||
}
|
||||
|
||||
// TryRun takes the CLI command as an argument (like "users add") and executes it.
|
||||
func (u *UserCommand) TryRun(cmd string, client *auth.TunClient) (match bool, err error) {
|
||||
switch cmd {
|
||||
case u.userAdd.FullCommand():
|
||||
err = u.Add(client)
|
||||
case u.userUpdate.FullCommand():
|
||||
err = u.Update(client)
|
||||
case u.userList.FullCommand():
|
||||
err = u.List(client)
|
||||
case u.userDelete.FullCommand():
|
||||
err = u.Delete(client)
|
||||
default:
|
||||
return false, nil
|
||||
}
|
||||
return true, trace.Wrap(err)
|
||||
}
|
||||
|
||||
// Add creates a new sign-up token and prints a token URL to stdout.
|
||||
|
|
Loading…
Reference in a new issue