Readit News logoReadit News
Posted by u/blackmamoth 3 months ago
Show HN: Sshsync – CLI tool to run shell commands across multiple remote serversgithub.com/Blackmamoth/ss...
I built a CLI tool called `sshsync` to run shell commands and transfer files across multiple servers over SSH concurrently.

It was inspired by tools like `pssh`, but I wanted something more modern, intuitive, and Pythonic.

What it does:

- Run shell commands on multiple servers (in parallel) - Push/pull files or directories with progress bars - Uses `~/.ssh/config` and lets you group hosts with YAML - Supports `--dry-run` mode to preview actions without executing - Outputs results using `rich` (tables, colors) - Built with `Typer`, `asyncssh`, and `rich`

There’s no daemon or extra setup, it reads your existing SSH config and just runs.

Would love feedback on general use and especially if there are ways to improve the `--dry-run` output.

jasongill · 3 months ago
In a past life, this is an interview question that I would ask people: "We have thousands of servers, and you need to run the same command on 10 of them, what's one way you would do it?" and follow up with "What if you wanted to run the command on hundreds or thousands - what problems would you expect with this approach, and what might you do differently?"

I didn't really expect them to write code on the spot (hate that in interviews), but just to describe a possible solution; there were no wrong answers. Seeing how people came up with a quick hack for 10 and then being able to evolve their thinking for thousands was the point of the question and it could be enlightening to see.

I had a lot of people come up with things that I never even thought of, such as SSH'ing in to all 10 machines and then using a terminal feature to type into all of the windows at once.

farka01 · 3 months ago
That's a great question you used in interviews! I'm curious – how would you personally approach this situation? What would your solution be for running a command on 10 servers, and how would you scale it to thousands?
jasongill · 3 months ago
For a handful of servers I would just do a for loop like `for i in {01..10};do echo "command" | ssh -T server$i.example.com;done` because it was quick and dirty and worked fine for something quick, but obviously it doesn't really ensure a common state or handle errors at all (but I still used a for loop like that many times a day for quick stuff like "I wonder what size a specific file is on each server" or "let me quickly grep a config file across a range of servers because I forget which one this thing is running on").

For more than that, I used Puppet at the time (this was a decade and a half ago); I was a contributor to Puppet and standardized on it in my company. Eventually we moved to Ansible and I sold that business but last I heard, they are still using Ansible and likely using playbooks that were ported over from my Puppet stuff

blackmamoth · 3 months ago
Are you still asking this question in an interview? I know a thing or two about sshing into servers, I think I'd be a good enough candidate
jasongill · 3 months ago
Unfortunately (or, fortunately) no - this was a lifetime ago when I owned a large hosting company
Alifatisk · 3 months ago
Is there a standard way of doing this that comes shipped with GNU/Linux?
yjftsjthsd-h · 3 months ago
I dunno about standard, but it's been done a bunch.

* As sibling notes, there's ansible (or chef/puppet/salt/...)

* The traditional solution was https://github.com/duncs/clusterssh which opens an xterm to each target plus a window that you type into to mirror input to all of them

* I do the same-ish in tmux with

  bind-key a set-window-option synchronize-panes
and I expect screen and such have equivalent features

* Likewise, there are terminal emulators that let you do splits and then select a menu option to mirror input to all of them

olddustytrail · 3 months ago
I'd just use Ansible for that.
nasretdinov · 3 months ago
Around 10 years ago I was supporting infrastructure at a PHP shop and we needed a similar thing, but for ~3000 servers, and the library that we were using (libpssh) didn't support async SSH agent authentication, so I built this small tool in Go to allow to implement such tooling in any language (PHP, Python, whatever) in a simple way: https://github.com/YuriyNasretdinov/GoSSHa

It's main advantage is that it allows you to do SSH agent forwarding that actually works at scale, since it limits concurrency when talking to SSH agent to a configurable amount (by default 128, the default connection backlog in OpenSSH ssh-agent)

blackmamoth · 3 months ago
Hey man that's really cool, I never really thought of making this interactive.
proxysna · 3 months ago
ansible my_servers -m shell -a 'fortune && reboot' -b

I know it is easy to be a hater, but sincerely do not see a reason to use something like that over Ansible or just pure sh, ssh and scp. All you have to do is to set up keys and the inventory. Takes 10 minutes, even if you are doing it for the first time. And you can expand it if you need it.

alerighi · 3 months ago
I use pssh often (not this tool, but as I understand is similar).

The reasons I find it over Ansible are:

- takes the same syntax and options as plain SSH, just run over multiple hosts. So if you already know SSH, you know how to use pssh that is an extension of the command. Ansible requires to study it. The configuration format is trivial, just a file that contains on each line one host, no need to study complex formats like Ansible

- doesn't require dependencies on the target machine. Ansible, as far as I know, requires a python3 installation on the target machine. Something that, for example, is not granted in all settings (e.g. embedded devices, that are not strictly GNU/Linux machines, for example consider a lot of network devices that espose an SSH server, like Microtik devices, with PSSH is possible to configure them in batch), or in some settings you maybe need to work on legacy machines that have an outdated python version.

- sometimes simpler tool that just do one thing are just better. Especially for tools like pssh that are to me like a swiss army knife, the kind of tool that you use obviously when you are bodging something up to make something work because you are in an hurry and saves your day

Of course if you already use Ansible to manage your infrastructure you may as well use it to run a simple command. But if you have to run a command on some devices, that were not previously setup for Ansible, and devices trough which you may not have a lot of control (e.g. a bunch of embedded devices of some sort), pssh is a tool that can come handy.

pug23 · 3 months ago
For clarity Ansible does allow to run commands on a target host without Python installed using the raw module

I do agree with your point, sometimes it's just easier to use native tools or simple wrappers around native tools. Use whatever makes your job easier

liamkearney · 3 months ago
Ansible is one of the best examples of needless complexity I’ve ever interacted with.
proxysna · 3 months ago
Ansible is the easiest tool for configuration management to onboard and start using. Great documentation, large community. It is as complex as you want it to be and it's complexity scales with your infra. ofc YMMV.
blackmamoth · 3 months ago
I know ansible or even custom shell scripts are way better and optimized for such use cases. However, I just wanted to show something I built that might be useful to someone.
proxysna · 3 months ago
My comparison is most likely unfair because i am looking at it through a distorted lens of running all sorts of configuration management in production or at home for years. So i might be the wrong person to make judgement on it and just being a hater for no good reason.
cynicalsecurity · 3 months ago
Ansible requires python to be installed on all of the target computers.
hoherd · 3 months ago
That's not necessarily true. There is the raw module that executes a bare command. https://docs.ansible.com/ansible/latest/collections/ansible/...
mmsc · 3 months ago
are there any linux distributions which don't require python installed, other than embedded?
revskill · 3 months ago
Ansible doesn't work on windows.

Stop assuming your method works across the universe of edge cases.

proxysna · 3 months ago
Ansible server does not work on windows, yes, but you can configure windows hosts with Ansible out of the box.
KAMSPioneer · 3 months ago
I mean, Ansible isn't the best choice for Windows configuration, I would agree, but you're not strictly correct: https://docs.ansible.com/ansible/latest/os_guide/windows_usa...
gamedna · 3 months ago
Curious to understand the need to create this over using tools like pssh, etc.

https://linux.die.net/man/1/pssh

blackmamoth · 3 months ago
I was getting bored, this seemed like a cool project to work on outside of work, that's why. One of my colleagues found it useful for his needs, so I figured there might be other people who'd find this useful too.

Deleted Comment

Joker_vD · 3 months ago
Looks interesting; my klunky script for doing something similar has been something along the lines of

    printf 'started: %s\n' "$(utcdate)"

    (
        trap 'kill 0' SIGINT
        for REMOTE in "${REMOTES[@]}"
        do
            ssh -- "$REMOTE" "$COMMAND" "$@" &
        done
        wait
    )

    printf 'ended: %s\n' "$(utcdate)"
but twiddling with it has been quite annoying, so I'll look into this tool.

mkayokay · 3 months ago
How are commands handled, that require user input? E.g. password for sudo in your example:

  sshsync group web-servers "sudo systemctl restart nginx"
I like that you included a demo in the README, but it is too long for a gif, as I can't pause/rewind/forward. So splitting into multiple short gifs or converting into a video (if GitHub supports them) could improve the experience.

blackmamoth · 3 months ago
As of now there is no way to take user input in transit, so either the user is required to have the privilege to execute the specified command or have passwordless sudo available.

And Yeah, now that you've mentioned it multiple shorter gifs would be better.

mrbluecoat · 3 months ago
Or https://asciinema.org/ instead of gifs
monster_truck · 3 months ago
Have you looked at what powershell does? Invoke-Command (and the Job stuff it meshes perfectly with via AsJob) is really nice

I only needed a very small fraction of what it can do to bail a client out of a problem their customer caused on several hundred computers the night before an event, but it absolutely saved the day and a lot of money.

blackmamoth · 3 months ago
Haven't really used powershell for my tasks and I'm not as experienced as you are, but what you said sounds absolutely cool, I'll check it out
igetspam · 3 months ago
I’m surprised no one has mentioned pdsh yet. Piped to dshbak and output was grouped by response. I’d probably use a config management tool for anything more than simple commands now but that tool was indispensable for managing our fleet, when we used to actually connect to machines.
lcall · 3 months ago
Ditto. I posted (here: https://news.ycombinator.com/item?id=42881123 ) about them and other options:

To send the same command to multiple servers, use pdsh: https://linux.die.net/man/1/pdsh

To collect all the results and show which ones are the same or different, use dshbak (i.e., "pdsh <parameters including servers>|dshbak"): https://linux.die.net/man/1/dshbak

Similar things, sometimes more convenient but less efficient for a large number of servers, are to use the konsole terminal program and link multiple window tabs together so the same typed command goes to all, and quickly view the results across the tabs; or to use tmux and send the same commands to multiple windows (possible useful "man tmux" page terms: link-window, pipe-pane, related things to those, activity, focus, hooks, control mode).

And others that I haven't used but which also look possibly interesting for platforms where pdsh and dshbak might not be available (like OpenBSD at least):

- https://github.com/duncs/clusterssh/wiki (available on OpenBSD as a package)

- https://www.gnu.org/software/parallel/ (also available as a package on OpenBSD 7.6: named "parallel-20221122"; might relate to "pdksh")

- Also clusterit.