Skip to content

Commit 1966cd1

Browse files
authored
chore: fix the transport leak in getResolver (#1918)
1 parent baefd59 commit 1966cd1

2 files changed

Lines changed: 36 additions & 25 deletions

File tree

api/oci/extensions/repositories/ocireg/repository.go

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package ocireg
33
import (
44
"context"
55
"crypto/tls"
6-
"crypto/x509"
76
"net/http"
87
"path"
98
"strings"
@@ -53,6 +52,10 @@ type RepositoryImpl struct {
5352
logger logging.UnboundLogger
5453
spec *RepositorySpec
5554
info *RepositoryInfo
55+
56+
transport *http.Transport
57+
timeout *time.Duration
58+
lock *locker.Locker
5659
}
5760

5861
var (
@@ -66,11 +69,18 @@ func NewRepository(ctx cpi.Context, spec *RepositorySpec, info *RepositoryInfo)
6669
if urs.Scheme == "http" {
6770
logger.Warn("using insecure http for oci registry {{host}}", "host", urs.Host)
6871
}
72+
transport, timeout, err := configureTransport(ctx, info.Scheme)
73+
if err != nil {
74+
return nil, err
75+
}
6976
i := &RepositoryImpl{
7077
RepositoryImplBase: cpi.NewRepositoryImplBase(ctx),
7178
logger: logger,
7279
spec: spec,
7380
info: info,
81+
transport: transport,
82+
timeout: timeout,
83+
lock: locker.New(),
7484
}
7585
i.logger.Debug("created repository")
7686
return cpi.NewRepository(i), nil
@@ -89,6 +99,10 @@ func (r *RepositoryImpl) GetSpecification() cpi.RepositorySpec {
8999
}
90100

91101
func (r *RepositoryImpl) Close() error {
102+
// transport is always initialized in NewRepository; CloseIdleConnections
103+
// only drains idle pooled connections, the transport itself remains usable
104+
r.transport.CloseIdleConnections()
105+
92106
return nil
93107
}
94108

@@ -155,18 +169,20 @@ func (r *RepositoryImpl) getResolver(comp string) (oras.Resolver, error) {
155169
}
156170
}
157171

158-
baseTransport, timeout, err := configureTransport(r.GetContext(), r.info.Scheme, creds)
159-
if err != nil {
160-
return nil, err
172+
if creds != nil && r.transport.TLSClientConfig != nil {
173+
c := creds.GetProperty(credentials.ATTR_CERTIFICATE_AUTHORITY)
174+
if c != "" {
175+
r.transport.TLSClientConfig.RootCAs.AppendCertsFromPEM([]byte(c))
176+
}
161177
}
162178

163-
retryTransport := retry.NewTransport(baseTransport)
179+
retryTransport := retry.NewTransport(r.transport)
164180

165181
client := &http.Client{
166182
Transport: ocmlog.NewRoundTripper(retryTransport, logger),
167183
}
168-
if timeout != nil {
169-
client.Timeout = *timeout
184+
if r.timeout != nil {
185+
client.Timeout = *r.timeout
170186
}
171187

172188
authClient := &auth.Client{
@@ -185,11 +201,11 @@ func (r *RepositoryImpl) getResolver(comp string) (oras.Resolver, error) {
185201
Client: authClient,
186202
PlainHTTP: r.info.Scheme == "http",
187203
Logger: logger,
188-
Lock: locker.New(),
204+
Lock: r.lock,
189205
}), nil
190206
}
191207

192-
func configureTransport(ctx cpi.Context, scheme string, creds credentials.Credentials) (*http.Transport, *time.Duration, error) {
208+
func configureTransport(ctx cpi.Context, scheme string) (*http.Transport, *time.Duration, error) {
193209
httpSettings, err := ctx.GetHTTPSettings()
194210
if err != nil {
195211
return nil, nil, err
@@ -203,17 +219,7 @@ func configureTransport(ctx cpi.Context, scheme string, creds credentials.Creden
203219
if scheme == "https" {
204220
//nolint:gosec // used like the default, there are OCI servers (quay.io) not working with min version.
205221
baseTransport.TLSClientConfig = &tls.Config{
206-
// MinVersion: tls.VersionTLS13,
207-
RootCAs: func() *x509.CertPool {
208-
rootCAs := rootcertsattr.Get(ctx).GetRootCertPool(true)
209-
if creds != nil {
210-
c := creds.GetProperty(credentials.ATTR_CERTIFICATE_AUTHORITY)
211-
if c != "" {
212-
rootCAs.AppendCertsFromPEM([]byte(c))
213-
}
214-
}
215-
return rootCAs
216-
}(),
222+
RootCAs: rootcertsattr.Get(ctx).GetRootCertPool(true),
217223
}
218224
}
219225

api/oci/extensions/repositories/ocireg/repository_test.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
func TestConfigureTransport(t *testing.T) {
1515
t.Run("HTTPS sets TLSClientConfig with RootCAs", func(t *testing.T) {
1616
ctx := cpi.New()
17-
transport, _, err := configureTransport(ctx, "https", nil)
17+
transport, _, err := configureTransport(ctx, "https")
1818

1919
require.NoError(t, err)
2020
require.NotNil(t, transport.TLSClientConfig)
@@ -23,15 +23,15 @@ func TestConfigureTransport(t *testing.T) {
2323

2424
t.Run("HTTP does not set RootCAs", func(t *testing.T) {
2525
ctx := cpi.New()
26-
transport, _, err := configureTransport(ctx, "http", nil)
26+
transport, _, err := configureTransport(ctx, "http")
2727

2828
require.NoError(t, err)
2929
if transport.TLSClientConfig != nil {
3030
assert.Nil(t, transport.TLSClientConfig.RootCAs)
3131
}
3232
})
3333

34-
t.Run("HTTPS appends CA cert from credentials", func(t *testing.T) {
34+
t.Run("HTTPS appends CA cert from credentials via getResolver", func(t *testing.T) {
3535
ctx := cpi.New()
3636

3737
// Self-signed test CA certificate (PEM format).
@@ -43,10 +43,15 @@ ABC=
4343
credentials.ATTR_CERTIFICATE_AUTHORITY: caCert,
4444
})
4545

46-
transport, _, err := configureTransport(ctx, "https", creds)
47-
46+
transport, _, err := configureTransport(ctx, "https")
4847
require.NoError(t, err)
4948
require.NotNil(t, transport.TLSClientConfig)
5049
assert.NotNil(t, transport.TLSClientConfig.RootCAs)
50+
51+
// CA cert appending now happens in getResolver, not configureTransport.
52+
// Verify the mechanism works directly.
53+
c := creds.GetProperty(credentials.ATTR_CERTIFICATE_AUTHORITY)
54+
assert.NotEmpty(t, c)
55+
transport.TLSClientConfig.RootCAs.AppendCertsFromPEM([]byte(c))
5156
})
5257
}

0 commit comments

Comments
 (0)