• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 19448983682

17 Nov 2025 11:32PM UTC coverage: 72.503% (-0.2%) from 72.719%
19448983682

push

github

web-flow
core/unit: unit_process_job() tweaks (#39753)

6 of 8 new or added lines in 1 file covered. (75.0%)

3363 existing lines in 68 files now uncovered.

308308 of 425234 relevant lines covered (72.5%)

1141671.45 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/ssh-generator/ssh-generator.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <unistd.h>
4

5
#include "alloc-util.h"
6
#include "creds-util.h"
7
#include "errno-util.h"
8
#include "fd-util.h"
9
#include "fileio.h"
10
#include "generator.h"
11
#include "install.h"
12
#include "log.h"
13
#include "parse-util.h"
14
#include "path-lookup.h"
15
#include "path-util.h"
16
#include "proc-cmdline.h"
17
#include "socket-netlink.h"
18
#include "socket-util.h"
19
#include "special.h"
20
#include "string-util.h"
21
#include "strv.h"
22
#include "virt.h"
23

24
/* A small generator binding potentially five or more SSH sockets:
25
 *
26
 *     1. Listen on AF_VSOCK port 22 if we run in a VM with AF_VSOCK enabled
27
 *     2. Listen on AF_UNIX socket /run/host/unix-export/ssh if we run in a container with /run/host/ support
28
 *     3. Listen on AF_UNIX socket /run/ssh-unix-local/socket (always)
29
 *     4. Listen on any socket specified via kernel command line option systemd.ssh_listen=
30
 *     5. Similar, but from system credential ssh.listen
31
 *
32
 * The first two provide a nice way for hosts to connect to containers and VMs they invoke via the usual SSH
33
 * logic, but without waiting for networking or suchlike. The third allows the same for local clients. */
34

35
static const char *arg_dest = NULL;
36
static bool arg_auto = true;
37
static char **arg_listen_extra = NULL;
38

39
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
×
40
        int r;
×
41

42
        assert(key);
×
43

44
        if (proc_cmdline_key_streq(key, "systemd.ssh_auto")) {
×
45
                r = value ? parse_boolean(value) : 1;
×
46
                if (r < 0)
×
47
                        log_warning_errno(r, "Failed to parse systemd.ssh_auto switch \"%s\", ignoring: %m", value);
×
48
                else
49
                        arg_auto = r;
×
50

51
        } else if (proc_cmdline_key_streq(key, "systemd.ssh_listen")) {
×
52

53
                if (proc_cmdline_value_missing(key, value))
×
54
                        return 0;
×
55

56
                SocketAddress sa;
×
57
                r = socket_address_parse(&sa, value);
×
58
                if (r < 0)
×
59
                        log_warning_errno(r, "Failed to parse systemd.ssh_listen= expression, ignoring: %s", value);
×
60
                else {
61
                        _cleanup_free_ char *s = NULL;
×
62
                        r = socket_address_print(&sa, &s);
×
63
                        if (r < 0)
×
64
                                return log_error_errno(r, "Failed to format socket address: %m");
×
65

66
                        if (strv_consume(&arg_listen_extra, TAKE_PTR(s)) < 0)
×
67
                                return log_oom();
×
68
                }
69
        }
70

71
        return 0;
72
}
73

74
static int make_sshd_template_unit(
×
75
                const char *dest,
76
                const char *template,
77
                const char *sshd_binary,
78
                const char *found_sshd_template_service,
79
                char **generated_sshd_template_unit) {
80

81
        int r;
×
82

83
        assert(dest);
×
84
        assert(template);
×
85
        assert(sshd_binary);
×
86
        assert(generated_sshd_template_unit);
×
87

88
        /* If the system has a suitable template already, symlink it under the name we want to use */
89
        if (found_sshd_template_service)
×
90
                return generator_add_symlink(
×
91
                                dest,
92
                                template,
93
                                /* dep_type= */ NULL,
94
                                found_sshd_template_service);
95

96
        if (!*generated_sshd_template_unit) {
×
97
                _cleanup_fclose_ FILE *f = NULL;
×
98

99
                /* We use a generic name for the unit, since we'll use it for both AF_UNIX and AF_VSOCK  */
UNCOV
100
                r = generator_open_unit_file_full(
×
101
                                dest,
102
                                /* source= */ NULL,
103
                                "sshd-generated@.service",
104
                                &f,
105
                                generated_sshd_template_unit,
106
                                /* ret_temp_path= */ NULL);
UNCOV
107
                if (r < 0)
×
108
                        return r;
109

UNCOV
110
                fprintf(f,
×
111
                        "[Unit]\n"
112
                        "Description=OpenSSH Per-Connection Server Daemon\n"
113
                        "Documentation=man:systemd-ssh-generator(8) man:sshd(8)\n"
114
                        "\n"
115
                        "[Service]\n"
116
                        "ExecStart=-%s -i -o \"AuthorizedKeysFile ${CREDENTIALS_DIRECTORY}/ssh.ephemeral-authorized_keys-all .ssh/authorized_keys\"\n"
117
                        "StandardInput=socket\n"
118
                        "ImportCredential=ssh.ephemeral-authorized_keys-all\n",
119
                        sshd_binary);
120

121
                r = fflush_and_check(f);
×
122
                if (r < 0)
×
UNCOV
123
                        return log_error_errno(r, "Failed to write sshd template: %m");
×
124
        }
125

UNCOV
126
        return generator_add_symlink(
×
127
                        dest,
128
                        template,
129
                        /* dep_type= */ NULL,
130
                        *generated_sshd_template_unit);
131
}
132

UNCOV
133
static int write_socket_unit(
×
134
                const char *dest,
135
                const char *unit,
136
                const char *listen_stream,
137
                const char *comment,
138
                const char *extra,
139
                bool with_ssh_access_target_dependency) {
140

UNCOV
141
        int r;
×
142

143
        assert(dest);
×
144
        assert(unit);
×
145
        assert(listen_stream);
×
UNCOV
146
        assert(comment);
×
147

148
        _cleanup_fclose_ FILE *f = NULL;
×
UNCOV
149
        r = generator_open_unit_file(
×
150
                        dest,
151
                        /* source= */ NULL,
152
                        unit,
153
                        &f);
UNCOV
154
        if (r < 0)
×
155
                return r;
156

UNCOV
157
        fprintf(f,
×
158
                "[Unit]\n"
159
                "Description=OpenSSH Server Socket (systemd-ssh-generator, %s)\n"
160
                "Documentation=man:systemd-ssh-generator(8)\n",
161
                comment);
162

163
        /* When this is a remotely accessible socket let's mark this with a milestone: ssh-access.target */
164
        if (with_ssh_access_target_dependency)
×
UNCOV
165
                fputs("Wants=ssh-access.target\n"
×
166
                      "Before=ssh-access.target\n",
167
                      f);
168

UNCOV
169
        fprintf(f,
×
170
                "\n[Socket]\n"
171
                "ListenStream=%s\n"
172
                "Accept=yes\n"
173
                "PollLimitIntervalSec=30s\n"
174
                "PollLimitBurst=50\n",
175
                listen_stream);
176

177
        if (extra)
×
UNCOV
178
                fputs(extra, f);
×
179

180
        r = fflush_and_check(f);
×
181
        if (r < 0)
×
UNCOV
182
                return log_error_errno(r, "Failed to write %s SSH socket unit: %m", comment);
×
183

UNCOV
184
        r = generator_add_symlink(
×
185
                        dest,
186
                        SPECIAL_SOCKETS_TARGET,
187
                        "wants",
188
                        unit);
189
        if (r < 0)
×
UNCOV
190
                return r;
×
191

192
        return 0;
193
}
194

UNCOV
195
static int add_vsock_socket(
×
196
                const char *dest,
197
                const char *sshd_binary,
198
                const char *found_sshd_template_unit,
199
                char **generated_sshd_template_unit) {
200

UNCOV
201
        int r;
×
202

203
        assert(dest);
×
UNCOV
204
        assert(generated_sshd_template_unit);
×
205

206
        Virtualization v = detect_virtualization();
×
207
        if (v < 0)
×
208
                return log_error_errno(v, "Failed to detect if we run in a VM: %m");
×
UNCOV
209
        if (!VIRTUALIZATION_IS_VM(v)) {
×
210
                /* NB: if we are running in a container inside a VM, then we'll *not* do AF_VSOCK stuff */
211
                log_debug("Not running in a VM, not listening on AF_VSOCK.");
×
UNCOV
212
                return 0;
×
213
        }
214

215
        _cleanup_close_ int vsock_fd = socket(AF_VSOCK, SOCK_STREAM|SOCK_CLOEXEC, 0);
×
216
        if (vsock_fd < 0) {
×
217
                if (ERRNO_IS_NOT_SUPPORTED(errno)) {
×
218
                        log_debug("Not creating AF_VSOCK ssh listener, since AF_VSOCK is not available.");
×
UNCOV
219
                        return 0;
×
220
                }
221

UNCOV
222
                return log_error_errno(errno, "Unable to test if AF_VSOCK is available: %m");
×
223
        }
224

UNCOV
225
        vsock_fd = safe_close(vsock_fd);
×
226

227
        /* Determine the local CID so that we can log it to help users to connect to this VM */
228
        unsigned local_cid;
×
229
        r = vsock_get_local_cid(&local_cid);
×
230
        if (r < 0) {
×
231
                if (ERRNO_IS_DEVICE_ABSENT(r)) {
×
232
                        log_debug("Not creating AF_VSOCK ssh listener, since /dev/vsock is not available (even though AF_VSOCK is).");
×
UNCOV
233
                        return 0;
×
234
                }
235

UNCOV
236
                return log_error_errno(r, "Failed to query local AF_VSOCK CID: %m");
×
237
        }
238

UNCOV
239
        r = make_sshd_template_unit(
×
240
                        dest,
241
                        "sshd-vsock@.service",
242
                        sshd_binary,
243
                        found_sshd_template_unit,
244
                        generated_sshd_template_unit);
UNCOV
245
        if (r < 0)
×
246
                return r;
247

UNCOV
248
        r = write_socket_unit(
×
249
                        dest,
250
                        "sshd-vsock.socket",
251
                        "vsock::22",
252
                        "AF_VSOCK",
253
                        "ExecStartPost=-/usr/lib/systemd/systemd-ssh-issue --make-vsock\n"
254
                        "ExecStopPre=-/usr/lib/systemd/systemd-ssh-issue --rm-vsock\n",
255
                        /* with_ssh_access_target_dependency= */ true);
UNCOV
256
        if (r < 0)
×
257
                return r;
258

UNCOV
259
        log_debug("Binding SSH to AF_VSOCK vsock::22.\n"
×
260
                  "→ connect via 'ssh vsock/%u' from host", local_cid);
261
        return 0;
262
}
263

UNCOV
264
static int add_local_unix_socket(
×
265
                const char *dest,
266
                const char *sshd_binary,
267
                const char *found_sshd_template_unit,
268
                char **generated_sshd_template_unit) {
269

UNCOV
270
        int r;
×
271

272
        assert(dest);
×
273
        assert(sshd_binary);
×
UNCOV
274
        assert(generated_sshd_template_unit);
×
275

UNCOV
276
        r = make_sshd_template_unit(
×
277
                        dest,
278
                        "sshd-unix-local@.service",
279
                        sshd_binary,
280
                        found_sshd_template_unit,
281
                        generated_sshd_template_unit);
UNCOV
282
        if (r < 0)
×
283
                return r;
284

UNCOV
285
        r = write_socket_unit(
×
286
                        dest,
287
                        "sshd-unix-local.socket",
288
                        "/run/ssh-unix-local/socket",
289
                        "AF_UNIX Local",
290
                        /* extra= */ NULL,
291
                        /* with_ssh_access_target_dependency= */ false);
UNCOV
292
        if (r < 0)
×
293
                return r;
294

UNCOV
295
        log_debug("Binding SSH to AF_UNIX socket /run/ssh-unix-local/socket.\n"
×
296
                  "→ connect via 'ssh .host' locally");
297
        return 0;
298
}
299

UNCOV
300
static int add_export_unix_socket(
×
301
                const char *dest,
302
                const char *sshd_binary,
303
                const char *found_sshd_template_unit,
304
                char **generated_sshd_template_unit) {
305

UNCOV
306
        int r;
×
307

308
        assert(dest);
×
309
        assert(sshd_binary);
×
UNCOV
310
        assert(generated_sshd_template_unit);
×
311

312
        Virtualization v = detect_container();
×
313
        if (v < 0)
×
314
                return log_error_errno(v, "Failed to detect if we run in a container: %m");
×
315
        if (v == VIRTUALIZATION_NONE) {
×
316
                log_debug("Not running in container, not listening on /run/host/unix-export/ssh");
×
UNCOV
317
                return 0;
×
318
        }
319

320
        if (access("/run/host/unix-export/", W_OK) < 0) {
×
321
                if (errno == ENOENT) {
×
322
                        log_debug("Container manager does not provide /run/host/unix-export/ mount, not binding AF_UNIX socket there.");
×
UNCOV
323
                        return 0;
×
324
                }
325
                if (ERRNO_IS_FS_WRITE_REFUSED(errno)) {
×
326
                        log_debug("Container manager does not provide write access to /run/host/unix-export/, not binding AF_UNIX socket there.");
×
UNCOV
327
                        return 0;
×
328
                }
329

UNCOV
330
                return log_error_errno(errno, "Unable to check if /run/host/unix-export exists: %m");
×
331
        }
332

UNCOV
333
        r = make_sshd_template_unit(
×
334
                        dest,
335
                        "sshd-unix-export@.service",
336
                        sshd_binary,
337
                        found_sshd_template_unit,
338
                        generated_sshd_template_unit);
UNCOV
339
        if (r < 0)
×
340
                return r;
341

UNCOV
342
        r = write_socket_unit(
×
343
                        dest,
344
                        "sshd-unix-export.socket",
345
                        "/run/host/unix-export/ssh",
346
                        "AF_UNIX Export",
347
                        /* extra= */ NULL,
348
                        /* with_ssh_access_target_dependency= */ true);
UNCOV
349
        if (r < 0)
×
350
                return r;
351

UNCOV
352
        log_debug("Binding SSH to AF_UNIX socket /run/host/unix-export/ssh\n"
×
353
                  "→ connect via 'ssh unix/run/systemd/nspawn/unix-export/\?\?\?/ssh' from host");
354

355
        return 0;
356
}
357

UNCOV
358
static int add_extra_sockets(
×
359
                const char *dest,
360
                const char *sshd_binary,
361
                const char *found_sshd_template_unit,
362
                char **generated_sshd_template_unit) {
363

364
        unsigned n = 1;
×
UNCOV
365
        int r;
×
366

367
        assert(dest);
×
368
        assert(sshd_binary);
×
UNCOV
369
        assert(generated_sshd_template_unit);
×
370

UNCOV
371
        if (strv_isempty(arg_listen_extra))
×
372
                return 0;
373

374
        STRV_FOREACH(i, arg_listen_extra) {
×
UNCOV
375
                _cleanup_free_ char *service = NULL, *socket = NULL;
×
376

377
                if (n > 1) {
×
378
                        if (asprintf(&service, "sshd-extra-%u@.service", n) < 0)
×
UNCOV
379
                                return log_oom();
×
380

381
                        if (asprintf(&socket, "sshd-extra-%u.socket", n) < 0)
×
UNCOV
382
                                return log_oom();
×
383
                }
384

UNCOV
385
                r = make_sshd_template_unit(
×
386
                                dest,
UNCOV
387
                                service ?: "sshd-extra@.service",
×
388
                                sshd_binary,
389
                                found_sshd_template_unit,
390
                                generated_sshd_template_unit);
UNCOV
391
                if (r < 0)
×
392
                        return r;
393

UNCOV
394
                r = write_socket_unit(
×
395
                                dest,
UNCOV
396
                                socket ?: "sshd-extra.socket",
×
397
                                *i,
398
                                *i,
399
                                /* extra= */ NULL,
400
                                /* with_ssh_access_target_dependency= */ true);
UNCOV
401
                if (r < 0)
×
402
                        return r;
403

404
                log_debug("Binding SSH to socket %s.", *i);
×
UNCOV
405
                n++;
×
406
        }
407

408
        return 0;
409
}
410

411
static int parse_credentials(void) {
×
412
        _cleanup_free_ char *b = NULL;
×
413
        size_t sz = 0;
×
UNCOV
414
        int r;
×
415

416
        r = read_credential_with_decryption("ssh.listen", (void**) &b, &sz);
×
UNCOV
417
        if (r <= 0)
×
418
                return r;
419

420
        _cleanup_fclose_ FILE *f = NULL;
×
421
        f = fmemopen_unlocked(b, sz, "r");
×
422
        if (!f)
×
UNCOV
423
                return log_oom();
×
424

425
        for (;;) {
×
UNCOV
426
                _cleanup_free_ char *item = NULL;
×
427

428
                r = read_stripped_line(f, LINE_MAX, &item);
×
UNCOV
429
                if (r == 0)
×
430
                        break;
431
                if (r < 0) {
×
UNCOV
432
                        log_error_errno(r, "Failed to parse credential 'ssh.listen': %m");
×
433
                        break;
434
                }
435

436
                if (startswith(item, "#"))
×
UNCOV
437
                        continue;
×
438

439
                SocketAddress sa;
×
440
                r = socket_address_parse(&sa, item);
×
441
                if (r < 0) {
×
442
                        log_warning_errno(r, "Failed to parse systemd.ssh_listen= expression, ignoring: %s", item);
×
UNCOV
443
                        continue;
×
444
                }
445

446
                _cleanup_free_ char *s = NULL;
×
447
                r = socket_address_print(&sa, &s);
×
448
                if (r < 0)
×
UNCOV
449
                        return log_error_errno(r, "Failed to format socket address: %m");
×
450

451
                if (strv_consume(&arg_listen_extra, TAKE_PTR(s)) < 0)
×
UNCOV
452
                        return log_oom();
×
453
        }
454

UNCOV
455
        return 0;
×
456
}
457

458
static int run(const char *dest, const char *dest_early, const char *dest_late) {
×
UNCOV
459
        int r;
×
460

UNCOV
461
        assert_se(arg_dest = dest);
×
462

463
        r = proc_cmdline_parse(parse_proc_cmdline_item, /* userdata= */ NULL, /* flags= */ 0);
×
464
        if (r < 0)
×
UNCOV
465
                log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
×
466

UNCOV
467
        (void) parse_credentials();
×
468

UNCOV
469
        strv_sort_uniq(arg_listen_extra);
×
470

471
        if (!arg_auto && strv_isempty(arg_listen_extra)) {
×
472
                log_debug("Disabling SSH generator logic, because it has been turned off explicitly.");
×
UNCOV
473
                return 0;
×
474
        }
475

476
        _cleanup_free_ char *sshd_binary = NULL;
×
477
        r = find_executable("sshd", &sshd_binary);
×
478
        if (r == -ENOENT) {
×
479
                log_debug("Disabling SSH generator logic, since sshd is not installed.");
×
UNCOV
480
                return 0;
×
481
        }
482
        if (r < 0)
×
UNCOV
483
                return log_error_errno(r, "Failed to determine if sshd is installed: %m");
×
484

485
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
×
486
        r = lookup_paths_init_or_warn(&lp, RUNTIME_SCOPE_SYSTEM, LOOKUP_PATHS_EXCLUDE_GENERATED, /* root_dir= */ NULL);
×
UNCOV
487
        if (r < 0)
×
488
                return r;
489

490
        _cleanup_free_ char *found_sshd_template_unit = NULL;
×
491
        r = unit_file_exists_full(RUNTIME_SCOPE_SYSTEM, &lp, "sshd@.service", &found_sshd_template_unit);
×
492
        if (r < 0)
×
UNCOV
493
                return log_error_errno(r, "Unable to detect if sshd@.service exists: %m");
×
494

495
        _cleanup_free_ char *generated_sshd_template_unit = NULL;
×
UNCOV
496
        RET_GATHER(r, add_extra_sockets(dest, sshd_binary, found_sshd_template_unit, &generated_sshd_template_unit));
×
497

498
        if (arg_auto) {
×
499
                RET_GATHER(r, add_vsock_socket(dest, sshd_binary, found_sshd_template_unit, &generated_sshd_template_unit));
×
500
                RET_GATHER(r, add_local_unix_socket(dest, sshd_binary, found_sshd_template_unit, &generated_sshd_template_unit));
×
UNCOV
501
                RET_GATHER(r, add_export_unix_socket(dest, sshd_binary, found_sshd_template_unit, &generated_sshd_template_unit));
×
502
        }
503

UNCOV
504
        return r;
×
505
}
506

UNCOV
507
DEFINE_MAIN_GENERATOR_FUNCTION(run);
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc