How To
(require '[clojuressh.core :as clojuressh])
Exit the clojure mainline cleanly
If there are any ssh connections open apon program termination, the JVM will not exit. Make exit clean by closing sessions after use:
(let [session (clojuressh/ssh "remotehost" {:username "remoteusername"})]
(try
;; do things with session here
(finally
(clojuressh.session/disconnect session))))
(shutdown-agents)
If you do not care about elegance and just want to force a shutdown, use System/exit. This will close all connections and exit the JVM.
(let [session (clojuressh/ssh "remotehost" {:username "remoteusername"})]
;; do things here
;; dont close
)
(System/exit 0) ;; closes all connections and exits
Debug the ssh connection process
Register an error reporting function with clojuressh.agent/set-debug-fn before initiating the connection
(agent/set-debug-fn
(fn [_level message]
(binding [*out* *err*]
(println message))))
Then when you connect you should see verbose error messages appear.
Connect to a machine with a hard coded password
You can hard code a password in the options hash.
Note: This is not recommended. You may accidentally commit your code to a repository with the password or inadvertantly expose it.
(clojuressh/ssh "remotehost"
{:username "remoteusername"
:port 22
:password "the-password"})]
Connect to a machine with an encrypted private key and a hard coded passphrase
You can hard code the passphrase in the options hash.
Note: This is not recommended. You may accidentally commit your code to a repository with the passphrase or inadvertantly expose it.
(clojuressh/ssh "remotehost"
{:username "remoteusername"
:port 22
:identity (str (System/getenv "HOME") "/.ssh/id_rsa")
:passphrase "the-key-passphrase"})
Turn off strict host key checking
(clojuressh/ssh "remotehost"
{:strict-host-key-checking false})
Accept a key and add it to known hosts without complaint only on first connection
(clojuressh/ssh "remotehost"
{:accept-host-key :new})
Accept a key if it matches a fingerprint
(clojuressh/ssh "remotehost"
{:accept-host-key "SHA256:/tCQlmGVCXhwqJFq3h5aiEqD1UlUD9Eg5bDwd5yF52k"})
Allow connection to a legacy server that only supports RSA/SHA1 signatures
(clojuressh/ssh "remotehost"
{:connection-options
{:server-host-key #(str % ",ssh-rsa")
:client-pubkey #(str % ",ssh-rsa")}})
Connect to a dropbear ssh server
A very old server:
(clojuressh/ssh "remotehost"
{:connection-options
{:kex "diffie-hellman-group1-sha1"})]
Or perhaps:
(clojuressh/ssh "remotehost"
{:connection-options
{:cipher "aes128-cbc"})]
Execute a remote ssh command using authentication forwarding
(-> (clojuressh/ssh "remotehost")
(clojuressh/exec "ssh -o StrictHostKeyChecking=no git@github.com"
{:err :string
:agent-forwarding true})
deref
:err
clojure.string/split-lines
second)
;; => "Hi retrogradeorbit! You've successfully authenticated, but GitHub does not provide shell access.\n"
Allocate a pseudo terminal for the remote shell
(-> (clojuressh/ssh "remotehost")
(clojuressh/exec "tty"
{:out :string
:pty true})
deref
:out)
;; => "/dev/pts/72\r\n"
Prevent the native access warning
If you are on JDK 22+, as your terminal gets put into raw mode (for a password or passphrase prompt), you will see a native access warning. It will probably look like this:
Enter Password for crispin@localhost: WARNING: A restricted method in java.lang.System has been called
WARNING: java.lang.System::load has been called by com.sun.jna.Native in an unnamed module (file:/home/crispin/.m2/repository/net/java/dev/jna/jna/5.14.0/jna-5.14.0.jar)
WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
WARNING: Restricted methods will be blocked in a future release unless native access is enabled
You either print this warning early by calling clojuressh.terminal/get-width
(clojuressh.terminal/get-width) ;; prints native access warning when first called
You can silence the message completely by starting the JVM with --enable-native-access=ALL-UNNAMED. Pick whichever of these matches your launcher:
deps.edn — add to the alias (or top-level) that runs your app:
{:aliases
{:run {:main-opts ["-m" "my.app"]
:jvm-opts ["--enable-native-access=ALL-UNNAMED"]}}}
Leiningen project.clj:
:jvm-opts ["--enable-native-access=ALL-UNNAMED"]
Plain java (e.g. running an uberjar):
java --enable-native-access=ALL-UNNAMED -jar my-app.jar
Environment variable (applies to every JVM spawned in the shell):
export JDK_JAVA_OPTIONS=--enable-native-access=ALL-UNNAMED
No flag is required on JDK 21 and earlier. If you hit an IllegalCallerException about native access on a JDK where the warning has become a hard error, clojuressh will rethrow it wrapped in an ex-info pointing you at this section.