Skip to content

Instantly share code, notes, and snippets.

@luizberti
Last active July 23, 2024 17:31
Show Gist options
  • Save luizberti/bc9dd3b17508d8fdf197d2a3a69ebf2d to your computer and use it in GitHub Desktop.
Save luizberti/bc9dd3b17508d8fdf197d2a3a69ebf2d to your computer and use it in GitHub Desktop.
My stash of useful and perhaps over-engineered POSIX tricks
#!/usr/bin/env bash
set -o errexit
set -o pipefail
set -o nounset
if [ "$EUID" = 0 ]; then echo "DO NOT RUN THIS AS ROOT!" >&2 && exit 1; fi
# GLOBAL PARAMETERS
user='luizberti' # sometimes used when $USER would yield incorrect results
# USER AND SSH KEY MANAGEMENT
# ===========================
# MAKE USER A PASSWORDLESS SUDOER FOR NON-INTERACTIVE USE (LINUX ONLY)
# 1. You need to set the `user` variable to the user this operation should take effect on;
# 2. This operation needs to be performed by a user who is already a non-interactive sudoer,
# or by the `root` user itself by stripping the `sudo` from the second command;
# 3. Provided the conditions from nº 2 are satisfied, this command runs non-interactively;
# 4. `visudo` validates that the edit hasn't corrupted the sudoers file, as it's dangerous
# to edit the file without it. If the edit fails validation, this command will error;
# 5. Setting $EDITOR to `tee -a` causes `visudo` to open it, rather than an interactive
# editor to modify the sudoers file. `tee -a` will slurp STDIN appending to sudoers
# non-interactively and then exiting.
user_promote-to-passwordless-sudoer() (
# ENV PARAM user: the user the operation should be performed on
set -o errexit
set -o pipefail
set -o nounset
# TODO add to `wheel` group instead of granting passwordless sudoing directly?
echo "$user ALL=(ALL) NOPASSWD:ALL" | sudo EDITOR='tee -a' visudo > /dev/null
)
# IDEMPOTENTLY CREATE NEW PASSWORD-LESS SSH KEY
# 1. Most non-interactivity is provided by `-q -N ''`, which enables quiet mode and sets
# the password to an empty-string, which disables the password prompt;
# 2. Security parameter is set by `-a 100`, which at this time seems like a good margin;
# 3. Stores key in new format with `-o`, only supported by OpenSSH 6.5+. This is already
# the default for Ed25519 keys, but this makes it explicit, and allows an easier
# adaptation of this command to produce RSA keys;
# 4. Key type is set to Ed25519 instead of RSA with the `-t ed25519` option;
# 5. The output path for generated keys is specified with `-f <path>`. Private key is
# written to `<path>`, public is written to `<path>.pub`;
# 6. Key comment is set with `-C <comment>`. I generally use either `luizberti`, `<hostname>`
# when the key is bound to a specific computer, `luizberti@<hostname>` when the key is
# bound to a shared computer, or work email for corporate infrastructure;
# 7. The `< /dev/zero` makes that file STDIN for the process. This answers a "no" to the
# "overwrite?" prompt that shows up if the `-f` argument already exists, making the command
# more idempotent, and bypassing that interaction for better non-interactivity;
# 8. The tailing `&>/dev/null` shushes the rest of the outputs that haven't been turned off
# by the "quiet" option, such as the prompt from nº 7;
# 9. The `ssh-add` part ensures the identity is added to ssh-agent for Agent Forwarding and
# other features to work properly. On macOS it needs to use the `-K` flag so that the identity
# is added to the user's keychain otherwise it won't persist across reboots, while on Linux
# it's just added to the ssh-agent directly.
ssh_keygen() (
# ENV PARAM name: choose name for key as `id_ed25519.<name>[.pub], if empty defaults to `id_ed25519[.pub]`
# ENV PARAM comment: set the key's comment, if unset defaults to $USER
set -o errexit
set -o nounset
local file="$(if [ -z "${name:-}" ]; then echo id_ed25519; else echo id_ed25519."$name"; fi)"
ssh-keygen -q -N '' -o -a 100 -t ed25519 -f ~/.ssh/"$file" -C "${comment:=$USER}" </dev/zero &>/dev/null
if [ "$(uname -s)" = Darwin ]; then ssh-add --apple-use-keychain ~/.ssh/"$file"; else ssh-add ~/.ssh/"$file"; fi
)
# SETS CORRECT PERMISSIONS FOR SSH KEYS
# 1. This command takes effect on the SSH directory relative to the calling user, thus you should
# not be in a rooted shell for this, else it will point at the wrong directories and give the
# wrong $USER the permissions;
# 2. You might need to customize the fourth command if you have private keys that aren't named
# as `id_*`. A good naming scheme for keys is `id_<algorithm>.<purpose>[.pub]`, and you should
# try using that or something like it if possible (e.g. id_ed25519.build-server[.pub]);
# 3. The `touch` command ensures that the configuration and access management files are present,
# so that they can pick up the correct permissions from the subsequent commands;
# 4. The `chown` command ensures that the current user's SSH directory and its contents are owned
# by that user;
# 5. The `chmod` commands modify its permissions relative to the owner set by `chown`. 700 on a
# directory allows only the owner to write to the directory, 600 allows only the owner to read
# and write to them, 644 allows only the owner to write but makes it globally readable
ssh_fix-permissions() (
set -o errexit
sudo mkdir -p ~/.ssh
sudo touch ~/.ssh/{authorized_keys,known_hosts,config}
sudo chown $USER:$USER ~/.ssh ~/.ssh/*
sudo chmod 700 ~/.ssh
sudo chmod 600 ~/.ssh/id_* # could arguably be ~/.ssh/* but let's be conservative
sudo chmod 644 ~/.ssh/*.pub
sudo chmod 644 ~/.ssh/{authorized_keys,known_hosts,config}
)
# SUBCOMMAND INVOKATION LOGIC AND FLAG PARSING
# ============================================
AVAILABLE="$(declare -F | awk '{print $NF}')"
"$1" "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment