Skip to content

Commit 27a5643

Browse files
committed
Registries proxy config: initial impl
1 parent f8cd81a commit 27a5643

2 files changed

Lines changed: 61 additions & 3 deletions

File tree

image/docker/docker_client.go

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ type dockerClient struct {
114114
// tlsClientConfig is setup by newDockerClient and will be used and updated
115115
// by detectProperties(). Callers can edit tlsClientConfig.InsecureSkipVerify in the meantime.
116116
tlsClientConfig *tls.Config
117+
// registryProxyURL is the proxy URL from the registry configuration, if any.
118+
// It has the lowest priority and can be overridden by either DockerProxyURL or environment variables.
119+
registryProxyURL *url.URL
117120
// The following members are not set by newDockerClient and must be set by callers if needed.
118121
auth types.DockerAuthConfig
119122
registryToken string
@@ -262,18 +265,26 @@ func newDockerClient(sys *types.SystemContext, registry, reference string) (*doc
262265
return nil, err
263266
}
264267

265-
// Check if TLS verification shall be skipped (default=false) which can
266-
// be specified in the sysregistriesv2 configuration.
267-
skipVerify := false
268+
// Fetch and load sysregistriesv2 configurations.
268269
reg, err := sysregistriesv2.FindRegistry(sys, reference)
269270
if err != nil {
270271
return nil, fmt.Errorf("loading registries: %w", err)
271272
}
273+
skipVerify := false
274+
var registryProxyURL *url.URL
272275
if reg != nil {
273276
if reg.Blocked {
274277
return nil, fmt.Errorf("registry %s is blocked in %s or %s", reg.Prefix, sysregistriesv2.ConfigPath(sys), sysregistriesv2.ConfigDirPath(sys))
275278
}
279+
// Check if TLS verification shall be skipped (default=false).
276280
skipVerify = reg.Insecure
281+
// Set registry proxy.
282+
if reg.Proxy != "" {
283+
registryProxyURL, err = url.Parse(reg.Proxy)
284+
if err != nil {
285+
return nil, fmt.Errorf("parsing proxy URL %q: %w", reg.Proxy, err)
286+
}
287+
}
277288
}
278289
tlsClientConfig.InsecureSkipVerify = skipVerify
279290

@@ -287,6 +298,7 @@ func newDockerClient(sys *types.SystemContext, registry, reference string) (*doc
287298
registry: registry,
288299
userAgent: userAgent,
289300
tlsClientConfig: tlsClientConfig,
301+
registryProxyURL: registryProxyURL,
290302
tokenCache: map[string]*bearerToken{},
291303
reportedWarnings: set.New[string](),
292304
}, nil
@@ -968,6 +980,16 @@ func (c *dockerClient) detectPropertiesHelper(ctx context.Context) error {
968980
}
969981
tr := tlsclientconfig.NewTransport()
970982
tr.TLSClientConfig = c.tlsClientConfig
983+
// Set registry-specific proxy with lowest priority, which can be overridden by environment variables.
984+
if c.registryProxyURL != nil {
985+
registryProxy := c.registryProxyURL
986+
tr.Proxy = func(req *http.Request) (*url.URL, error) {
987+
if envProxy, err := http.ProxyFromEnvironment(req); err != nil || envProxy != nil {
988+
return envProxy, err
989+
}
990+
return registryProxy, nil
991+
}
992+
}
971993
// if set DockerProxyURL explicitly, use the DockerProxyURL instead of system proxy
972994
if c.sys != nil && c.sys.DockerProxyURL != nil {
973995
tr.Proxy = http.ProxyURL(c.sys.DockerProxyURL)

image/pkg/sysregistriesv2/system_registries_v2.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ type Endpoint struct {
5959
// If true, certs verification will be skipped and HTTP (non-TLS)
6060
// connections will be allowed.
6161
Insecure bool `toml:"insecure,omitempty"`
62+
// The forwarding proxy to be used for accessing this endpoint.
63+
Proxy string `toml:"proxy,omitempty"`
6264
// PullFromMirror is used for adding restrictions to image pull through the mirror.
6365
// Set to "all", "digest-only", or "tag-only".
6466
// If "digest-only", mirrors will only be used for digest pulls. Pulling images by
@@ -341,6 +343,27 @@ func parseLocation(input string) (string, error) {
341343
return trimmed, nil
342344
}
343345

346+
// validateProxy validates the input string for a proxy configuration.
347+
// Errors if a scheme is unsupported or unspecified.
348+
func validateProxy(input string) error {
349+
if input == "" {
350+
return nil
351+
}
352+
353+
var hasSupportedScheme bool
354+
for _, scheme := range []string{"http://", "https://", "socks5://", "socks5h://"} {
355+
if strings.HasPrefix(input, scheme) {
356+
hasSupportedScheme = true
357+
break
358+
}
359+
}
360+
if !hasSupportedScheme {
361+
return &InvalidRegistries{s: "invalid proxy: proxy URL must specify one of the supported schemes: http://, https://, socks5://, socks5h://"}
362+
}
363+
364+
return nil
365+
}
366+
344367
// ConvertToV2 returns a v2 config corresponding to a v1 one.
345368
func (config *V1RegistriesConf) ConvertToV2() (*V2RegistriesConf, error) {
346369
regMap := make(map[string]*Registry)
@@ -409,6 +432,10 @@ func (config *V2RegistriesConf) postProcessRegistries() error {
409432
return err
410433
}
411434

435+
if err = validateProxy(reg.Proxy); err != nil {
436+
return err
437+
}
438+
412439
if reg.Prefix == "" {
413440
if reg.Location == "" {
414441
return &InvalidRegistries{s: "invalid condition: both location and prefix are unset"}
@@ -438,6 +465,10 @@ func (config *V2RegistriesConf) postProcessRegistries() error {
438465
return err
439466
}
440467

468+
if err = validateProxy(mir.Proxy); err != nil {
469+
return err
470+
}
471+
441472
// FIXME: unqualifiedSearchRegistries now also accepts empty values
442473
// and shouldn't
443474
// https://github.com/containers/image/pull/1191#discussion_r610623216
@@ -483,6 +514,11 @@ func (config *V2RegistriesConf) postProcessRegistries() error {
483514
return &InvalidRegistries{s: msg}
484515
}
485516

517+
if reg.Proxy != other.Proxy {
518+
msg := fmt.Sprintf("registry '%s' is defined multiple times with conflicting 'proxy' setting", reg.Location)
519+
return &InvalidRegistries{s: msg}
520+
}
521+
486522
if reg.Blocked != other.Blocked {
487523
msg := fmt.Sprintf("registry '%s' is defined multiple times with conflicting 'blocked' setting", reg.Location)
488524
return &InvalidRegistries{s: msg}

0 commit comments

Comments
 (0)