Remove use of DefaultTransport

Using DefaultTransport and manipulating its tls configuration may lead to unexpected behaviour
This commit is contained in:
Ben Einaudi 2020-05-04 15:03:17 +02:00
parent 31feaa4c12
commit 2f6090dcd7
10 changed files with 227 additions and 247 deletions

10
pkg/cache/cache.go vendored
View File

@ -17,9 +17,7 @@ limitations under the License.
package cache
import (
"crypto/tls"
"fmt"
"net/http"
"os"
"path"
"path/filepath"
@ -27,6 +25,7 @@ import (
"github.com/GoogleContainerTools/kaniko/pkg/config"
"github.com/GoogleContainerTools/kaniko/pkg/creds"
"github.com/GoogleContainerTools/kaniko/pkg/util"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/remote"
@ -67,12 +66,7 @@ func (rc *RegistryCache) RetrieveLayer(ck string) (v1.Image, error) {
cacheRef.Repository.Registry = newReg
}
tr := http.DefaultTransport.(*http.Transport)
if rc.Opts.SkipTLSVerifyRegistries.Contains(registryName) {
tr.TLSClientConfig = &tls.Config{
InsecureSkipVerify: true,
}
}
tr := util.MakeTransport(rc.Opts, registryName)
img, err := remote.Image(cacheRef, remote.WithTransport(tr), remote.WithAuthFromKeychain(creds.GetKeychain()))
if err != nil {

View File

@ -44,6 +44,7 @@ import (
"github.com/GoogleContainerTools/kaniko/pkg/config"
"github.com/GoogleContainerTools/kaniko/pkg/constants"
"github.com/GoogleContainerTools/kaniko/pkg/dockerfile"
image_util "github.com/GoogleContainerTools/kaniko/pkg/image"
"github.com/GoogleContainerTools/kaniko/pkg/snapshot"
"github.com/GoogleContainerTools/kaniko/pkg/timing"
"github.com/GoogleContainerTools/kaniko/pkg/util"
@ -84,7 +85,7 @@ type stageBuilder struct {
// newStageBuilder returns a new type stageBuilder which contains all the information required to build the stage
func newStageBuilder(opts *config.KanikoOptions, stage config.KanikoStage, crossStageDeps map[int][]string, dcm map[string]string, sid map[string]string, stageNameToIdx map[string]string) (*stageBuilder, error) {
sourceImage, err := util.RetrieveSourceImage(stage, opts)
sourceImage, err := image_util.RetrieveSourceImage(stage, opts)
if err != nil {
return nil, err
}
@ -517,7 +518,7 @@ func CalculateDependencies(stages []config.KanikoStage, opts *config.KanikoOptio
} else if s.Name == constants.NoBaseImage {
image = empty.Image
} else {
image, err = util.RetrieveSourceImage(s, opts)
image, err = image_util.RetrieveSourceImage(s, opts)
if err != nil {
return nil, err
}
@ -751,7 +752,7 @@ func fetchExtraStages(stages []config.KanikoStage, opts *config.KanikoOptions) e
// This must be an image name, fetch it.
logrus.Debugf("Found extra base image stage %s", c.From)
sourceImage, err := util.RetrieveRemoteImage(c.From, opts)
sourceImage, err := image_util.RetrieveRemoteImage(c.From, opts)
if err != nil {
return err
}

View File

@ -17,8 +17,6 @@ limitations under the License.
package executor
import (
"crypto/tls"
"crypto/x509"
"encoding/json"
"fmt"
"io/ioutil"
@ -34,6 +32,7 @@ import (
"github.com/GoogleContainerTools/kaniko/pkg/constants"
"github.com/GoogleContainerTools/kaniko/pkg/creds"
"github.com/GoogleContainerTools/kaniko/pkg/timing"
"github.com/GoogleContainerTools/kaniko/pkg/util"
"github.com/GoogleContainerTools/kaniko/pkg/version"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
@ -48,15 +47,15 @@ import (
"github.com/spf13/afero"
)
type withUserAgent struct {
t http.RoundTripper
}
// for testing
var (
newRetry = transport.NewRetry
)
type withUserAgent struct {
t http.RoundTripper
}
const (
UpstreamClientUaKey = "UPSTREAM_CLIENT_TYPE"
)
@ -82,41 +81,6 @@ func (w *withUserAgent) RoundTrip(r *http.Request) (*http.Response, error) {
return w.t.RoundTrip(r)
}
type CertPool interface {
value() *x509.CertPool
append(path string) error
}
type X509CertPool struct {
inner x509.CertPool
}
func (p *X509CertPool) value() *x509.CertPool {
return &p.inner
}
func (p *X509CertPool) append(path string) error {
pem, err := ioutil.ReadFile(path)
if err != nil {
return err
}
p.inner.AppendCertsFromPEM(pem)
return nil
}
type systemCertLoader func() CertPool
var defaultX509Handler systemCertLoader = func() CertPool {
systemCertPool, err := x509.SystemCertPool()
if err != nil {
logrus.Warn("Failed to load system cert pool. Loading empty one instead.")
systemCertPool = x509.NewCertPool()
}
return &X509CertPool{
inner: *systemCertPool,
}
}
// for testing
var (
fs = afero.NewOsFs()
@ -161,7 +125,7 @@ func CheckPushPermissions(opts *config.KanikoOptions) error {
}
destRef.Repository.Registry = newReg
}
tr := makeTransport(opts, registryName, defaultX509Handler)
tr := newRetry(util.MakeTransport(opts, registryName))
if err := checkRemotePushPermission(destRef, creds.GetKeychain(), tr); err != nil {
return errors.Wrapf(err, "checking push permission for %q", destRef)
}
@ -258,7 +222,7 @@ func DoPush(image v1.Image, opts *config.KanikoOptions) error {
return errors.Wrap(err, "resolving pushAuth")
}
tr := makeTransport(opts, registryName, defaultX509Handler)
tr := newRetry(util.MakeTransport(opts, registryName))
rt := &withUserAgent{t: tr}
if err := remote.Write(destRef, image, remote.WithAuth(pushAuth), remote.WithTransport(rt)); err != nil {
@ -300,26 +264,6 @@ func writeImageOutputs(image v1.Image, destRefs []name.Tag) error {
return nil
}
func makeTransport(opts *config.KanikoOptions, registryName string, loader systemCertLoader) http.RoundTripper {
// Create a transport to set our user-agent.
var tr http.RoundTripper = http.DefaultTransport.(*http.Transport).Clone()
if opts.SkipTLSVerify || opts.SkipTLSVerifyRegistries.Contains(registryName) {
tr.(*http.Transport).TLSClientConfig = &tls.Config{
InsecureSkipVerify: true,
}
} else if certificatePath := opts.RegistriesCertificates[registryName]; certificatePath != "" {
systemCertPool := loader()
if err := systemCertPool.append(certificatePath); err != nil {
logrus.WithError(err).Warnf("Failed to load certificate %s for %s\n", certificatePath, registryName)
} else {
tr.(*http.Transport).TLSClientConfig = &tls.Config{
RootCAs: systemCertPool.value(),
}
}
}
return newRetry(tr)
}
// pushLayerToCache pushes layer (tagged with cacheKey) to opts.Cache
// if opts.Cache doesn't exist, infer the cache from the given destination
func pushLayerToCache(opts *config.KanikoOptions, cacheKey string, tarPath string, createdBy string) error {

View File

@ -18,8 +18,6 @@ package executor
import (
"bytes"
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net/http"
@ -34,7 +32,6 @@ import (
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/layout"
"github.com/google/go-containerregistry/pkg/v1/random"
"github.com/google/go-containerregistry/pkg/v1/remote/transport"
"github.com/google/go-containerregistry/pkg/v1/validate"
"github.com/spf13/afero"
@ -272,103 +269,6 @@ func TestImageNameDigestFile(t *testing.T) {
}
type mockedCertPool struct {
certificatesPath []string
}
func (m *mockedCertPool) value() *x509.CertPool {
return &x509.CertPool{}
}
func (m *mockedCertPool) append(path string) error {
m.certificatesPath = append(m.certificatesPath, path)
return nil
}
type retryTransport struct {
inner http.RoundTripper
}
func (t *retryTransport) RoundTrip(in *http.Request) (out *http.Response, err error) {
return nil, nil
}
func Test_makeTransport(t *testing.T) {
registryName := "my.registry.name"
tests := []struct {
name string
opts *config.KanikoOptions
check func(*tls.Config, *mockedCertPool)
}{
{
name: "SkipTLSVerify set",
opts: &config.KanikoOptions{SkipTLSVerify: true},
check: func(config *tls.Config, pool *mockedCertPool) {
if !config.InsecureSkipVerify {
t.Errorf("makeTransport().TLSClientConfig.InsecureSkipVerify not set while SkipTLSVerify set")
}
},
},
{
name: "SkipTLSVerifyRegistries set with expected registry",
opts: &config.KanikoOptions{SkipTLSVerifyRegistries: []string{registryName}},
check: func(config *tls.Config, pool *mockedCertPool) {
if !config.InsecureSkipVerify {
t.Errorf("makeTransport().TLSClientConfig.InsecureSkipVerify not set while SkipTLSVerifyRegistries set with registry name")
}
},
},
{
name: "SkipTLSVerifyRegistries set with other registry",
opts: &config.KanikoOptions{SkipTLSVerifyRegistries: []string{fmt.Sprintf("other.%s", registryName)}},
check: func(config *tls.Config, pool *mockedCertPool) {
if config.InsecureSkipVerify {
t.Errorf("makeTransport().TLSClientConfig.InsecureSkipVerify set while SkipTLSVerifyRegistries not set with registry name")
}
},
},
{
name: "RegistriesCertificates set for registry",
opts: &config.KanikoOptions{RegistriesCertificates: map[string]string{registryName: "/path/to/the/certificate.cert"}},
check: func(config *tls.Config, pool *mockedCertPool) {
if len(pool.certificatesPath) != 1 || pool.certificatesPath[0] != "/path/to/the/certificate.cert" {
t.Errorf("makeTransport().RegistriesCertificates certificate not appended to system certificates")
}
},
},
{
name: "RegistriesCertificates set for another registry",
opts: &config.KanikoOptions{RegistriesCertificates: map[string]string{fmt.Sprintf("other.%s=", registryName): "/path/to/the/certificate.cert"}},
check: func(config *tls.Config, pool *mockedCertPool) {
if len(pool.certificatesPath) != 0 {
t.Errorf("makeTransport().RegistriesCertificates certificate appended to system certificates while added for other registry")
}
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
originalRetry := newRetry
newRetry = func(inner http.RoundTripper, _ ...transport.Option) http.RoundTripper {
return &retryTransport{
inner: inner,
}
}
defer func() { newRetry = originalRetry }()
var certificatesPath []string
certPool := mockedCertPool{
certificatesPath: certificatesPath,
}
var mockedSystemCertLoader systemCertLoader = func() CertPool {
return &certPool
}
actual := makeTransport(tt.opts, registryName, mockedSystemCertLoader)
tt.check(actual.(*retryTransport).inner.(*http.Transport).TLSClientConfig, &certPool)
})
}
}
var calledExecCommand = false
var calledCheckPushPermission = false

View File

@ -1,43 +0,0 @@
/*
Copyright 2018 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package image
import (
"os"
"strings"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/sirupsen/logrus"
)
// SetEnvVariables sets environment variables as specified in the image
func SetEnvVariables(img v1.Image) error {
cfg, err := img.ConfigFile()
if err != nil {
return err
}
envVars := cfg.Config.Env
for _, envVar := range envVars {
split := strings.SplitN(envVar, "=", 2)
if err := os.Setenv(split[0], split[1]); err != nil {
return err
}
logrus.Infof("Setting environment variable %s", envVar)
}
return nil
}

View File

@ -14,29 +14,28 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package util
package image
import (
"crypto/tls"
"fmt"
"net/http"
"path/filepath"
"runtime"
"strconv"
"github.com/GoogleContainerTools/kaniko/pkg/timing"
"github.com/GoogleContainerTools/kaniko/pkg/cache"
"github.com/GoogleContainerTools/kaniko/pkg/config"
"github.com/GoogleContainerTools/kaniko/pkg/constants"
"github.com/GoogleContainerTools/kaniko/pkg/creds"
"github.com/GoogleContainerTools/kaniko/pkg/timing"
"github.com/GoogleContainerTools/kaniko/pkg/util"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/empty"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/google/go-containerregistry/pkg/v1/tarball"
"github.com/sirupsen/logrus"
"github.com/GoogleContainerTools/kaniko/pkg/cache"
"github.com/GoogleContainerTools/kaniko/pkg/config"
"github.com/GoogleContainerTools/kaniko/pkg/constants"
"github.com/sirupsen/logrus"
)
var (
@ -55,7 +54,7 @@ func RetrieveSourceImage(stage config.KanikoStage, opts *config.KanikoOptions) (
buildArgs = append(buildArgs, fmt.Sprintf("%s=%s", arg.Key, arg.ValueString()))
}
buildArgs = append(buildArgs, opts.BuildArgs...)
currentBaseName, err := ResolveEnvironmentReplacement(stage.BaseName, buildArgs, false)
currentBaseName, err := util.ResolveEnvironmentReplacement(stage.BaseName, buildArgs, false)
if err != nil {
return nil, err
}
@ -146,12 +145,7 @@ func remoteImage(image string, opts *config.KanikoOptions) (v1.Image, error) {
}
func remoteOptions(registryName string, opts *config.KanikoOptions) []remote.Option {
tr := http.DefaultTransport.(*http.Transport)
if opts.SkipTLSVerifyPull || opts.SkipTLSVerifyRegistries.Contains(registryName) {
tr.TLSClientConfig = &tls.Config{
InsecureSkipVerify: true,
}
}
tr := util.MakeTransport(opts, registryName)
// on which v1.Platform is this currently running?
platform := currentPlatform()
@ -182,3 +176,11 @@ func cachedImage(opts *config.KanikoOptions, image string) (v1.Image, error) {
}
return cache.LocalSource(&opts.CacheOptions, cacheKey)
}
// CurrentPlatform returns the v1.Platform on which the code runs
func currentPlatform() v1.Platform {
return v1.Platform{
OS: runtime.GOOS,
Architecture: runtime.GOARCH,
}
}

View File

@ -14,18 +14,19 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package util
package image
import (
"bytes"
"testing"
"github.com/GoogleContainerTools/kaniko/pkg/config"
"github.com/GoogleContainerTools/kaniko/testutil"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/empty"
"github.com/moby/buildkit/frontend/dockerfile/instructions"
"github.com/moby/buildkit/frontend/dockerfile/parser"
"github.com/GoogleContainerTools/kaniko/pkg/config"
"github.com/GoogleContainerTools/kaniko/testutil"
)
var (

View File

@ -0,0 +1,82 @@
/*
Copyright 2020 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"crypto/tls"
"crypto/x509"
"io/ioutil"
"net/http"
"github.com/GoogleContainerTools/kaniko/pkg/config"
"github.com/sirupsen/logrus"
)
type CertPool interface {
value() *x509.CertPool
append(path string) error
}
type X509CertPool struct {
inner x509.CertPool
}
func (p *X509CertPool) value() *x509.CertPool {
return &p.inner
}
func (p *X509CertPool) append(path string) error {
pem, err := ioutil.ReadFile(path)
if err != nil {
return err
}
p.inner.AppendCertsFromPEM(pem)
return nil
}
var systemCertLoader CertPool
func init() {
systemCertPool, err := x509.SystemCertPool()
if err != nil {
logrus.Warn("Failed to load system cert pool. Loading empty one instead.")
systemCertPool = x509.NewCertPool()
}
systemCertLoader = &X509CertPool{
inner: *systemCertPool,
}
}
func MakeTransport(opts *config.KanikoOptions, registryName string) http.RoundTripper {
// Create a transport to set our user-agent.
var tr http.RoundTripper = http.DefaultTransport.(*http.Transport).Clone()
if opts.SkipTLSVerify || opts.SkipTLSVerifyRegistries.Contains(registryName) {
tr.(*http.Transport).TLSClientConfig = &tls.Config{
InsecureSkipVerify: true,
}
} else if certificatePath := opts.RegistriesCertificates[registryName]; certificatePath != "" {
if err := systemCertLoader.append(certificatePath); err != nil {
logrus.WithError(err).Warnf("Failed to load certificate %s for %s\n", certificatePath, registryName)
} else {
tr.(*http.Transport).TLSClientConfig = &tls.Config{
RootCAs: systemCertLoader.value(),
}
}
}
return tr
}

View File

@ -0,0 +1,110 @@
/*
Copyright 2020 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"crypto/tls"
"crypto/x509"
"fmt"
"net/http"
"testing"
"github.com/GoogleContainerTools/kaniko/pkg/config"
)
type mockedCertPool struct {
certificatesPath []string
}
func (m *mockedCertPool) value() *x509.CertPool {
return &x509.CertPool{}
}
func (m *mockedCertPool) append(path string) error {
m.certificatesPath = append(m.certificatesPath, path)
return nil
}
func Test_makeTransport(t *testing.T) {
registryName := "my.registry.name"
tests := []struct {
name string
opts *config.KanikoOptions
check func(*tls.Config, *mockedCertPool)
}{
{
name: "SkipTLSVerify set",
opts: &config.KanikoOptions{SkipTLSVerify: true},
check: func(config *tls.Config, pool *mockedCertPool) {
if !config.InsecureSkipVerify {
t.Errorf("makeTransport().TLSClientConfig.InsecureSkipVerify not set while SkipTLSVerify set")
}
},
},
{
name: "SkipTLSVerifyRegistries set with expected registry",
opts: &config.KanikoOptions{SkipTLSVerifyRegistries: []string{registryName}},
check: func(config *tls.Config, pool *mockedCertPool) {
if !config.InsecureSkipVerify {
t.Errorf("makeTransport().TLSClientConfig.InsecureSkipVerify not set while SkipTLSVerifyRegistries set with registry name")
}
},
},
{
name: "SkipTLSVerifyRegistries set with other registry",
opts: &config.KanikoOptions{SkipTLSVerifyRegistries: []string{fmt.Sprintf("other.%s", registryName)}},
check: func(config *tls.Config, pool *mockedCertPool) {
if config.InsecureSkipVerify {
t.Errorf("makeTransport().TLSClientConfig.InsecureSkipVerify set while SkipTLSVerifyRegistries not set with registry name")
}
},
},
{
name: "RegistriesCertificates set for registry",
opts: &config.KanikoOptions{RegistriesCertificates: map[string]string{registryName: "/path/to/the/certificate.cert"}},
check: func(config *tls.Config, pool *mockedCertPool) {
if len(pool.certificatesPath) != 1 || pool.certificatesPath[0] != "/path/to/the/certificate.cert" {
t.Errorf("makeTransport().RegistriesCertificates certificate not appended to system certificates")
}
},
},
{
name: "RegistriesCertificates set for another registry",
opts: &config.KanikoOptions{RegistriesCertificates: map[string]string{fmt.Sprintf("other.%s=", registryName): "/path/to/the/certificate.cert"}},
check: func(config *tls.Config, pool *mockedCertPool) {
if len(pool.certificatesPath) != 0 {
t.Errorf("makeTransport().RegistriesCertificates certificate appended to system certificates while added for other registry")
}
},
},
}
savedSystemCertLoader := systemCertLoader
defer func() { systemCertLoader = savedSystemCertLoader }()
for _, tt := range tests {
var certificatesPath []string
certPool := &mockedCertPool{
certificatesPath: certificatesPath,
}
systemCertLoader = certPool
t.Run(tt.name, func(t *testing.T) {
tr := MakeTransport(tt.opts, registryName)
tt.check(tr.(*http.Transport).TLSClientConfig, certPool)
})
}
}

View File

@ -23,14 +23,11 @@ import (
"io"
"io/ioutil"
"os"
"runtime"
"strconv"
"sync"
"syscall"
"github.com/minio/highwayhash"
v1 "github.com/google/go-containerregistry/pkg/v1"
)
// Hasher returns a hash function, used in snapshotting to determine if a file has changed
@ -128,14 +125,6 @@ func SHA256(r io.Reader) (string, error) {
return hex.EncodeToString(hasher.Sum(make([]byte, 0, hasher.Size()))), nil
}
// CurrentPlatform returns the v1.Platform on which the code runs
func currentPlatform() v1.Platform {
return v1.Platform{
OS: runtime.GOOS,
Architecture: runtime.GOARCH,
}
}
// GetInputFrom returns Reader content
func GetInputFrom(r io.Reader) ([]byte, error) {
output, err := ioutil.ReadAll(r)