Skip to content

Commit 409751d

Browse files
committed
image/docker: use unified configfile search for cert directories
Switch `dockerCertDir` to use the `configfile.GetSearchPaths` for resolving certificate directories. New `ExtraDropInDirectories` is introduced to add extra drop-in directory after XDG and before admin override. Signed-off-by: Jan Kaluza <jkaluza@redhat.com>
1 parent eacd47a commit 409751d

3 files changed

Lines changed: 95 additions & 44 deletions

File tree

image/docker/docker_client.go

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ import (
3333
"go.podman.io/image/v5/pkg/sysregistriesv2"
3434
"go.podman.io/image/v5/pkg/tlsclientconfig"
3535
"go.podman.io/image/v5/types"
36+
"go.podman.io/storage/pkg/configfile"
3637
"go.podman.io/storage/pkg/fileutils"
37-
"go.podman.io/storage/pkg/homedir"
38+
"go.podman.io/storage/pkg/unshare"
3839
"golang.org/x/sync/semaphore"
3940
)
4041

@@ -60,19 +61,6 @@ const (
6061
backoffMaxDelay = 60 * time.Second
6162
)
6263

63-
type certPath struct {
64-
path string
65-
absolute bool
66-
}
67-
68-
var (
69-
homeCertDir = filepath.FromSlash(".config/containers/certs.d")
70-
perHostCertDirs = []certPath{
71-
{path: etcDir + "/containers/certs.d", absolute: true},
72-
{path: etcDir + "/docker/certs.d", absolute: true},
73-
}
74-
)
75-
7664
// extensionSignature and extensionSignatureList come from github.com/openshift/origin/pkg/dockerregistry/server/signaturedispatcher.go:
7765
// signature represents a Docker image signature.
7866
type extensionSignature struct {
@@ -167,22 +155,35 @@ func dockerCertDir(sys *types.SystemContext, hostPort string) (string, error) {
167155
return filepath.Join(sys.DockerPerHostCertDirPath, hostPort), nil
168156
}
169157

170-
var (
171-
hostCertDir string
172-
fullCertDirPath string
173-
)
158+
rootForImplicitAbsolutePaths := ""
159+
if sys != nil {
160+
rootForImplicitAbsolutePaths = sys.RootForImplicitAbsolutePaths
161+
}
174162

175-
for _, perHostCertDir := range append([]certPath{{path: filepath.Join(homedir.Get(), homeCertDir), absolute: false}}, perHostCertDirs...) {
176-
if sys != nil && sys.RootForImplicitAbsolutePaths != "" && perHostCertDir.absolute {
177-
hostCertDir = filepath.Join(sys.RootForImplicitAbsolutePaths, perHostCertDir.path)
178-
} else {
179-
hostCertDir = perHostCertDir.path
180-
}
163+
paths, err := configfile.GetSearchPaths(&configfile.File{
164+
Name: "certs",
165+
Extension: "d",
166+
DoNotUseExtensionForConfigName: true,
167+
UserId: unshare.GetRootlessUID(),
168+
RootForImplicitAbsolutePaths: rootForImplicitAbsolutePaths,
169+
})
170+
if err != nil {
171+
return "", err
172+
}
173+
174+
candidates := make([]string, 0, len(paths.DropInDirectories)+1)
175+
candidates = append(candidates, paths.DropInDirectories...)
176+
perHostCertDir := etcDir + "/docker/certs.d"
177+
if rootForImplicitAbsolutePaths != "" {
178+
perHostCertDir = filepath.Join(rootForImplicitAbsolutePaths, perHostCertDir)
179+
}
180+
candidates = append(candidates, perHostCertDir)
181181

182-
fullCertDirPath = filepath.Join(hostCertDir, hostPort)
183-
err := fileutils.Exists(fullCertDirPath)
182+
for _, baseDir := range candidates {
183+
fullCertDirPath := filepath.Join(baseDir, hostPort)
184+
err = fileutils.Exists(fullCertDirPath)
184185
if err == nil {
185-
break
186+
return fullCertDirPath, nil
186187
}
187188
if os.IsNotExist(err) {
188189
continue
@@ -193,7 +194,7 @@ func dockerCertDir(sys *types.SystemContext, hostPort string) (string, error) {
193194
}
194195
return "", err
195196
}
196-
return fullCertDirPath, nil
197+
return "", nil
197198
}
198199

199200
// newDockerClientFromRef returns a new dockerClient instance for refHostname (a host a specified in the Docker image reference, not canonicalized to dockerRegistry)
@@ -263,8 +264,10 @@ func newDockerClient(sys *types.SystemContext, registry, reference string) (*doc
263264
if err != nil {
264265
return nil, err
265266
}
266-
if err := tlsclientconfig.SetupCertificates(certDir, tlsClientConfig); err != nil {
267-
return nil, err
267+
if certDir != "" {
268+
if err := tlsclientconfig.SetupCertificates(certDir, tlsClientConfig); err != nil {
269+
return nil, err
270+
}
268271
}
269272

270273
// Check if TLS verification shall be skipped (default=false) which can

image/docker/docker_client_test.go

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"net/http"
1010
"net/http/httptest"
1111
"net/url"
12+
"os"
1213
"path/filepath"
1314
"strings"
1415
"testing"
@@ -21,26 +22,44 @@ import (
2122
)
2223

2324
func TestDockerCertDir(t *testing.T) {
24-
const nondefaultFullPath = "/this/is/not/the/default/full/path"
25-
const nondefaultPerHostDir = "/this/is/not/the/default/certs.d"
25+
tempRoot := t.TempDir()
26+
t.Setenv("XDG_CONFIG_HOME", filepath.Join(tempRoot, "xdg"))
27+
28+
nondefaultFullPath := filepath.Join(tempRoot, "nondefault", "full", "path")
29+
nondefaultPerHostDir := filepath.Join(tempRoot, "nondefault", "certs.d")
2630
const variableReference = "$HOME"
27-
const rootPrefix = "/root/prefix"
31+
rootPrefix := filepath.Join(tempRoot, "rootprefix")
2832
const registryHostPort = "thishostdefinitelydoesnotexist:5000"
33+
const registryHostPortPerHostOverVendor = "thishostdefinitelydoesnotexist:5001"
34+
35+
hostDirs := []string{
36+
"/etc/containers/certs.d",
37+
"/etc/docker/certs.d",
38+
}
39+
40+
// Create RootForImplicitAbsolutePaths-prefixed locations.
41+
for _, d := range hostDirs {
42+
require.NoError(t, os.MkdirAll(filepath.Join(rootPrefix, d, registryHostPort), 0o755))
43+
}
44+
require.NoError(t, os.MkdirAll(filepath.Join(rootPrefix, "/etc/docker/certs.d", registryHostPortPerHostOverVendor), 0o755))
45+
require.NoError(t, os.MkdirAll(filepath.Join(rootPrefix, "/usr/share/containers/certs.d", registryHostPortPerHostOverVendor), 0o755))
46+
require.NoError(t, os.MkdirAll(filepath.Join(nondefaultPerHostDir, registryHostPort), 0o755))
2947

30-
systemPerHostResult := filepath.Join(perHostCertDirs[len(perHostCertDirs)-1].path, registryHostPort)
3148
for _, c := range []struct {
3249
sys *types.SystemContext
50+
hostPort string
3351
expected string
3452
}{
35-
// The common case
36-
{nil, systemPerHostResult},
37-
// There is a context, but it does not override the path.
38-
{&types.SystemContext{}, systemPerHostResult},
53+
// Work with nil SystemContext.
54+
{nil, registryHostPort, ""},
55+
// Work with empty SystemContext.
56+
{&types.SystemContext{}, registryHostPort, ""},
3957
// Full path overridden
40-
{&types.SystemContext{DockerCertPath: nondefaultFullPath}, nondefaultFullPath},
58+
{&types.SystemContext{DockerCertPath: nondefaultFullPath}, registryHostPort, nondefaultFullPath},
4159
// Per-host path overridden
4260
{
4361
&types.SystemContext{DockerPerHostCertDirPath: nondefaultPerHostDir},
62+
registryHostPort,
4463
filepath.Join(nondefaultPerHostDir, registryHostPort),
4564
},
4665
// Both overridden
@@ -49,26 +68,35 @@ func TestDockerCertDir(t *testing.T) {
4968
DockerCertPath: nondefaultFullPath,
5069
DockerPerHostCertDirPath: nondefaultPerHostDir,
5170
},
71+
registryHostPort,
5272
nondefaultFullPath,
5373
},
5474
// Root overridden
5575
{
5676
&types.SystemContext{RootForImplicitAbsolutePaths: rootPrefix},
57-
filepath.Join(rootPrefix, systemPerHostResult),
77+
registryHostPort,
78+
filepath.Join(rootPrefix, "/etc/containers/certs.d", registryHostPort),
79+
},
80+
{
81+
&types.SystemContext{RootForImplicitAbsolutePaths: rootPrefix},
82+
registryHostPortPerHostOverVendor,
83+
filepath.Join(rootPrefix, "/usr/share/containers/certs.d", registryHostPortPerHostOverVendor),
5884
},
5985
// Root and path overrides present simultaneously,
6086
{
6187
&types.SystemContext{
6288
DockerCertPath: nondefaultFullPath,
6389
RootForImplicitAbsolutePaths: rootPrefix,
6490
},
91+
registryHostPort,
6592
nondefaultFullPath,
6693
},
6794
{
6895
&types.SystemContext{
6996
DockerPerHostCertDirPath: nondefaultPerHostDir,
7097
RootForImplicitAbsolutePaths: rootPrefix,
7198
},
99+
registryHostPort,
72100
filepath.Join(nondefaultPerHostDir, registryHostPort),
73101
},
74102
// … and everything at once
@@ -78,16 +106,18 @@ func TestDockerCertDir(t *testing.T) {
78106
DockerPerHostCertDirPath: nondefaultPerHostDir,
79107
RootForImplicitAbsolutePaths: rootPrefix,
80108
},
109+
registryHostPort,
81110
nondefaultFullPath,
82111
},
83112
// No environment expansion happens in the overridden paths
84-
{&types.SystemContext{DockerCertPath: variableReference}, variableReference},
113+
{&types.SystemContext{DockerCertPath: variableReference}, registryHostPort, variableReference},
85114
{
86115
&types.SystemContext{DockerPerHostCertDirPath: variableReference},
116+
registryHostPort,
87117
filepath.Join(variableReference, registryHostPort),
88118
},
89119
} {
90-
path, err := dockerCertDir(c.sys, registryHostPort)
120+
path, err := dockerCertDir(c.sys, c.hostPort)
91121
require.Equal(t, nil, err)
92122
assert.Equal(t, c.expected, path)
93123
}

image/docs/containers-certs.d.5.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,26 @@
44
containers-certs.d - Directory for storing custom container-registry TLS configurations
55

66
# DESCRIPTION
7-
A custom TLS configuration for a container registry can be configured by creating a directory under `$HOME/.config/containers/certs.d` or `/etc/containers/certs.d`.
8-
The name of the directory must correspond to the `host`[`:port`] of the registry (e.g., `my-registry.com:5000`).
7+
A custom TLS configuration for a container registry can be configured by creating a directory named after the registry `host`[`:port`] (for example, `my-registry.com:5000`) in one of the following locations.
8+
Directories are consulted in this order (highest priority first):
9+
10+
- For both rootful and rootless:
11+
- `$XDG_CONFIG_HOME/containers/certs.d/` (or `$HOME/.config/containers/certs.d/` if `XDG_CONFIG_HOME` is unset)
12+
- `/etc/containers/certs.d/`
13+
- For rootful (UID == 0):
14+
- `/etc/containers/certs.rootful.d/`
15+
- For rootless (UID > 0):
16+
- `/etc/containers/certs.rootless.d/`
17+
- `/etc/containers/certs.rootless.d/<UID>/`
18+
- For both rootful and rootless:
19+
- `/usr/share/containers/certs.d/`
20+
- For rootful (UID == 0):
21+
- `/usr/share/containers/certs.rootful.d/`
22+
- For rootless (UID > 0):
23+
- `/usr/share/containers/certs.rootless.d/`
24+
- `/usr/share/containers/certs.rootless.d/<UID>/`
25+
- Compatibility fallback:
26+
- `/etc/docker/certs.d/`
927

1028
The port part presence / absence must precisely match the port usage in image references,
1129
e.g. to affect `podman pull registry.example/foo`,

0 commit comments

Comments
 (0)