Skip to content

Commit 2d08185

Browse files
committed
cmd/docker-trust: remove dependency on cli/internal
Create a copy of the registry package to use, so that code used only for trust can be removed from the cli/internal package. Signed-off-by: Sebastiaan van Stijn <[email protected]>
1 parent 8951279 commit 2d08185

File tree

8 files changed

+502
-8
lines changed

8 files changed

+502
-8
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package registry
2+
3+
// AuthClientID is used the ClientID used for the token server
4+
const AuthClientID = "docker"
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
2+
//go:build go1.24
3+
4+
package registry
5+
6+
import (
7+
"net"
8+
"strings"
9+
10+
"github.com/distribution/reference"
11+
"github.com/moby/moby/api/types/registry"
12+
)
13+
14+
// IndexName is the name of the index
15+
const IndexName = "docker.io"
16+
17+
func normalizeIndexName(val string) string {
18+
if val == "index.docker.io" {
19+
return "docker.io"
20+
}
21+
return val
22+
}
23+
24+
// NewIndexInfo creates a new [registry.IndexInfo] or the given
25+
// repository-name, and detects whether the registry is considered
26+
// "secure" (non-localhost).
27+
func NewIndexInfo(reposName reference.Named) *registry.IndexInfo {
28+
indexName := normalizeIndexName(reference.Domain(reposName))
29+
if indexName == IndexName {
30+
return &registry.IndexInfo{
31+
Name: IndexName,
32+
Secure: true,
33+
Official: true,
34+
}
35+
}
36+
37+
return &registry.IndexInfo{
38+
Name: indexName,
39+
Secure: !isInsecure(indexName),
40+
}
41+
}
42+
43+
// isInsecure is used to detect whether a registry domain or IP-address is allowed
44+
// to use an insecure (non-TLS, or self-signed cert) connection according to the
45+
// defaults, which allows for insecure connections with registries running on a
46+
// loopback address ("localhost", "::1/128", "127.0.0.0/8").
47+
//
48+
// It is used in situations where we don't have access to the daemon's configuration,
49+
// for example, when used from the client / CLI.
50+
func isInsecure(hostNameOrIP string) bool {
51+
// Attempt to strip port if present; this also strips brackets for
52+
// IPv6 addresses with a port (e.g. "[::1]:5000").
53+
//
54+
// This is best-effort; we'll continue using the address as-is if it fails.
55+
if host, _, err := net.SplitHostPort(hostNameOrIP); err == nil {
56+
hostNameOrIP = host
57+
}
58+
if hostNameOrIP == "127.0.0.1" || hostNameOrIP == "::1" || strings.EqualFold(hostNameOrIP, "localhost") {
59+
// Fast path; no need to resolve these, assuming nobody overrides
60+
// "localhost" for anything else than a loopback address (sorry, not sorry).
61+
return true
62+
}
63+
64+
var addresses []net.IP
65+
if ip := net.ParseIP(hostNameOrIP); ip != nil {
66+
addresses = append(addresses, ip)
67+
} else {
68+
// Try to resolve the host's IP-addresses.
69+
addrs, _ := net.LookupIP(hostNameOrIP)
70+
addresses = append(addresses, addrs...)
71+
}
72+
73+
for _, addr := range addresses {
74+
if addr.IsLoopback() {
75+
return true
76+
}
77+
}
78+
return false
79+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Package registry is a fork of [github.com/docker/docker/registry], taken
2+
// at commit [moby@49306c6]. Git history was not preserved in this fork,
3+
// but can be found using the URLs provided.
4+
//
5+
// This fork was created to remove the dependency on the "Moby" codebase,
6+
// and because the CLI only needs a subset of its features. The original
7+
// package was written specifically for use in the daemon code, and includes
8+
// functionality that cannot be used in the CLI.
9+
//
10+
// [github.com/docker/docker/registry]: https://pkg.go.dev/github.com/docker/[email protected]+incompatible/registry
11+
// [moby@49306c6]: https://github.com/moby/moby/tree/49306c607b72c5bf0a8e426f5a9760fa5ef96ea0/registry
12+
package registry
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
2+
//go:build go1.24
3+
4+
package registry
5+
6+
import (
7+
"fmt"
8+
)
9+
10+
func invalidParam(err error) error {
11+
return invalidParameterErr{err}
12+
}
13+
14+
func invalidParamf(format string, args ...any) error {
15+
return invalidParameterErr{fmt.Errorf(format, args...)}
16+
}
17+
18+
type invalidParameterErr struct{ error }
19+
20+
func (invalidParameterErr) InvalidParameter() {}
21+
22+
func (e invalidParameterErr) Unwrap() error {
23+
return e.error
24+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Package registry contains client primitives to interact with a remote Docker registry.
2+
package registry
3+
4+
import (
5+
"context"
6+
"crypto/tls"
7+
"fmt"
8+
"net/http"
9+
"os"
10+
"path/filepath"
11+
12+
"github.com/docker/distribution/registry/client/transport"
13+
"github.com/docker/go-connections/tlsconfig"
14+
"github.com/sirupsen/logrus"
15+
)
16+
17+
func hasFile(files []os.DirEntry, name string) bool {
18+
for _, f := range files {
19+
if f.Name() == name {
20+
return true
21+
}
22+
}
23+
return false
24+
}
25+
26+
// ReadCertsDirectory reads the directory for TLS certificates
27+
// including roots and certificate pairs and updates the
28+
// provided TLS configuration.
29+
func ReadCertsDirectory(tlsConfig *tls.Config, directory string) error {
30+
return loadTLSConfig(context.TODO(), directory, tlsConfig)
31+
}
32+
33+
// loadTLSConfig reads the directory for TLS certificates including roots and
34+
// certificate pairs, and updates the provided TLS configuration.
35+
func loadTLSConfig(ctx context.Context, directory string, tlsConfig *tls.Config) error {
36+
fs, err := os.ReadDir(directory)
37+
if err != nil {
38+
if os.IsNotExist(err) {
39+
return nil
40+
}
41+
return invalidParam(err)
42+
}
43+
44+
for _, f := range fs {
45+
if ctx.Err() != nil {
46+
return ctx.Err()
47+
}
48+
switch filepath.Ext(f.Name()) {
49+
case ".crt":
50+
if tlsConfig.RootCAs == nil {
51+
systemPool, err := tlsconfig.SystemCertPool()
52+
if err != nil {
53+
return invalidParam(fmt.Errorf("unable to get system cert pool: %w", err))
54+
}
55+
tlsConfig.RootCAs = systemPool
56+
}
57+
fileName := filepath.Join(directory, f.Name())
58+
logrus.Debugf("crt: %s", fileName)
59+
data, err := os.ReadFile(fileName)
60+
if err != nil {
61+
return err
62+
}
63+
tlsConfig.RootCAs.AppendCertsFromPEM(data)
64+
case ".cert":
65+
certName := f.Name()
66+
keyName := certName[:len(certName)-5] + ".key"
67+
logrus.Debugf("cert: %s", filepath.Join(directory, certName))
68+
if !hasFile(fs, keyName) {
69+
return invalidParamf("missing key %s for client certificate %s. CA certificates must use the extension .crt", keyName, certName)
70+
}
71+
cert, err := tls.LoadX509KeyPair(filepath.Join(directory, certName), filepath.Join(directory, keyName))
72+
if err != nil {
73+
return err
74+
}
75+
tlsConfig.Certificates = append(tlsConfig.Certificates, cert)
76+
case ".key":
77+
keyName := f.Name()
78+
certName := keyName[:len(keyName)-4] + ".cert"
79+
logrus.Debugf("key: %s", filepath.Join(directory, keyName))
80+
if !hasFile(fs, certName) {
81+
return invalidParamf("missing client certificate %s for key %s", certName, keyName)
82+
}
83+
}
84+
}
85+
86+
return nil
87+
}
88+
89+
// Headers returns request modifiers with a User-Agent and metaHeaders
90+
func Headers(userAgent string, metaHeaders http.Header) []transport.RequestModifier {
91+
modifiers := []transport.RequestModifier{}
92+
if userAgent != "" {
93+
modifiers = append(modifiers, transport.NewHeaderRequestModifier(http.Header{
94+
"User-Agent": []string{userAgent},
95+
}))
96+
}
97+
if metaHeaders != nil {
98+
modifiers = append(modifiers, transport.NewHeaderRequestModifier(metaHeaders))
99+
}
100+
return modifiers
101+
}

0 commit comments

Comments
 (0)