github.com/blixtra/rkt@v0.8.1-0.20160204105720-ab0d1add1a43/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 "github.com/coreos/rkt/pkg/keystore" 24 "github.com/coreos/rkt/rkt/config" 25 rktflag "github.com/coreos/rkt/rkt/flag" 26 "github.com/coreos/rkt/store" 27 "github.com/hashicorp/errwrap" 28 ) 29 30 // httpFetcher is used to download images from http or https URLs. 31 type httpFetcher struct { 32 InsecureFlags *rktflag.SecFlags 33 S *store.Store 34 Ks *keystore.Keystore 35 Rem *store.Remote 36 Debug bool 37 Headers map[string]config.Headerer 38 } 39 40 // GetHash fetches the URL, optionally verifies it against passed asc, 41 // stores it in the store and returns the hash. 42 func (f *httpFetcher) GetHash(u *url.URL, a *asc) (string, error) { 43 ensureLogger(f.Debug) 44 urlStr := u.String() 45 46 if f.Debug { 47 log.Printf("fetching image from %s", urlStr) 48 } 49 50 aciFile, cd, err := f.fetchURL(u, a, f.getETag()) 51 if err != nil { 52 return "", err 53 } 54 defer func() { maybeClose(aciFile) }() 55 if key := f.maybeUseCached(cd); key != "" { 56 // TODO(krnowak): that does not update the store with 57 // the new CacheMaxAge and Download Time, so it will 58 // query the server every time after initial 59 // CacheMaxAge is exceeded 60 return key, nil 61 } 62 key, err := f.S.WriteACI(aciFile, false) 63 if err != nil { 64 return "", err 65 } 66 67 // TODO(krnowak): What's the point of the second parameter? 68 // The SigURL field in store.Remote seems to be completely 69 // unused. 70 newRem := store.NewRemote(urlStr, a.Location) 71 newRem.BlobKey = key 72 newRem.DownloadTime = time.Now() 73 if cd != nil { 74 newRem.ETag = cd.ETag 75 newRem.CacheMaxAge = cd.MaxAge 76 } 77 err = f.S.WriteRemote(newRem) 78 if err != nil { 79 return "", err 80 } 81 82 return key, nil 83 } 84 85 func (f *httpFetcher) maybeUseCached(cd *cacheData) string { 86 if f.Rem == nil || cd == nil { 87 return "" 88 } 89 if cd.UseCached { 90 return f.Rem.BlobKey 91 } 92 if useCached(f.Rem.DownloadTime, cd.MaxAge) { 93 return f.Rem.BlobKey 94 } 95 return "" 96 } 97 98 func (f *httpFetcher) fetchURL(u *url.URL, a *asc, etag string) (readSeekCloser, *cacheData, error) { 99 if f.InsecureFlags.SkipTLSCheck() && f.Ks != nil { 100 log.Printf("warning: TLS verification has been disabled") 101 } 102 if f.InsecureFlags.SkipImageCheck() && f.Ks != nil { 103 log.Printf("warning: image signature verification has been disabled") 104 } 105 106 if f.InsecureFlags.SkipImageCheck() || f.Ks == nil { 107 o := f.getHTTPOps() 108 aciFile, cd, err := o.DownloadImageWithETag(u, etag) 109 if err != nil { 110 return nil, nil, err 111 } 112 return aciFile, cd, err 113 } 114 115 return f.fetchVerifiedURL(u, a, etag) 116 } 117 118 func (f *httpFetcher) fetchVerifiedURL(u *url.URL, a *asc, etag string) (readSeekCloser, *cacheData, error) { 119 o := f.getHTTPOps() 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 func() { maybeClose(ascFile) }() 126 127 aciFile, cd, err := o.DownloadImageWithETag(u, etag) 128 if err != nil { 129 return nil, nil, err 130 } 131 defer func() { maybeClose(aciFile) }() 132 if cd.UseCached { 133 return nil, cd, nil 134 } 135 136 if retry { 137 ascFile, err = o.DownloadSignatureAgain(a) 138 if err != nil { 139 return nil, nil, err 140 } 141 } 142 143 if err := f.validate(aciFile, ascFile); err != nil { 144 return nil, nil, err 145 } 146 retAciFile := aciFile 147 aciFile = nil 148 return retAciFile, cd, nil 149 } 150 151 func (f *httpFetcher) getHTTPOps() *httpOps { 152 return &httpOps{ 153 InsecureSkipTLSVerify: f.InsecureFlags.SkipTLSCheck(), 154 S: f.S, 155 Headers: f.Headers, 156 Debug: f.Debug, 157 } 158 } 159 160 func (f *httpFetcher) validate(aciFile, ascFile io.ReadSeeker) error { 161 v, err := newValidator(aciFile) 162 if err != nil { 163 return err 164 } 165 entity, err := v.ValidateWithSignature(f.Ks, ascFile) 166 if err != nil { 167 return err 168 } 169 if _, err := aciFile.Seek(0, 0); err != nil { 170 return errwrap.Wrap(errors.New("error seeking ACI file"), err) 171 } 172 173 printIdentities(entity) 174 return nil 175 } 176 177 func (f *httpFetcher) getETag() string { 178 if f.Rem != nil { 179 return f.Rem.ETag 180 } 181 return "" 182 } 183 184 func (f *httpFetcher) maybeOverrideAscFetcherWithRemote(o *httpOps, u *url.URL, a *asc) { 185 if a.Fetcher != nil { 186 return 187 } 188 u2 := ascURLFromImgURL(u) 189 a.Location = u2.String() 190 a.Fetcher = o.GetAscRemoteFetcher() 191 }