github.com/coreos/rocket@v1.30.1-0.20200224141603-171c416fac02/rkt/image/httpfetcher.go (about) 1 // Copyright 2015 The rkt Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package image 16 17 import ( 18 "errors" 19 "io" 20 "net/url" 21 "time" 22 23 rktflag "github.com/rkt/rkt/rkt/flag" 24 25 "github.com/hashicorp/errwrap" 26 "github.com/rkt/rkt/pkg/keystore" 27 "github.com/rkt/rkt/rkt/config" 28 "github.com/rkt/rkt/store/imagestore" 29 ) 30 31 // httpFetcher is used to download images from http or https URLs. 32 type httpFetcher struct { 33 InsecureFlags *rktflag.SecFlags 34 S *imagestore.Store 35 Ks *keystore.Keystore 36 Rem *imagestore.Remote 37 NoCache bool 38 Debug bool 39 Headers map[string]config.Headerer 40 } 41 42 // Hash fetches the URL, optionally verifies it against passed asc, 43 // stores it in the store and returns the hash. 44 func (f *httpFetcher) Hash(u *url.URL, a *asc) (string, error) { 45 ensureLogger(f.Debug) 46 urlStr := u.String() 47 48 if !f.NoCache && f.Rem != nil { 49 if useCached(f.Rem.DownloadTime, f.Rem.CacheMaxAge) { 50 diag.Printf("image for %s isn't expired, not fetching.", urlStr) 51 return f.Rem.BlobKey, nil 52 } 53 } 54 55 diag.Printf("fetching image from %s", urlStr) 56 57 aciFile, cd, err := f.fetchURL(u, a, eTag(f.Rem)) 58 if err != nil { 59 return "", err 60 } 61 defer aciFile.Close() 62 63 if key := maybeUseCached(f.Rem, cd); key != "" { 64 // TODO(krnowak): that does not update the store with 65 // the new CacheMaxAge and Download Time, so it will 66 // query the server every time after initial 67 // CacheMaxAge is exceeded 68 return key, nil 69 } 70 key, err := f.S.WriteACI(aciFile, imagestore.ACIFetchInfo{ 71 Latest: false, 72 }) 73 if err != nil { 74 return "", err 75 } 76 77 // TODO(krnowak): What's the point of the second parameter? 78 // The SigURL field in imagestore.Remote seems to be completely 79 // unused. 80 newRem := imagestore.NewRemote(urlStr, a.Location) 81 newRem.BlobKey = key 82 newRem.DownloadTime = time.Now() 83 if cd != nil { 84 newRem.ETag = cd.ETag 85 newRem.CacheMaxAge = cd.MaxAge 86 } 87 err = f.S.WriteRemote(newRem) 88 if err != nil { 89 return "", err 90 } 91 92 return key, nil 93 } 94 95 func (f *httpFetcher) fetchURL(u *url.URL, a *asc, etag string) (readSeekCloser, *cacheData, error) { 96 if f.InsecureFlags.SkipTLSCheck() && f.Ks != nil { 97 log.Printf("warning: TLS verification has been disabled") 98 } 99 if f.InsecureFlags.SkipImageCheck() && f.Ks != nil { 100 log.Printf("warning: image signature verification has been disabled") 101 } 102 103 if f.InsecureFlags.SkipImageCheck() || f.Ks == nil { 104 o := f.httpOps() 105 aciFile, cd, err := o.DownloadImageWithETag(u, etag) 106 if err != nil { 107 return nil, nil, err 108 } 109 return aciFile, cd, err 110 } 111 112 return f.fetchVerifiedURL(u, a, etag) 113 } 114 115 func (f *httpFetcher) fetchVerifiedURL(u *url.URL, a *asc, etag string) (readSeekCloser, *cacheData, error) { 116 var aciFile readSeekCloser // closed on error 117 var errClose error // error signaling to close aciFile 118 119 o := f.httpOps() 120 f.maybeOverrideAscFetcherWithRemote(o, u, a) 121 ascFile, retry, err := o.DownloadSignature(a) 122 if err != nil { 123 return nil, nil, err 124 } 125 defer ascFile.Close() 126 127 aciFile, cd, err := o.DownloadImageWithETag(u, etag) 128 if err != nil { 129 return nil, nil, err 130 } 131 132 defer func() { 133 if errClose != nil { 134 aciFile.Close() 135 } 136 }() 137 138 if cd.UseCached { 139 aciFile.Close() 140 return NopReadSeekCloser(nil), cd, nil 141 } 142 143 if retry { 144 ascFile.Close() 145 ascFile, errClose = o.DownloadSignatureAgain(a) 146 if errClose != nil { 147 ascFile = NopReadSeekCloser(nil) 148 return nil, nil, errClose 149 } 150 } 151 152 errClose = f.validate(aciFile, ascFile) 153 if errClose != nil { 154 return nil, nil, errClose 155 } 156 157 return aciFile, cd, nil 158 } 159 160 func (f *httpFetcher) httpOps() *httpOps { 161 return &httpOps{ 162 InsecureSkipTLSVerify: f.InsecureFlags.SkipTLSCheck(), 163 S: f.S, 164 Headers: f.Headers, 165 Debug: f.Debug, 166 } 167 } 168 169 func (f *httpFetcher) validate(aciFile, ascFile io.ReadSeeker) error { 170 v, err := newValidator(aciFile) 171 if err != nil { 172 return err 173 } 174 entity, err := v.ValidateWithSignature(f.Ks, ascFile) 175 if err != nil { 176 return err 177 } 178 if _, err := aciFile.Seek(0, 0); err != nil { 179 return errwrap.Wrap(errors.New("error seeking ACI file"), err) 180 } 181 182 printIdentities(entity) 183 return nil 184 } 185 186 func (f *httpFetcher) maybeOverrideAscFetcherWithRemote(o *httpOps, u *url.URL, a *asc) { 187 if a.Fetcher != nil { 188 return 189 } 190 u2 := ascURLFromImgURL(u) 191 a.Location = u2.String() 192 a.Fetcher = o.AscRemoteFetcher() 193 }