Skip to content
Permalink
Browse files
upstream commit
Add a ProxyJump ssh_config(5) option and corresponding -J
ssh(1) command-line flag to allow simplified indirection through a SSH
bastion or "jump host".

These options construct a proxy command that connects to the
specified jump host(s) (more than one may be specified) and uses
port-forwarding to establish a connection to the next destination.

This codifies the safest way of indirecting connections through SSH
servers and makes it easy to use.

ok markus@

Upstream-ID: fa899cb8b26d889da8f142eb9774c1ea36b04397
  • Loading branch information
djmdjm committed Jul 15, 2016
1 parent 5c02dd1 commit ed877ef
Show file tree
Hide file tree
Showing 7 changed files with 271 additions and 27 deletions.
63 misc.c
@@ -1,4 +1,4 @@
/* $OpenBSD: misc.c,v 1.104 2016/04/06 06:42:17 djm Exp $ */
/* $OpenBSD: misc.c,v 1.105 2016/07/15 00:24:30 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2005,2006 Damien Miller. All rights reserved.
@@ -451,6 +451,67 @@ colon(char *cp)
return NULL;
}

/*
* Parse a [user@]host[:port] string.
* Caller must free returned user and host.
* Any of the pointer return arguments may be NULL (useful for syntax checking).
* If user was not specified then *userp will be set to NULL.
* If port was not specified then *portp will be -1.
* Returns 0 on success, -1 on failure.
*/
int
parse_user_host_port(const char *s, char **userp, char **hostp, int *portp)
{
char *sdup, *cp, *tmp;
char *user = NULL, *host = NULL;
int port = -1, ret = -1;

if (userp != NULL)
*userp = NULL;
if (hostp != NULL)
*hostp = NULL;
if (portp != NULL)
*portp = -1;

if ((sdup = tmp = strdup(s)) == NULL)
return -1;
/* Extract optional username */
if ((cp = strchr(tmp, '@')) != NULL) {
*cp = '\0';
if (*tmp == '\0')
goto out;
if ((user = strdup(tmp)) == NULL)
goto out;
tmp = cp + 1;
}
/* Extract mandatory hostname */
if ((cp = hpdelim(&tmp)) == NULL || *cp == '\0')
goto out;
host = xstrdup(cleanhostname(cp));
/* Convert and verify optional port */
if (tmp != NULL && *tmp != '\0') {
if ((port = a2port(tmp)) <= 0)
goto out;
}
/* Success */
if (userp != NULL) {
*userp = user;
user = NULL;
}
if (hostp != NULL) {
*hostp = host;
host = NULL;
}
if (portp != NULL)
*portp = port;
ret = 0;
out:
free(sdup);
free(user);
free(host);
return ret;
}

/* function to assist building execv() arguments */
void
addargs(arglist *args, char *fmt, ...)
3 misc.h
@@ -1,4 +1,4 @@
/* $OpenBSD: misc.h,v 1.56 2016/04/06 06:42:17 djm Exp $ */
/* $OpenBSD: misc.h,v 1.57 2016/07/15 00:24:30 djm Exp $ */

/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -49,6 +49,7 @@ char *put_host_port(const char *, u_short);
char *hpdelim(char **);
char *cleanhostname(char *);
char *colon(char *);
int parse_user_host_port(const char *, char **, char **, int *);
long convtime(const char *);
char *tilde_expand_filename(const char *, uid_t);
char *percent_expand(const char *, ...) __attribute__((__sentinel__));
@@ -1,4 +1,4 @@
/* $OpenBSD: readconf.c,v 1.256 2016/06/03 04:09:38 dtucker Exp $ */
/* $OpenBSD: readconf.c,v 1.257 2016/07/15 00:24:30 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -170,7 +170,7 @@ typedef enum {
oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs,
oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys,
oFingerprintHash, oUpdateHostkeys, oHostbasedKeyTypes,
oPubkeyAcceptedKeyTypes,
oPubkeyAcceptedKeyTypes, oProxyJump,
oIgnoredUnknownOption, oDeprecated, oUnsupported
} OpCodes;

@@ -295,6 +295,7 @@ static struct {
{ "hostbasedkeytypes", oHostbasedKeyTypes },
{ "pubkeyacceptedkeytypes", oPubkeyAcceptedKeyTypes },
{ "ignoreunknown", oIgnoreUnknown },
{ "proxyjump", oProxyJump },

{ NULL, oBadOption }
};
@@ -1121,6 +1122,9 @@ process_config_line_depth(Options *options, struct passwd *pw, const char *host,

case oProxyCommand:
charptr = &options->proxy_command;
/* Ignore ProxyCommand if ProxyJump already specified */
if (options->jump_host != NULL)
charptr = &options->jump_host; /* Skip below */
parse_command:
if (s == NULL)
fatal("%.200s line %d: Missing argument.", filename, linenum);
@@ -1129,6 +1133,18 @@ process_config_line_depth(Options *options, struct passwd *pw, const char *host,
*charptr = xstrdup(s + len);
return 0;

case oProxyJump:
if (s == NULL) {
fatal("%.200s line %d: Missing argument.",
filename, linenum);
}
len = strspn(s, WHITESPACE "=");
if (parse_jump(s + len, options, *activep) == -1) {
fatal("%.200s line %d: Invalid ProxyJump \"%s\"",
filename, linenum, s + len);
}
return 0;

case oPort:
intptr = &options->port;
parse_int:
@@ -1789,6 +1805,10 @@ initialize_options(Options * options)
options->hostname = NULL;
options->host_key_alias = NULL;
options->proxy_command = NULL;
options->jump_user = NULL;
options->jump_host = NULL;
options->jump_port = -1;
options->jump_extra = NULL;
options->user = NULL;
options->escape_char = -1;
options->num_system_hostfiles = 0;
@@ -2261,6 +2281,44 @@ parse_forward(struct Forward *fwd, const char *fwdspec, int dynamicfwd, int remo
return (0);
}

int
parse_jump(const char *s, Options *o, int active)
{
char *orig, *sdup, *cp;
char *host = NULL, *user = NULL;
int ret = -1, port = -1;

active &= o->proxy_command == NULL && o->jump_host == NULL;

orig = sdup = xstrdup(s);
while ((cp = strsep(&sdup, ",")) && cp != NULL) {
if (active) {
/* First argument and configuration is active */
if (parse_user_host_port(cp, &user, &host, &port) != 0)
goto out;
} else {
/* Subsequent argument or inactive configuration */
if (parse_user_host_port(cp, NULL, NULL, NULL) != 0)
goto out;
}
active = 0; /* only check syntax for subsequent hosts */
}
/* success */
free(orig);
o->jump_user = user;
o->jump_host = host;
o->jump_port = port;
o->proxy_command = xstrdup("none");
user = host = NULL;
if ((cp = strchr(s, ',')) != NULL && cp[1] != '\0')
o->jump_extra = xstrdup(cp + 1);
ret = 0;
out:
free(user);
free(host);
return ret;
}

/* XXX the following is a near-vebatim copy from servconf.c; refactor */
static const char *
fmt_multistate_int(int val, const struct multistate *m)
@@ -2412,7 +2470,7 @@ void
dump_client_config(Options *o, const char *host)
{
int i;
char vbuf[5];
char buf[8];

/* This is normally prepared in ssh_kex2 */
if (kex_assemble_names(KEX_DEFAULT_PK_ALG, &o->hostkeyalgorithms) != 0)
@@ -2490,7 +2548,6 @@ dump_client_config(Options *o, const char *host)
dump_cfg_string(oMacs, o->macs ? o->macs : KEX_CLIENT_MAC);
dump_cfg_string(oPKCS11Provider, o->pkcs11_provider);
dump_cfg_string(oPreferredAuthentications, o->preferred_authentications);
dump_cfg_string(oProxyCommand, o->proxy_command);
dump_cfg_string(oPubkeyAcceptedKeyTypes, o->pubkey_key_types);
dump_cfg_string(oRevokedHostKeys, o->revoked_host_keys);
dump_cfg_string(oXAuthLocation, o->xauth_location);
@@ -2551,8 +2608,8 @@ dump_client_config(Options *o, const char *host)
if (o->escape_char == SSH_ESCAPECHAR_NONE)
printf("escapechar none\n");
else {
vis(vbuf, o->escape_char, VIS_WHITE, 0);
printf("escapechar %s\n", vbuf);
vis(buf, o->escape_char, VIS_WHITE, 0);
printf("escapechar %s\n", buf);
}

/* oIPQoS */
@@ -2566,4 +2623,30 @@ dump_client_config(Options *o, const char *host)
/* oStreamLocalBindMask */
printf("streamlocalbindmask 0%o\n",
o->fwd_opts.streamlocal_bind_mask);

/* oProxyCommand / oProxyJump */
if (o->jump_host == NULL)
dump_cfg_string(oProxyCommand, o->proxy_command);
else {
/* Check for numeric addresses */
i = strchr(o->jump_host, ':') != NULL ||
strspn(o->jump_host, "1234567890.") == strlen(o->jump_host);
snprintf(buf, sizeof(buf), "%d", o->jump_port);
printf("proxyjump %s%s%s%s%s%s%s%s%s\n",
/* optional user */
o->jump_user == NULL ? "" : o->jump_user,
o->jump_user == NULL ? "" : "@",
/* opening [ if hostname is numeric */
i ? "[" : "",
/* mandatory hostname */
o->jump_host,
/* closing ] if hostname is numeric */
i ? "]" : "",
/* optional port number */
o->jump_port <= 0 ? "" : ":",
o->jump_port <= 0 ? "" : buf,
/* optional additional jump spec */
o->jump_extra == NULL ? "" : ",",
o->jump_extra == NULL ? "" : o->jump_extra);
}
}
@@ -1,4 +1,4 @@
/* $OpenBSD: readconf.h,v 1.116 2016/06/03 03:14:41 dtucker Exp $ */
/* $OpenBSD: readconf.h,v 1.117 2016/07/15 00:24:30 djm Exp $ */

/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -163,6 +163,11 @@ typedef struct {
char *hostbased_key_types;
char *pubkey_key_types;

char *jump_user;
char *jump_host;
int jump_port;
char *jump_extra;

char *ignored_unknown; /* Pattern list of unknown tokens to ignore */
} Options;

@@ -198,6 +203,7 @@ int process_config_line(Options *, struct passwd *, const char *,
int read_config_file(const char *, struct passwd *, const char *,
const char *, Options *, int);
int parse_forward(struct Forward *, const char *, int, int);
int parse_jump(const char *, Options *, int);
int default_ssh_port(void);
int option_clear_or_none(const char *);
void dump_client_config(Options *o, const char *host);
24 ssh.1
@@ -33,8 +33,8 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.\" $OpenBSD: ssh.1,v 1.374 2016/06/29 17:14:28 jmc Exp $
.Dd $Mdocdate: June 29 2016 $
.\" $OpenBSD: ssh.1,v 1.375 2016/07/15 00:24:30 djm Exp $
.Dd $Mdocdate: July 15 2016 $
.Dt SSH 1
.Os
.Sh NAME
@@ -52,6 +52,7 @@
.Op Fl F Ar configfile
.Op Fl I Ar pkcs11
.Op Fl i Ar identity_file
.Oo Fl J Ar user Ns @ Oc Ns Ar host Ns Op : Ns Ar port
.Op Fl L Ar address
.Op Fl l Ar login_name
.Op Fl m Ar mac_spec
@@ -312,6 +313,24 @@ by appending
.Pa -cert.pub
to identity filenames.
.Pp
.It Fl J Xo
.Sm off
.Oo Ar jump_user @ Oc
.Ar jump_host
.Ns Op : Ns Ar jump_port
.Sm on
.Xc
Connect to the target host by first making a
.Nm
connection to
.Ar jump_host
and then establishing a TCP forward to the ultimate destination from
there.
Multiple jump hops may be specified separated by comma characters.
This is a shortcut to specify a
.Cm ProxyJump
configuration directive.
.Pp
.It Fl K
Enables GSSAPI-based authentication and forwarding (delegation) of GSSAPI
credentials to the server.
@@ -523,6 +542,7 @@ For full details of the options listed below, and their possible values, see
.It PreferredAuthentications
.It Protocol
.It ProxyCommand
.It ProxyJump
.It ProxyUseFdpass
.It PubkeyAcceptedKeyTypes
.It PubkeyAuthentication

0 comments on commit ed877ef

Please sign in to comment.