Archive

Posts Tagged ‘ssh’

ssh-argv0: for the lazzy ‘ssh’ typer

June 9th, 2009 1 comment

Those of us who often use often ssh to login or start remote commands will find this useful:

$ ln -s /usr/bin/ssh-argv ~/bin/$HOST
$ $HOST

You can use the FQDN of the host of course, but a short alias is even more shorter. Put this in your .ssh/config and use the alias for the link:

Host $FQDN $ALIAS
  HostName $FQDN

And yes, you want to put both $FQDN and $ALIAS in the Host line. If you ever add more options to this Host entry you want to apply it to both $FQDN and $ALIAS. If you omit $FQDN the options will only apply to the $ALIAS.

Categories: linux, software Tags:

Connection sharing with OpenSSH – 3rd Update

May 26th, 2009 No comments

Want to speed up your svn/cvs operations? Want fast path completions for scp commands? Than you should enable the connection sharing feature from OpenSSH.

The essence is, that your first connection to a remote host acts as a master connection. All subsequent connection attempts will use the master connection to re-use the connection of the master.

What you save is the repeated authentication procedure, especially if you use passwd authentication.

First you need to create a directory where all master connections create a socket so that other clients can find already established connections and communicate with the master.

$ mkdir -p ~/.ssh/masters
$ chmod 0700 ~/.ssh/masters

Next we need to enable the feature in your ~/.ssh/config:

ControlMaster auto
ControlPath "~/.ssh/masters/%r@%h:%p"

The ControlPath is a format string where %r is the remote user name, %h the remote host, and %p the port. If your ~/.ssh directory is on a mounted share, you should also include a %l for the local host. So that you can connect to a remote from multiple hosts.

ControlPath "~/.ssh/masters/%l -> %r@%h:%p"

It would be nice to put these into separate directories, but OpenSSH does not create directories.

If you try this out you will probably encounter a problem. If your master is a interactive shell and you close it, ssh will not return to the console if other clients still use this shared connection.

To circumvent this you can start a ssh process into the back ground:

$ ssh -nNf host

You can also use sshfs to act as the master:

$ sshfs host: mntpoint

I still haven’t found a nice way to make this somehow automatic. My wish would be a hook into the NetworkManager to get notified when a network connection is established. With this one can automatically connect to predefined hosts.

A second problem, which most of you will only notice if your computer hangs-up periodically and you try to reconnect after the restart, is that ssh complains that it can not use the still existing socket in the masters directories. The problem was more on my side and the solution is obvious:

Set the ControlMaster to no in the ~/.ssh/config file.

In this mode (which is the default) the client tries to connect to a master and fallbacks to a normal connection if it fails.

Now you need to start your master connection explicitly with the ControlMaster=yes option:

$ ssh -nNf -o ControlMaster=yes host

or

$ sshfs -o ControlMaster=yes host: mntpoint

respectively.

Supplement 1:

To close a master connection initiated with ssh -nNf ... run this:

$ ssh -O exit host

Supplement 2:

Now to the fun part:

I just hacked a formidable solution to the problem, when to start the master control. And by hacked I mean it.

Get the latest OpenSSH portable package and extract it:

$ wget ftp://mirror.roothell.org/pub/OpenBSD/OpenSSH/portable/openssh-5.2p1.tar.gz
$ tar xf openssh-5.2p1.tar.gz
$ cd openssh-5.2p1

Now safe the following patch as controlcommand.patch and apply it to the source:

diff --git a/mux.c b/mux.c
index 79f8376..1b07bab 100644
--- a/mux.c
+++ b/mux.c
@@ -518,6 +518,9 @@ muxclient(const char *path)
/* FALLTHROUGH */
case SSHCTL_MASTER_NO:
break;
+	case SSHCTL_MASTER_COMMAND:
+		debug("command-mux: Start control command");
+		break;
default:
return;
}
@@ -534,13 +537,22 @@ muxclient(const char *path)
if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
fatal("%s socket(): %s", __func__, strerror(errno));

+retry:
if (connect(sock, (struct sockaddr *)&addr, addr_len) == -1) {
if (muxclient_command != SSHMUX_COMMAND_OPEN) {
fatal("Control socket connect(%.100s): %s", path,
strerror(errno));
}
if (errno == ENOENT)
-			debug("Control socket \"%.100s\" does not exist", path);
+			if (options.control_master != SSHCTL_MASTER_COMMAND ||
+			    options.control_command == NULL) {
+				debug("Control socket \"%.100s\" does not exist", path);
+			} else {
+				int rc = ssh_local_cmd(options.control_command);
+				debug("Executing control command: %.500s: %d", options.control_command, rc);
+				if (!rc)
+					goto retry;
+			}
else {
error("Control socket connect(%.100s): %s", path,
strerror(errno));
diff --git a/readconf.c b/readconf.c
index 53fc6c7..f2be9c5 100644
--- a/readconf.c
+++ b/readconf.c
@@ -128,8 +128,9 @@ typedef enum {
oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
oAddressFamily, oGssAuthentication, oGssDelegateCreds,
oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
-	oSendEnv, oControlPath, oControlMaster, oHashKnownHosts,
-	oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand,
+	oSendEnv, oControlPath, oControlMaster, oControlCommand,
+	oHashKnownHosts, oTunnel, oTunnelDevice,
+	oLocalCommand, oPermitLocalCommand,
oVisualHostKey, oZeroKnowledgePasswordAuthentication,
oDeprecated, oUnsupported
} OpCodes;
@@ -222,6 +223,7 @@ static struct {
{ "sendenv", oSendEnv },
{ "controlpath", oControlPath },
{ "controlmaster", oControlMaster },
+	{ "controlcommand", oControlCommand },
{ "hashknownhosts", oHashKnownHosts },
{ "tunnel", oTunnel },
{ "tunneldevice", oTunnelDevice },
@@ -856,6 +858,8 @@ parse_int:
value = SSHCTL_MASTER_ASK;
else if (strcmp(arg, "autoask") == 0)
value = SSHCTL_MASTER_AUTO_ASK;
+		else if (strcmp(arg, "command") == 0)
+			value = SSHCTL_MASTER_COMMAND;
else
fatal("%.200s line %d: Bad ControlMaster argument.",
filename, linenum);
@@ -863,6 +867,10 @@ parse_int:
*intptr = value;
break;

+	case oControlCommand:
+		charptr = &options->control_command;
+		goto parse_string;
+
case oHashKnownHosts:
intptr = &options->hash_known_hosts;
goto parse_flag;
@@ -1057,6 +1065,7 @@ initialize_options(Options * options)
options->num_send_env = 0;
options->control_path = NULL;
options->control_master = -1;
+	options->control_command = NULL;
options->hash_known_hosts = -1;
options->tun_open = -1;
options->tun_local = -1;
diff --git a/readconf.h b/readconf.h
index 8fb3a85..93255a0 100644
--- a/readconf.h
+++ b/readconf.h
@@ -112,6 +112,7 @@ typedef struct {

char	*control_path;
int	control_master;
+	char	*control_command;

int	hash_known_hosts;

@@ -130,6 +131,7 @@ typedef struct {
#define SSHCTL_MASTER_AUTO	2
#define SSHCTL_MASTER_ASK	3
#define SSHCTL_MASTER_AUTO_ASK	4
+#define SSHCTL_MASTER_COMMAND	5

void     initialize_options(Options *);
void     fill_default_options(Options *);
diff --git a/ssh.c b/ssh.c
index 9d43bb7..02f1960 100644
--- a/ssh.c
+++ b/ssh.c
@@ -212,6 +212,7 @@ main(int ac, char **av)
extern char *optarg;
struct servent *sp;
Forward fwd;
+	char *host_arg;

/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
sanitise_stdfd();
@@ -547,6 +548,9 @@ main(int ac, char **av)
if (!host)
usage();

+	/* safe the hostname given on the command-line */
+	host_arg = host;
+
SSLeay_add_all_algorithms();
ERR_load_crypto_strings();

@@ -622,6 +626,9 @@ main(int ac, char **av)
&options, 0);
}

+	if (options.hostname != NULL)
+		host = options.hostname;
+
/* Fill configuration defaults. */
fill_default_options(&options);

@@ -651,15 +658,12 @@ main(int ac, char **av)
cp = options.local_command;
options.local_command = percent_expand(cp, "d", pw->pw_dir,
"h", options.hostname? options.hostname : host,
-                    "l", thishost, "n", host, "r", options.user, "p", buf,
+                    "l", thishost, "n", host_arg, "r", options.user, "p", buf,
"u", pw->pw_name, (char *)NULL);
debug3("expanded LocalCommand: %s", options.local_command);
xfree(cp);
}

-	if (options.hostname != NULL)
-		host = options.hostname;
-
/* force lowercase for hostkey matching */
if (options.host_key_alias != NULL) {
for (p = options.host_key_alias; *p; p++)
@@ -672,12 +676,12 @@ main(int ac, char **av)
xfree(options.proxy_command);
options.proxy_command = NULL;
}
+
if (options.control_path != NULL &&
strcmp(options.control_path, "none") == 0) {
xfree(options.control_path);
options.control_path = NULL;
}
-
if (options.control_path != NULL) {
char thishost[NI_MAXHOST];

@@ -691,6 +695,32 @@ main(int ac, char **av)
"r", options.user, "l", thishost, (char *)NULL);
xfree(cp);
}
+
+	if (options.control_command != NULL &&
+	    strcmp(options.control_command, "none") == 0) {
+		xfree(options.control_command);
+		options.control_command = NULL;
+	}
+	if (options.control_command != NULL && options.control_path != NULL) {
+		char thishost[NI_MAXHOST];
+
+		if (gethostname(thishost, sizeof(thishost)) == -1)
+			fatal("gethostname: %s", strerror(errno));
+		snprintf(buf, sizeof(buf), "%d", options.port);
+		cp = options.control_command;
+		options.control_command = percent_expand(cp,
+		    "l", thishost,
+		    "h", options.hostname ?: host,
+		    "p", buf,
+		    "r", options.user,
+		    "n", host_arg,
+		    "u", pw->pw_name,
+		    "d", pw->pw_dir,
+		    "s", options.control_path,
+		    (char *)NULL);
+		xfree(cp);
+	}
+
if (muxclient_command != 0 && options.control_path == NULL)
fatal("No ControlPath specified for \"-O\" command");
if (options.control_path != NULL)
diff --git a/sshconnect.c b/sshconnect.c
index c04aa10..9d6e6c2 100644
--- a/sshconnect.c
+++ b/sshconnect.c
@@ -1155,8 +1155,7 @@ ssh_local_cmd(const char *args)
pid_t pid;
int status;

-	if (!options.permit_local_command ||
-	    args == NULL || !*args)
+	if (args == NULL || !*args)
return (1);

if ((shell = getenv("SHELL")) == NULL)
$ patch -p1 < controlcommand.patch

configure and build openssh with --bindir=$HOME/bin and use $DESTDIR to install the complete package:

$ make DESTDIR=$PWD/root install-nosysconf

Copy the binaries into your PATH:

$ cp $PWD/root$HOME/bin/{ssh,scp,sftp} $HOME/bin

Change your ~/.ssh/config:

ControlMaster command
ControlCommand "ssh-cc.sh %h"

Finally put this script as ssh-cc.sh somewhere into your PATH:

#!/bin/bash
 
ssh -nNf -o ControlMaster=yes "$1"

I used a script to put some sshfs mounts into this.

To be sure your tools use the new ssh command add something like this into a proper file:

export GIT_SSH=$HOME/bin/ssh
export CVS_RSH=$HOME/bin/ssh
export RSYNC_RSH=$HOME/bin/ssh
export SVN_SSH=$HOME/bin/ssh

Categories: linux, software Tags:

Terminating SSH Sessions

May 14th, 2009 No comments

I think we all know the problem: a ssh session hangs for some reason, either because you are no longer connected to that wifi or because the remote host just crashed.

A more elegant solution than closing your local term (or killing the ssh client) consists of a well hidden feature:
by sequentially pressing the three keys Return ~ . you can exit the client and return to your local prompt.

Categories: bashism, linux, software Tags: , ,

@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @

February 24th, 2009 1 comment

Ach ja, jeden Tag nutzt man ssh aber eigentlich hat man keine Ahnung davon 😉

Hand auf Herz Leute, was macht ihr wenn “WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!” in der Kommandozeile steht? Genau,

vim ~/.ssh/known_hosts

und die Zeile für den betreffenden Host löschen. Das man das eigentlich nicht so machen sollte steht auf einem ganz anden Blatt. Wie es eigentlich geht? So:

Auf meinem Laptop kommt der Fehler:

[21:46:24][robin@robin-laptop:~]$ ssh vpv
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@       WARNING: POSSIBLE DNS SPOOFING DETECTED!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
The RSA host key for vpv has changed,
and the key for the according IP address 141.xxx.xxx.2
is unchanged. This could either mean that
DNS SPOOFING is happening or the IP address for the host
and its host key have changed at the same time.
Offending key for IP in /home/robin/.ssh/known_hosts:54
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that the RSA host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
cf:86:16:04:13:e9:15:6c:8d:b3:45:4e:5c:ff:57:65.
Please contact your system administrator.
Add correct host key in /home/robin/.ssh/known_hosts to get rid of this message.
Offending key in /home/robin/.ssh/known_hosts:53
RSA host key for vpv has changed and you have requested strict checking.
Host key verification failed.

Wichtig ist dabei der Fingerprint der mir mitgeteilt wird (cf:86:16:…). Dieser muss gegengecheckt werden. Dazu habe ich zwei Möglichkeiten. Entweder ich geh zum Admin der Kiste und frage ihn nach dem aktuellen Fingerprint (Was schlecht ist wenn man selber Admin ist und den Fingerprint nicht im Kopf hat 😉 ). Oder, ich checke ihn mit der eines Clients bei dem es ohne Warnmeldung funktioniert. Das geht so:

Auf zum Beispiel meiner Workstation kann ich mich ohne Probleme auf den Server verbinden. Dann checke ich dort mit

1
2
3
cd ~/.ssh
ssh-keygen -l -f known_hosts
1024 cf:86:16:04:13:e9:15:6c:8d:b3:45:4e:5c:ff:57:65 vpv,141.xxx.xxx.2

den Fingerprint gegen. Ist der Fingerprint aus der Warnmeldung identisch mit dem auf meiner Workstation kann ich mich ohne Bedenken ans Löschen der betreffenden known_hosts Zeile machen.