From 1f09fd6ef05ad6b0e27f1456790ba2917425d4a2 Mon Sep 17 00:00:00 2001 From: Andrei Krasnitski Date: Tue, 9 Feb 2021 17:35:36 +0100 Subject: [PATCH] feat: add ability to specify a CA certificate Signed-off-by: Andrei Krasnitski --- README.md | 18 ++++++++++++++++++ commands/out.go | 23 ++++++++++++++++++++--- types.go | 34 ++++++++++++++++++++++++++++++++-- 3 files changed, 70 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index bae3938..089e660 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,24 @@ differences: * `tls_key`: *Optional. Default `""`* TLS key for the notary server. * `tls_cert`: *Optional. Default `""`* TLS certificate for the notary server. +* `ca_certs`: *Optional.* An array of PEM-encoded CA certificates: + + ```yaml + ca_certs: + - | + -----BEGIN CERTIFICATE----- + ... + -----END CERTIFICATE----- + - | + -----BEGIN CERTIFICATE----- + ... + -----END CERTIFICATE----- + ``` + + Each entry specifies the x509 CA certificate for the trusted docker registry. + This is used to validate the certificate of the docker registry when the + registry's certificate is signed by a custom authority (or itself). + ### Signing with Docker Hub Configure Docker Content Trust for use with the [Docker Hub](https:/hub.docker.io) and Notary service by specifying the above source parameters as follows: diff --git a/commands/out.go b/commands/out.go index e87decc..938d573 100644 --- a/commands/out.go +++ b/commands/out.go @@ -193,8 +193,18 @@ func put(req resource.OutRequest, img v1.Image, tags []name.Tag) error { identifiers = append(identifiers, tag.Identifier()) } + repo, err := name.NewRepository(req.Source.Repository) + if err != nil { + return fmt.Errorf("resolve repository name: %w", err) + } + + opts, err := req.Source.AuthOptions(repo) + if err != nil { + return err + } + logrus.Infof("pushing tag(s) %s", strings.Join(identifiers, ", ")) - err := remote.MultiWrite(images, remote.WithAuth(createAuth(req))) + err = remote.MultiWrite(images, opts...) if err != nil { return fmt.Errorf("pushing tag(s): %w", err) } @@ -248,8 +258,15 @@ func createAuth(req resource.OutRequest) *authn.Basic { func aliasesToBump(req resource.OutRequest, repo name.Repository, ver *semver.Version) ([]name.Tag, error) { variant := req.Source.Variant - opts := []remote.Option{} - opts = append(opts, remote.WithAuth(createAuth(req))) + repo, err := name.NewRepository(req.Source.Repository) + if err != nil { + return nil, fmt.Errorf("resolve repository name: %w", err) + } + + opts, err := req.Source.AuthOptions(repo) + if err != nil { + return nil, err + } versions, err := remote.List(repo, opts...) if err != nil { diff --git a/types.go b/types.go index 4b867b3..7c868a2 100644 --- a/types.go +++ b/types.go @@ -1,6 +1,8 @@ package resource import ( + "crypto/tls" + "crypto/x509" "encoding/base64" "encoding/json" "fmt" @@ -87,6 +89,8 @@ type Source struct { ContentTrust *ContentTrust `json:"content_trust,omitempty"` + DomainCerts []string `json:"ca_certs,omitempty"` + Debug bool `json:"debug,omitempty"` } @@ -138,9 +142,35 @@ func (source Source) AuthOptions(repo name.Repository) ([]remote.Option, error) auth = authn.Anonymous } - opts := []remote.Option{remote.WithAuth(auth)} + tr := http.DefaultTransport.(*http.Transport) + // a cert was provided + if len(source.DomainCerts) > 0 { + rootCAs, err := x509.SystemCertPool() + if err != nil { + return nil, err + } + if rootCAs == nil { + rootCAs = x509.NewCertPool() + } + + for _, cert := range source.DomainCerts { + // append our cert to the system pool + if ok := rootCAs.AppendCertsFromPEM([]byte(cert)); !ok { + return nil, fmt.Errorf("failed to append registry certificate: %w", err) + } + } + + // trust the augmented cert pool in our client + config := &tls.Config{ + RootCAs: rootCAs, + } + + tr.TLSClientConfig = config + } + + opts := []remote.Option{remote.WithAuth(auth), remote.WithTransport(tr)} - rt, err := transport.New(repo.Registry, auth, http.DefaultTransport, []string{repo.Scope(transport.PullScope)}) + rt, err := transport.New(repo.Registry, auth, tr, []string{repo.Scope(transport.PullScope)}) if err != nil { return nil, fmt.Errorf("initialize transport: %w", err) }