Skip to content

feat(registry): list private-registry tags in autocomplete - #60

Merged
malickyeu merged 1 commit into
mainfrom
feat/private-registry-tags
Jun 16, 2026
Merged

feat(registry): list private-registry tags in autocomplete#60
malickyeu merged 1 commit into
mainfrom
feat/private-registry-tags

Conversation

@malickyeu

Copy link
Copy Markdown
Contributor

Summary

Image-tag suggestions (editor image: lines and the create-container form) now also list tags from a configured private registry via the Docker Registry HTTP API v2 — previously only Docker Hub tags were offered. The frontend already sent the full repo (incl. host), so this is almost entirely backend.

  • ImageTags branches on the ref's host: docker.io keeps using Hub's public API; any other host is served by a new v2 client that performs the Bearer-token handshake (or Basic auth) using the registry's stored, encrypted credentials.
  • Only hosts the admin configured under Registries are ever contacted (resolved via AuthForHost); an unconfigured host yields no remote tags and makes no request.

Security — the SSRF-sensitive part (HIGH finding found in review, fixed, re-verified)

The token realm and redirect targets come from the registry's responses (attacker-influenced if a registry is compromised). Controls:

  • Guarded dialer: at connect time, on the resolved IP, every request and redirect must go either to the configured registry host or to a non-internal address. This closes IPv6 ([::1], [fd00::1]), IPv4-mapped ([::ffff:169.254.169.254]), decimal/hex/octal IPv4 encodings, and DNS-rebinding — vectors a naive literal check misses. It dials the verified IPs directly (no re-resolution → no TOCTOU), and fails closed if any resolved IP is internal.
  • Token realms must be https for non-local registries (no cleartext credential leak); cleartext Basic is confined to loopback registries the admin explicitly configured.
  • The repo path is regex-validated before reaching the URL; responses are size-capped (2 MiB) and time-bounded (8s); redirects are capped (3) and can't downgrade https.

Type of change

  • New feature

Checklist

  • go test -short ./... and go vet ./... pass
  • gofmt gate is clean
  • Frontend type-checks + unit tests pass (comment-only FE change)
  • Rebuilt and committed web/dist
  • Added/updated tests for the change
  • Updated docs/ and CHANGELOG.md

Notes for reviewers

Pentests in internal/docker/registry_tags_test.go: insecure/internal realms (incl. IPv6 / v4-mapped / unspecified) rejected, TestGuardedDialer_BlocksInternalNonRegistry, unconfigured-host-makes-no-request, and unsatisfiable-Basic-challenge → no tags; plus the happy-path bearer handshake and direct Basic auth against an httptest registry. The HIGH SSRF gap from the first review pass (IPv6/numeric realm bypass) was fixed with the guarded dialer and independently re-verified.

Image-tag suggestions (editor `image:` lines and the create-container form)
now also list tags from a configured private registry via the Docker Registry
HTTP API v2 — previously only Docker Hub tags were offered.

- ImageTags branches on the ref's host: docker.io keeps using Hub's public
  API; any other host is served by a new v2 client that performs the
  Bearer-token handshake (or Basic auth) using the registry's stored,
  encrypted credentials.
- Only hosts the admin configured under Registries are ever contacted; an
  unconfigured host yields no remote tags (no request is made).

Security (this is the SSRF-sensitive part):
- A guarded dialer enforces, at connect time on the resolved IP, that every
  outbound request and redirect goes either to the configured registry host
  or to a non-internal address — closing IPv6, IPv4-mapped, decimal/hex/octal
  and DNS-rebinding vectors. It dials the verified IPs directly (no second
  resolution → no TOCTOU).
- Token realms must be https for non-local registries (no cleartext credential
  leak); cleartext Basic is confined to loopback registries.
- The repo path is validated before it reaches the URL; responses are size-
  and time-bounded; redirects are capped and can't downgrade https.

Tests cover the bearer handshake, direct Basic auth, repo-path parsing, and
pentests: insecure/internal realms (incl. IPv6 / v4-mapped / unspecified) are
rejected, the guarded dialer blocks internal non-registry targets, an
unconfigured host makes no request, and an unsatisfiable Basic challenge
yields no tags.
Copilot AI review requested due to automatic review settings June 16, 2026 08:42

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@malickyeu
malickyeu merged commit 794f118 into main Jun 16, 2026
3 checks passed
@malickyeu
malickyeu deleted the feat/private-registry-tags branch June 16, 2026 08:45
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.

2 participants