Skip to content

Commit 7b609eb

Browse files
committed
bump c/image for subscription/certificate verification
Signed-off-by: Antonio Murdaca <runcom@redhat.com>
1 parent 0fdc778 commit 7b609eb

25 files changed

Lines changed: 466 additions & 158 deletions

File tree

hack/vendor.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,13 +168,13 @@ clone git github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42
168168
clone git github.com/flynn-archive/go-shlex 3f9db97f856818214da2e1057f8ad84803971cff
169169

170170
# signatures
171-
clone git github.com/containers/image master
172-
clone git github.com/opencontainers/image-spec v1.0.0-rc4
171+
clone git github.com/containers/image e6ced5e26c9b60ec23f0f7bd49b82a106f1d4db4
172+
clone git github.com/opencontainers/image-spec v1.0.0
173173
clone git k8s.io/kubernetes 4a3f9c5b19c7ff804cbc1bf37a15c044ca5d2353 https://github.com/openshift/kubernetes
174174
clone git github.com/golang/glog 44145f04b68cf362d9c4df2182967c2275eaefed
175175
clone git github.com/ghodss/yaml 73d445a93680fa1a78ae23a5839bad48f32ba1ee
176176
clone git gopkg.in/yaml.v2 d466437aa4adc35830964cffc5b5f262c63ddcb4
177177
clone git github.com/mtrmac/gpgme master
178-
clone git github.com/opencontainers/go-digest master
178+
clone git github.com/opencontainers/go-digest aa2ec055abd10d26d539eb630a92241b781ce4bc
179179

180180
clean

vendor/src/github.com/containers/image/docker/docker_client.go

Lines changed: 64 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ const (
3434
dockerCfgFileName = "config.json"
3535
dockerCfgObsolete = ".dockercfg"
3636

37+
systemPerHostCertDirPath = "/etc/docker/certs.d"
38+
3739
resolvedPingV2URL = "%s://%s/v2/"
3840
resolvedPingV1URL = "%s://%s/v1/_ping"
3941
tagsPath = "/v2/%s/tags/list"
@@ -129,12 +131,29 @@ func newTransport() *http.Transport {
129131
return tr
130132
}
131133

132-
func setupCertificates(dir string, tlsc *tls.Config) error {
133-
if dir == "" {
134-
return nil
134+
// dockerCertDir returns a path to a directory to be consumed by setupCertificates() depending on ctx and hostPort.
135+
func dockerCertDir(ctx *types.SystemContext, hostPort string) string {
136+
if ctx != nil && ctx.DockerCertPath != "" {
137+
return ctx.DockerCertPath
135138
}
139+
var hostCertDir string
140+
if ctx != nil && ctx.DockerPerHostCertDirPath != "" {
141+
hostCertDir = ctx.DockerPerHostCertDirPath
142+
} else if ctx != nil && ctx.RootForImplicitAbsolutePaths != "" {
143+
hostCertDir = filepath.Join(ctx.RootForImplicitAbsolutePaths, systemPerHostCertDirPath)
144+
} else {
145+
hostCertDir = systemPerHostCertDirPath
146+
}
147+
return filepath.Join(hostCertDir, hostPort)
148+
}
149+
150+
func setupCertificates(dir string, tlsc *tls.Config) error {
151+
logrus.Debugf("Looking for TLS certificates and private keys in %s", dir)
136152
fs, err := ioutil.ReadDir(dir)
137-
if err != nil && !os.IsNotExist(err) {
153+
if err != nil {
154+
if os.IsNotExist(err) {
155+
return nil
156+
}
138157
return err
139158
}
140159

@@ -146,7 +165,7 @@ func setupCertificates(dir string, tlsc *tls.Config) error {
146165
return errors.Wrap(err, "unable to get system cert pool")
147166
}
148167
tlsc.RootCAs = systemPool
149-
logrus.Debugf("crt: %s", fullPath)
168+
logrus.Debugf(" crt: %s", fullPath)
150169
data, err := ioutil.ReadFile(fullPath)
151170
if err != nil {
152171
return err
@@ -156,7 +175,7 @@ func setupCertificates(dir string, tlsc *tls.Config) error {
156175
if strings.HasSuffix(f.Name(), ".cert") {
157176
certName := f.Name()
158177
keyName := certName[:len(certName)-5] + ".key"
159-
logrus.Debugf("cert: %s", fullPath)
178+
logrus.Debugf(" cert: %s", fullPath)
160179
if !hasFile(fs, keyName) {
161180
return errors.Errorf("missing key %s for client certificate %s. Note that CA certificates should use the extension .crt", keyName, certName)
162181
}
@@ -169,7 +188,7 @@ func setupCertificates(dir string, tlsc *tls.Config) error {
169188
if strings.HasSuffix(f.Name(), ".key") {
170189
keyName := f.Name()
171190
certName := keyName[:len(keyName)-4] + ".cert"
172-
logrus.Debugf("key: %s", fullPath)
191+
logrus.Debugf(" key: %s", fullPath)
173192
if !hasFile(fs, certName) {
174193
return errors.Errorf("missing client certificate %s for key %s", certName, keyName)
175194
}
@@ -199,18 +218,18 @@ func newDockerClient(ctx *types.SystemContext, ref dockerReference, write bool,
199218
return nil, err
200219
}
201220
tr := newTransport()
202-
if ctx != nil && (ctx.DockerCertPath != "" || ctx.DockerInsecureSkipTLSVerify) {
203-
tlsc := &tls.Config{}
204-
205-
if err := setupCertificates(ctx.DockerCertPath, tlsc); err != nil {
206-
return nil, err
207-
}
208-
209-
tlsc.InsecureSkipVerify = ctx.DockerInsecureSkipTLSVerify
210-
tr.TLSClientConfig = tlsc
221+
tr.TLSClientConfig = serverDefault()
222+
// It is undefined whether the host[:port] string for dockerHostname should be dockerHostname or dockerRegistry,
223+
// because docker/docker does not read the certs.d subdirectory at all in that case. We use the user-visible
224+
// dockerHostname here, because it is more symmetrical to read the configuration in that case as well, and because
225+
// generally the UI hides the existence of the different dockerRegistry. But note that this behavior is
226+
// undocumented and may change if docker/docker changes.
227+
certDir := dockerCertDir(ctx, reference.Domain(ref.ref))
228+
if err := setupCertificates(certDir, tr.TLSClientConfig); err != nil {
229+
return nil, err
211230
}
212-
if tr.TLSClientConfig == nil {
213-
tr.TLSClientConfig = serverDefault()
231+
if ctx != nil && ctx.DockerInsecureSkipTLSVerify {
232+
tr.TLSClientConfig.InsecureSkipVerify = true
214233
}
215234
client := &http.Client{Transport: tr}
216235

@@ -289,31 +308,36 @@ func (c *dockerClient) setupRequestAuth(req *http.Request) error {
289308
if len(c.challenges) == 0 {
290309
return nil
291310
}
292-
// assume just one...
293-
challenge := c.challenges[0]
294-
switch challenge.Scheme {
295-
case "basic":
296-
req.SetBasicAuth(c.username, c.password)
297-
return nil
298-
case "bearer":
299-
if c.token == nil || time.Now().After(c.tokenExpiration) {
300-
realm, ok := challenge.Parameters["realm"]
301-
if !ok {
302-
return errors.Errorf("missing realm in bearer auth challenge")
311+
schemeNames := make([]string, 0, len(c.challenges))
312+
for _, challenge := range c.challenges {
313+
schemeNames = append(schemeNames, challenge.Scheme)
314+
switch challenge.Scheme {
315+
case "basic":
316+
req.SetBasicAuth(c.username, c.password)
317+
return nil
318+
case "bearer":
319+
if c.token == nil || time.Now().After(c.tokenExpiration) {
320+
realm, ok := challenge.Parameters["realm"]
321+
if !ok {
322+
return errors.Errorf("missing realm in bearer auth challenge")
323+
}
324+
service, _ := challenge.Parameters["service"] // Will be "" if not present
325+
scope := fmt.Sprintf("repository:%s:%s", c.scope.remoteName, c.scope.actions)
326+
token, err := c.getBearerToken(realm, service, scope)
327+
if err != nil {
328+
return err
329+
}
330+
c.token = token
331+
c.tokenExpiration = token.IssuedAt.Add(time.Duration(token.ExpiresIn) * time.Second)
303332
}
304-
service, _ := challenge.Parameters["service"] // Will be "" if not present
305-
scope := fmt.Sprintf("repository:%s:%s", c.scope.remoteName, c.scope.actions)
306-
token, err := c.getBearerToken(realm, service, scope)
307-
if err != nil {
308-
return err
309-
}
310-
c.token = token
311-
c.tokenExpiration = token.IssuedAt.Add(time.Duration(token.ExpiresIn) * time.Second)
333+
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.token.Token))
334+
return nil
335+
default:
336+
logrus.Debugf("no handler for %s authentication", challenge.Scheme)
312337
}
313-
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.token.Token))
314-
return nil
315338
}
316-
return errors.Errorf("no handler for %s authentication", challenge.Scheme)
339+
logrus.Infof("None of the challenges sent by server (%s) are supported, trying an unauthenticated request anyway", strings.Join(schemeNames, ", "))
340+
return nil
317341
}
318342

319343
func (c *dockerClient) getBearerToken(realm, service, scope string) (*bearerToken, error) {

vendor/src/github.com/containers/image/docker/docker_image_dest.go

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ import (
1616
"github.com/containers/image/docker/reference"
1717
"github.com/containers/image/manifest"
1818
"github.com/containers/image/types"
19+
"github.com/docker/distribution/registry/api/errcode"
20+
"github.com/docker/distribution/registry/api/v2"
21+
"github.com/docker/distribution/registry/client"
1922
"github.com/opencontainers/go-digest"
2023
"github.com/pkg/errors"
2124
)
@@ -96,6 +99,11 @@ func (d *dockerImageDestination) AcceptsForeignLayerURLs() bool {
9699
return true
97100
}
98101

102+
// MustMatchRuntimeOS returns true iff the destination can store only images targeted for the current runtime OS. False otherwise.
103+
func (d *dockerImageDestination) MustMatchRuntimeOS() bool {
104+
return false
105+
}
106+
99107
// sizeCounter is an io.Writer which only counts the total size of its input.
100108
type sizeCounter struct{ size int64 }
101109

@@ -209,6 +217,10 @@ func (d *dockerImageDestination) ReapplyBlob(info types.BlobInfo) (types.BlobInf
209217
return info, nil
210218
}
211219

220+
// PutManifest writes manifest to the destination.
221+
// FIXME? This should also receive a MIME type if known, to differentiate between schema versions.
222+
// If the destination is in principle available, refuses this manifest type (e.g. it does not recognize the schema),
223+
// but may accept a different manifest type, the returned error must be an ManifestTypeRejectedError.
212224
func (d *dockerImageDestination) PutManifest(m []byte) error {
213225
digest, err := manifest.Digest(m)
214226
if err != nil {
@@ -233,16 +245,31 @@ func (d *dockerImageDestination) PutManifest(m []byte) error {
233245
}
234246
defer res.Body.Close()
235247
if res.StatusCode != http.StatusCreated {
236-
body, err := ioutil.ReadAll(res.Body)
237-
if err == nil {
238-
logrus.Debugf("Error body %s", string(body))
248+
err = errors.Wrapf(client.HandleErrorResponse(res), "Error uploading manifest to %s", path)
249+
if isManifestInvalidError(errors.Cause(err)) {
250+
err = types.ManifestTypeRejectedError{Err: err}
239251
}
240-
logrus.Debugf("Error uploading manifest, status %d, %#v", res.StatusCode, res)
241-
return errors.Errorf("Error uploading manifest to %s, status %d", path, res.StatusCode)
252+
return err
242253
}
243254
return nil
244255
}
245256

257+
// isManifestInvalidError returns true iff err from client.HandleErrorReponse is a “manifest invalid” error.
258+
func isManifestInvalidError(err error) bool {
259+
errors, ok := err.(errcode.Errors)
260+
if !ok || len(errors) == 0 {
261+
return false
262+
}
263+
ec, ok := errors[0].(errcode.ErrorCoder)
264+
if !ok {
265+
return false
266+
}
267+
// ErrorCodeManifestInvalid is returned by OpenShift with acceptschema2=false.
268+
// ErrorCodeTagInvalid is returned by docker/distribution (at least as of commit ec87e9b6971d831f0eff752ddb54fb64693e51cd)
269+
// when uploading to a tag (because it can’t find a matching tag inside the manifest)
270+
return ec.ErrorCode() == v2.ErrorCodeManifestInvalid || ec.ErrorCode() == v2.ErrorCodeTagInvalid
271+
}
272+
246273
func (d *dockerImageDestination) PutSignatures(signatures [][]byte) error {
247274
// Do not fail if we don’t really need to support signatures.
248275
if len(signatures) == 0 {

vendor/src/github.com/containers/image/image/docker_list.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ type platformSpec struct {
1616
OSVersion string `json:"os.version,omitempty"`
1717
OSFeatures []string `json:"os.features,omitempty"`
1818
Variant string `json:"variant,omitempty"`
19-
Features []string `json:"features,omitempty"`
19+
Features []string `json:"features,omitempty"` // removed in OCI
2020
}
2121

2222
// A manifestDescriptor references a platform-specific manifest.

vendor/src/github.com/containers/image/image/docker_schema1.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,27 @@ func (m *manifestSchema1) LayerInfos() []types.BlobInfo {
135135
return layers
136136
}
137137

138+
// EmbeddedDockerReferenceConflicts whether a Docker reference embedded in the manifest, if any, conflicts with destination ref.
139+
// It returns false if the manifest does not embed a Docker reference.
140+
// (This embedding unfortunately happens for Docker schema1, please do not add support for this in any new formats.)
141+
func (m *manifestSchema1) EmbeddedDockerReferenceConflicts(ref reference.Named) bool {
142+
// This is a bit convoluted: We can’t just have a "get embedded docker reference" method
143+
// and have the “does it conflict” logic in the generic copy code, because the manifest does not actually
144+
// embed a full docker/distribution reference, but only the repo name and tag (without the host name).
145+
// So we would have to provide a “return repo without host name, and tag” getter for the generic code,
146+
// which would be very awkward. Instead, we do the matching here in schema1-specific code, and all the
147+
// generic copy code needs to know about is reference.Named and that a manifest may need updating
148+
// for some destinations.
149+
name := reference.Path(ref)
150+
var tag string
151+
if tagged, isTagged := ref.(reference.NamedTagged); isTagged {
152+
tag = tagged.Tag()
153+
} else {
154+
tag = ""
155+
}
156+
return m.Name != name || m.Tag != tag
157+
}
158+
138159
func (m *manifestSchema1) imageInspectInfo() (*types.ImageInspectInfo, error) {
139160
v1 := &v1Image{}
140161
if err := json.Unmarshal([]byte(m.History[0].V1Compatibility), v1); err != nil {
@@ -173,6 +194,14 @@ func (m *manifestSchema1) UpdatedImage(options types.ManifestUpdateOptions) (typ
173194
copy.FSLayers[(len(options.LayerInfos)-1)-i].BlobSum = info.Digest
174195
}
175196
}
197+
if options.EmbeddedDockerReference != nil {
198+
copy.Name = reference.Path(options.EmbeddedDockerReference)
199+
if tagged, isTagged := options.EmbeddedDockerReference.(reference.NamedTagged); isTagged {
200+
copy.Tag = tagged.Tag()
201+
} else {
202+
copy.Tag = ""
203+
}
204+
}
176205

177206
switch options.ManifestMIMEType {
178207
case "": // No conversion, OK

vendor/src/github.com/containers/image/image/docker_schema2.go

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"strings"
1010

1111
"github.com/Sirupsen/logrus"
12+
"github.com/containers/image/docker/reference"
1213
"github.com/containers/image/manifest"
1314
"github.com/containers/image/types"
1415
"github.com/opencontainers/go-digest"
@@ -140,6 +141,13 @@ func (m *manifestSchema2) LayerInfos() []types.BlobInfo {
140141
return blobs
141142
}
142143

144+
// EmbeddedDockerReferenceConflicts whether a Docker reference embedded in the manifest, if any, conflicts with destination ref.
145+
// It returns false if the manifest does not embed a Docker reference.
146+
// (This embedding unfortunately happens for Docker schema1, please do not add support for this in any new formats.)
147+
func (m *manifestSchema2) EmbeddedDockerReferenceConflicts(ref reference.Named) bool {
148+
return false
149+
}
150+
143151
func (m *manifestSchema2) imageInspectInfo() (*types.ImageInspectInfo, error) {
144152
config, err := m.ConfigBlob()
145153
if err != nil {
@@ -175,11 +183,13 @@ func (m *manifestSchema2) UpdatedImage(options types.ManifestUpdateOptions) (typ
175183
}
176184
copy.LayersDescriptors = make([]descriptor, len(options.LayerInfos))
177185
for i, info := range options.LayerInfos {
186+
copy.LayersDescriptors[i].MediaType = m.LayersDescriptors[i].MediaType
178187
copy.LayersDescriptors[i].Digest = info.Digest
179188
copy.LayersDescriptors[i].Size = info.Size
180189
copy.LayersDescriptors[i].URLs = info.URLs
181190
}
182191
}
192+
// Ignore options.EmbeddedDockerReference: it may be set when converting from schema1 to schema2, but we really don't care.
183193

184194
switch options.ManifestMIMEType {
185195
case "": // No conversion, OK
@@ -204,15 +214,17 @@ func (m *manifestSchema2) convertToManifestOCI1() (types.Image, error) {
204214
return nil, err
205215
}
206216

207-
config := descriptor{
208-
MediaType: imgspecv1.MediaTypeImageConfig,
209-
Size: int64(len(configOCIBytes)),
210-
Digest: digest.FromBytes(configOCIBytes),
217+
config := descriptorOCI1{
218+
descriptor: descriptor{
219+
MediaType: imgspecv1.MediaTypeImageConfig,
220+
Size: int64(len(configOCIBytes)),
221+
Digest: digest.FromBytes(configOCIBytes),
222+
},
211223
}
212224

213-
layers := make([]descriptor, len(m.LayersDescriptors))
225+
layers := make([]descriptorOCI1, len(m.LayersDescriptors))
214226
for idx := range layers {
215-
layers[idx] = m.LayersDescriptors[idx]
227+
layers[idx] = descriptorOCI1{descriptor: m.LayersDescriptors[idx]}
216228
if m.LayersDescriptors[idx].MediaType == manifest.DockerV2Schema2ForeignLayerMediaType {
217229
layers[idx].MediaType = imgspecv1.MediaTypeImageLayerNonDistributable
218230
} else {

vendor/src/github.com/containers/image/image/manifest.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ package image
33
import (
44
"time"
55

6+
"github.com/containers/image/docker/reference"
67
"github.com/containers/image/manifest"
8+
"github.com/containers/image/pkg/strslice"
79
"github.com/containers/image/types"
8-
"github.com/docker/engine-api/types/strslice"
910
"github.com/opencontainers/go-digest"
1011
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
1112
)
@@ -72,6 +73,10 @@ type genericManifest interface {
7273
// The Digest field is guaranteed to be provided; Size may be -1.
7374
// WARNING: The list may contain duplicates, and they are semantically relevant.
7475
LayerInfos() []types.BlobInfo
76+
// EmbeddedDockerReferenceConflicts whether a Docker reference embedded in the manifest, if any, conflicts with destination ref.
77+
// It returns false if the manifest does not embed a Docker reference.
78+
// (This embedding unfortunately happens for Docker schema1, please do not add support for this in any new formats.)
79+
EmbeddedDockerReferenceConflicts(ref reference.Named) bool
7580
imageInspectInfo() (*types.ImageInspectInfo, error) // To be called by inspectManifest
7681
// UpdatedImageNeedsLayerDiffIDs returns true iff UpdatedImage(options) needs InformationOnly.LayerDiffIDs.
7782
// This is a horribly specific interface, but computing InformationOnly.LayerDiffIDs can be very expensive to compute

0 commit comments

Comments
 (0)