diff --git a/api/oci/extensions/repositories/docker/client.go b/api/oci/extensions/repositories/docker/client.go
index 6d66be26c1..064613aee3 100644
--- a/api/oci/extensions/repositories/docker/client.go
+++ b/api/oci/extensions/repositories/docker/client.go
@@ -9,10 +9,13 @@ import (
"github.com/docker/cli/cli/config"
cliflags "github.com/docker/cli/cli/flags"
dockerclient "github.com/docker/docker/client"
+ mlog "github.com/mandelsoft/logging"
"github.com/spf13/pflag"
+
+ "ocm.software/ocm/api/utils/logging"
)
-func newDockerClient(dockerhost string) (*dockerclient.Client, error) {
+func newDockerClient(dockerhost string, logger mlog.UnboundLogger) (*dockerclient.Client, error) {
if dockerhost == "" {
opts := cliflags.NewClientOptions()
// set defaults
@@ -30,7 +33,14 @@ func newDockerClient(dockerhost string) (*dockerclient.Client, error) {
}
url, err := dockerclient.ParseHostURL(dockerhost)
if err == nil && url.Scheme == "unix" {
- dockerclient.WithScheme(url.Scheme)(c)
+ if err := dockerclient.WithScheme(url.Scheme)(c); err != nil {
+ return nil, err
+ }
+ }
+ clnt := c.HTTPClient()
+ clnt.Transport = logging.NewRoundTripper(clnt.Transport, logger)
+ if err := dockerclient.WithHTTPClient(clnt)(c); err != nil {
+ return nil, err
}
return c, nil
}
diff --git a/api/oci/extensions/repositories/docker/logging.go b/api/oci/extensions/repositories/docker/logging.go
new file mode 100644
index 0000000000..154d919345
--- /dev/null
+++ b/api/oci/extensions/repositories/docker/logging.go
@@ -0,0 +1,5 @@
+package docker
+
+import ocmlog "ocm.software/ocm/api/utils/logging"
+
+var REALM = ocmlog.DefineSubRealm("Docker repository handling", "oci", "docker")
diff --git a/api/oci/extensions/repositories/docker/repository.go b/api/oci/extensions/repositories/docker/repository.go
index 52836d19bf..9f80351041 100644
--- a/api/oci/extensions/repositories/docker/repository.go
+++ b/api/oci/extensions/repositories/docker/repository.go
@@ -6,8 +6,10 @@ import (
"github.com/containers/image/v5/types"
dockertypes "github.com/docker/docker/api/types/image"
"github.com/docker/docker/client"
+ "github.com/mandelsoft/logging"
"ocm.software/ocm/api/oci/cpi"
+ ocmlog "ocm.software/ocm/api/utils/logging"
)
type RepositoryImpl struct {
@@ -20,7 +22,9 @@ type RepositoryImpl struct {
var _ cpi.RepositoryImpl = (*RepositoryImpl)(nil)
func NewRepository(ctx cpi.Context, spec *RepositorySpec) (cpi.Repository, error) {
- client, err := newDockerClient(spec.DockerHost)
+ urs := spec.UniformRepositorySpec()
+ logger := logging.DynamicLogger(ctx, REALM, logging.NewAttribute(ocmlog.ATTR_HOST, urs.Host))
+ client, err := newDockerClient(spec.DockerHost, logger)
if err != nil {
return nil, err
}
diff --git a/api/oci/extensions/repositories/docker/type.go b/api/oci/extensions/repositories/docker/type.go
index adcf5fa05d..8fd2ade17a 100644
--- a/api/oci/extensions/repositories/docker/type.go
+++ b/api/oci/extensions/repositories/docker/type.go
@@ -3,9 +3,12 @@ package docker
import (
"context"
+ "github.com/mandelsoft/logging"
+
"ocm.software/ocm/api/credentials"
"ocm.software/ocm/api/oci/cpi"
"ocm.software/ocm/api/utils"
+ ocmlog "ocm.software/ocm/api/utils/logging"
"ocm.software/ocm/api/utils/runtime"
)
@@ -50,7 +53,9 @@ func (a *RepositorySpec) Repository(ctx cpi.Context, creds credentials.Credentia
}
func (a *RepositorySpec) Validate(ctx cpi.Context, creds credentials.Credentials, usageContext ...credentials.UsageContext) error {
- client, err := newDockerClient(a.DockerHost)
+ urs := a.UniformRepositorySpec()
+ logger := logging.DynamicLogger(ctx, REALM, logging.NewAttribute(ocmlog.ATTR_HOST, urs.Host))
+ client, err := newDockerClient(a.DockerHost, logger)
if err != nil {
return err
}
diff --git a/api/oci/extensions/repositories/ocireg/logging.go b/api/oci/extensions/repositories/ocireg/logging.go
index 9e78b02c7c..1c8904b00b 100644
--- a/api/oci/extensions/repositories/ocireg/logging.go
+++ b/api/oci/extensions/repositories/ocireg/logging.go
@@ -1,7 +1,5 @@
package ocireg
-import (
- ocmlog "ocm.software/ocm/api/utils/logging"
-)
+import ocmlog "ocm.software/ocm/api/utils/logging"
var REALM = ocmlog.DefineSubRealm("OCI repository handling", "oci", "ocireg")
diff --git a/api/oci/extensions/repositories/ocireg/repository.go b/api/oci/extensions/repositories/ocireg/repository.go
index 1bae127a71..bff1e51078 100644
--- a/api/oci/extensions/repositories/ocireg/repository.go
+++ b/api/oci/extensions/repositories/ocireg/repository.go
@@ -4,6 +4,7 @@ import (
"context"
"crypto/tls"
"crypto/x509"
+ "net/http"
"path"
"strings"
@@ -58,12 +59,13 @@ var (
func NewRepository(ctx cpi.Context, spec *RepositorySpec, info *RepositoryInfo) (cpi.Repository, error) {
urs := spec.UniformRepositorySpec()
+ logger := logging.DynamicLogger(ctx, REALM, logging.NewAttribute(ocmlog.ATTR_HOST, urs.Host))
if urs.Scheme == "http" {
- ocmlog.Logger(REALM).Warn("using insecure http for oci registry {{host}}", "host", urs.Host)
+ logger.Warn("using insecure http for oci registry {{host}}", "host", urs.Host)
}
i := &RepositoryImpl{
RepositoryImplBase: cpi.NewRepositoryImplBase(ctx),
- logger: logging.DynamicLogger(ctx, REALM, logging.NewAttribute(ocmlog.ATTR_HOST, urs.Host)),
+ logger: logger,
spec: spec,
info: info,
}
@@ -126,6 +128,11 @@ func (r *RepositoryImpl) getResolver(comp string) (resolve.Resolver, error) {
opts := docker.ResolverOptions{
Hosts: docker.ConvertHosts(config.ConfigureHosts(context.Background(), config.HostOptions{
+ UpdateClient: func(client *http.Client) error {
+ // copy from http.DefaultTransport with a roundtripper injection
+ client.Transport = ocmlog.NewRoundTripper(client.Transport, logger)
+ return nil
+ },
Credentials: func(host string) (string, string, error) {
if creds != nil {
p := creds.GetProperty(credentials.ATTR_IDENTITY_TOKEN)
diff --git a/api/utils/logging/roundtripper.go b/api/utils/logging/roundtripper.go
new file mode 100644
index 0000000000..99a3dadd48
--- /dev/null
+++ b/api/utils/logging/roundtripper.go
@@ -0,0 +1,36 @@
+package logging
+
+import (
+ "net/http"
+
+ "github.com/mandelsoft/logging"
+)
+
+func NewRoundTripper(rt http.RoundTripper, logger logging.Logger) *RoundTripper {
+ return &RoundTripper{
+ logger: logger,
+ RoundTripper: rt,
+ }
+}
+
+// RoundTripper is a http.RoundTripper that logs requests.
+type RoundTripper struct {
+ logger logging.Logger
+ http.RoundTripper
+}
+
+func (t *RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
+ // Redact the Authorization header to make sure it doesn't get logged at any point.
+ header := req.Header
+ if key := "Authorization"; req.Header.Get(key) != "" {
+ header = header.Clone()
+ header.Set(key, "***")
+ }
+
+ t.logger.Trace("roundtrip",
+ "url", req.URL,
+ "method", req.Method,
+ "header", header,
+ )
+ return t.RoundTripper.RoundTrip(req)
+}
diff --git a/api/utils/logging/roundtripper_test.go b/api/utils/logging/roundtripper_test.go
new file mode 100644
index 0000000000..a7bdbf8a72
--- /dev/null
+++ b/api/utils/logging/roundtripper_test.go
@@ -0,0 +1,65 @@
+package logging_test
+
+import (
+ "bytes"
+ "net/http"
+ "net/http/httptest"
+
+ logcfg "github.com/mandelsoft/logging/config"
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+ "github.com/tonglil/buflogr"
+
+ "github.com/mandelsoft/logging"
+
+ local "ocm.software/ocm/api/utils/logging"
+)
+
+var _ = Describe("RoundTripper", func() {
+ var buf bytes.Buffer
+ var ctx *local.StaticContext
+ var roundTripper http.RoundTripper
+ var server *httptest.Server
+
+ BeforeEach(func() {
+ buf.Reset()
+ local.SetContext(logging.NewDefault())
+ ctx = local.Context()
+ ctx.SetBaseLogger(buflogr.NewWithBuffer(&buf))
+ })
+
+ AfterEach(func() {
+ if server != nil {
+ server.Close()
+ }
+ })
+
+ It("redacts Authorization header", func() {
+ r := logcfg.ConditionalRule("trace")
+ cfg := &logcfg.Config{
+ Rules: []logcfg.Rule{r},
+ }
+ Expect(ctx.Configure(cfg)).To(Succeed())
+
+ server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(http.StatusOK)
+ }))
+
+ roundTripper = local.NewRoundTripper(http.DefaultTransport, ctx.Logger())
+ client := &http.Client{Transport: roundTripper}
+
+ req, err := http.NewRequest("GET", server.URL, nil)
+ Expect(err).NotTo(HaveOccurred())
+ req.Header.Set("Authorization", "this should be redacted")
+
+ _, err = client.Do(req)
+ Expect(err).NotTo(HaveOccurred())
+
+ Expect(buf.String()).To(ContainSubstring("roundtrip"))
+ Expect(buf.String()).To(ContainSubstring("url"))
+ Expect(buf.String()).To(ContainSubstring("method"))
+ Expect(buf.String()).To(ContainSubstring("header"))
+ Expect(buf.String()).To(ContainSubstring("***"))
+ Expect(buf.String()).NotTo(ContainSubstring("this should be redacted"))
+ })
+})
diff --git a/docs/reference/ocm_logging.md b/docs/reference/ocm_logging.md
index 7d48096c93..9a1c45800b 100644
--- a/docs/reference/ocm_logging.md
+++ b/docs/reference/ocm_logging.md
@@ -29,6 +29,7 @@ The following *realms* are used by the command line tool:
- ocm/downloader
: Downloaders
- ocm/maven
: Maven repository
- ocm/npm
: NPM registry
+ - ocm/oci/docker
: Docker repository handling
- ocm/oci/mapping
: OCM to OCI Registry Mapping
- ocm/oci/ocireg
: OCI repository handling
- ocm/plugins
: OCM plugin handling