Skip to content

feat(registry): adding initial support for containerd registry mirror configurations#120

Draft
yajith wants to merge 5 commits intodevelopfrom
feat/registry-mirror-config
Draft

feat(registry): adding initial support for containerd registry mirror configurations#120
yajith wants to merge 5 commits intodevelopfrom
feat/registry-mirror-config

Conversation

@yajith
Copy link
Member

@yajith yajith commented Mar 21, 2026

No description provided.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 through types.Embedded into the containerd runtime service.
  • Writes per-registry /etc/containerd/certs.d/<upstream>/hosts.toml with a “managed by kubesolo” marker and override_path handling for path-based mirrors.
  • Sets containerd registry.config_path to /etc/containerd/certs.d, updates install.sh to 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.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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, "..")
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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

Copilot uses AI. Check for mistakes.
Comment on lines +775 to +783
--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"
;;
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Copy link
Member

@stevensbkang stevensbkang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please take a look at comments

Comment on lines +31 to +36
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
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it necessary to terminate the program if writing registry mirror files fails?

sandboxImageFile string
localPathProvisionerImageFile string
isPortainerEdge bool
registryMirrors map[string]string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we name registryMirrors to registries instead? I think it can be used for normal private registries as well, not just mirrors!

Comment on lines +115 to +128
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
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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, "..")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this line is to check if upstream string is safe to use as a directory name, how about using filepath.IsLocal(upstream)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants