rfd/0061-tsh-aliases.md
Proposes a way for users to define custom tsh commands.
Defining custom commands will allow users to extend tsh CLI to fit their
use-case without having to modify Teleport's source code.
Examples of what users might want to do include:
Define shorthands for the existing tsh commands.
Tweak the default behavior of existing tsh commands, for example to have
tsh login always default to a particular auth connector or username.
Define new subcommands combining multiple actions in the same command, for example logging into a cluster, a database and connecting to the database.
The simplest analogue that comes to mind is the bash alias:
alias ll="ls -l"
Bash aliases are quite simplistic as they only allow to define new commands that expand to the strings they alias and don't provide a way to refer to the command's arguments.
Git aliases are defined in the repo-specific .gitconfig (or .git/config)
or global /etc/gitconfig file, and are much closer conceptually to what we
want to accomplish as they allow to define custom git subcommands:
[alias]
co = checkout
ls = log --oneline
Git aliases also support calling non-git commands (using ! notation) and
refer to the command's arguments (via $@, $1, etc.) which allows to build
powerful new commands.
For example, the following alias will make git top to show 15 most recently
updated local branches:
[alias]
top = "!bash -c 'git branch --sort=-committerdate | head -15'"
Users will define aliases in their tsh configuration file which is kept
in $TELEPORT_HOME/config/config.yaml using the following syntax:
aliases:
"<alias>": "<command>"
The <alias> can only be a top-level subcommand. In other words, users can
define a new tsh mycommand alias but cannot define tsh my command command.
A few notes regarding the <command>:
git aliases explored above, proposal is to always require the binary
name instead of defaulting to tsh and requiring special syntax otherwise.<command> will be fed to exec.Command for execution. To chain multiple
commands, use pipes, etc. users will explicitly use bash -c.<command> will require $@, $1, etc. to reference the arguments
provided to the alias command.Starting with a very simple example, suppose you're tired of typing tsh login
every morning and want to alias it to something shorter e.g. tsh l:
aliases:
"l": "tsh login $@"
As a more practical example, the following alias will make tsh login default
to the specific auth connector and username:
aliases:
"login": "tsh login --auth=local --user=alice $@"
Similarly, as discussed in the search-based access requests RFD, some users
may want tsh ssh to default to the mode in which it will auto-request access
to the node upon encountering an access denied error:
aliases:
"ssh": "tsh ssh -P $@"
Note: command arguments will be resolved prior to invoking the alias. So in the
example above, when a user runs tsh ssh root@node1, the alias command executed
will be tsh ssh -P root@node1.
Each alias invocation will set a few environment variables that can be consumed within the alias.
To prevent infinite recursion, TSH_ALIAS variable will be set indicating the
command is invoked as an alias. If it's detected, tsh won't attempt to expand
the same command again. For example, TSH_ALIAS=login. This will prevent other
login commands from being expanded but will allow using aliases in other
aliases (see below for an example).
Each alias will also set TSH environment variable to the path of the tsh
binary that invoked the alias and SHELL environment variable to the default
shell of the user invoking the command.
An alias can also define a custom subcommand that combines multiple commands.
The following alias will connect to a node within a specific leaf cluster
using tsh connect leaf ubuntu@node-1 command:
aliases:
"connect": "bash -c '$TSH login $1 && $TSH ssh $2'"
The following alias will list nodes in all clusters:
aliases:
"lsall": "bash -c 'for cluster in $($TSH clusters | tail -3 | head -2 | cut -d \' \' -f1); $TSH ls --cluster=$cluster; done'"
Multiple aliases can be defined in the config and can reference each other:
aliases:
"login": "$TSH login --auth=local --user=alice $@"
"ssh": "$TSH ssh -P $@"
"connect": "bash -c '$TSH login $1 && $TSH ssh $2'"
In this example, tsh login and tsh ssh will use the aliases when invoked
as a part of the "connect" command.
Currently tsh config is read from $TELEPORT_HOME/config/config.yaml which by
default is user-specific since $TELEPORT_HOME defaults to ~/.tsh.
As a part of this feature, introduce global tsh config /etc/tsh.yaml. Settings
from the global config will merge with the user-local config, with the local
config taking precedence.
For easier troubleshooting of aliases, make sure that tsh logs the actual command being executed when invoking an alias.
When we have bash/zsh auto-completion, it should take aliased commands into
account as well. For reference, git autocompletion supports it.