Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions common/libimage/copier.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,6 @@ const (

// CopyOptions allow for customizing image-copy operations.
type CopyOptions struct {
// If set, will be used for copying the image. Fields below may
// override certain settings.
SystemContext *types.SystemContext
// Allows for customizing the source reference lookup. This can be
// used to use custom blob caches.
SourceLookupReferenceFunc LookupReferenceFunc
Expand Down
2 changes: 1 addition & 1 deletion common/libimage/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func (i *Image) isCorrupted(ctx context.Context, name string) error {
return err
}

img, err := ref.NewImage(ctx, nil)
img, err := ref.NewImage(ctx, &i.runtime.systemContext)
if err != nil {
if name == "" {
name = i.ID()[:12]
Expand Down
4 changes: 3 additions & 1 deletion common/libimage/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ func (r *Runtime) Import(ctx context.Context, path string, options *ImportOption
if err == nil && u.Scheme != "" {
// If source is a URL, download the file.
fmt.Printf("Downloading from %q\n", path) //nolint:forbidigo
file, err := download.FromURL(r.systemContext.BigFilesTemporaryDir, path)
file, err := download.FromURL(ctx, r.systemContext.BigFilesTemporaryDir, path, download.Options{
BaseTLSConfig: r.systemContext.BaseTLSConfig,
})
if err != nil {
return "", err
}
Expand Down
12 changes: 8 additions & 4 deletions common/libimage/manifest_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,9 @@ func (i *Image) ConvertToManifestList(ctx context.Context) (*ManifestList, error
// Copy from the OCI layout into the same image record, so that it gets
// both its own manifest and the image index.
copyOptions := imageCopy.Options{
SourceCtx: &i.runtime.systemContext,
DestinationCtx: &i.runtime.systemContext,

ForceManifestMIMEType: imageManifestType,
}
if _, err := imageCopy.Image(ctx, policyContext, i.storageReference, bundle, &copyOptions); err != nil {
Expand Down Expand Up @@ -561,6 +564,9 @@ func (m *ManifestList) AddArtifact(ctx context.Context, options *ManifestListAdd
if options == nil {
options = &ManifestListAddArtifactOptions{}
}

systemContext := m.image.runtime.systemContextCopy()

opts := manifests.AddArtifactOptions{
ManifestArtifactType: options.Type,
Annotations: maps.Clone(options.Annotations),
Expand Down Expand Up @@ -592,7 +598,7 @@ func (m *ManifestList) AddArtifact(ctx context.Context, options *ManifestListAdd
opts.LayerMediaType = &options.LayerType
}
if options.Subject != "" {
ref, err := m.parseNameToExtantReference(ctx, nil, options.Subject, true, "subject for artifact manifest")
ref, err := m.parseNameToExtantReference(ctx, systemContext, options.Subject, true, "subject for artifact manifest")
if err != nil {
return "", err
}
Expand All @@ -607,8 +613,6 @@ func (m *ManifestList) AddArtifact(ctx context.Context, options *ManifestListAdd
locker.Lock()
defer locker.Unlock()

systemContext := m.image.runtime.systemContextCopy()

// Make sure to reload the image from the containers storage to fetch
// the latest data (e.g., new or delete digests).
if err := m.reload(); err != nil {
Expand Down Expand Up @@ -709,7 +713,7 @@ func (m *ManifestList) AnnotateInstance(d digest.Digest, options *ManifestListAn
}
}
if options.Subject != "" {
ref, err := m.parseNameToExtantReference(ctx, nil, options.Subject, true, "subject for image index")
ref, err := m.parseNameToExtantReference(ctx, &m.image.runtime.systemContext, options.Subject, true, "subject for image index")
if err != nil {
return err
}
Expand Down
6 changes: 3 additions & 3 deletions common/libimage/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ func (r *Runtime) copyFromDefault(ctx context.Context, ref types.ImageReference,
if !ok || refName == "" {
// Same trick as for the dir transport: we cannot use
// the path to a directory as the name.
storageName, err = getImageID(ctx, ref, nil)
storageName, err = getImageID(ctx, ref, &r.systemContext)
if err != nil {
return nil, nil, err
}
Expand All @@ -276,7 +276,7 @@ func (r *Runtime) copyFromDefault(ctx context.Context, ref types.ImageReference,
switch len(storageName) {
case 0:
// If there's no reference name in the annotations, compute an ID.
storageName, err = getImageID(ctx, ref, nil)
storageName, err = getImageID(ctx, ref, &r.systemContext)
if err != nil {
return nil, nil, err
}
Expand All @@ -302,7 +302,7 @@ func (r *Runtime) copyFromDefault(ctx context.Context, ref types.ImageReference,
// Path-based transports (e.g., dir) may include invalid
// characters, so we should pessimistically generate an ID
// instead of looking at the StringWithinTransport().
storageName, err = getImageID(ctx, ref, nil)
storageName, err = getImageID(ctx, ref, &r.systemContext)
if err != nil {
return nil, nil, err
}
Expand Down
5 changes: 1 addition & 4 deletions common/libimage/push_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"github.com/stretchr/testify/require"
"go.podman.io/common/pkg/config"
"go.podman.io/image/v5/pkg/compression"
"go.podman.io/image/v5/types"
)

func TestPush(t *testing.T) {
Expand Down Expand Up @@ -99,7 +98,7 @@ func TestPushOtherPlatform(t *testing.T) {
}

func TestPushWithForceCompression(t *testing.T) {
runtime := testNewRuntime(t)
runtime := testNewRuntime(t, testNewRuntimeOptions{dirForceDecompress: true})
ctx := context.Background()

// Prefetch alpine.
Expand All @@ -116,8 +115,6 @@ func TestPushWithForceCompression(t *testing.T) {

// Push newly pulled alpine to directory with uncompressed blobs
pushOptions := &PushOptions{}
pushOptions.SystemContext = &types.SystemContext{}
pushOptions.SystemContext.DirForceDecompress = true
pushOptions.Writer = os.Stdout
dirDest := t.TempDir()
_, err = runtime.Push(ctx, "quay.io/libpod/alpine:latest", "dir:"+dirDest, pushOptions)
Expand Down
10 changes: 8 additions & 2 deletions common/libimage/runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func TestMain(m *testing.M) {

type testNewRuntimeOptions struct {
registriesConfPath string
dirForceDecompress bool
}

// Create a new Runtime that can be used for testing.
Expand All @@ -41,8 +42,13 @@ func testNewRuntime(t *testing.T, options ...testNewRuntimeOptions) *Runtime {
SystemRegistriesConfDirPath: "/dev/null",
}

if len(options) == 1 && options[0].registriesConfPath != "" {
systemContext.SystemRegistriesConfPath = options[0].registriesConfPath
if len(options) == 1 {
if options[0].registriesConfPath != "" {
systemContext.SystemRegistriesConfPath = options[0].registriesConfPath
}
if options[0].dirForceDecompress {
systemContext.DirForceDecompress = true
}
}

runtime, err := RuntimeFromStoreOptions(&RuntimeOptions{SystemContext: systemContext}, storeOptions)
Expand Down
33 changes: 31 additions & 2 deletions common/pkg/download/download.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,50 @@
package download

import (
"context"
"crypto/tls"
"fmt"
"io"
"net/http"
"os"
)

// Options holds named options for FromURL.
type Options struct {
// If not nil, may contain TLS _algorithm_ options (e.g. TLS version, cipher suites, “curves”, etc.).
BaseTLSConfig *tls.Config
}

// FromURL downloads the specified source to a file in tmpdir (OS defaults if
// empty).
func FromURL(tmpdir, source string) (string, error) {
func FromURL(ctx context.Context, tmpdir, source string, options Options) (string, error) {
tmp, err := os.CreateTemp(tmpdir, "")
if err != nil {
return "", fmt.Errorf("creating temporary download file: %w", err)
}
defer tmp.Close()
succeeded := false
defer func() {
if !succeeded {
os.Remove(tmp.Name())
}
}()

response, err := http.Get(source) // nolint:noctx
var transport *http.Transport // nil means http.DefaultTransport
if options.BaseTLSConfig != nil {
transport = &http.Transport{
TLSClientConfig: options.BaseTLSConfig,
}
defer transport.CloseIdleConnections()
}
client := &http.Client{
Transport: transport,
}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, source, nil)
if err != nil {
return "", fmt.Errorf("preparing to download %q: %w", source, err)
}
response, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("downloading %s: %w", source, err)
}
Expand All @@ -27,5 +55,6 @@ func FromURL(tmpdir, source string) (string, error) {
return "", fmt.Errorf("copying %s to %s: %w", source, tmp.Name(), err)
}

succeeded = true
return tmp.Name(), nil
}
67 changes: 53 additions & 14 deletions image/docker/daemon/client.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package daemon

import (
"crypto/tls"
"crypto/x509"
"fmt"
"net/http"
"os"
"path/filepath"
"time"

Expand Down Expand Up @@ -63,27 +67,62 @@ func newDockerClient(sys *types.SystemContext) (*dockerclient.Client, error) {
}

func tlsConfig(sys *types.SystemContext) (*http.Client, error) {
options := tlsconfig.Options{}
if sys != nil && sys.DockerDaemonInsecureSkipTLSVerify {
options.InsecureSkipVerify = true
}
// This is intended to follow github.com/moby/moby/client.defaultHTTPClient
// and approximately github.com/moby/moby/client.WithTLSClientConfigFromEnv;
// we can’t use WithTLSClientConfig because 1) it uses ExclusiveRootPools:true,
// and 2) there is no clean way to override the crypto choices of tlsconfig.Client.
// So, we need to load the certs and keys ourselves in the BaseTLSConfig case
// anyway, we might just as well do it in the other case as well.

if sys != nil && sys.DockerDaemonCertPath != "" {
options.CAFile = filepath.Join(sys.DockerDaemonCertPath, "ca.pem")
options.CertFile = filepath.Join(sys.DockerDaemonCertPath, "cert.pem")
options.KeyFile = filepath.Join(sys.DockerDaemonCertPath, "key.pem")
var tlsConfig *tls.Config
if sys != nil && sys.BaseTLSConfig != nil {
tlsConfig = sys.BaseTLSConfig.Clone()
} else {
tlsConfig = &tls.Config{
// As of 2025-08, tlsconfig.ClientDefault() differs from Go 1.23 defaults only in CipherSuites;
// so, limit us to only using that value. If go-connections/tlsconfig changes its policy, we
// will want to consider that and make a decision whether to follow suit.
// There is some chance that eventually the Go default will be to require TLS 1.3, and that point
// we might want to drop the dependency on go-connections entirely.
CipherSuites: tlsconfig.ClientDefault().CipherSuites,
}
}

tlsc, err := tlsconfig.Client(options)
if err != nil {
return nil, err
if sys != nil {
if sys.DockerDaemonInsecureSkipTLSVerify {
tlsConfig.InsecureSkipVerify = true
} else if sys.DockerDaemonCertPath != "" {
caFilePath := filepath.Join(sys.DockerDaemonCertPath, "ca.pem")
caData, err := os.ReadFile(caFilePath)
if err != nil {
return nil, fmt.Errorf("reading CA file %q: %w", caFilePath, err)
}
caPool, err := x509.SystemCertPool()
if err != nil {
return nil, fmt.Errorf("reading systemwide CAs: %w", err)
}
if !caPool.AppendCertsFromPEM(caData) {
return nil, fmt.Errorf("no certificate found in %q", caFilePath)
}
tlsConfig.RootCAs = caPool
}

if sys.DockerDaemonCertPath != "" {
cert, err := tls.LoadX509KeyPair(
filepath.Join(sys.DockerDaemonCertPath, "cert.pem"),
filepath.Join(sys.DockerDaemonCertPath, "key.pem"))
if err != nil {
return nil, fmt.Errorf("loading X509 key pair: %w", err)
}
tlsConfig.Certificates = []tls.Certificate{cert}
}
}

return &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: tlsc,
// In general we want to follow docker/daemon/client.defaultHTTPClient , as long as it doesn’t affect compatibility.
TLSClientConfig: tlsConfig,
// In general we want to follow github.com/moby/moby/client.defaultHTTPClient , as long as it doesn’t affect compatibility.
// These idle connection limits really only apply to long-running clients, which is not our case here;
// we include the same values purely for symmetry.
MaxIdleConns: 6,
Expand All @@ -98,7 +137,7 @@ func httpConfig() *http.Client {
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: nil,
// In general we want to follow docker/daemon/client.defaultHTTPClient , as long as it doesn’t affect compatibility.
// In general we want to follow github.com/moby/moby/client.defaultHTTPClient , as long as it doesn’t affect compatibility.
// These idle connection limits really only apply to long-running clients, which is not our case here;
// we include the same values purely for symmetry.
MaxIdleConns: 6,
Expand Down
Loading
Loading