Packages

Remote task runner using Erlang distribution over SSH. Push modules and execute functions on remote machines without pre-installing your application. Zero dependencies.

Current section

Files

Jump to
fusion lib fusion utilities ssh.ex
Raw

lib/fusion/utilities/ssh.ex

defmodule Fusion.Utilities.Ssh do
@moduledoc """
SSH command string generation.
## SSH command options used:
* -p: Port to connect to on the remote host.
* -n: Prevent reading from stdin.
* -N: Do not execute a remote command (just forward ports).
* -T: Disable pseudo-tty allocation.
* -R: Reverse tunnel - bind remote port to local host:port.
* -L: Forward tunnel - bind local port to remote host:port.
* -4: Force IPv4.
"""
alias Fusion.Net.Spot
alias Fusion.Utilities.Bash
@default_ssh_path "/usr/bin/env ssh"
@default_sshpass_path "/usr/bin/env sshpass"
@default_ssh_opts "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR"
@doc """
Generate sshpass prefix for password authentication.
Uses `-e` flag to read password from SSHPASS environment variable,
avoiding shell injection and password exposure in `ps` output.
## Examples
iex> Fusion.Utilities.Ssh.partial_cmd_sshpass("/usr/bin/sshpass")
"/usr/bin/sshpass -e"
"""
def partial_cmd_sshpass(sshpass_path \\ @default_sshpass_path) do
"#{sshpass_path} -e"
end
@doc """
Generate reverse tunnel SSH flags.
## Examples
iex> Fusion.Utilities.Ssh.partial_cmd_reverse_tunnel(9004, %Fusion.Net.Spot{host: "localhost", port: 8003})
"-nNT -R 9004:localhost:8003"
"""
def partial_cmd_reverse_tunnel(from_port, %Spot{} = to_spot) do
"-nNT -R #{from_port}:#{to_spot.host}:#{to_spot.port}"
end
@doc """
Generate forward tunnel SSH flags.
## Examples
iex> Fusion.Utilities.Ssh.partial_cmd_forward_tunnel(9004, %Fusion.Net.Spot{host: "localhost", port: 8003})
"-nNT -4 -L 9004:localhost:8003"
"""
def partial_cmd_forward_tunnel(from_port, %Spot{} = to_spot) do
"-nNT -4 -L #{from_port}:#{to_spot.host}:#{to_spot.port}"
end
@doc """
Generate a full SSH command string.
For password auth, the caller must set the `SSHPASS` environment variable
before executing the returned command.
## Examples
iex> Fusion.Utilities.Ssh.cmd("-nNT -R 3001:localhost:3002", %{username: "john", password: "abcd1234"},
...> %Fusion.Net.Spot{host: "example.com", port: 22})
"/usr/bin/env sshpass -e /usr/bin/env ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR -p 22 -nNT -R 3001:localhost:3002 john@example.com"
iex> Fusion.Utilities.Ssh.cmd("-nNT -R 3001:localhost:3002", %{username: "john", key_path: "/home/john/.ssh/id_rsa"},
...> %Fusion.Net.Spot{host: "example.com", port: 22})
~s(/usr/bin/env ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR -p 22 -i "/home/john/.ssh/id_rsa" -nNT -R 3001:localhost:3002 john@example.com)
"""
def cmd(cmd, auth, remote, ssh_path \\ @default_ssh_path)
def cmd(cmd, %{username: username, password: _password}, %Spot{} = remote, ssh_path) do
"#{partial_cmd_sshpass()} #{ssh_path} #{@default_ssh_opts} -p #{remote.port} #{cmd} #{username}@#{remote.host}"
end
def cmd(cmd, %{username: username, key_path: key_path}, %Spot{} = remote, ssh_path) do
"#{ssh_path} #{@default_ssh_opts} -p #{remote.port} -i \"#{Bash.escape_str(key_path)}\" #{cmd} #{username}@#{remote.host}"
end
@doc "Generate an SSH command to execute a remote command."
def cmd_remote(remote_cmd, auth, remote) do
cmd("", auth, remote) <> " " <> "\"#{Bash.escape_str(remote_cmd)}\""
end
@doc """
Generate a full SSH port tunnel command.
## Examples
iex> Fusion.Utilities.Ssh.cmd_port_tunnel(%{username: "john", password: "abcd1234"},
...> %Fusion.Net.Spot{host: "example.com", port: 22},
...> 4567,
...> %Fusion.Net.Spot{host: "localhost", port: 2345},
...> :reverse)
"/usr/bin/env sshpass -e /usr/bin/env ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR -p 22 -nNT -R 4567:localhost:2345 john@example.com"
iex> Fusion.Utilities.Ssh.cmd_port_tunnel(%{username: "john", password: "abcd1234"},
...> %Fusion.Net.Spot{host: "example.com", port: 22},
...> 4567,
...> %Fusion.Net.Spot{host: "localhost", port: 2345},
...> :forward)
"/usr/bin/env sshpass -e /usr/bin/env ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR -p 22 -nNT -4 -L 4567:localhost:2345 john@example.com"
"""
def cmd_port_tunnel(auth, %Spot{} = remote, from_port, %Spot{} = to_spot, :reverse) do
partial_cmd_reverse_tunnel(from_port, to_spot)
|> cmd(auth, remote)
end
def cmd_port_tunnel(auth, %Spot{} = remote, from_port, %Spot{} = to_spot, :forward) do
partial_cmd_forward_tunnel(from_port, to_spot)
|> cmd(auth, remote)
end
end