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

systemd / systemd / 15199265962

22 May 2025 09:40PM UTC coverage: 72.061% (-0.02%) from 72.079%
15199265962

push

github

bluca
tests: fix TEST-74-AUX-UTILS.varlinkctl.sh (#37562)

per Daan's explanation:
other subtests running as testuser apparently use systemd-run --user
--machine testuser@.host which turns user tracking in logind into "by
pin" mode. when the last pinning session exits it terminates the user.

299156 of 415145 relevant lines covered (72.06%)

703915.84 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 "missing_socket.h"
14
#include "parse-util.h"
15
#include "path-lookup.h"
16
#include "path-util.h"
17
#include "proc-cmdline.h"
18
#include "socket-netlink.h"
19
#include "socket-util.h"
20
#include "special.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 const char *arg_dest = NULL;
37
static bool arg_auto = true;
38
static char **arg_listen_extra = NULL;
39

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

43
        assert(key);
×
44

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

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

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

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

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

72
        return 0;
73
}
74

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

82
        int r;
×
83

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

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

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

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

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
                        "[Service]\n"
115
                        "ExecStart=-%s -i -o \"AuthorizedKeysFile ${CREDENTIALS_DIRECTORY}/ssh.ephemeral-authorized_keys-all .ssh/authorized_keys\"\n"
116
                        "StandardInput=socket\n"
117
                        "ImportCredential=ssh.ephemeral-authorized_keys-all",
118
                        sshd_binary);
119

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

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

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

139
        int r;
×
140

141
        assert(dest);
×
142
        assert(unit);
×
143
        assert(listen_stream);
×
144
        assert(comment);
×
145

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

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

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

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

175
        r = fflush_and_check(f);
×
176
        if (r < 0)
×
177
                return log_error_errno(r, "Failed to write %s SSH socket unit: %m", comment);
×
178

179
        r = generator_add_symlink(
×
180
                        dest,
181
                        SPECIAL_SOCKETS_TARGET,
182
                        "wants",
183
                        unit);
184
        if (r < 0)
×
185
                return r;
×
186

187
        return 0;
188
}
189

190
static int add_vsock_socket(
×
191
                const char *dest,
192
                const char *sshd_binary,
193
                const char *found_sshd_template_unit,
194
                char **generated_sshd_template_unit) {
195

196
        int r;
×
197

198
        assert(dest);
×
199
        assert(generated_sshd_template_unit);
×
200

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

210
        _cleanup_close_ int vsock_fd = socket(AF_VSOCK, SOCK_STREAM|SOCK_CLOEXEC, 0);
×
211
        if (vsock_fd < 0) {
×
212
                if (ERRNO_IS_NOT_SUPPORTED(errno)) {
×
213
                        log_debug("Not creating AF_VSOCK ssh listener, since AF_VSOCK is not available.");
×
214
                        return 0;
×
215
                }
216

217
                return log_error_errno(errno, "Unable to test if AF_VSOCK is available: %m");
×
218
        }
219

220
        vsock_fd = safe_close(vsock_fd);
×
221

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

231
                return log_error_errno(r, "Failed to query local AF_VSOCK CID: %m");
×
232
        }
233

234
        r = make_sshd_template_unit(
×
235
                        dest,
236
                        "sshd-vsock@.service",
237
                        sshd_binary,
238
                        found_sshd_template_unit,
239
                        generated_sshd_template_unit);
240
        if (r < 0)
×
241
                return r;
242

243
        r = write_socket_unit(
×
244
                        dest,
245
                        "sshd-vsock.socket",
246
                        "vsock::22",
247
                        "AF_VSOCK",
248
                        /* with_ssh_access_target_dependency= */ true);
249
        if (r < 0)
×
250
                return r;
251

252
        log_debug("Binding SSH to AF_VSOCK vsock::22.\n"
×
253
                  "→ connect via 'ssh vsock/%u' from host", local_cid);
254
        return 0;
255
}
256

257
static int add_local_unix_socket(
×
258
                const char *dest,
259
                const char *sshd_binary,
260
                const char *found_sshd_template_unit,
261
                char **generated_sshd_template_unit) {
262

263
        int r;
×
264

265
        assert(dest);
×
266
        assert(sshd_binary);
×
267
        assert(generated_sshd_template_unit);
×
268

269
        r = make_sshd_template_unit(
×
270
                        dest,
271
                        "sshd-unix-local@.service",
272
                        sshd_binary,
273
                        found_sshd_template_unit,
274
                        generated_sshd_template_unit);
275
        if (r < 0)
×
276
                return r;
277

278
        r = write_socket_unit(
×
279
                        dest,
280
                        "sshd-unix-local.socket",
281
                        "/run/ssh-unix-local/socket",
282
                        "AF_UNIX Local",
283
                        /* with_ssh_access_target_dependency= */ false);
284
        if (r < 0)
×
285
                return r;
286

287
        log_debug("Binding SSH to AF_UNIX socket /run/ssh-unix-local/socket.\n"
×
288
                  "→ connect via 'ssh .host' locally");
289
        return 0;
290
}
291

292
static int add_export_unix_socket(
×
293
                const char *dest,
294
                const char *sshd_binary,
295
                const char *found_sshd_template_unit,
296
                char **generated_sshd_template_unit) {
297

298
        int r;
×
299

300
        assert(dest);
×
301
        assert(sshd_binary);
×
302
        assert(generated_sshd_template_unit);
×
303

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

312
        if (access("/run/host/unix-export/", W_OK) < 0) {
×
313
                if (errno == ENOENT) {
×
314
                        log_debug("Container manager does not provide /run/host/unix-export/ mount, not binding AF_UNIX socket there.");
×
315
                        return 0;
×
316
                }
317
                if (errno == EROFS || ERRNO_IS_PRIVILEGE(errno)) {
×
318
                        log_debug("Container manager does not provide write access to /run/host/unix-export/, not binding AF_UNIX socket there.");
×
319
                        return 0;
×
320
                }
321

322
                return log_error_errno(errno, "Unable to check if /run/host/unix-export exists: %m");
×
323
        }
324

325
        r = make_sshd_template_unit(
×
326
                        dest,
327
                        "sshd-unix-export@.service",
328
                        sshd_binary,
329
                        found_sshd_template_unit,
330
                        generated_sshd_template_unit);
331
        if (r < 0)
×
332
                return r;
333

334
        r = write_socket_unit(
×
335
                        dest,
336
                        "sshd-unix-export.socket",
337
                        "/run/host/unix-export/ssh",
338
                        "AF_UNIX Export",
339
                        /* with_ssh_access_target_dependency= */ true);
340
        if (r < 0)
×
341
                return r;
342

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

346
        return 0;
347
}
348

349
static int add_extra_sockets(
×
350
                const char *dest,
351
                const char *sshd_binary,
352
                const char *found_sshd_template_unit,
353
                char **generated_sshd_template_unit) {
354

355
        unsigned n = 1;
×
356
        int r;
×
357

358
        assert(dest);
×
359
        assert(sshd_binary);
×
360
        assert(generated_sshd_template_unit);
×
361

362
        if (strv_isempty(arg_listen_extra))
×
363
                return 0;
364

365
        STRV_FOREACH(i, arg_listen_extra) {
×
366
                _cleanup_free_ char *service = NULL, *socket = NULL;
×
367

368
                if (n > 1) {
×
369
                        if (asprintf(&service, "sshd-extra-%u@.service", n) < 0)
×
370
                                return log_oom();
×
371

372
                        if (asprintf(&socket, "sshd-extra-%u.socket", n) < 0)
×
373
                                return log_oom();
×
374
                }
375

376
                r = make_sshd_template_unit(
×
377
                                dest,
378
                                service ?: "sshd-extra@.service",
×
379
                                sshd_binary,
380
                                found_sshd_template_unit,
381
                                generated_sshd_template_unit);
382
                if (r < 0)
×
383
                        return r;
384

385
                r = write_socket_unit(
×
386
                                dest,
387
                                socket ?: "sshd-extra.socket",
×
388
                                *i,
389
                                *i,
390
                                /* with_ssh_access_target_dependency= */ true);
391
                if (r < 0)
×
392
                        return r;
393

394
                log_debug("Binding SSH to socket %s.", *i);
×
395
                n++;
×
396
        }
397

398
        return 0;
399
}
400

401
static int parse_credentials(void) {
×
402
        _cleanup_free_ char *b = NULL;
×
403
        size_t sz = 0;
×
404
        int r;
×
405

406
        r = read_credential_with_decryption("ssh.listen", (void**) &b, &sz);
×
407
        if (r <= 0)
×
408
                return r;
409

410
        _cleanup_fclose_ FILE *f = NULL;
×
411
        f = fmemopen_unlocked(b, sz, "r");
×
412
        if (!f)
×
413
                return log_oom();
×
414

415
        for (;;) {
×
416
                _cleanup_free_ char *item = NULL;
×
417

418
                r = read_stripped_line(f, LINE_MAX, &item);
×
419
                if (r == 0)
×
420
                        break;
421
                if (r < 0) {
×
422
                        log_error_errno(r, "Failed to parse credential 'ssh.listen': %m");
×
423
                        break;
424
                }
425

426
                if (startswith(item, "#"))
×
427
                        continue;
×
428

429
                SocketAddress sa;
×
430
                r = socket_address_parse(&sa, item);
×
431
                if (r < 0) {
×
432
                        log_warning_errno(r, "Failed to parse systemd.ssh_listen= expression, ignoring: %s", item);
×
433
                        continue;
×
434
                }
435

436
                _cleanup_free_ char *s = NULL;
×
437
                r = socket_address_print(&sa, &s);
×
438
                if (r < 0)
×
439
                        return log_error_errno(r, "Failed to format socket address: %m");
×
440

441
                if (strv_consume(&arg_listen_extra, TAKE_PTR(s)) < 0)
×
442
                        return log_oom();
×
443
        }
444

445
        return 0;
×
446
}
447

448
static int run(const char *dest, const char *dest_early, const char *dest_late) {
×
449
        int r;
×
450

451
        assert_se(arg_dest = dest);
×
452

453
        r = proc_cmdline_parse(parse_proc_cmdline_item, /* userdata= */ NULL, /* flags= */ 0);
×
454
        if (r < 0)
×
455
                log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
×
456

457
        (void) parse_credentials();
×
458

459
        strv_sort_uniq(arg_listen_extra);
×
460

461
        if (!arg_auto && strv_isempty(arg_listen_extra)) {
×
462
                log_debug("Disabling SSH generator logic, because as it has been turned off explicitly.");
×
463
                return 0;
×
464
        }
465

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

475
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
×
476
        r = lookup_paths_init_or_warn(&lp, RUNTIME_SCOPE_SYSTEM, LOOKUP_PATHS_EXCLUDE_GENERATED, /* root_dir= */ NULL);
×
477
        if (r < 0)
×
478
                return r;
479

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

485
        _cleanup_free_ char *generated_sshd_template_unit = NULL;
×
486
        RET_GATHER(r, add_extra_sockets(dest, sshd_binary, found_sshd_template_unit, &generated_sshd_template_unit));
×
487

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

494
        return r;
×
495
}
496

497
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