mirror of
https://github.com/GoogleContainerTools/kaniko
synced 2026-10-19 06:04:28 +02:00
Merge pull request #1140 from samos123/fix-1122-public-gcr
Add ability to use public GCR repos without being authenticated
This commit is contained in:
@@ -20,7 +20,6 @@ WORKDIR /go/src/github.com/GoogleContainerTools/kaniko
|
||||
# Get GCR credential helper
|
||||
ADD https://github.com/GoogleCloudPlatform/docker-credential-gcr/releases/download/v1.5.0/docker-credential-gcr_linux_amd64-1.5.0.tar.gz /usr/local/bin/
|
||||
RUN tar -C /usr/local/bin/ -xvzf /usr/local/bin/docker-credential-gcr_linux_amd64-1.5.0.tar.gz
|
||||
RUN docker-credential-gcr configure-docker
|
||||
# Get Amazon ECR credential helper
|
||||
RUN go get -u github.com/awslabs/amazon-ecr-credential-helper/ecr-login/cli/docker-credential-ecr-login
|
||||
RUN make -C /go/src/github.com/awslabs/amazon-ecr-credential-helper linux-amd64
|
||||
@@ -37,7 +36,6 @@ COPY --from=0 /usr/local/bin/docker-credential-gcr /kaniko/docker-credential-gcr
|
||||
COPY --from=0 /go/src/github.com/awslabs/amazon-ecr-credential-helper/bin/linux-amd64/docker-credential-ecr-login /kaniko/docker-credential-ecr-login
|
||||
COPY --from=0 /usr/local/bin/docker-credential-acr-linux /kaniko/docker-credential-acr
|
||||
COPY files/ca-certificates.crt /kaniko/ssl/certs/
|
||||
COPY --from=0 /root/.docker/config.json /kaniko/.docker/config.json
|
||||
ENV HOME /root
|
||||
ENV USER /root
|
||||
ENV PATH /usr/local/bin:/kaniko
|
||||
|
||||
@@ -21,7 +21,6 @@ WORKDIR /go/src/github.com/GoogleContainerTools/kaniko
|
||||
# Get GCR credential helper
|
||||
ADD https://github.com/GoogleCloudPlatform/docker-credential-gcr/releases/download/v1.5.0/docker-credential-gcr_linux_amd64-1.5.0.tar.gz /usr/local/bin/
|
||||
RUN tar -C /usr/local/bin/ -xvzf /usr/local/bin/docker-credential-gcr_linux_amd64-1.5.0.tar.gz
|
||||
RUN docker-credential-gcr configure-docker
|
||||
# Get Amazon ECR credential helper
|
||||
RUN go get -u github.com/awslabs/amazon-ecr-credential-helper/ecr-login/cli/docker-credential-ecr-login
|
||||
RUN make -C /go/src/github.com/awslabs/amazon-ecr-credential-helper linux-amd64
|
||||
@@ -48,7 +47,6 @@ COPY --from=1 /distroless/bazel-bin/experimental/busybox/busybox/ /busybox/
|
||||
# Declare /busybox as a volume to get it automatically whitelisted
|
||||
VOLUME /busybox
|
||||
COPY files/ca-certificates.crt /kaniko/ssl/certs/
|
||||
COPY --from=0 /root/.docker/config.json /kaniko/.docker/config.json
|
||||
ENV HOME /root
|
||||
ENV USER /root
|
||||
ENV PATH /usr/local/bin:/kaniko:/busybox
|
||||
|
||||
@@ -20,7 +20,6 @@ WORKDIR /go/src/github.com/GoogleContainerTools/kaniko
|
||||
# Get GCR credential helper
|
||||
ADD https://github.com/GoogleCloudPlatform/docker-credential-gcr/releases/download/v1.5.0/docker-credential-gcr_linux_amd64-1.5.0.tar.gz /usr/local/bin/
|
||||
RUN tar -C /usr/local/bin/ -xvzf /usr/local/bin/docker-credential-gcr_linux_amd64-1.5.0.tar.gz
|
||||
RUN docker-credential-gcr configure-docker
|
||||
# Get Amazon ECR credential helper
|
||||
RUN go get -u github.com/awslabs/amazon-ecr-credential-helper/ecr-login/cli/docker-credential-ecr-login
|
||||
RUN make -C /go/src/github.com/awslabs/amazon-ecr-credential-helper linux-amd64
|
||||
@@ -37,7 +36,6 @@ COPY --from=0 /usr/local/bin/docker-credential-gcr /kaniko/docker-credential-gcr
|
||||
COPY --from=0 /go/src/github.com/awslabs/amazon-ecr-credential-helper/bin/linux-amd64/docker-credential-ecr-login /kaniko/docker-credential-ecr-login
|
||||
COPY --from=0 /usr/local/bin/docker-credential-acr-linux /kaniko/docker-credential-acr
|
||||
COPY files/ca-certificates.crt /kaniko/ssl/certs/
|
||||
COPY --from=0 /root/.docker/config.json /kaniko/.docker/config.json
|
||||
ENV HOME /root
|
||||
ENV USER /root
|
||||
ENV PATH /usr/local/bin:/kaniko
|
||||
|
||||
@@ -102,15 +102,6 @@ func launchTests(m *testing.M) (int, error) {
|
||||
|
||||
RunOnInterrupt(func() { DeleteFromBucket(fileInBucket) })
|
||||
defer DeleteFromBucket(fileInBucket)
|
||||
} else {
|
||||
var err error
|
||||
var migratedFiles []string
|
||||
if migratedFiles, err = MigrateGCRRegistry(dockerfilesPath, allDockerfiles, config.imageRepo); err != nil {
|
||||
RollbackMigratedFiles(dockerfilesPath, migratedFiles)
|
||||
return 1, errors.Wrap(err, "Fail to migrate dockerfiles from gcs")
|
||||
}
|
||||
RunOnInterrupt(func() { RollbackMigratedFiles(dockerfilesPath, migratedFiles) })
|
||||
defer RollbackMigratedFiles(dockerfilesPath, migratedFiles)
|
||||
}
|
||||
if err := buildRequiredImages(); err != nil {
|
||||
return 1, errors.Wrap(err, "Error while building images")
|
||||
|
||||
@@ -1,155 +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 integration
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// This function crawl through dockerfiles and replace all reference to "gcr.io/" in FROM instruction and replace it targetRepo
|
||||
// The result is the array containing all modified file
|
||||
// Each of this file is saved
|
||||
func MigrateGCRRegistry(dockerfilesPath string, dockerfiles []string, targetRepo string) ([]string, error) {
|
||||
var savedFiles []string
|
||||
importedImages := map[string]interface{}{}
|
||||
|
||||
for _, dockerfile := range dockerfiles {
|
||||
if referencedImages, savedFile, err := migrateFile(dockerfilesPath, dockerfile, targetRepo); err != nil {
|
||||
if savedFile {
|
||||
savedFiles = append(savedFiles, dockerfile)
|
||||
}
|
||||
return savedFiles, err
|
||||
} else if savedFile {
|
||||
savedFiles = append(savedFiles, dockerfile)
|
||||
for _, referencedImage := range referencedImages {
|
||||
importedImages[referencedImage] = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
for image := range importedImages {
|
||||
if err := importImage(image, targetRepo); err != nil {
|
||||
return savedFiles, err
|
||||
}
|
||||
}
|
||||
return savedFiles, nil
|
||||
}
|
||||
|
||||
// This function rollback all previously modified files
|
||||
func RollbackMigratedFiles(dockerfilesPath string, dockerfiles []string) []error {
|
||||
var result []error
|
||||
for _, dockerfile := range dockerfiles {
|
||||
fmt.Printf("Rolling back %s\n", dockerfile)
|
||||
if err := recoverDockerfile(dockerfilesPath, dockerfile); err != nil {
|
||||
result = append(result, err)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Import the gcr.io image such as gcr.io/my-image to targetRepo
|
||||
func importImage(image string, targetRepo string) error {
|
||||
fmt.Printf("Importing %s to %s\n", image, targetRepo)
|
||||
targetImage := strings.ReplaceAll(image, "gcr.io/", targetRepo)
|
||||
pullCmd := exec.Command("docker", "pull", image)
|
||||
if out, err := RunCommandWithoutTest(pullCmd); err != nil {
|
||||
return fmt.Errorf("Failed to pull image %s with docker command \"%s\": %s %s", image, pullCmd.Args, err, string(out))
|
||||
}
|
||||
|
||||
tagCmd := exec.Command("docker", "tag", image, targetImage)
|
||||
if out, err := RunCommandWithoutTest(tagCmd); err != nil {
|
||||
return fmt.Errorf("Failed to tag image %s to %s with docker command \"%s\": %s %s", image, targetImage, tagCmd.Args, err, string(out))
|
||||
}
|
||||
|
||||
pushCmd := exec.Command("docker", "push", targetImage)
|
||||
if out, err := RunCommandWithoutTest(pushCmd); err != nil {
|
||||
return fmt.Errorf("Failed to push image %s with docker command \"%s\": %s %s", targetImage, pushCmd.Args, err, string(out))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// takes a dockerfile and replace each gcr.io/ occurrence in FROM instruction and replace it with imageRepo
|
||||
// return true if the file was saved
|
||||
// if so, the array is non nil and contains each gcr image name
|
||||
func migrateFile(dockerfilesPath string, dockerfile string, imageRepo string) ([]string, bool, error) {
|
||||
var input *os.File
|
||||
var output *os.File
|
||||
var err error
|
||||
var referencedImages []string
|
||||
|
||||
if input, err = os.Open(path.Join(dockerfilesPath, dockerfile)); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
defer input.Close()
|
||||
|
||||
var lines []string
|
||||
scanner := bufio.NewScanner(input)
|
||||
scanner.Split(bufio.ScanLines)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if isFromGcrBaseImageInstruction(line) {
|
||||
referencedImages = append(referencedImages, strings.Trim(strings.Split(line, " ")[1], " "))
|
||||
lines = append(lines, strings.ReplaceAll(line, gcrRepoPrefix, imageRepo))
|
||||
} else {
|
||||
lines = append(lines, line)
|
||||
}
|
||||
}
|
||||
rawOutput := []byte(strings.Join(append(lines, ""), "\n"))
|
||||
|
||||
if len(referencedImages) == 0 {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
if err = saveDockerfile(dockerfilesPath, dockerfile); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
if output, err = os.Create(path.Join(dockerfilesPath, dockerfile)); err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
defer output.Close()
|
||||
|
||||
if written, err := output.Write(rawOutput); err != nil {
|
||||
return nil, true, err
|
||||
} else if written != len(rawOutput) {
|
||||
return nil, true, fmt.Errorf("invalid number of byte written. Got %d, expected %d", written, len(rawOutput))
|
||||
}
|
||||
return referencedImages, true, nil
|
||||
|
||||
}
|
||||
|
||||
func isFromGcrBaseImageInstruction(line string) bool {
|
||||
result, _ := regexp.MatchString(fmt.Sprintf("FROM +%s", gcrRepoPrefix), line)
|
||||
return result
|
||||
}
|
||||
|
||||
func saveDockerfile(dockerfilesPath string, dockerfile string) error {
|
||||
return os.Rename(path.Join(dockerfilesPath, dockerfile), path.Join(dockerfilesPath, saveName(dockerfile)))
|
||||
}
|
||||
|
||||
func recoverDockerfile(dockerfilesPath string, dockerfile string) error {
|
||||
return os.Rename(path.Join(dockerfilesPath, saveName(dockerfile)), path.Join(dockerfilesPath, dockerfile))
|
||||
}
|
||||
|
||||
func saveName(dockerfile string) string {
|
||||
return fmt.Sprintf("%s_save_%d", dockerfile, os.Getpid())
|
||||
}
|
||||
+22
-3
@@ -24,6 +24,7 @@ import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -52,6 +53,7 @@ type withUserAgent struct {
|
||||
|
||||
const (
|
||||
UpstreamClientUaKey = "UPSTREAM_CLIENT_TYPE"
|
||||
DockerConfLocation = "/kaniko/.docker/config.json"
|
||||
)
|
||||
|
||||
func (w *withUserAgent) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
@@ -98,6 +100,13 @@ var defaultX509Handler systemCertLoader = func() CertPool {
|
||||
}
|
||||
}
|
||||
|
||||
// for testing
|
||||
var (
|
||||
fs = afero.NewOsFs()
|
||||
execCommand = exec.Command
|
||||
checkRemotePushPermission = remote.CheckPushPermission
|
||||
)
|
||||
|
||||
// CheckPushPermissions checks that the configured credentials can be used to
|
||||
// push to every specified destination.
|
||||
func CheckPushPermissions(opts *config.KanikoOptions) error {
|
||||
@@ -115,6 +124,18 @@ func CheckPushPermissions(opts *config.KanikoOptions) error {
|
||||
continue
|
||||
}
|
||||
|
||||
// Historically kaniko was pre-configured by default with gcr credential helper,
|
||||
// in here we keep the backwards compatibility by enabling the GCR helper only
|
||||
// when gcr.io is in one of the destinations.
|
||||
if strings.Contains(destRef.RegistryStr(), "gcr.io") {
|
||||
// Checking for existence of docker.config as it's normally required for
|
||||
// authenticated registries and prevent overwriting user provided docker conf
|
||||
if _, err := fs.Stat(DockerConfLocation); os.IsNotExist(err) {
|
||||
if err := execCommand("docker-credential-gcr", "configure-docker").Run(); err != nil {
|
||||
return errors.Wrap(err, "error while configuring docker-credential-gcr helper")
|
||||
}
|
||||
}
|
||||
}
|
||||
registryName := destRef.Repository.Registry.Name()
|
||||
if opts.Insecure || opts.InsecureRegistries.Contains(registryName) {
|
||||
newReg, err := name.NewRegistry(registryName, name.WeakValidation, name.Insecure)
|
||||
@@ -124,7 +145,7 @@ func CheckPushPermissions(opts *config.KanikoOptions) error {
|
||||
destRef.Repository.Registry = newReg
|
||||
}
|
||||
tr := makeTransport(opts, registryName, defaultX509Handler)
|
||||
if err := remote.CheckPushPermission(destRef, creds.GetKeychain(), tr); err != nil {
|
||||
if err := checkRemotePushPermission(destRef, creds.GetKeychain(), tr); err != nil {
|
||||
return errors.Wrapf(err, "checking push permission for %q", destRef)
|
||||
}
|
||||
checked[destRef.Context().RepositoryStr()] = true
|
||||
@@ -231,8 +252,6 @@ func DoPush(image v1.Image, opts *config.KanikoOptions) error {
|
||||
return writeImageOutputs(image, destRefs)
|
||||
}
|
||||
|
||||
var fs = afero.NewOsFs()
|
||||
|
||||
func writeImageOutputs(image v1.Image, destRefs []name.Tag) error {
|
||||
dir := os.Getenv("BUILDER_OUTPUT")
|
||||
if dir == "" {
|
||||
|
||||
@@ -24,11 +24,13 @@ import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/config"
|
||||
"github.com/GoogleContainerTools/kaniko/testutil"
|
||||
"github.com/google/go-containerregistry/pkg/authn"
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
"github.com/google/go-containerregistry/pkg/v1/layout"
|
||||
"github.com/google/go-containerregistry/pkg/v1/random"
|
||||
@@ -307,3 +309,68 @@ func Test_makeTransport(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var calledExecCommand = false
|
||||
var calledCheckPushPermission = false
|
||||
|
||||
func setCalledFalse() {
|
||||
calledExecCommand = false
|
||||
calledCheckPushPermission = false
|
||||
}
|
||||
|
||||
func fakeExecCommand(command string, args ...string) *exec.Cmd {
|
||||
calledExecCommand = true
|
||||
cs := []string{"-test.run=TestHelperProcess", "--", command}
|
||||
cs = append(cs, args...)
|
||||
cmd := exec.Command(os.Args[0], cs...)
|
||||
cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func fakeCheckPushPermission(ref name.Reference, kc authn.Keychain, t http.RoundTripper) error {
|
||||
calledCheckPushPermission = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestCheckPushPermissions(t *testing.T) {
|
||||
tests := []struct {
|
||||
Destination string
|
||||
ShouldCallExecCommand bool
|
||||
ExistingConfig bool
|
||||
}{
|
||||
{"gcr.io/test-image", true, false},
|
||||
{"gcr.io/test-image", false, true},
|
||||
{"localhost:5000/test-image", false, false},
|
||||
{"localhost:5000/test-image", false, true},
|
||||
}
|
||||
|
||||
execCommand = fakeExecCommand
|
||||
checkRemotePushPermission = fakeCheckPushPermission
|
||||
for _, test := range tests {
|
||||
testName := fmt.Sprintf("%s_ExistingDockerConf_%v", test.Destination, test.ExistingConfig)
|
||||
t.Run(testName, func(t *testing.T) {
|
||||
fs = afero.NewMemMapFs()
|
||||
opts := config.KanikoOptions{
|
||||
Destinations: []string{test.Destination},
|
||||
}
|
||||
if test.ExistingConfig {
|
||||
afero.WriteFile(fs, DockerConfLocation, []byte(""), os.FileMode(0644))
|
||||
defer fs.Remove(DockerConfLocation)
|
||||
}
|
||||
CheckPushPermissions(&opts)
|
||||
if test.ShouldCallExecCommand != calledExecCommand {
|
||||
t.Errorf("Expected calledExecCommand to be %v however it was %v",
|
||||
calledExecCommand, test.ShouldCallExecCommand)
|
||||
}
|
||||
setCalledFalse()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHelperProcess(t *testing.T) {
|
||||
if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(os.Stdout, "fake result")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user