parent
17ceb312c6
commit
b73c2c10c8
|
|
@ -154,6 +154,8 @@ func addKanikoOptionsFlags() {
|
|||
RootCmd.PersistentFlags().DurationVarP(&opts.CacheTTL, "cache-ttl", "", time.Hour*336, "Cache timeout in hours. Defaults to two weeks.")
|
||||
RootCmd.PersistentFlags().VarP(&opts.InsecureRegistries, "insecure-registry", "", "Insecure registry using plain HTTP to push and pull. Set it repeatedly for multiple registries.")
|
||||
RootCmd.PersistentFlags().VarP(&opts.SkipTLSVerifyRegistries, "skip-tls-verify-registry", "", "Insecure registry ignoring TLS verify to push and pull. Set it repeatedly for multiple registries.")
|
||||
opts.RegistriesCertificates = make(map[string]string)
|
||||
RootCmd.PersistentFlags().VarP(&opts.RegistriesCertificates, "registry-certificate", "", "Use the provided certificate for TLS communication with the given registry. Expected format is 'my.registry.url=/path/to/the/server/certificate'.")
|
||||
RootCmd.PersistentFlags().StringVarP(&opts.RegistryMirror, "registry-mirror", "", "", "Registry mirror to use has pull-through cache instead of docker.io.")
|
||||
RootCmd.PersistentFlags().BoolVarP(&opts.WhitelistVarRun, "whitelist-var-run", "", true, "Ignore /var/run directory when taking image snapshot. Set it to false to preserve /var/run/ in destination image. (Default true).")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
# Builds the static Go image to execute in a Kubernetes job
|
||||
|
||||
FROM golang:1.12
|
||||
FROM golang:1.13
|
||||
ARG GOARCH=amd64
|
||||
WORKDIR /go/src/github.com/GoogleContainerTools/kaniko
|
||||
# Get GCR credential helper
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
# Builds the static Go image to execute in a Kubernetes job
|
||||
|
||||
# Stage 0: Build the executor binary and get credential helpers
|
||||
FROM golang:1.12
|
||||
FROM golang:1.13
|
||||
ARG GOARCH=amd64
|
||||
WORKDIR /go/src/github.com/GoogleContainerTools/kaniko
|
||||
# Get GCR credential helper
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
# Builds the static Go image to execute in a Kubernetes job
|
||||
|
||||
FROM golang:1.12
|
||||
FROM golang:1.13
|
||||
ARG GOARCH=amd64
|
||||
WORKDIR /go/src/github.com/GoogleContainerTools/kaniko
|
||||
# Get GCR credential helper
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
|
@ -39,6 +40,7 @@ func (b *multiArg) Set(value string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// The third is Type() string
|
||||
func (b *multiArg) Type() string {
|
||||
return "multi-arg type"
|
||||
}
|
||||
|
|
@ -51,3 +53,32 @@ func (b *multiArg) Contains(v string) bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// This type is used to supported passing in multiple key=value flags
|
||||
type keyValueArg map[string]string
|
||||
|
||||
// Now, for our new type, implement the two methods of
|
||||
// the flag.Value interface...
|
||||
// The first method is String() string
|
||||
func (a *keyValueArg) String() string {
|
||||
var result []string
|
||||
for key := range *a {
|
||||
result = append(result, fmt.Sprintf("%s=%s", key, (*a)[key]))
|
||||
}
|
||||
return strings.Join(result, ",")
|
||||
}
|
||||
|
||||
// The second method is Set(value string) error
|
||||
func (a *keyValueArg) Set(value string) error {
|
||||
valueSplit := strings.SplitN(value, "=", 2)
|
||||
if len(valueSplit) < 2 {
|
||||
return fmt.Errorf("invalid argument value. expect key=value, got %s", value)
|
||||
}
|
||||
(*a)[valueSplit[0]] = valueSplit[1]
|
||||
return nil
|
||||
}
|
||||
|
||||
// The third is Type() string
|
||||
func (a *keyValueArg) Type() string {
|
||||
return "key-value-arg type"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
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 config
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestMultiArg_Set_shouldAppendValue(t *testing.T) {
|
||||
var arg multiArg
|
||||
arg.Set("value1")
|
||||
if len(arg) != 1 || arg[0] != "value1" {
|
||||
t.Error("Fist value was not appended")
|
||||
}
|
||||
arg.Set("value2")
|
||||
if len(arg) != 2 || arg[1] != "value2" {
|
||||
t.Error("Second value was not appended")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_KeyValueArg_Set_shouldSplitArgument(t *testing.T) {
|
||||
arg := make(keyValueArg)
|
||||
arg.Set("key=value")
|
||||
if arg["key"] != "value" {
|
||||
t.Error("Invalid split. key=value should be split to key=>value")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_KeyValueArg_Set_shouldAcceptEqualAsValue(t *testing.T) {
|
||||
arg := make(keyValueArg)
|
||||
arg.Set("key=value=something")
|
||||
if arg["key"] != "value=something" {
|
||||
t.Error("Invalid split. key=value=something should be split to key=>value=something")
|
||||
}
|
||||
}
|
||||
|
|
@ -44,6 +44,7 @@ type KanikoOptions struct {
|
|||
BuildArgs multiArg
|
||||
InsecureRegistries multiArg
|
||||
SkipTLSVerifyRegistries multiArg
|
||||
RegistriesCertificates keyValueArg
|
||||
Insecure bool
|
||||
SkipTLSVerify bool
|
||||
InsecurePull bool
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package executor
|
|||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
|
@ -62,6 +63,41 @@ 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,
|
||||
}
|
||||
}
|
||||
|
||||
// CheckPushPermissions checks that the configured credentials can be used to
|
||||
// push to every specified destination.
|
||||
func CheckPushPermissions(opts *config.KanikoOptions) error {
|
||||
|
|
@ -87,7 +123,7 @@ func CheckPushPermissions(opts *config.KanikoOptions) error {
|
|||
}
|
||||
destRef.Repository.Registry = newReg
|
||||
}
|
||||
tr := makeTransport(opts, registryName)
|
||||
tr := makeTransport(opts, registryName, defaultX509Handler)
|
||||
if err := remote.CheckPushPermission(destRef, creds.GetKeychain(), tr); err != nil {
|
||||
return errors.Wrapf(err, "checking push permission for %q", destRef)
|
||||
}
|
||||
|
|
@ -184,7 +220,7 @@ func DoPush(image v1.Image, opts *config.KanikoOptions) error {
|
|||
return errors.Wrap(err, "resolving pushAuth")
|
||||
}
|
||||
|
||||
tr := makeTransport(opts, registryName)
|
||||
tr := makeTransport(opts, registryName, defaultX509Handler)
|
||||
rt := &withUserAgent{t: tr}
|
||||
|
||||
if err := remote.Write(destRef, image, remote.WithAuth(pushAuth), remote.WithTransport(rt)); err != nil {
|
||||
|
|
@ -228,13 +264,22 @@ func writeImageOutputs(image v1.Image, destRefs []name.Tag) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func makeTransport(opts *config.KanikoOptions, registryName string) http.RoundTripper {
|
||||
func makeTransport(opts *config.KanikoOptions, registryName string, loader systemCertLoader) http.RoundTripper {
|
||||
// Create a transport to set our user-agent.
|
||||
tr := http.DefaultTransport
|
||||
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 tr
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ package executor
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
|
@ -223,3 +225,85 @@ func TestImageNameDigestFile(t *testing.T) {
|
|||
testutil.CheckErrorAndDeepEqual(t, false, err, want, got)
|
||||
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var certificatesPath []string
|
||||
certPool := mockedCertPool{
|
||||
certificatesPath: certificatesPath,
|
||||
}
|
||||
var mockedSystemCertLoader systemCertLoader = func() CertPool {
|
||||
return &certPool
|
||||
}
|
||||
transport := makeTransport(tt.opts, registryName, mockedSystemCertLoader)
|
||||
tt.check(transport.(*http.Transport).TLSClientConfig, &certPool)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue