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

systemd / systemd / 21652912420

03 Feb 2026 07:37PM UTC coverage: 72.767% (-0.04%) from 72.804%
21652912420

push

github

web-flow
Introduce name_to_handle_at_u64() helper function and use it for getting pidfd ID and cgroup ID (#40500)

26 of 28 new or added lines in 8 files covered. (92.86%)

2713 existing lines in 47 files now uncovered.

311340 of 427861 relevant lines covered (72.77%)

1144580.1 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 "ssh-util.h"
21
#include "string-util.h"
22
#include "strv.h"
23
#include "virt.h"
24

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

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

UNCOV
39
STATIC_DESTRUCTOR_REGISTER(arg_listen_extra, strv_freep);
×
40

41
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
×
UNCOV
42
        int r;
×
43

UNCOV
44
        assert(key);
×
45

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

UNCOV
53
        } else if (proc_cmdline_key_streq(key, "systemd.ssh_listen")) {
×
54

55
                if (proc_cmdline_value_missing(key, value))
×
UNCOV
56
                        return 0;
×
57

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

68
                        if (strv_consume(&arg_listen_extra, TAKE_PTR(s)) < 0)
×
UNCOV
69
                                return log_oom();
×
70
                }
71
        }
72

73
        return 0;
74
}
75

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

UNCOV
83
        int r;
×
84

85
        assert(dest);
×
86
        assert(template);
×
87
        assert(sshd_binary);
×
UNCOV
88
        assert(generated_sshd_template_unit);
×
89

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

98
        if (!*generated_sshd_template_unit) {
×
UNCOV
99
                _cleanup_fclose_ FILE *f = NULL;
×
100

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

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

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

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

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

UNCOV
143
        int r;
×
144

145
        assert(dest);
×
146
        assert(unit);
×
147
        assert(listen_stream);
×
UNCOV
148
        assert(comment);
×
149

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

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

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

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

179
        if (extra)
×
UNCOV
180
                fputs(extra, f);
×
181

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

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

194
        return 0;
195
}
196

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

UNCOV
203
        int r;
×
204

205
        assert(dest);
×
UNCOV
206
        assert(generated_sshd_template_unit);
×
207

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

217
        r = vsock_open_or_warn(/* ret= */ NULL);
×
UNCOV
218
        if (r <= 0)
×
219
                return r;
220

221
        /* Determine the local CID so that we can log it to help users to connect to this VM */
222
        unsigned local_cid;
×
223
        r = vsock_get_local_cid_or_warn(&local_cid);
×
UNCOV
224
        if (r <= 0)
×
225
                return r;
226

UNCOV
227
        r = make_sshd_template_unit(
×
228
                        dest,
229
                        "sshd-vsock@.service",
230
                        sshd_binary,
231
                        found_sshd_template_unit,
232
                        generated_sshd_template_unit);
UNCOV
233
        if (r < 0)
×
234
                return r;
235

UNCOV
236
        r = write_socket_unit(
×
237
                        dest,
238
                        "sshd-vsock.socket",
239
                        "vsock::22",
240
                        "AF_VSOCK",
241
                        "ExecStartPost=-/usr/lib/systemd/systemd-ssh-issue --make-vsock\n"
242
                        "ExecStopPre=-/usr/lib/systemd/systemd-ssh-issue --rm-vsock\n",
243
                        /* with_ssh_access_target_dependency= */ true);
UNCOV
244
        if (r < 0)
×
245
                return r;
246

UNCOV
247
        log_debug("Binding SSH to AF_VSOCK vsock::22.\n"
×
248
                  "→ connect via 'ssh vsock/%u' from host", local_cid);
249
        return 0;
250
}
251

UNCOV
252
static int add_local_unix_socket(
×
253
                const char *dest,
254
                const char *sshd_binary,
255
                const char *found_sshd_template_unit,
256
                char **generated_sshd_template_unit) {
257

UNCOV
258
        int r;
×
259

260
        assert(dest);
×
261
        assert(sshd_binary);
×
UNCOV
262
        assert(generated_sshd_template_unit);
×
263

UNCOV
264
        r = make_sshd_template_unit(
×
265
                        dest,
266
                        "sshd-unix-local@.service",
267
                        sshd_binary,
268
                        found_sshd_template_unit,
269
                        generated_sshd_template_unit);
UNCOV
270
        if (r < 0)
×
271
                return r;
272

UNCOV
273
        r = write_socket_unit(
×
274
                        dest,
275
                        "sshd-unix-local.socket",
276
                        "/run/ssh-unix-local/socket",
277
                        "AF_UNIX Local",
278
                        /* extra= */ NULL,
279
                        /* with_ssh_access_target_dependency= */ false);
UNCOV
280
        if (r < 0)
×
281
                return r;
282

UNCOV
283
        log_debug("Binding SSH to AF_UNIX socket /run/ssh-unix-local/socket.\n"
×
284
                  "→ connect via 'ssh .host' locally");
285
        return 0;
286
}
287

UNCOV
288
static int add_export_unix_socket(
×
289
                const char *dest,
290
                const char *sshd_binary,
291
                const char *found_sshd_template_unit,
292
                char **generated_sshd_template_unit) {
293

UNCOV
294
        int r;
×
295

296
        assert(dest);
×
297
        assert(sshd_binary);
×
UNCOV
298
        assert(generated_sshd_template_unit);
×
299

300
        Virtualization v = detect_container();
×
301
        if (v < 0)
×
302
                return log_error_errno(v, "Failed to detect if we run in a container: %m");
×
303
        if (v == VIRTUALIZATION_NONE) {
×
304
                log_debug("Not running in container, not listening on /run/host/unix-export/ssh");
×
UNCOV
305
                return 0;
×
306
        }
307

308
        if (access("/run/host/unix-export/", W_OK) < 0) {
×
309
                if (errno == ENOENT) {
×
310
                        log_debug("Container manager does not provide /run/host/unix-export/ mount, not binding AF_UNIX socket there.");
×
UNCOV
311
                        return 0;
×
312
                }
313
                if (ERRNO_IS_FS_WRITE_REFUSED(errno)) {
×
314
                        log_debug("Container manager does not provide write access to /run/host/unix-export/, not binding AF_UNIX socket there.");
×
UNCOV
315
                        return 0;
×
316
                }
317

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

UNCOV
321
        r = make_sshd_template_unit(
×
322
                        dest,
323
                        "sshd-unix-export@.service",
324
                        sshd_binary,
325
                        found_sshd_template_unit,
326
                        generated_sshd_template_unit);
UNCOV
327
        if (r < 0)
×
328
                return r;
329

UNCOV
330
        r = write_socket_unit(
×
331
                        dest,
332
                        "sshd-unix-export.socket",
333
                        "/run/host/unix-export/ssh",
334
                        "AF_UNIX Export",
335
                        /* extra= */ NULL,
336
                        /* with_ssh_access_target_dependency= */ true);
UNCOV
337
        if (r < 0)
×
338
                return r;
339

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

343
        return 0;
344
}
345

UNCOV
346
static int add_extra_sockets(
×
347
                const char *dest,
348
                const char *sshd_binary,
349
                const char *found_sshd_template_unit,
350
                char **generated_sshd_template_unit) {
351

352
        unsigned n = 1;
×
UNCOV
353
        int r;
×
354

355
        assert(dest);
×
356
        assert(sshd_binary);
×
UNCOV
357
        assert(generated_sshd_template_unit);
×
358

UNCOV
359
        STRV_FOREACH(i, arg_listen_extra) {
×
UNCOV
360
                _cleanup_free_ char *service = NULL, *socket = NULL;
×
361

362
                if (n > 1) {
×
UNCOV
363
                        if (asprintf(&service, "sshd-extra-%u@.service", n) < 0)
×
364
                                return log_oom();
×
365

366
                        if (asprintf(&socket, "sshd-extra-%u.socket", n) < 0)
×
UNCOV
367
                                return log_oom();
×
368
                }
369

UNCOV
370
                r = make_sshd_template_unit(
×
371
                                dest,
372
                                service ?: "sshd-extra@.service",
×
373
                                sshd_binary,
374
                                found_sshd_template_unit,
375
                                generated_sshd_template_unit);
UNCOV
376
                if (r < 0)
×
377
                        return r;
378

UNCOV
379
                r = write_socket_unit(
×
380
                                dest,
381
                                socket ?: "sshd-extra.socket",
×
382
                                *i,
383
                                *i,
384
                                /* extra= */ NULL,
385
                                /* with_ssh_access_target_dependency= */ true);
UNCOV
386
                if (r < 0)
×
387
                        return r;
388

UNCOV
389
                log_debug("Binding SSH to socket %s.", *i);
×
UNCOV
390
                n++;
×
391
        }
392

393
        return 0;
394
}
395

UNCOV
396
static int parse_credentials(void) {
×
UNCOV
397
        _cleanup_free_ char *b = NULL;
×
398
        size_t sz = 0;
×
399
        int r;
×
400

401
        r = read_credential_with_decryption("ssh.listen", (void**) &b, &sz);
×
UNCOV
402
        if (r <= 0)
×
403
                return r;
404

UNCOV
405
        _cleanup_fclose_ FILE *f = NULL;
×
UNCOV
406
        f = fmemopen_unlocked(b, sz, "r");
×
407
        if (!f)
×
408
                return log_oom();
×
409

410
        for (;;) {
×
UNCOV
411
                _cleanup_free_ char *item = NULL;
×
412

413
                r = read_stripped_line(f, LINE_MAX, &item);
×
UNCOV
414
                if (r == 0)
×
415
                        break;
416
                if (r < 0) {
×
UNCOV
417
                        log_error_errno(r, "Failed to parse credential 'ssh.listen': %m");
×
418
                        break;
419
                }
420

UNCOV
421
                if (startswith(item, "#"))
×
UNCOV
422
                        continue;
×
423

424
                SocketAddress sa;
×
UNCOV
425
                r = socket_address_parse(&sa, item);
×
426
                if (r < 0) {
×
427
                        log_warning_errno(r, "Failed to parse systemd.ssh_listen= expression, ignoring: %s", item);
×
428
                        continue;
×
429
                }
430

UNCOV
431
                _cleanup_free_ char *s = NULL;
×
UNCOV
432
                r = socket_address_print(&sa, &s);
×
433
                if (r < 0)
×
434
                        return log_error_errno(r, "Failed to format socket address: %m");
×
435

436
                if (strv_consume(&arg_listen_extra, TAKE_PTR(s)) < 0)
×
UNCOV
437
                        return log_oom();
×
438
        }
439

UNCOV
440
        return 0;
×
441
}
442

UNCOV
443
static int run(const char *dest, const char *dest_early, const char *dest_late) {
×
UNCOV
444
        int r;
×
445

446
        assert(dest);
×
447

448
        r = proc_cmdline_parse(parse_proc_cmdline_item, /* userdata= */ NULL, /* flags= */ 0);
×
UNCOV
449
        if (r < 0)
×
450
                log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
×
451

452
        (void) parse_credentials();
×
453

454
        strv_sort_uniq(arg_listen_extra);
×
455

456
        if (!arg_auto && strv_isempty(arg_listen_extra)) {
×
UNCOV
457
                log_debug("Disabling SSH generator logic, because it has been turned off explicitly.");
×
458
                return 0;
×
459
        }
460

UNCOV
461
        _cleanup_free_ char *sshd_binary = NULL;
×
UNCOV
462
        r = find_executable("sshd", &sshd_binary);
×
463
        if (r == -ENOENT) {
×
464
                log_debug("Disabling SSH generator logic, since sshd is not installed.");
×
465
                return 0;
×
466
        }
467
        if (r < 0)
×
UNCOV
468
                return log_error_errno(r, "Failed to determine if sshd is installed: %m");
×
469

470
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
×
UNCOV
471
        r = lookup_paths_init_or_warn(&lp, RUNTIME_SCOPE_SYSTEM, LOOKUP_PATHS_EXCLUDE_GENERATED, /* root_dir= */ NULL);
×
472
        if (r < 0)
×
473
                return r;
474

UNCOV
475
        _cleanup_free_ char *found_sshd_template_unit = NULL;
×
UNCOV
476
        r = unit_file_exists_full(RUNTIME_SCOPE_SYSTEM, &lp,
×
477
                                  SEARCH_FOLLOW_CONFIG_SYMLINKS,
478
                                  "sshd@.service",
479
                                  &found_sshd_template_unit);
UNCOV
480
        if (r < 0)
×
UNCOV
481
                return log_error_errno(r, "Unable to detect if sshd@.service exists: %m");
×
482

483
        _cleanup_free_ char *generated_sshd_template_unit = NULL;
×
UNCOV
484
        RET_GATHER(r, add_extra_sockets(dest, sshd_binary, found_sshd_template_unit, &generated_sshd_template_unit));
×
485

486
        if (arg_auto) {
×
UNCOV
487
                RET_GATHER(r, add_vsock_socket(dest, sshd_binary, found_sshd_template_unit, &generated_sshd_template_unit));
×
488
                RET_GATHER(r, add_local_unix_socket(dest, sshd_binary, found_sshd_template_unit, &generated_sshd_template_unit));
×
489
                RET_GATHER(r, add_export_unix_socket(dest, sshd_binary, found_sshd_template_unit, &generated_sshd_template_unit));
×
490
        }
491

UNCOV
492
        return r;
×
493
}
494

UNCOV
495
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