mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 00:14:32 +09:00
ssh-generator: generate /etc/issue.d/ with VSOCK ssh info data (#37819)
ssh-generator: generate /etc/issue.d/ with VSOCK ssh info data
I find myself trying to log into a fresh ParticleOS VM started via
systemd-vmspawn all the time, but I don't know its CID. Let's show it on
the getty screen, to make it immediately visible.
This commit is contained in:
4
TODO
4
TODO
@@ -118,6 +118,10 @@ Deprecations and removals:
|
||||
|
||||
* Consider removing root=gpt-auto, and push people to use root=dissect instead.
|
||||
|
||||
* Once
|
||||
https://github.com/util-linux/util-linux/commit/508fb0e7ac103b68531a59db2a4473897853ab52
|
||||
has hit the prominent distributions, revert --issue-file= hack in units/*getty*sevice.in
|
||||
|
||||
Features:
|
||||
|
||||
* replace bootctl's PE version check to actually use APIs from pe-binary.[ch]
|
||||
|
||||
@@ -1118,6 +1118,7 @@ manpages = [
|
||||
['systemd-socket-proxyd', '8', [], ''],
|
||||
['systemd-soft-reboot.service', '8', [], ''],
|
||||
['systemd-ssh-generator', '8', [], ''],
|
||||
['systemd-ssh-issue', '1', [], ''],
|
||||
['systemd-ssh-proxy', '1', [], ''],
|
||||
['systemd-stdio-bridge', '1', [], ''],
|
||||
['systemd-storagetm.service', '8', ['systemd-storagetm'], 'ENABLE_STORAGETM'],
|
||||
|
||||
99
man/systemd-ssh-issue.xml
Normal file
99
man/systemd-ssh-issue.xml
Normal file
@@ -0,0 +1,99 @@
|
||||
<?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-issue"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
|
||||
<refentryinfo>
|
||||
<title>systemd-ssh-issue</title>
|
||||
<productname>systemd</productname>
|
||||
</refentryinfo>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>systemd-ssh-issue</refentrytitle>
|
||||
<manvolnum>1</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>systemd-ssh-issue</refname>
|
||||
<refpurpose>Generator for SSH login prompt drop-ins</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>/usr/lib/systemd/systemd-ssh-issue <option>--make-vsock</option></command>
|
||||
<command>/usr/lib/systemd/systemd-ssh-issue <option>--rm-vsock</option></command>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para><command>systemd-ssh-issue</command> is a small tool that generates a
|
||||
<filename>/run/issue.d/50-ssh-vsock.issue</filename> drop-in file in case <constant>AF_VSOCK</constant>
|
||||
support is available in the kernel and the VM environment. The file contains brief information about how
|
||||
to contact the local system via SSH-over-<constant>AF_VSOCK</constant>, in particular it reports the
|
||||
system's <constant>AF_VSOCK</constant> CID. The file is typically read and displayed by <citerefentry
|
||||
project='man-pages'><refentrytitle>agetty</refentrytitle><manvolnum>8</manvolnum></citerefentry> on
|
||||
console or serial login prompts.</para>
|
||||
|
||||
<para>This tool is automatically used by
|
||||
<citerefentry><refentrytitle>systemd-ssh-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
in the <constant>AF_VSOCK</constant> socket units it generates.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Options</title>
|
||||
<para>The following options are understood:</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><option>--make-vsock</option></term>
|
||||
<listitem><para>Generates the issue file. This command has no effect if called on systems lacking
|
||||
<constant>AF_VSOCK</constant> support.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--rm-vsock</option></term>
|
||||
<listitem><para>Removes the issue file if it exists.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--issue-path=</option></term>
|
||||
<listitem><para>Changes the path to the issue file to write to/remove. If not specified, defaults to
|
||||
<filename>/run/issue.d/50-ssh-vsock.issue</filename>. If specified as empty string or
|
||||
<literal>-</literal> writes the issue file contents to standard output.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="help"/>
|
||||
<xi:include href="standard-options.xml" xpointer="version"/>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Exit status</title>
|
||||
|
||||
<para>On success, 0 is returned, a non-zero failure code
|
||||
otherwise.</para>
|
||||
</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>ssh</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
<member><citerefentry project="man-pages"><refentrytitle>sshd</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
|
||||
<member><citerefentry project='man-pages'><refentrytitle>agetty</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
|
||||
</simplelist></para>
|
||||
</refsect1>
|
||||
</refentry>
|
||||
@@ -9,6 +9,10 @@ executables += [
|
||||
'name' : 'systemd-ssh-proxy',
|
||||
'sources' : files('ssh-proxy.c'),
|
||||
},
|
||||
libexec_template + {
|
||||
'name' : 'systemd-ssh-issue',
|
||||
'sources' : files('ssh-issue.c'),
|
||||
},
|
||||
]
|
||||
|
||||
if conf.get('ENABLE_SSH_PROXY_CONFIG') == 1
|
||||
|
||||
@@ -134,6 +134,7 @@ static int write_socket_unit(
|
||||
const char *unit,
|
||||
const char *listen_stream,
|
||||
const char *comment,
|
||||
const char *extra,
|
||||
bool with_ssh_access_target_dependency) {
|
||||
|
||||
int r;
|
||||
@@ -172,6 +173,9 @@ static int write_socket_unit(
|
||||
"PollLimitBurst=50\n",
|
||||
listen_stream);
|
||||
|
||||
if (extra)
|
||||
fputs(extra, f);
|
||||
|
||||
r = fflush_and_check(f);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to write %s SSH socket unit: %m", comment);
|
||||
@@ -245,6 +249,8 @@ static int add_vsock_socket(
|
||||
"sshd-vsock.socket",
|
||||
"vsock::22",
|
||||
"AF_VSOCK",
|
||||
"ExecStartPost=-/usr/lib/systemd/systemd-ssh-issue --make-vsock\n"
|
||||
"ExecStopPre=-/usr/lib/systemd/systemd-ssh-issue --rm-vsock\n",
|
||||
/* with_ssh_access_target_dependency= */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -280,6 +286,7 @@ static int add_local_unix_socket(
|
||||
"sshd-unix-local.socket",
|
||||
"/run/ssh-unix-local/socket",
|
||||
"AF_UNIX Local",
|
||||
/* extra= */ NULL,
|
||||
/* with_ssh_access_target_dependency= */ false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -336,6 +343,7 @@ static int add_export_unix_socket(
|
||||
"sshd-unix-export.socket",
|
||||
"/run/host/unix-export/ssh",
|
||||
"AF_UNIX Export",
|
||||
/* extra= */ NULL,
|
||||
/* with_ssh_access_target_dependency= */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -387,6 +395,7 @@ static int add_extra_sockets(
|
||||
socket ?: "sshd-extra.socket",
|
||||
*i,
|
||||
*i,
|
||||
/* extra= */ NULL,
|
||||
/* with_ssh_access_target_dependency= */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
242
src/ssh-generator/ssh-issue.c
Normal file
242
src/ssh-generator/ssh-issue.c
Normal file
@@ -0,0 +1,242 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <getopt.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "ansi-color.h"
|
||||
#include "build.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "log.h"
|
||||
#include "main-func.h"
|
||||
#include "mkdir.h"
|
||||
#include "parse-argument.h"
|
||||
#include "pretty-print.h"
|
||||
#include "socket-util.h"
|
||||
#include "string-util.h"
|
||||
#include "tmpfile-util.h"
|
||||
#include "virt.h"
|
||||
|
||||
static enum {
|
||||
ACTION_MAKE_VSOCK,
|
||||
ACTION_RM_VSOCK,
|
||||
} arg_action = ACTION_MAKE_VSOCK;
|
||||
|
||||
static char* arg_issue_path = NULL;
|
||||
static bool arg_issue_stdout = false;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_issue_path, freep);
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
int r;
|
||||
|
||||
r = terminal_urlify_man("systemd-ssh-issue", "1", &link);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
printf("%s [OPTIONS...] --make-vsock\n"
|
||||
"%s [OPTIONS...] --rm-vsock\n"
|
||||
"\n%sCreate ssh /run/issue.d/ file reporting VSOCK address.%s\n\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Show package version\n"
|
||||
" --issue-path=PATH Change path to /run/issue.d/50-ssh-vsock.issue\n"
|
||||
"\nSee the %s for details.\n",
|
||||
program_invocation_short_name,
|
||||
program_invocation_short_name,
|
||||
ansi_highlight(),
|
||||
ansi_normal(),
|
||||
link);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
enum {
|
||||
ARG_MAKE_VSOCK = 0x100,
|
||||
ARG_RM_VSOCK,
|
||||
ARG_ISSUE_PATH,
|
||||
ARG_VERSION,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "make-vsock", no_argument, NULL, ARG_MAKE_VSOCK },
|
||||
{ "rm-vsock", no_argument, NULL, ARG_RM_VSOCK },
|
||||
{ "issue-path", required_argument, NULL, ARG_ISSUE_PATH },
|
||||
{}
|
||||
};
|
||||
|
||||
int c, r;
|
||||
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
|
||||
|
||||
switch (c) {
|
||||
|
||||
case 'h':
|
||||
return help();
|
||||
|
||||
case ARG_VERSION:
|
||||
return version();
|
||||
|
||||
case ARG_MAKE_VSOCK:
|
||||
arg_action = ACTION_MAKE_VSOCK;
|
||||
break;
|
||||
|
||||
case ARG_RM_VSOCK:
|
||||
arg_action = ACTION_RM_VSOCK;
|
||||
break;
|
||||
|
||||
case ARG_ISSUE_PATH:
|
||||
if (isempty(optarg) || streq(optarg, "-")) {
|
||||
arg_issue_path = mfree(arg_issue_path);
|
||||
arg_issue_stdout = true;
|
||||
break;
|
||||
}
|
||||
|
||||
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_issue_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
arg_issue_stdout = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!arg_issue_path && !arg_issue_stdout) {
|
||||
arg_issue_path = strdup("/run/issue.d/50-ssh-vsock.issue");
|
||||
if (!arg_issue_path)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int acquire_cid(unsigned *ret_cid) {
|
||||
int r;
|
||||
|
||||
assert(ret_cid);
|
||||
|
||||
Virtualization v = detect_virtualization();
|
||||
if (v < 0)
|
||||
return log_error_errno(v, "Failed to detect if we run in a VM: %m");
|
||||
if (!VIRTUALIZATION_IS_VM(v)) {
|
||||
/* NB: if we are running in a container inside a VM, then we'll *not* do AF_VSOCK stuff */
|
||||
log_debug("Not running in a VM, not creating issue file.");
|
||||
*ret_cid = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_cleanup_close_ int vsock_fd = socket(AF_VSOCK, SOCK_STREAM|SOCK_CLOEXEC, 0);
|
||||
if (vsock_fd < 0) {
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno)) {
|
||||
log_debug("Not creating issue file, since AF_VSOCK is not available.");
|
||||
*ret_cid = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return log_error_errno(errno, "Unable to test if AF_VSOCK is available: %m");
|
||||
}
|
||||
|
||||
vsock_fd = safe_close(vsock_fd);
|
||||
|
||||
unsigned local_cid;
|
||||
r = vsock_get_local_cid(&local_cid);
|
||||
if (r < 0) {
|
||||
if (ERRNO_IS_DEVICE_ABSENT(r)) {
|
||||
log_debug("Not creating issue file, since /dev/vsock is not available (even though AF_VSOCK is).");
|
||||
*ret_cid = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return log_error_errno(r, "Failed to query local AF_VSOCK CID: %m");
|
||||
}
|
||||
|
||||
*ret_cid = local_cid;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int run(int argc, char* argv[]) {
|
||||
int r;
|
||||
|
||||
log_setup();
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
switch (arg_action) {
|
||||
case ACTION_MAKE_VSOCK: {
|
||||
unsigned cid;
|
||||
|
||||
r = acquire_cid(&cid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
log_debug("Not running in a VSOCK enabled VM, skipping.");
|
||||
break;
|
||||
}
|
||||
|
||||
_cleanup_(unlink_and_freep) char *t = NULL;
|
||||
_cleanup_(fclosep) FILE *f = NULL;
|
||||
FILE *out;
|
||||
|
||||
if (arg_issue_path) {
|
||||
r = mkdir_parents(arg_issue_path, 0755);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create parent directories of '%s': %m", arg_issue_path);
|
||||
|
||||
r = fopen_tmpfile_linkable(arg_issue_path, O_WRONLY|O_CLOEXEC, &t, &f);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create '%s': %m", arg_issue_path);
|
||||
|
||||
out = f;
|
||||
} else
|
||||
out = stdout;
|
||||
|
||||
fprintf(out,
|
||||
"Try contacting this VM's SSH server via 'ssh vsock%%%u' from host.\n"
|
||||
"\n", cid);
|
||||
|
||||
if (f) {
|
||||
if (fchmod(fileno(f), 0644) < 0)
|
||||
return log_error_errno(errno, "Failed to adjust access mode of '%s': %m", arg_issue_path);
|
||||
|
||||
r = flink_tmpfile(f, t, arg_issue_path, LINK_TMPFILE_REPLACE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to move '%s' into place: %m", arg_issue_path);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ACTION_RM_VSOCK:
|
||||
if (arg_issue_path) {
|
||||
if (unlink(arg_issue_path) < 0) {
|
||||
if (errno != ENOENT)
|
||||
return log_error_errno(errno, "Failed to remove '%s': %m", arg_issue_path);
|
||||
|
||||
log_debug_errno(errno, "File '%s' does not exist, no operation executed.", arg_issue_path);
|
||||
} else
|
||||
log_debug("Successfully removed '%s'.", arg_issue_path);
|
||||
} else
|
||||
log_notice("STDOUT selected for issue file, not removing.");
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_MAIN_FUNCTION(run);
|
||||
@@ -20,7 +20,7 @@ Before=getty.target
|
||||
ConditionPathExists=/dev/console
|
||||
|
||||
[Service]
|
||||
ExecStart=-/sbin/agetty --noreset --noclear --keep-baud 115200,57600,38400,9600 - ${TERM}
|
||||
ExecStart=-/sbin/agetty --noreset --noclear --issue-file=/etc/issue:/etc/issue.d:/run/issue.d:/usr/lib/issue.d --keep-baud 115200,57600,38400,9600 - ${TERM}
|
||||
Type=idle
|
||||
Restart=always
|
||||
UtmpIdentifier=cons
|
||||
|
||||
@@ -25,7 +25,7 @@ Conflicts=rescue.service
|
||||
Before=rescue.service
|
||||
|
||||
[Service]
|
||||
ExecStart=-/sbin/agetty --noreset --noclear - ${TERM}
|
||||
ExecStart=-/sbin/agetty --noreset --noclear --issue-file=/etc/issue:/etc/issue.d:/run/issue.d:/usr/lib/issue.d - ${TERM}
|
||||
Type=idle
|
||||
Restart=always
|
||||
RestartSec=0
|
||||
|
||||
@@ -34,7 +34,7 @@ Before=rescue.service
|
||||
ConditionPathExists=/dev/tty0
|
||||
|
||||
[Service]
|
||||
ExecStart=-/sbin/agetty --noreset --noclear - ${TERM}
|
||||
ExecStart=-/sbin/agetty --noreset --noclear --issue-file=/etc/issue:/etc/issue.d:/run/issue.d:/usr/lib/issue.d - ${TERM}
|
||||
Type=idle
|
||||
Restart=always
|
||||
RestartSec=0
|
||||
|
||||
@@ -30,7 +30,7 @@ Conflicts=rescue.service
|
||||
Before=rescue.service
|
||||
|
||||
[Service]
|
||||
ExecStart=-/sbin/agetty --noreset --noclear --keep-baud 115200,57600,38400,9600 - ${TERM}
|
||||
ExecStart=-/sbin/agetty --noreset --noclear --issue-file=/etc/issue:/etc/issue.d:/run/issue.d:/usr/lib/issue.d --keep-baud 115200,57600,38400,9600 - ${TERM}
|
||||
Type=idle
|
||||
Restart=always
|
||||
UtmpIdentifier=%I
|
||||
|
||||
Reference in New Issue
Block a user