mirror of
https://github.com/systemd/systemd.git
synced 2024-12-22 17:35:35 +03:00
ssh-proxy: add ssh ProxyCommand tool that can connect to AF_UNIX + AF_VSOCK sockets
This adds a tiny binary that is hooked into SSH client config via ProxyCommand and which simply connects to an AF_UNIX or AF_VSOCK socket of choice. The syntax is as simple as this: ssh unix/some/path # (this connects to AF_UNIX socket /some/path) or: ssh vsock/4711 I used "/" as separator of the protocol ID and the value since ":" is already taken by SSH itself when doing sftp. And "@" is already taken for separating the user name.
This commit is contained in:
parent
0e3220684c
commit
0abd510f7f
@ -1055,6 +1055,7 @@ manpages = [
|
||||
['systemd-socket-proxyd', '8', [], ''],
|
||||
['systemd-soft-reboot.service', '8', [], ''],
|
||||
['systemd-ssh-generator', '8', [], ''],
|
||||
['systemd-ssh-proxy', '1', [], ''],
|
||||
['systemd-stdio-bridge', '1', [], ''],
|
||||
['systemd-storagetm.service', '8', ['systemd-storagetm'], 'ENABLE_STORAGETM'],
|
||||
['systemd-stub',
|
||||
|
116
man/systemd-ssh-proxy.xml
Normal file
116
man/systemd-ssh-proxy.xml
Normal file
@ -0,0 +1,116 @@
|
||||
<?xml version='1.0'?> <!--*-nxml-*-->
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
|
||||
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
|
||||
|
||||
<refentry id="systemd-ssh-proxy"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
|
||||
<refentryinfo>
|
||||
<title>systemd-ssh-proxy</title>
|
||||
<productname>systemd</productname>
|
||||
</refentryinfo>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>systemd-ssh-proxy</refentrytitle>
|
||||
<manvolnum>1</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>systemd-ssh-proxy</refname>
|
||||
<refpurpose>SSH client plugin for connecting to <constant>AF_VSOCK</constant> and
|
||||
<constant>AF_UNIX</constant> sockets</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<programlisting>
|
||||
Host unix/* vsock/*
|
||||
ProxyCommand /usr/lib/systemd/systemd-ssh-proxy %h %p
|
||||
ProxyUseFdpass yes
|
||||
</programlisting>
|
||||
<cmdsynopsis>
|
||||
<command>/usr/lib/systemd/systemd-ssh-proxy</command> <arg>ADDRESS</arg> <arg>PORT</arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para><command>systemd-ssh-proxy</command> is a small "proxy" plugin for the <citerefentry
|
||||
project="man-pages"><refentrytitle>ssh</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
tool that allows connecting to <constant>AF_UNIX</constant> and <constant>AF_VSOCK</constant> sockets. It
|
||||
implements the interface defined by <filename>ssh</filename>'s <varname>ProxyCommand</varname>
|
||||
configuration option. It's supposed to be used with an <citerefentry
|
||||
project="man-pages"><refentrytitle>ssh_config</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
configuration fragment like the following:</para>
|
||||
|
||||
<programlisting>
|
||||
Host unix/* vsock/*
|
||||
ProxyCommand /usr/lib/systemd/systemd-ssh-proxy %h %p
|
||||
ProxyUseFdpass yes
|
||||
CheckHostIP no
|
||||
|
||||
Host .host
|
||||
ProxyCommand /usr/lib/systemd/systemd-ssh-proxy unix/run/ssh-unix-local/socket %p
|
||||
ProxyUseFdpass yes
|
||||
CheckHostIP no
|
||||
</programlisting>
|
||||
|
||||
<para>A configuration fragment along these lines is by default installed into
|
||||
<filename>/etc/ssh/ssh_config.d/20-systemd-ssh-proxy.conf.in</filename>.</para>
|
||||
|
||||
<para>With this in place, SSH connections to host string <literal>unix/</literal> followed by an absolute
|
||||
<constant>AF_UNIX</constant> file system path to a socket will be directed to the specified socket, which
|
||||
must be of type <constant>SOCK_STREAM</constant>. Similar, SSH connections to <literal>vsock/</literal>
|
||||
followed by an <constant>AF_VSOCK</constant> CID will result in an SSH connection made to that
|
||||
CID. Moreover connecting to <literal>.host</literal> will connect to the local host via SSH, without
|
||||
involving networking.</para>
|
||||
|
||||
<para>This tool is supposed to be used together with
|
||||
<citerefentry><refentrytitle>systemd-ssh-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
which when run inside a VM or container will bind SSH to suitable
|
||||
addresses. <command>systemd-ssh-generator</command> is supposed to run in the container of VM guest, and
|
||||
<command>systemd-ssh-proxy</command> is run on the host, in order to connect to the container or VM
|
||||
guest.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Exit status</title>
|
||||
|
||||
<para>On success, 0 is returned, a non-zero failure code
|
||||
otherwise.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Examples</title>
|
||||
|
||||
<example>
|
||||
<title>Talk to a local VM with CID 4711</title>
|
||||
|
||||
<programlisting>ssh vsock/4711</programlisting>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<title>Talk to the local host via ssh</title>
|
||||
|
||||
<programlisting>ssh .host</programlisting>
|
||||
|
||||
<para>or equivalent:</para>
|
||||
|
||||
<programlisting>ssh unix/run/ssh-unix-local/socket</programlisting>
|
||||
</example>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
<para><simplelist type="inline">
|
||||
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd-ssh-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
|
||||
<member><citerefentry project="man-pages"><refentrytitle>vsock</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
|
||||
<member><citerefentry project="man-pages"><refentrytitle>unix</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
|
||||
<member><citerefentry project="man-pages"><refentrytitle>ssh</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
<member><citerefentry project="man-pages"><refentrytitle>sshd</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
|
||||
</simplelist></para>
|
||||
</refsect1>
|
||||
</refentry>
|
@ -199,6 +199,11 @@ if pamconfdir == ''
|
||||
pamconfdir = prefixdir / 'lib/pam.d'
|
||||
endif
|
||||
|
||||
sshconfdir = get_option('sshconfdir')
|
||||
if sshconfdir == ''
|
||||
sshconfdir = sysconfdir / 'ssh/ssh_config.d'
|
||||
endif
|
||||
|
||||
sshdconfdir = get_option('sshdconfdir')
|
||||
if sshdconfdir == ''
|
||||
sshdconfdir = sysconfdir / 'ssh/sshd_config.d'
|
||||
@ -235,6 +240,7 @@ conf.set_quoted('PREFIX_NOSLASH', prefixdir_noslash)
|
||||
conf.set_quoted('RANDOM_SEED', randomseeddir / 'random-seed')
|
||||
conf.set_quoted('RANDOM_SEED_DIR', randomseeddir)
|
||||
conf.set_quoted('RC_LOCAL_PATH', get_option('rc-local'))
|
||||
conf.set_quoted('SSHCONFDIR', sshconfdir)
|
||||
conf.set_quoted('SSHDCONFDIR', sshdconfdir)
|
||||
conf.set_quoted('SYSCONF_DIR', sysconfdir)
|
||||
conf.set_quoted('SYSCTL_DIR', sysctldir)
|
||||
@ -2689,7 +2695,8 @@ summary({
|
||||
'SysV rc?.d directories' : sysvrcnd_path,
|
||||
'PAM modules directory' : pamlibdir,
|
||||
'PAM configuration directory' : pamconfdir,
|
||||
'ssh configuration directory' : sshdconfdir,
|
||||
'ssh server configuration directory' : sshdconfdir,
|
||||
'ssh client configuration directory' : sshconfdir,
|
||||
'libcryptsetup plugins directory' : libcryptsetup_plugins_dir,
|
||||
'RPM macros directory' : rpmmacrosdir,
|
||||
'modprobe.d directory' : modprobedir,
|
||||
|
@ -211,6 +211,8 @@ option('pamlibdir', type : 'string',
|
||||
description : 'directory for PAM modules')
|
||||
option('pamconfdir', type : 'string',
|
||||
description : 'directory for PAM configuration ["no" disables]')
|
||||
option('sshconfdir', type : 'string',
|
||||
description : 'directory for SSH client configuration ["no" disables]')
|
||||
option('sshdconfdir', type : 'string',
|
||||
description : 'directory for SSH server configuration ["no" disables]')
|
||||
option('libcryptsetup-plugins-dir', type : 'string',
|
||||
|
18
src/ssh-generator/20-systemd-ssh-proxy.conf.in
Normal file
18
src/ssh-generator/20-systemd-ssh-proxy.conf.in
Normal file
@ -0,0 +1,18 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# Make sure unix/* and vsock/* can be used to connect to AF_UNIX and AF_VSOCK paths
|
||||
#
|
||||
Host unix/* vsock/*
|
||||
ProxyCommand {{LIBEXECDIR}}/systemd-ssh-proxy %h %p
|
||||
ProxyUseFdpass yes
|
||||
CheckHostIP no
|
||||
|
||||
# Disable all kinds of host identity checks, since these addresses are generally ephemeral.
|
||||
StrictHostKeyChecking no
|
||||
UserKnownHostsFile /dev/null
|
||||
|
||||
# Allow connecting to the local host directly via ".host"
|
||||
Host .host
|
||||
ProxyCommand {{LIBEXECDIR}}/systemd-ssh-proxy unix/run/ssh-unix-local/socket %p
|
||||
ProxyUseFdpass yes
|
||||
CheckHostIP no
|
@ -5,4 +5,21 @@ executables += [
|
||||
'name' : 'systemd-ssh-generator',
|
||||
'sources' : files('ssh-generator.c'),
|
||||
},
|
||||
libexec_template + {
|
||||
'name' : 'systemd-ssh-proxy',
|
||||
'sources' : files('ssh-proxy.c'),
|
||||
},
|
||||
]
|
||||
|
||||
custom_target(
|
||||
'20-systemd-ssh-proxy.conf',
|
||||
input : '20-systemd-ssh-proxy.conf.in',
|
||||
output : '20-systemd-ssh-proxy.conf',
|
||||
command : [jinja2_cmdline, '@INPUT@', '@OUTPUT@'],
|
||||
install : true,
|
||||
install_dir : libexecdir / 'ssh_config.d')
|
||||
|
||||
install_emptydir(sshconfdir)
|
||||
|
||||
meson.add_install_script(sh, '-c',
|
||||
ln_s.format(libexecdir / 'ssh_config.d' / '20-systemd-ssh-proxy.conf', sshconfdir / '20-systemd-ssh-proxy.conf'))
|
||||
|
102
src/ssh-generator/ssh-proxy.c
Normal file
102
src/ssh-generator/ssh-proxy.c
Normal file
@ -0,0 +1,102 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <net/if_arp.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "fd-util.h"
|
||||
#include "iovec-util.h"
|
||||
#include "log.h"
|
||||
#include "main-func.h"
|
||||
#include "missing_socket.h"
|
||||
#include "parse-util.h"
|
||||
#include "socket-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
|
||||
static int process_vsock(const char *host, const char *port) {
|
||||
int r;
|
||||
|
||||
assert(host);
|
||||
assert(port);
|
||||
|
||||
union sockaddr_union sa = {
|
||||
.vm.svm_family = AF_VSOCK,
|
||||
};
|
||||
|
||||
r = vsock_parse_cid(host, &sa.vm.svm_cid);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse vsock cid: %s", host);
|
||||
|
||||
r = vsock_parse_port(port, &sa.vm.svm_port);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse vsock port: %s", port);
|
||||
|
||||
_cleanup_close_ int fd = socket(AF_VSOCK, SOCK_STREAM|SOCK_CLOEXEC, 0);
|
||||
if (fd < 0)
|
||||
return log_error_errno(errno, "Failed to allocate AF_VSOCK socket: %m");
|
||||
|
||||
if (connect(fd, &sa.sa, SOCKADDR_LEN(sa)) < 0)
|
||||
return log_error_errno(errno, "Failed to connect to vsock:%u:%u: %m", sa.vm.svm_cid, sa.vm.svm_port);
|
||||
|
||||
/* OpenSSH wants us to send a single byte along with the file descriptor, hence do so */
|
||||
r = send_one_fd_iov(STDOUT_FILENO, fd, &IOVEC_NUL_BYTE, /* n_iovec= */ 1, /* flags= */ 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to send socket via STDOUT: %m");
|
||||
|
||||
log_debug("Successfully sent AF_VSOCK socket via STDOUT.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_unix(const char *path) {
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
|
||||
/* We assume the path is absolute unless it starts with a dot (or is already explicitly absolute) */
|
||||
_cleanup_free_ char *prefixed = NULL;
|
||||
if (!STARTSWITH_SET(path, "/", "./")) {
|
||||
prefixed = strjoin("/", path);
|
||||
if (!prefixed)
|
||||
return log_oom();
|
||||
|
||||
path = prefixed;
|
||||
}
|
||||
|
||||
_cleanup_close_ int fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
|
||||
if (fd < 0)
|
||||
return log_error_errno(errno, "Failed to allocate AF_UNIX socket: %m");
|
||||
|
||||
r = connect_unix_path(fd, AT_FDCWD, path);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to connect to AF_UNIX socket %s: %m", path);
|
||||
|
||||
r = send_one_fd_iov(STDOUT_FILENO, fd, &IOVEC_NUL_BYTE, /* n_iovec= */ 1, /* flags= */ 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to send socket via STDOUT: %m");
|
||||
|
||||
log_debug("Successfully sent AF_UNIX socket via STDOUT.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int run(int argc, char* argv[]) {
|
||||
|
||||
log_setup();
|
||||
|
||||
if (argc != 3)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected two arguments: host and port.");
|
||||
|
||||
const char *host = argv[1], *port = argv[2];
|
||||
|
||||
const char *p = startswith(host, "vsock/");
|
||||
if (p)
|
||||
return process_vsock(p, port);
|
||||
|
||||
p = startswith(host, "unix/");
|
||||
if (p)
|
||||
return process_unix(p);
|
||||
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Don't know how to parse host name specification: %s", host);
|
||||
}
|
||||
|
||||
DEFINE_MAIN_FUNCTION(run);
|
10
tmpfiles.d/20-systemd-ssh-generator.conf.in
Normal file
10
tmpfiles.d/20-systemd-ssh-generator.conf.in
Normal file
@ -0,0 +1,10 @@
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# systemd is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation; either version 2.1 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# See tmpfiles.d(5) for details
|
||||
|
||||
L {{SSHCONFDIR}}/20-systemd-ssh-proxy.conf - - - - {{LIBEXECDIR}}/ssh_config.d/20-systemd-ssh-proxy.conf
|
@ -35,6 +35,7 @@ in_files = [['etc.conf', ''],
|
||||
['systemd.conf', ''],
|
||||
['var.conf', ''],
|
||||
['20-systemd-userdb.conf', 'ENABLE_USERDB'],
|
||||
['20-systemd-ssh-generator.conf', ''],
|
||||
]
|
||||
|
||||
foreach pair : in_files
|
||||
|
Loading…
Reference in New Issue
Block a user