toys

Tools and things that make my life easier - y'all might like them too

View the Project on GitHub pfuntner/toys

megassh

Purpose

A tool that performs ssh to one or more target systems. Key features:

Syntax

Syntax: megassh [-h] [-b] [-j] [-v] hosts cmd [arg [arg ...]]

Arguments

| Option | Description | |——–|——————————————————————————————————————-| | hosts | A comma-separated list of hostnames. This can be all and megassh gets the list of hosts from ~/.ssh/coonfig | | cmd [arg [arg ...]] | Any command arguments. This can be in the form of a complicated pipeline or multiple commands separated by semicolons |

Options

| Option | Description | Default | |—————|—————————————————————————————————————————-|——————————————————————————————————–| | -h --help | Show online help | Online Help is not provided | | -b --become | Use sudo to escalate to superuser on remote system | There is no escalation | | -j --json | Produce JSON output | Output is printed in a simple format | | -v --verbose | Enable verbose debugging | Debugging is not enabled |

Examples

Multiple targets

It’s easy to run a command across multiple systems.

$ megassh vm1,vm2 hostname \; grep PRETTY_NAME /etc/os-release
Host: vm1
vm1
PRETTY_NAME="Ubuntu 22.04.3 LTS"

Host: vm2
vm2
PRETTY_NAME="AlmaLinux 9.3 (Shamrock Pampas Cat)"
$ 

JSON output

Sometimes the default output can be rather confusing. JSON can be a better alternative and the output is machine readable.

$ megassh -j all ls / \| head
[
  {
    "host": "vm1",
    "stdout": [
      "bin",
      "boot",
      "cdrom",
      "dev",
      "etc",
      "home",
      "lib",
      "lib32",
      "lib64",
      "libx32"
    ],
    "stderr": [],
    "rc": 0,
    "elapsed": "0:00:01.201967",
    "hostname": "localhost",
    "user": "mrbruno",
    "port": "3022"
  },
  {
    "host": "vm2",
    "stdout": [
      "afs",
      "bin",
      "boot",
      "dev",
      "etc",
      "home",
      "lib",
      "lib64",
      "media",
      "mnt"
    ],
    "stderr": [],
    "rc": 0,
    "elapsed": "0:00:00.801365",
    "hostname": "localhost",
    "user": "mrbruno",
    "port": "4022"
  }
]
$  

I escaped the pipe symbol to have it run head in the target system - I didn’t want it run locally on the output of megassh itself! head is run remotely as the last part of the pipeline.

If your hosts are present in ~/.ssh/config, all of the information about that host in the config file (such as user or port) is included.

Becoming root

Escalation to superuser isn’t especially difficult but the trick is to run everything in the context of the root user and prevent the initial shell interpret commands, variables, etc before escalation. When you use the -b option, megassh will automatically perform base64 encoding while the command is in transit. The encoding isn’t decoded until after escalation! This avoids the complication of escaping metacharacters - and potentially escaping the escapes!! What a nightmare!

$ megassh all 'echo $USER sees $(find / 2>/dev/null | wc -l) files'
Host: vm1
mrbruno sees 489411 files

Host: vm2
mrbruno sees 215062 files
$ megassh -b all 'echo $USER sees $(find / 2>/dev/null | wc -l) files'
Host: vm1
root sees 563058 files

Host: vm2
root sees 273445 files
$ 

Slick, eh? All I had to do was slip in the -b option - I didn’t touch the rest of the command!

Notes