feat(registry): adding initial support for containerd registry mirror configurations#120
feat(registry): adding initial support for containerd registry mirror configurations#120
Conversation
There was a problem hiding this comment.
Pull request overview
Adds initial containerd registry mirror support to KubeSolo by wiring a new --registry-mirror flag through startup, generating hosts.toml entries under containerd’s certs.d, and documenting the behavior/ownership model.
Changes:
- Introduces
--registry-mirror(StringMap) and plumbs mirror config throughtypes.Embeddedinto the containerd runtime service. - Writes per-registry
/etc/containerd/certs.d/<upstream>/hosts.tomlwith a “managed by kubesolo” marker andoverride_pathhandling for path-based mirrors. - Sets containerd
registry.config_pathto/etc/containerd/certs.d, updatesinstall.shto pass mirror flags, and adds docs + unit tests.
Reviewed changes
Copilot reviewed 11 out of 14 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
types/types.go |
Adds RegistryMirrors to embedded config passed into runtime components. |
types/const.go |
Defines DefaultContainerdCertsDir used for containerd registry config. |
cmd/kubesolo/main.go |
Plumbs flag value into types.Embedded for containerd service consumption. |
internal/config/flags/flags.go |
Adds --registry-mirror flag (and env var) as a string map. |
pkg/runtime/containerd/service.go |
Stores registry mirror map on the containerd service struct. |
pkg/runtime/containerd/executor.go |
Writes mirror files before generating/starting containerd. |
pkg/runtime/containerd/config.go |
Sets containerd registry config_path to the certs directory. |
pkg/runtime/containerd/registry.go |
Implements hosts.toml generation + managed-file ownership logic + writing. |
pkg/runtime/containerd/registry_test.go |
Unit tests for TOML generation and managed-marker detection. |
install.sh |
Accepts --registry-mirror and appends it to generated service args. |
docs/configuration/registry-mirrors.md |
Documents usage, generated config, and manual-ownership model. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 15 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // certs.d. It rejects empty values, path separators, and path traversal sequences that | ||
| // could allow writes outside the certs.d directory. | ||
| func isValidUpstream(upstream string) bool { | ||
| return upstream != "" && !strings.Contains(upstream, "/") && !strings.Contains(upstream, "..") |
There was a problem hiding this comment.
isValidUpstream still allows special directory names like "." which makes filepath.Join(types.DefaultContainerdCertsDir, upstream) resolve to the certs.d root and could cause kubesolo to write /etc/containerd/certs.d/hosts.toml instead of a per-registry subdir. Tighten validation to reject "." (and ideally any value where filepath.Clean(upstream) differs or results in "."/".."), and add a test case for it.
| return upstream != "" && !strings.Contains(upstream, "/") && !strings.Contains(upstream, "..") | |
| if upstream == "" { | |
| return false | |
| } | |
| // Reject obvious traversal patterns early. | |
| if strings.Contains(upstream, "..") { | |
| return false | |
| } | |
| // Ensure the value is a single, clean path element that is not "." or "..". | |
| cleaned := filepath.Clean(upstream) | |
| if cleaned != upstream { | |
| return false | |
| } | |
| if cleaned == "." || cleaned == ".." { | |
| return false | |
| } | |
| // Disallow any path separators to ensure we only ever create one subdirectory | |
| // under the certs.d root. | |
| if strings.Contains(upstream, "/") || strings.Contains(upstream, string(os.PathSeparator)) { | |
| return false | |
| } | |
| return true |
| --registry-mirror=*) | ||
| val="${arg#*=}" | ||
| case "$val" in | ||
| *[\;\&\|\`\$\<\>]*) | ||
| handle_error "--registry-mirror: mirror URL contains shell metacharacters: $val" | ||
| ;; | ||
| esac | ||
| REGISTRY_MIRRORS_ARGS="$REGISTRY_MIRRORS_ARGS --registry-mirror=$val" | ||
| ;; |
There was a problem hiding this comment.
The --registry-mirror value is appended into CMD_ARGS and later executed via eval (foreground/daemon modes). The current metacharacter filter does not block quotes, whitespace, or newlines, so a crafted argument could break out of the intended command and execute arbitrary shell code. Prefer avoiding eval entirely (build an argv array and exec it), or at minimum apply robust escaping/quoting and explicitly reject control characters/whitespace/quotes in val.
stevensbkang
left a comment
There was a problem hiding this comment.
Please take a look at comments
| if err := s.writeRegistryMirrorFiles(s.registryMirrors); err != nil { | ||
| log.Error().Str("component", "containerd").Msgf("failed to write registry mirror files: %v", err) | ||
| s.terminate() | ||
| return err | ||
| } | ||
|
|
There was a problem hiding this comment.
Is it necessary to terminate the program if writing registry mirror files fails?
| sandboxImageFile string | ||
| localPathProvisionerImageFile string | ||
| isPortainerEdge bool | ||
| registryMirrors map[string]string |
There was a problem hiding this comment.
Should we name registryMirrors to registries instead? I think it can be used for normal private registries as well, not just mirrors!
| u, err := url.Parse(mirrorURL) | ||
| if err != nil || (u.Scheme != "http" && u.Scheme != "https") || u.Host == "" { | ||
| log.Warn().Str("component", "containerd"). | ||
| Str("upstream", upstream). | ||
| Str("mirror", mirrorURL). | ||
| Msg("skipping registry mirror: invalid URL, unsupported scheme (must be http or https), or missing host") | ||
| continue | ||
| } | ||
| if u.User != nil { | ||
| log.Warn().Str("component", "containerd"). | ||
| Str("upstream", upstream). | ||
| Msg("skipping registry mirror: credentials in mirror URL are not supported — manage the hosts.toml file manually") | ||
| continue | ||
| } |
There was a problem hiding this comment.
How about relocating these checks into the isValidUpstream function?
| // certs.d. It rejects empty values, path separators, and path traversal sequences that | ||
| // could allow writes outside the certs.d directory. | ||
| func isValidUpstream(upstream string) bool { | ||
| return upstream != "" && !strings.Contains(upstream, "/") && !strings.Contains(upstream, "..") |
There was a problem hiding this comment.
If this line is to check if upstream string is safe to use as a directory name, how about using filepath.IsLocal(upstream)?
No description provided.