-
Notifications
You must be signed in to change notification settings - Fork 99
image/docker: use unified configfile search for cert directories #746
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,8 +33,9 @@ import ( | |
| "go.podman.io/image/v5/pkg/sysregistriesv2" | ||
| "go.podman.io/image/v5/pkg/tlsclientconfig" | ||
| "go.podman.io/image/v5/types" | ||
| "go.podman.io/storage/pkg/configfile" | ||
| "go.podman.io/storage/pkg/fileutils" | ||
| "go.podman.io/storage/pkg/homedir" | ||
| "go.podman.io/storage/pkg/unshare" | ||
| "golang.org/x/sync/semaphore" | ||
| ) | ||
|
|
||
|
|
@@ -60,19 +61,10 @@ const ( | |
| backoffMaxDelay = 60 * time.Second | ||
| ) | ||
|
|
||
| type certPath struct { | ||
| path string | ||
| absolute bool | ||
| var perHostCertDirs = []string{ | ||
| etcDir + "/docker/certs.d", | ||
| } | ||
|
|
||
| var ( | ||
| homeCertDir = filepath.FromSlash(".config/containers/certs.d") | ||
| perHostCertDirs = []certPath{ | ||
| {path: etcDir + "/containers/certs.d", absolute: true}, | ||
| {path: etcDir + "/docker/certs.d", absolute: true}, | ||
| } | ||
| ) | ||
|
|
||
| // extensionSignature and extensionSignatureList come from github.com/openshift/origin/pkg/dockerregistry/server/signaturedispatcher.go: | ||
| // signature represents a Docker image signature. | ||
| type extensionSignature struct { | ||
|
|
@@ -167,22 +159,26 @@ func dockerCertDir(sys *types.SystemContext, hostPort string) (string, error) { | |
| return filepath.Join(sys.DockerPerHostCertDirPath, hostPort), nil | ||
| } | ||
|
|
||
| var ( | ||
| hostCertDir string | ||
| fullCertDirPath string | ||
| ) | ||
| conf := &configfile.Directory{ | ||
| Name: "certs", | ||
| UserId: unshare.GetRootlessUID(), | ||
| ExtraDirs: perHostCertDirs, | ||
| } | ||
|
|
||
| for _, perHostCertDir := range append([]certPath{{path: filepath.Join(homedir.Get(), homeCertDir), absolute: false}}, perHostCertDirs...) { | ||
| if sys != nil && sys.RootForImplicitAbsolutePaths != "" && perHostCertDir.absolute { | ||
| hostCertDir = filepath.Join(sys.RootForImplicitAbsolutePaths, perHostCertDir.path) | ||
| } else { | ||
| hostCertDir = perHostCertDir.path | ||
| } | ||
| if sys != nil && sys.RootForImplicitAbsolutePaths != "" { | ||
| conf.RootForImplicitAbsolutePaths = sys.RootForImplicitAbsolutePaths | ||
| } | ||
|
|
||
| dirs, err := configfile.ContainersResourceDirs(conf) | ||
| if err != nil { | ||
| return "", err | ||
| } | ||
|
|
||
| fullCertDirPath = filepath.Join(hostCertDir, hostPort) | ||
| for _, baseDir := range dirs { | ||
| fullCertDirPath := filepath.Join(baseDir, hostPort) | ||
| err := fileutils.Exists(fullCertDirPath) | ||
| if err == nil { | ||
| break | ||
| return fullCertDirPath, nil | ||
| } | ||
| if os.IsNotExist(err) { | ||
| continue | ||
|
|
@@ -193,7 +189,7 @@ func dockerCertDir(sys *types.SystemContext, hostPort string) (string, error) { | |
| } | ||
| return "", err | ||
| } | ||
| return fullCertDirPath, nil | ||
| return "", nil | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hum, the code is not actually ready to consume
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, the code works, but it is not as good as it could be. The previous code returned non-existing directory while the current code returns The directory is then passed to But I will add check for empty |
||
| } | ||
|
|
||
| // newDockerClientFromRef returns a new dockerClient instance for refHostname (a host a specified in the Docker image reference, not canonicalized to dockerRegistry) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,6 +31,9 @@ var ( | |
| // This can be overridden at build time with the following go linker flag: | ||
| // -ldflags '-X go.podman.io/storage/pkg/configfile.adminOverrideConfigPath=$your_path' | ||
| adminOverrideConfigPath = getAdminOverrideConfigPath() | ||
|
|
||
| // userConfigPathForResourceDirs is a test hook for ContainersResourceDirs. | ||
| userConfigPathForResourceDirs = UserConfigPath | ||
|
Comment on lines
+35
to
+36
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it is not clear to me why do you need this? The other test does not need it? you can just sentenv the XDG_CONFIG_HOME dir?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was the best way I found out to let the In that test, I want to test that even if I therefore needed to find a way how to monkey-patch the |
||
| ) | ||
|
|
||
| type File struct { | ||
|
|
@@ -308,6 +311,98 @@ func getDropInPaths(mainPath, suffix string, uid int) []string { | |
| return paths | ||
| } | ||
|
|
||
| type Directory struct { | ||
| // The base name of the config directory. | ||
| // Must not be empty and must not contain the path separator. | ||
| // The full path is then constructed by the ContainersResourceDirs function. | ||
| // For example, "certs" will be joined with ".d" to form "certs.d". | ||
| Name string | ||
|
|
||
| // RootForImplicitAbsolutePaths is the path to an alternate root | ||
| // If not "", prefixed to any absolute paths used by default in the package. | ||
| // NOTE: This does NOT affect paths starting by $HOME or environment variables paths. | ||
| RootForImplicitAbsolutePaths string | ||
|
|
||
| // UserId is the id of the user running this. Used to know where to search in the | ||
| // different "rootful" and "rootless" drop in lookup paths. | ||
| UserId int | ||
|
|
||
| // ExtraDirs is a list of additional directories to include in the search. | ||
| ExtraDirs []string | ||
| } | ||
|
|
||
| // ContainersResourceDirs returns a list of configuration directories for a | ||
| // logical resource name (for example "registries" or "certs") using the | ||
| // unified configfile search semantics. | ||
| // | ||
| // The returned slice is ordered from highest to lowest priority and contains | ||
| // only directories that exist and can be accessed. Non‑existent or | ||
| // permission‑denied directories are silently skipped; other filesystem errors | ||
| // are returned to the caller. | ||
| // | ||
| // The search covers, where configured (listed here from lowest to highest precedence. | ||
| // It can be extended with additional absolute directories via extraDirs (lowest precedence). | ||
| func ContainersResourceDirs(conf *Directory) ([]string, error) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. API wise I am not to sure we just want to return the list here? certs.d just needs the first match starting with home, etc, /usr... so would it not be more logical to pass in the name we search as argument and the return a signle full path and exit early?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wanted to make it more generic and let the caller to decide what to do with these directories. But if you think we can just return single directory, I'm also fine changing it. |
||
| candidates := make([]string, 0, 7+len(conf.ExtraDirs)) | ||
|
|
||
| userConfig, _ := userConfigPathForResourceDirs() | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. error should not be silently ignored
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is aligned with the old code - it used I can add warning here in the |
||
| if userConfig != "" { | ||
| userConfig = filepath.Join(userConfig, conf.Name) | ||
| candidates = append(candidates, userConfig+dropInSuffix) | ||
|
|
||
| } | ||
|
|
||
| overrideConfig := adminOverrideConfigPath | ||
| if overrideConfig != "" { | ||
| overrideConfig = filepath.Join(overrideConfig, conf.Name) | ||
| if conf.RootForImplicitAbsolutePaths != "" { | ||
| overrideConfig = filepath.Join(conf.RootForImplicitAbsolutePaths, overrideConfig) | ||
| } | ||
| overridePaths := getDropInPaths(overrideConfig, "", conf.UserId) | ||
| for i := len(overridePaths) - 1; i >= 0; i-- { | ||
| candidates = append(candidates, overridePaths[i]) | ||
| } | ||
| } | ||
|
|
||
| for i := len(conf.ExtraDirs) - 1; i >= 0; i-- { | ||
| dir := conf.ExtraDirs[i] | ||
| if conf.RootForImplicitAbsolutePaths != "" { | ||
| dir = filepath.Join(conf.RootForImplicitAbsolutePaths, dir) | ||
| } | ||
| candidates = append(candidates, dir) | ||
| } | ||
|
|
||
| defaultConfig := systemConfigPath | ||
| if defaultConfig != "" { | ||
| defaultConfig = filepath.Join(defaultConfig, conf.Name) | ||
| if conf.RootForImplicitAbsolutePaths != "" { | ||
| defaultConfig = filepath.Join(conf.RootForImplicitAbsolutePaths, defaultConfig) | ||
| } | ||
| defaultPaths := getDropInPaths(defaultConfig, "", conf.UserId) | ||
| for i := len(defaultPaths) - 1; i >= 0; i-- { | ||
| candidates = append(candidates, defaultPaths[i]) | ||
| } | ||
| } | ||
|
|
||
| dirs := make([]string, 0, len(candidates)+len(conf.ExtraDirs)) | ||
|
|
||
| for _, dir := range candidates { | ||
| info, err := os.Stat(dir) | ||
| if err != nil { | ||
| if errors.Is(err, fs.ErrNotExist) || errors.Is(err, fs.ErrPermission) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why do we skip permission errors?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought it is a good idea to fallback to next directory if we cannot access some directory. In my opinion, this function should find valid directory which we can read files from. Thinking about it now, maybe showing a warning in the ErrPermission case would be good thing to do. Or do you think this condition should be removed? |
||
| continue | ||
| } | ||
| return nil, err | ||
| } | ||
| if !info.IsDir() { | ||
| continue | ||
| } | ||
| dirs = append(dirs, dir) | ||
| } | ||
|
|
||
| return dirs, nil | ||
| } | ||
|
|
||
| func moduleDirectories(defaultConfig, overrideConfig, userConfig string) []string { | ||
| const moduleSuffix = ".modules" | ||
| modules := make([]string, 0, 3) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mtrmac Do we still want this path? API wise it seems rather ugly to define that search order with that additional path.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good point and it would make the API cleaner definitely.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do need it. It’s never been deprecated by us, and it’s the one place users can use to affect both consumers.
See e.g. https://github.com/search?q=repo%3Aopenshift%2Fmachine-config-operator%20%2Fdocker%2Fcerts.d&type=code for a minimal proof.