--- date: 2024-03-27 title: Shell Customizations and SSH tags: - homelab - linux - bash - development --- Taking a detour from actual deployments, I recently started experimenting with different shell customizations. Until now, I've just used whatever default shell I have available on a given system, but a recent live stream from [Lawrence Systems](https://www.youtube.com/@LAWRENCESYSTEMS) mentioned shell customizations and tmux and lead me down this rabbithole. I'll also mention SSH configuration as its something that greatly helps my daily productivity and is tangentially related to some of my customizations. ## tmux I only recently tried using tmux and I don't know why I went so long without it! The [tmux wiki](https://github.com/tmux/tmux/wiki) offers a complete explanation of what tmux is and all of the things it can do; in short, it manages processes and terminals. `tmux` has a pretty steep learning curve, but it only took me a day to get the hang of a few basic shortcuts; `ctrl`+`b`, or the "prefix key" enables interacting with `tmux` and then a command can be issued. Some of the commands I find most useful are: - `?` - Shows a list of tmux key bindings - `"` - Splits the window vertically and adds a pane - `up`/`down` - Navigates between vertical panes; if you hold `ctrl`+`b` then it moves the split up and down - `d` - Detaches the temux session; easy to remember since `ctrl`+`d` is how you detach an SSH session I'm not (yet) using tmux by default for local sessions, but I do have it enabled to run when I SSH into some systems; this is the primary reason I really like `tmux`. I can ssh into my server from my laptop and start a command (i.e.a very long rsync process) without worrying about disowning the process before my laptop goes to sleep; when I ssh back in, perhaps from another computer, I have the same terminal history and the same process attached. I will go into more detail in the "ssh" section of this post, but all I had to do is run `tmux new -A -s ssh_tmux` when I connect to attach a tmux session named `ssh_tmux`, creating it if it doesn't exist. ## ssh For SSH keys, I have one key pair I use for personal systems and another for work. I won't go into detail about key management or key rotation and instead will focus on SSH configuration. My SSH config simply contains global config options and includes other configuration files for specific hosts. I find this makes it easier to manage hosts since I can group hosts in different files. My `~/.ssh/config` looks like: ```text Include config.d/* Host * AddKeysToAgent yes IdentitiesOnly yes ``` - `AddKeysToAgent` adds keys to my ssh agent so I don't need to enter a passcode after the first time I use a key - `IdentitiesOnly` prevents SSH from trying to infer an identity file to use if it isn't specified in SSH config or explicitly supplied as an argument. > You can find descriptions of all the SSH config options in the [BSD manpages](https://man.openbsd.org/ssh_config). A couple example hosts look like: ```text Host work.server HostName User ┌──[22:27]─[d_mcknight@MCKNIGHT-FW13]─(~) $ And a snippet from .bashrc related to that: ```shell color_off="\[\033[0m\]" # Text Reset # Regular Colors black="\[\033[0;30m\]" red="\[\033[0;31m\]" green="\[\033[0;32m\]" yellow="\[\033[0;33m\]" blue="\[\033[0;34m\]" purple="\[\033[0;35m\]" cyan="\[\033[0;36m\]" white="\[\033[0;37m\]" path_color=$blue chrome_color=$purple context_color=$cyan prompt_symbol=@ # 🚀💲 prompt='\$' if [ "$EUID" -eq 0 ]; then # Change prompt colors for root user context_color=$red prompt_symbol=💀 fi PROMPT_COMMAND='if [[ $? != 0 && $? != 130 ]];then echo "⚠️";fi' PS1="$chrome_color┌──[${context_color}\A${chrome_color}]─"'${debian_chroot:+('${path_color}'$debian_chroot'${chrome_color}')─}${VIRTUAL_ENV:+('${path_color}'$(realpath $VIRTUAL_ENV --relative-to $PWD --relative-base /home)'${chrome_color}')─}'"[${context_color}\u${chrome_color}${prompt_symbol}${context_color}\h${chrome_color}]─(${path_color}\w${chrome_color})\n${chrome_color}└${context_color}${prompt}${color_off} " PS2=$chrome_color'└>$color_off ' export VIRTUAL_ENV_DISABLE_PROMPT=1 ``` There's a lot going on in the prompt, but I will highlight pieces of it; keep in mind that the snippets below may not be valid on their own and that some of the color formatting may be lost as I cut up the long prompt string. - `PROMPT_COMMAND='if [[ $? != 0 && $? != 130 ]];then echo "⚠️";fi'` isn't part of the prompt string, but rather an expression evaluated before the prompt string. This renders a ⚠️ if the previous command fails and isn't just an empty command. - `[${context_color}\A${chrome_color}]` prints the current time in 24H format. Helpful if you open a terminal and want to quickly reference when you ran the last command and when it completed. - `${debian_chroot:+('${path_color}'$debian_chroot'${chrome_color}')─}` and `${VIRTUAL_ENV:+('${path_color}'$(realpath $VIRTUAL_ENV --relative-to $PWD --relative-base /home'${chrome_color}')─})` print an active chroot or Python venv path, if active. I print the venv path relative to the current directory if within `home`; I find this helpful when I have multiple projects to make sure I know which one I'm looking at. Note that I also specify `export VIRTUAL_ENV_DISABLE_PROMPT=1` to suppress the default `(venv)` prompt prefix. - `[${context_color}\u${chrome_color}${prompt_symbol}${context_color}\h${chrome_color}]` looks like the default shell, `user@hostname` except when root the `@` is replaced with `💀` as an extra visual reminder that its a root shell. - `(${path_color}\w${chrome_color})` shows the current shell path like the default shell - `\n${chrome_color}└${context_color}${prompt}${color_off} ` continues to the next line and inserts the `$` or `#` prompt, followed by a space. - `PS2=$chrome_color'└>$color_off '` updates the prefix used when there's a newline in a command. I like that this makes each line start vertically aligned and it keeps the chrome colored differently from the inputs. ## Further Reading SSH, BASH, and tmux are all over a decade old with plenty of good documentation and write-ups that I've referenced. My current `.bashrc` is in [this gist](https://gist.github.com/d-mcknight/176899ca924b5b4cfdf7692e36ca568e) and I will try to keep that updated as I make changes; maybe one day I'll promote it to an actual repository with other config files.