Skip to content

Commit 6d6c98f

Browse files
committed
Give access to the CA certificates from the host
This uses the same approach taken by Flatpak [1] to ensure that the certificates from certificate authorities (or CAs) that are available inside a Toolbx container are kept synchronized with the host operating system. Any program that uses PKCS containers#11 to access CA certificates should see the same ones both inside the container and on the host. During every 'enter' and 'run' command, toolbox(1) ensures that an instance of 'p11-kit server' is running on the host listening on a local file system socket that's accessible to both the container and the host. If an instance is already running, then a second one is not created. The location of the socket is injected into the container through the P11_KIT_SERVER_ADDRESS environment variable. Jsut like Flatpak, the singleton 'p11-kit server' process is not terminated, when the last 'enter' or 'run' command exits. The Toolbx container's entry point configures it to use the p11-kit-client.so PKCS containers#11 module instead of the usual p11-kit-trust.so module. This talks to the 'p11-kit server' instance running on the host over the socket instead of reading the CA certificates that are present inside the container. However, unlike Flatpak, this doesn't use D-Bus to set up the communication between the container and the host, because when invoked as 'sudo toolbox ...' there's no user or session D-Bus instance available for the root user. This set-up is skipped if 'p11-kit server' can't be run on the host, or if the /etc/pkcs11/modules directory for configuring PKCS containers#11 modules or p11-kit-client.so are missing inside the container. None of these are considered hard dependencies to accommodate size-constrained OSes like Fedora CoreOS that might not have 'p11-kit server', and existing Toolbx containers and old images that might not have p11-kit-client.so. The UBI-based toolbox images haven't yet been updated to contain p11-kit-client.so. Until that happens, containers created from them won't have access to the CA certificates from the host. The CI needs to be run without 'p11-kit server' because the lingering singleton process causes Bats to hang when tearing down the suite of system tests [2]. To terminate the 'p11-kit server' instance run by the system tests, it needs to be distinguishable from the instance run by 'normal' use of Toolbx by the user. One way to do this is to isolate the host operating system's XDG_RUNTIME_DIR from the system tests. Unfortunately, this is easier said than done [3]. So, this workaround has to suffice until the problem is solved. [1] Flatpak commit 66b2ff40f7caf3a7 flatpak/flatpak@66b2ff40f7caf3a7 flatpak/flatpak#1757 p11-glue/p11-kit#68 [2] https://bats-core.readthedocs.io/en/stable/writing-tests.html [3] containers#1652 containers#626
1 parent 9e776b6 commit 6d6c98f

11 files changed

+202
-9
lines changed

.github/workflows/ubuntu-tests.yaml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#
2-
# Copyright © 2023 – 2024 Red Hat, Inc.
2+
# Copyright © 2023 – 2025 Red Hat, Inc.
33
#
44
# Licensed under the Apache License, Version 2.0 (the "License");
55
# you may not use this file except in compliance with the License.
@@ -55,6 +55,9 @@ jobs:
5555
systemd \
5656
udisks2
5757
58+
- name: Ensure that 'p11-kit server' is absent
59+
run: sudo dpkg --purge p11-kit
60+
5861
- name: Set up PATH for Go 1.21
5962
run: |
6063
echo "PATH=/usr/lib/go-1.21/bin:$PATH" >> "$GITHUB_ENV"
@@ -131,7 +134,7 @@ jobs:
131134
working-directory: containers/toolbox/src
132135

133136
- name: Set up build directory
134-
run: meson setup --fatal-meson-warnings builddir
137+
run: meson setup builddir
135138
working-directory: containers/toolbox
136139

137140
- name: Build

meson.build

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,27 @@ bats = find_program('bats', required: false)
2323
codespell = find_program('codespell', required: false)
2424
htpasswd = find_program('htpasswd', required: false)
2525
openssl = find_program('openssl', required: false)
26+
27+
p11kit_server_works = false
28+
p11kit = find_program('p11-kit', required: false)
29+
if p11kit.found()
30+
res = run_command(p11kit, 'server', check: false)
31+
if res.returncode() == 0
32+
error('Command \'p11-kit server\' was supposed to fail')
33+
endif
34+
35+
res_stdout = res.stdout()
36+
if res_stdout.contains('--name') and res_stdout.contains('--provider')
37+
p11kit_server_works = true
38+
else
39+
warning('Command \'p11-kit server\' doesn\'t work')
40+
endif
41+
endif
42+
43+
if not p11kit_server_works
44+
warning('Containers won\'t have access to the CA certificates from the host')
45+
endif
46+
2647
podman = find_program('podman', required: false)
2748
shellcheck = find_program('shellcheck', required: false)
2849
skopeo = find_program('skopeo', required: false)

playbooks/dependencies-centos-9-stream.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@
3838
- codespell
3939
- fish
4040

41+
- name: Ensure that 'p11-kit server' is absent
42+
become: yes
43+
package:
44+
name:
45+
- p11-kit-server
46+
state: absent
47+
4148
- name: Download Go modules
4249
command: go mod download -x
4350
environment:

playbooks/dependencies-fedora-restricted.yaml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#
2-
# Copyright © 2023 – 2024 Red Hat, Inc.
2+
# Copyright © 2023 – 2025 Red Hat, Inc.
33
#
44
# Licensed under the Apache License, Version 2.0 (the "License");
55
# you may not use this file except in compliance with the License.
@@ -47,6 +47,15 @@
4747
update_cache: "{{ true if zuul.attempts > 1 else false }}"
4848
use: "{{ 'dnf' if zuul.attempts > 1 else 'auto' }}"
4949

50+
- name: Ensure that 'p11-kit server' is absent
51+
become: yes
52+
package:
53+
name:
54+
- p11-kit-server
55+
state: absent
56+
update_cache: "{{ true if zuul.attempts > 1 else false }}"
57+
use: "{{ 'dnf' if zuul.attempts > 1 else 'auto' }}"
58+
5059
- name: Ensure that podman(1) is absent
5160
become: yes
5261
package:

playbooks/dependencies-fedora.yaml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#
2-
# Copyright © 2022 – 2024 Red Hat, Inc.
2+
# Copyright © 2022 – 2025 Red Hat, Inc.
33
#
44
# Licensed under the Apache License, Version 2.0 (the "License");
55
# you may not use this file except in compliance with the License.
@@ -39,6 +39,15 @@
3939
- udisks2
4040
use: "{{ 'dnf' if zuul.attempts > 1 else 'auto' }}"
4141

42+
- name: Ensure that 'p11-kit server' is absent
43+
become: yes
44+
package:
45+
name:
46+
- p11-kit-server
47+
state: absent
48+
update_cache: "{{ true if zuul.attempts > 1 else false }}"
49+
use: "{{ 'dnf' if zuul.attempts > 1 else 'auto' }}"
50+
4251
- name: Download Go modules
4352
command: go mod download -x
4453
environment:

playbooks/setup-env-migration-path-for-coreos-toolbox.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@
2020
- include_tasks: dependencies-centos-9-stream.yaml
2121

2222
- name: Set up build directory
23-
command: meson -Dmigration_path_for_coreos_toolbox=true --fatal-meson-warnings builddir
23+
command: meson -Dmigration_path_for_coreos_toolbox=true builddir
2424
args:
2525
chdir: '{{ zuul.project.src_dir }}'

playbooks/setup-env-restricted.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#
2-
# Copyright © 2023 – 2024 Red Hat, Inc.
2+
# Copyright © 2023 – 2025 Red Hat, Inc.
33
#
44
# Licensed under the Apache License, Version 2.0 (the "License");
55
# you may not use this file except in compliance with the License.
@@ -20,6 +20,6 @@
2020
- include_tasks: dependencies-fedora-restricted.yaml
2121

2222
- name: Set up build directory
23-
command: meson setup --fatal-meson-warnings builddir
23+
command: meson setup builddir
2424
args:
2525
chdir: '{{ zuul.project.src_dir }}'

playbooks/setup-env.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@
2020
- include_tasks: dependencies-fedora.yaml
2121

2222
- name: Set up build directory
23-
command: meson setup --fatal-meson-warnings builddir
23+
command: meson setup builddir
2424
args:
2525
chdir: '{{ zuul.project.src_dir }}'

src/cmd/initContainer.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,10 @@ func initContainer(cmd *cobra.Command, args []string) error {
301301
return err
302302
}
303303

304+
if err := configurePKCS11(targetUser); err != nil {
305+
return err
306+
}
307+
304308
if err := configureRPM(); err != nil {
305309
return err
306310
}
@@ -569,6 +573,57 @@ func configureKerberos() error {
569573
return nil
570574
}
571575

576+
func configurePKCS11(targetUser *user.User) error {
577+
const logPrefix = "Configuring PKCS #11 to read from the host"
578+
logrus.Debugf("%s", logPrefix)
579+
580+
if path := "/etc/pkcs11/modules"; !utils.PathExists(path) {
581+
logrus.Debugf("%s: directory %s not found", logPrefix, path)
582+
logrus.Debugf("%s: skipping", logPrefix)
583+
return nil
584+
}
585+
586+
if ok, err := utils.IsP11KitClientPresent(); err != nil {
587+
logrus.Debugf("%s: %s", logPrefix, err)
588+
589+
if !ok {
590+
logrus.Debugf("%s: p11-kit-client.so not found", logPrefix)
591+
logrus.Debugf("%s: skipping", logPrefix)
592+
return nil
593+
}
594+
} else {
595+
if !ok {
596+
logrus.Debugf("%s: p11-kit-client.so not found", logPrefix)
597+
logrus.Debugf("%s: skipping", logPrefix)
598+
return nil
599+
}
600+
}
601+
602+
if path, err := utils.GetP11KitServerSocket(targetUser); err != nil {
603+
return err
604+
} else if !utils.PathExists(path) {
605+
logrus.Debugf("%s: socket %s not found", logPrefix, path)
606+
logrus.Debugf("%s: skipping", logPrefix)
607+
return nil
608+
}
609+
610+
var builder strings.Builder
611+
builder.WriteString("# Written by Toolbx\n")
612+
builder.WriteString("# https://containertoolbx.org/\n")
613+
builder.WriteString("\n")
614+
builder.WriteString("module: p11-kit-client.so\n")
615+
616+
pkcs11ConfigString := builder.String()
617+
pkcs11ConfigBytes := []byte(pkcs11ConfigString)
618+
if err := renameio.WriteFile("/etc/pkcs11/modules/p11-kit-trust.module",
619+
pkcs11ConfigBytes,
620+
0644); err != nil {
621+
return fmt.Errorf("failed to configure PKCS #11 to read from the host: %w", err)
622+
}
623+
624+
return nil
625+
}
626+
572627
func configureRPM() error {
573628
if !utils.PathExists("/usr/lib/rpm/macros.d") {
574629
return nil

src/cmd/run.go

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"path/filepath"
2828
"strconv"
2929
"strings"
30+
"syscall"
3031
"time"
3132

3233
"github.com/containers/toolbox/pkg/nvidia"
@@ -283,6 +284,11 @@ func runCommand(container string,
283284
cdiEnviron = append(cdiEnviron, cdiSpecForNvidia.ContainerEdits.Env...)
284285
}
285286

287+
p11KitServerEnviron, err := startP11KitServer()
288+
if err != nil {
289+
return err
290+
}
291+
286292
startContainerTimestamp := time.Unix(-1, 0)
287293

288294
if entryPointPID <= 0 {
@@ -335,10 +341,11 @@ func runCommand(container string,
335341

336342
logrus.Debugf("Container %s is initialized", container)
337343

344+
environ := append(cdiEnviron, p11KitServerEnviron...)
338345
if err := runCommandWithFallbacks(container,
339346
preserveFDs,
340347
command,
341-
cdiEnviron,
348+
environ,
342349
emitEscapeSequence,
343350
fallbackToBash); err != nil {
344351
return err
@@ -1033,6 +1040,68 @@ func startContainer(container string) error {
10331040
return nil
10341041
}
10351042

1043+
func startP11KitServer() ([]string, error) {
1044+
serverSocket, err := utils.GetP11KitServerSocket(currentUser)
1045+
if err != nil {
1046+
return nil, err
1047+
}
1048+
1049+
const logPrefix = "Starting 'p11-kit server'"
1050+
logrus.Debugf("%s with socket %s", logPrefix, serverSocket)
1051+
1052+
serverSocketLock, err := utils.GetP11KitServerSocketLock(currentUser)
1053+
if err != nil {
1054+
return nil, err
1055+
}
1056+
1057+
serverSocketLockFile, err := utils.Flock(serverSocketLock, syscall.LOCK_EX)
1058+
if err != nil {
1059+
logrus.Debugf("%s: %s", logPrefix, err)
1060+
1061+
var errFlock *utils.FlockError
1062+
1063+
if errors.As(err, &errFlock) {
1064+
if errors.Is(err, utils.ErrFlockAcquire) {
1065+
err = utils.ErrFlockAcquire
1066+
} else if errors.Is(err, utils.ErrFlockCreate) {
1067+
err = utils.ErrFlockCreate
1068+
} else {
1069+
panicMsg := fmt.Sprintf("unexpected %T: %s", err, err)
1070+
panic(panicMsg)
1071+
}
1072+
}
1073+
1074+
return nil, err
1075+
}
1076+
1077+
defer serverSocketLockFile.Close()
1078+
1079+
serverSocketAddress := fmt.Sprintf("P11_KIT_SERVER_ADDRESS=unix:path=%s", serverSocket)
1080+
serverEnviron := []string{
1081+
serverSocketAddress,
1082+
}
1083+
1084+
if utils.PathExists(serverSocket) {
1085+
logrus.Debugf("%s: socket %s already exists", logPrefix, serverSocket)
1086+
logrus.Debugf("%s: skipping", logPrefix)
1087+
return serverEnviron, nil
1088+
}
1089+
1090+
serverArgs := []string{
1091+
"server",
1092+
"--name", serverSocket,
1093+
"--provider", "p11-kit-trust.so",
1094+
"pkcs11:model=p11-kit-trust?write-protected=yes",
1095+
}
1096+
1097+
if err := shell.Run("p11-kit", nil, nil, nil, serverArgs...); err != nil {
1098+
logrus.Debugf("%s failed: %s", logPrefix, err)
1099+
return nil, nil
1100+
}
1101+
1102+
return serverEnviron, nil
1103+
}
1104+
10361105
func (err *entryPointError) Error() string {
10371106
return err.msg
10381107
}

0 commit comments

Comments
 (0)