You've already forked sap-jenkins-library
mirror of
https://github.com/SAP/jenkins-library.git
synced 2025-11-06 09:09:19 +02:00
fix(whitesourceExecuteScan): docker image download if dockerConfigJSON is set in vault (#3591)
Co-authored-by: ffeldmann <f.feldmann@sap.com>
This commit is contained in:
@@ -1,12 +1,14 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
piperDocker "github.com/SAP/jenkins-library/pkg/docker"
|
piperDocker "github.com/SAP/jenkins-library/pkg/docker"
|
||||||
"github.com/SAP/jenkins-library/pkg/log"
|
"github.com/SAP/jenkins-library/pkg/log"
|
||||||
|
"github.com/SAP/jenkins-library/pkg/piperutils"
|
||||||
"github.com/SAP/jenkins-library/pkg/telemetry"
|
"github.com/SAP/jenkins-library/pkg/telemetry"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@@ -19,13 +21,17 @@ func containerSaveImage(config containerSaveImageOptions, telemetryData *telemet
|
|||||||
dClient := &piperDocker.Client{}
|
dClient := &piperDocker.Client{}
|
||||||
dClient.SetOptions(dClientOptions)
|
dClient.SetOptions(dClientOptions)
|
||||||
|
|
||||||
_, err := runContainerSaveImage(&config, telemetryData, cachePath, "", dClient)
|
_, err := runContainerSaveImage(&config, telemetryData, cachePath, "", dClient, piperutils.Files{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Entry().WithError(err).Fatal("step execution failed")
|
log.Entry().WithError(err).Fatal("step execution failed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runContainerSaveImage(config *containerSaveImageOptions, telemetryData *telemetry.CustomData, cachePath, rootPath string, dClient piperDocker.Download) (string, error) {
|
func runContainerSaveImage(config *containerSaveImageOptions, telemetryData *telemetry.CustomData, cachePath, rootPath string, dClient piperDocker.Download, fileUtils piperutils.FileUtils) (string, error) {
|
||||||
|
if err := correctContainerDockerConfigEnvVar(config, fileUtils); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
err := os.RemoveAll(cachePath)
|
err := os.RemoveAll(cachePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.Wrap(err, "failed to prepare cache")
|
return "", errors.Wrap(err, "failed to prepare cache")
|
||||||
@@ -76,3 +82,35 @@ func runContainerSaveImage(config *containerSaveImageOptions, telemetryData *tel
|
|||||||
func filenameFromContainer(rootPath, containerImage string) string {
|
func filenameFromContainer(rootPath, containerImage string) string {
|
||||||
return filepath.Join(rootPath, strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(containerImage, "/", "_"), ":", "_"), ".", "_")+".tar")
|
return filepath.Join(rootPath, strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(containerImage, "/", "_"), ":", "_"), ".", "_")+".tar")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func correctContainerDockerConfigEnvVar(config *containerSaveImageOptions, utils piperutils.FileUtils) error {
|
||||||
|
dockerConfigDir, err := utils.TempDir("", "docker")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "unable to create docker config dir")
|
||||||
|
}
|
||||||
|
|
||||||
|
dockerConfigFile := fmt.Sprintf("%s/%s", dockerConfigDir, "config.json")
|
||||||
|
|
||||||
|
if len(config.DockerConfigJSON) > 0 {
|
||||||
|
log.Entry().Infof("Docker credentials configuration: %v", config.DockerConfigJSON)
|
||||||
|
|
||||||
|
if exists, _ := utils.FileExists(config.DockerConfigJSON); exists {
|
||||||
|
if _, err = utils.Copy(config.DockerConfigJSON, dockerConfigFile); err != nil {
|
||||||
|
return errors.Wrap(err, "unable to copy docker config")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Entry().Info("Docker credentials configuration: NONE")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(config.ContainerRegistryURL) > 0 && len(config.ContainerRegistryUser) > 0 && len(config.ContainerRegistryPassword) > 0 {
|
||||||
|
if _, err = piperDocker.CreateDockerConfigJSON(config.ContainerRegistryURL, config.ContainerRegistryUser, config.ContainerRegistryPassword, dockerConfigFile, dockerConfigFile, utils); err != nil {
|
||||||
|
log.Entry().Warningf("failed to update Docker config.json: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Setenv("DOCKER_CONFIG", dockerConfigDir)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,10 +16,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type containerSaveImageOptions struct {
|
type containerSaveImageOptions struct {
|
||||||
ContainerRegistryURL string `json:"containerRegistryUrl,omitempty"`
|
ContainerRegistryURL string `json:"containerRegistryUrl,omitempty"`
|
||||||
ContainerImage string `json:"containerImage,omitempty"`
|
ContainerImage string `json:"containerImage,omitempty"`
|
||||||
FilePath string `json:"filePath,omitempty"`
|
ContainerRegistryPassword string `json:"containerRegistryPassword,omitempty"`
|
||||||
IncludeLayers bool `json:"includeLayers,omitempty"`
|
ContainerRegistryUser string `json:"containerRegistryUser,omitempty"`
|
||||||
|
FilePath string `json:"filePath,omitempty"`
|
||||||
|
IncludeLayers bool `json:"includeLayers,omitempty"`
|
||||||
|
DockerConfigJSON string `json:"dockerConfigJSON,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContainerSaveImageCommand Saves a container image as a tar file
|
// ContainerSaveImageCommand Saves a container image as a tar file
|
||||||
@@ -55,6 +58,9 @@ It can be used no matter if a Docker daemon is available or not. It will also wo
|
|||||||
log.SetErrorCategory(log.ErrorConfiguration)
|
log.SetErrorCategory(log.ErrorConfiguration)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
log.RegisterSecret(stepConfig.ContainerRegistryPassword)
|
||||||
|
log.RegisterSecret(stepConfig.ContainerRegistryUser)
|
||||||
|
log.RegisterSecret(stepConfig.DockerConfigJSON)
|
||||||
|
|
||||||
if len(GeneralConfig.HookConfig.SentryConfig.Dsn) > 0 {
|
if len(GeneralConfig.HookConfig.SentryConfig.Dsn) > 0 {
|
||||||
sentryHook := log.NewSentryHook(GeneralConfig.HookConfig.SentryConfig.Dsn, GeneralConfig.CorrelationID)
|
sentryHook := log.NewSentryHook(GeneralConfig.HookConfig.SentryConfig.Dsn, GeneralConfig.CorrelationID)
|
||||||
@@ -115,8 +121,11 @@ It can be used no matter if a Docker daemon is available or not. It will also wo
|
|||||||
func addContainerSaveImageFlags(cmd *cobra.Command, stepConfig *containerSaveImageOptions) {
|
func addContainerSaveImageFlags(cmd *cobra.Command, stepConfig *containerSaveImageOptions) {
|
||||||
cmd.Flags().StringVar(&stepConfig.ContainerRegistryURL, "containerRegistryUrl", os.Getenv("PIPER_containerRegistryUrl"), "The reference to the container registry where the image is located.")
|
cmd.Flags().StringVar(&stepConfig.ContainerRegistryURL, "containerRegistryUrl", os.Getenv("PIPER_containerRegistryUrl"), "The reference to the container registry where the image is located.")
|
||||||
cmd.Flags().StringVar(&stepConfig.ContainerImage, "containerImage", os.Getenv("PIPER_containerImage"), "Container image to be saved.")
|
cmd.Flags().StringVar(&stepConfig.ContainerImage, "containerImage", os.Getenv("PIPER_containerImage"), "Container image to be saved.")
|
||||||
|
cmd.Flags().StringVar(&stepConfig.ContainerRegistryPassword, "containerRegistryPassword", os.Getenv("PIPER_containerRegistryPassword"), "For `buildTool: docker`: Password for container registry access - typically provided by the CI/CD environment.")
|
||||||
|
cmd.Flags().StringVar(&stepConfig.ContainerRegistryUser, "containerRegistryUser", os.Getenv("PIPER_containerRegistryUser"), "For `buildTool: docker`: Username for container registry access - typically provided by the CI/CD environment.")
|
||||||
cmd.Flags().StringVar(&stepConfig.FilePath, "filePath", os.Getenv("PIPER_filePath"), "The path to the file to which the image should be saved. Defaults to `containerImage.tar`")
|
cmd.Flags().StringVar(&stepConfig.FilePath, "filePath", os.Getenv("PIPER_filePath"), "The path to the file to which the image should be saved. Defaults to `containerImage.tar`")
|
||||||
cmd.Flags().BoolVar(&stepConfig.IncludeLayers, "includeLayers", false, "Flag if the docker layers should be included")
|
cmd.Flags().BoolVar(&stepConfig.IncludeLayers, "includeLayers", false, "Flag if the docker layers should be included")
|
||||||
|
cmd.Flags().StringVar(&stepConfig.DockerConfigJSON, "dockerConfigJSON", os.Getenv("PIPER_dockerConfigJSON"), "Path to the file `.docker/config.json` - this is typically provided by your CI/CD system. You can find more details about the Docker credentials in the [Docker documentation](https://docs.docker.com/engine/reference/commandline/login/).")
|
||||||
|
|
||||||
cmd.MarkFlagRequired("containerRegistryUrl")
|
cmd.MarkFlagRequired("containerRegistryUrl")
|
||||||
cmd.MarkFlagRequired("containerImage")
|
cmd.MarkFlagRequired("containerImage")
|
||||||
@@ -132,6 +141,9 @@ func containerSaveImageMetadata() config.StepData {
|
|||||||
},
|
},
|
||||||
Spec: config.StepSpec{
|
Spec: config.StepSpec{
|
||||||
Inputs: config.StepInputs{
|
Inputs: config.StepInputs{
|
||||||
|
Secrets: []config.StepSecrets{
|
||||||
|
{Name: "dockerConfigJsonCredentialsId", Description: "Jenkins 'Secret file' credentials ID containing Docker config.json (with registry credential(s)). You can find more details about the Docker credentials in the [Docker documentation](https://docs.docker.com/engine/reference/commandline/login/).", Type: "jenkins", Aliases: []config.Alias{{Name: "dockerCredentialsId", Deprecated: true}}},
|
||||||
|
},
|
||||||
Parameters: []config.StepParameters{
|
Parameters: []config.StepParameters{
|
||||||
{
|
{
|
||||||
Name: "containerRegistryUrl",
|
Name: "containerRegistryUrl",
|
||||||
@@ -161,6 +173,34 @@ func containerSaveImageMetadata() config.StepData {
|
|||||||
Aliases: []config.Alias{{Name: "dockerImage"}, {Name: "scanImage"}},
|
Aliases: []config.Alias{{Name: "dockerImage"}, {Name: "scanImage"}},
|
||||||
Default: os.Getenv("PIPER_containerImage"),
|
Default: os.Getenv("PIPER_containerImage"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "containerRegistryPassword",
|
||||||
|
ResourceRef: []config.ResourceReference{
|
||||||
|
{
|
||||||
|
Name: "commonPipelineEnvironment",
|
||||||
|
Param: "custom/repositoryPassword",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||||
|
Type: "string",
|
||||||
|
Mandatory: false,
|
||||||
|
Aliases: []config.Alias{},
|
||||||
|
Default: os.Getenv("PIPER_containerRegistryPassword"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "containerRegistryUser",
|
||||||
|
ResourceRef: []config.ResourceReference{
|
||||||
|
{
|
||||||
|
Name: "commonPipelineEnvironment",
|
||||||
|
Param: "custom/repositoryUsername",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||||
|
Type: "string",
|
||||||
|
Mandatory: false,
|
||||||
|
Aliases: []config.Alias{},
|
||||||
|
Default: os.Getenv("PIPER_containerRegistryUser"),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "filePath",
|
Name: "filePath",
|
||||||
ResourceRef: []config.ResourceReference{},
|
ResourceRef: []config.ResourceReference{},
|
||||||
@@ -179,6 +219,31 @@ func containerSaveImageMetadata() config.StepData {
|
|||||||
Aliases: []config.Alias{},
|
Aliases: []config.Alias{},
|
||||||
Default: false,
|
Default: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "dockerConfigJSON",
|
||||||
|
ResourceRef: []config.ResourceReference{
|
||||||
|
{
|
||||||
|
Name: "commonPipelineEnvironment",
|
||||||
|
Param: "custom/dockerConfigJSON",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Name: "dockerConfigJsonCredentialsId",
|
||||||
|
Type: "secret",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Name: "dockerConfigFileVaultSecretName",
|
||||||
|
Type: "vaultSecretFile",
|
||||||
|
Default: "docker-config",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||||
|
Type: "string",
|
||||||
|
Mandatory: false,
|
||||||
|
Aliases: []config.Alias{},
|
||||||
|
Default: os.Getenv("PIPER_dockerConfigJSON"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
pkgutil "github.com/GoogleContainerTools/container-diff/pkg/util"
|
pkgutil "github.com/GoogleContainerTools/container-diff/pkg/util"
|
||||||
|
"github.com/SAP/jenkins-library/pkg/mock"
|
||||||
"github.com/SAP/jenkins-library/pkg/telemetry"
|
"github.com/SAP/jenkins-library/pkg/telemetry"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
@@ -64,8 +65,9 @@ func TestRunContainerSaveImage(t *testing.T) {
|
|||||||
config.FilePath = "testfile"
|
config.FilePath = "testfile"
|
||||||
|
|
||||||
dClient := containerMock{}
|
dClient := containerMock{}
|
||||||
|
files := mock.FilesMock{}
|
||||||
|
|
||||||
filePath, err := runContainerSaveImage(&config, &telemetryData, cacheFolder, tmpFolder, &dClient)
|
filePath, err := runContainerSaveImage(&config, &telemetryData, cacheFolder, tmpFolder, &dClient, &files)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, cacheFolder, dClient.filePath)
|
assert.Equal(t, cacheFolder, dClient.filePath)
|
||||||
@@ -81,7 +83,8 @@ func TestRunContainerSaveImage(t *testing.T) {
|
|||||||
t.Run("failure - cache creation", func(t *testing.T) {
|
t.Run("failure - cache creation", func(t *testing.T) {
|
||||||
config := containerSaveImageOptions{}
|
config := containerSaveImageOptions{}
|
||||||
dClient := containerMock{}
|
dClient := containerMock{}
|
||||||
_, err := runContainerSaveImage(&config, &telemetryData, "", "", &dClient)
|
files := mock.FilesMock{}
|
||||||
|
_, err := runContainerSaveImage(&config, &telemetryData, "", "", &dClient, &files)
|
||||||
assert.Contains(t, fmt.Sprint(err), "failed to create cache: mkdir :")
|
assert.Contains(t, fmt.Sprint(err), "failed to create cache: mkdir :")
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -94,7 +97,8 @@ func TestRunContainerSaveImage(t *testing.T) {
|
|||||||
defer os.RemoveAll(tmpFolder)
|
defer os.RemoveAll(tmpFolder)
|
||||||
|
|
||||||
dClient := containerMock{imageSourceErr: "image source error"}
|
dClient := containerMock{imageSourceErr: "image source error"}
|
||||||
_, err = runContainerSaveImage(&config, &telemetryData, filepath.Join(tmpFolder, "cache"), tmpFolder, &dClient)
|
files := mock.FilesMock{}
|
||||||
|
_, err = runContainerSaveImage(&config, &telemetryData, filepath.Join(tmpFolder, "cache"), tmpFolder, &dClient, &files)
|
||||||
assert.EqualError(t, err, "failed to get docker image source: image source error")
|
assert.EqualError(t, err, "failed to get docker image source: image source error")
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -107,7 +111,8 @@ func TestRunContainerSaveImage(t *testing.T) {
|
|||||||
defer os.RemoveAll(tmpFolder)
|
defer os.RemoveAll(tmpFolder)
|
||||||
|
|
||||||
dClient := containerMock{downloadImageErr: "download error"}
|
dClient := containerMock{downloadImageErr: "download error"}
|
||||||
_, err = runContainerSaveImage(&config, &telemetryData, filepath.Join(tmpFolder, "cache"), tmpFolder, &dClient)
|
files := mock.FilesMock{}
|
||||||
|
_, err = runContainerSaveImage(&config, &telemetryData, filepath.Join(tmpFolder, "cache"), tmpFolder, &dClient, &files)
|
||||||
assert.EqualError(t, err, "failed to download docker image: download error")
|
assert.EqualError(t, err, "failed to download docker image: download error")
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -120,7 +125,8 @@ func TestRunContainerSaveImage(t *testing.T) {
|
|||||||
defer os.RemoveAll(tmpFolder)
|
defer os.RemoveAll(tmpFolder)
|
||||||
|
|
||||||
dClient := containerMock{tarImageErr: "tar error"}
|
dClient := containerMock{tarImageErr: "tar error"}
|
||||||
_, err = runContainerSaveImage(&config, &telemetryData, filepath.Join(tmpFolder, "cache"), tmpFolder, &dClient)
|
files := mock.FilesMock{}
|
||||||
|
_, err = runContainerSaveImage(&config, &telemetryData, filepath.Join(tmpFolder, "cache"), tmpFolder, &dClient, &files)
|
||||||
assert.EqualError(t, err, "failed to tar container image: tar error")
|
assert.EqualError(t, err, "failed to tar container image: tar error")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -143,3 +149,54 @@ func TestFilenameFromContainer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCorrectContainerDockerConfigEnvVar(t *testing.T) {
|
||||||
|
t.Run("with credentials", func(t *testing.T) {
|
||||||
|
// init
|
||||||
|
utilsMock := mock.FilesMock{}
|
||||||
|
utilsMock.CurrentDir = "/tmp/test"
|
||||||
|
|
||||||
|
dockerConfigFile := "myConfig/docker.json"
|
||||||
|
utilsMock.AddFile(dockerConfigFile, []byte("{}"))
|
||||||
|
|
||||||
|
resetValue := os.Getenv("DOCKER_CONFIG")
|
||||||
|
os.Setenv("DOCKER_CONFIG", "")
|
||||||
|
defer os.Setenv("DOCKER_CONFIG", resetValue)
|
||||||
|
|
||||||
|
// test
|
||||||
|
correctContainerDockerConfigEnvVar(&containerSaveImageOptions{DockerConfigJSON: dockerConfigFile}, &utilsMock)
|
||||||
|
// assert
|
||||||
|
assert.NotNil(t, os.Getenv("DOCKER_CONFIG"))
|
||||||
|
})
|
||||||
|
t.Run("with added credentials", func(t *testing.T) {
|
||||||
|
// init
|
||||||
|
utilsMock := mock.FilesMock{}
|
||||||
|
utilsMock.CurrentDir = "/tmp/test"
|
||||||
|
|
||||||
|
dockerConfigFile := "myConfig/docker.json"
|
||||||
|
utilsMock.AddFile(dockerConfigFile, []byte("{}"))
|
||||||
|
|
||||||
|
resetValue := os.Getenv("DOCKER_CONFIG")
|
||||||
|
os.Setenv("DOCKER_CONFIG", "")
|
||||||
|
defer os.Setenv("DOCKER_CONFIG", resetValue)
|
||||||
|
|
||||||
|
// test
|
||||||
|
correctContainerDockerConfigEnvVar(&containerSaveImageOptions{DockerConfigJSON: dockerConfigFile, ContainerRegistryURL: "https://test.registry", ContainerRegistryUser: "testuser", ContainerRegistryPassword: "testPassword"}, &utilsMock)
|
||||||
|
// assert
|
||||||
|
assert.NotNil(t, os.Getenv("DOCKER_CONFIG"))
|
||||||
|
absoluteFilePath, _ := utilsMock.Abs(fmt.Sprintf("%s/%s", os.Getenv("DOCKER_CONFIG"), "config.json"))
|
||||||
|
content, _ := utilsMock.FileRead(absoluteFilePath)
|
||||||
|
assert.Contains(t, string(content), "https://test.registry")
|
||||||
|
})
|
||||||
|
t.Run("without credentials", func(t *testing.T) {
|
||||||
|
// init
|
||||||
|
utilsMock := mock.FilesMock{}
|
||||||
|
resetValue := os.Getenv("DOCKER_CONFIG")
|
||||||
|
os.Setenv("DOCKER_CONFIG", "")
|
||||||
|
defer os.Setenv("DOCKER_CONFIG", resetValue)
|
||||||
|
// test
|
||||||
|
correctContainerDockerConfigEnvVar(&containerSaveImageOptions{}, &utilsMock)
|
||||||
|
// assert
|
||||||
|
assert.NotNil(t, os.Getenv("DOCKER_CONFIG"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -145,18 +144,19 @@ func selectAndPrepareFileForMalwareScan(config *malwareExecuteScanOptions, utils
|
|||||||
|
|
||||||
// automatically detect the file to be scanned depending on the buildtool
|
// automatically detect the file to be scanned depending on the buildtool
|
||||||
if len(config.ScanImage) > 0 {
|
if len(config.ScanImage) > 0 {
|
||||||
correctMalwareDockerConfigEnvVar(config, utils)
|
|
||||||
|
|
||||||
saveImageOptions := containerSaveImageOptions{
|
saveImageOptions := containerSaveImageOptions{
|
||||||
ContainerImage: config.ScanImage,
|
ContainerImage: config.ScanImage,
|
||||||
ContainerRegistryURL: config.ScanImageRegistryURL,
|
ContainerRegistryURL: config.ScanImageRegistryURL,
|
||||||
IncludeLayers: config.ScanImageIncludeLayers,
|
ContainerRegistryUser: config.ContainerRegistryUser,
|
||||||
FilePath: config.ScanImage,
|
ContainerRegistryPassword: config.ContainerRegistryPassword,
|
||||||
|
DockerConfigJSON: config.DockerConfigJSON,
|
||||||
|
IncludeLayers: config.ScanImageIncludeLayers,
|
||||||
|
FilePath: config.ScanImage,
|
||||||
}
|
}
|
||||||
dClientOptions := piperDocker.ClientOptions{ImageName: saveImageOptions.ContainerImage, RegistryURL: saveImageOptions.ContainerRegistryURL, LocalPath: "", IncludeLayers: saveImageOptions.IncludeLayers}
|
dClientOptions := piperDocker.ClientOptions{ImageName: saveImageOptions.ContainerImage, RegistryURL: saveImageOptions.ContainerRegistryURL, LocalPath: "", IncludeLayers: saveImageOptions.IncludeLayers}
|
||||||
dClient := utils.newDockerClient(dClientOptions)
|
dClient := utils.newDockerClient(dClientOptions)
|
||||||
|
|
||||||
tarFile, err := runContainerSaveImage(&saveImageOptions, &telemetry.CustomData{}, "./cache", "", dClient)
|
tarFile, err := runContainerSaveImage(&saveImageOptions, &telemetry.CustomData{}, "./cache", "", dClient, utils)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(fmt.Sprint(err), "no image found") {
|
if strings.Contains(fmt.Sprint(err), "no image found") {
|
||||||
@@ -215,23 +215,3 @@ func createMalwareScanReport(config *malwareExecuteScanOptions, scanResult *malw
|
|||||||
|
|
||||||
return utils.FileWrite(config.ReportFileName, scanResultJSON, 0666)
|
return utils.FileWrite(config.ReportFileName, scanResultJSON, 0666)
|
||||||
}
|
}
|
||||||
|
|
||||||
func correctMalwareDockerConfigEnvVar(config *malwareExecuteScanOptions, utils malwareScanUtils) {
|
|
||||||
path := config.DockerConfigJSON
|
|
||||||
if len(path) > 0 {
|
|
||||||
log.Entry().Infof("Docker credentials configuration: %v", path)
|
|
||||||
if len(config.ScanImageRegistryURL) > 0 && len(config.ContainerRegistryUser) > 0 && len(config.ContainerRegistryPassword) > 0 {
|
|
||||||
var err error
|
|
||||||
path, err = piperDocker.CreateDockerConfigJSON(config.ScanImageRegistryURL, config.ContainerRegistryUser, config.ContainerRegistryPassword, "", config.DockerConfigJSON, utils)
|
|
||||||
if err != nil {
|
|
||||||
log.Entry().Warningf("failed to update Docker config.json: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
path, _ := utils.Abs(path)
|
|
||||||
// use parent directory
|
|
||||||
path = filepath.Dir(path)
|
|
||||||
os.Setenv("DOCKER_CONFIG", path)
|
|
||||||
} else {
|
|
||||||
log.Entry().Info("Docker credentials configuration: NONE")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -160,21 +160,22 @@ func runWhitesourceExecuteScan(config *ScanOptions, scan *ws.Scan, utils whiteso
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runWhitesourceScan(config *ScanOptions, scan *ws.Scan, utils whitesourceUtils, sys whitesource, commonPipelineEnvironment *whitesourceExecuteScanCommonPipelineEnvironment, influx *whitesourceExecuteScanInflux) error {
|
func runWhitesourceScan(config *ScanOptions, scan *ws.Scan, utils whitesourceUtils, sys whitesource, commonPipelineEnvironment *whitesourceExecuteScanCommonPipelineEnvironment, influx *whitesourceExecuteScanInflux) error {
|
||||||
correctWhitesourceDockerConfigEnvVar(config, utils)
|
|
||||||
|
|
||||||
// Download Docker image for container scan
|
// Download Docker image for container scan
|
||||||
// ToDo: move it to improve testability
|
// ToDo: move it to improve testability
|
||||||
if config.BuildTool == "docker" {
|
if config.BuildTool == "docker" {
|
||||||
saveImageOptions := containerSaveImageOptions{
|
saveImageOptions := containerSaveImageOptions{
|
||||||
ContainerImage: config.ScanImage,
|
ContainerImage: config.ScanImage,
|
||||||
ContainerRegistryURL: config.ScanImageRegistryURL,
|
ContainerRegistryURL: config.ScanImageRegistryURL,
|
||||||
IncludeLayers: config.ScanImageIncludeLayers,
|
ContainerRegistryUser: config.ContainerRegistryUser,
|
||||||
FilePath: config.ProjectName,
|
ContainerRegistryPassword: config.ContainerRegistryPassword,
|
||||||
|
DockerConfigJSON: config.DockerConfigJSON,
|
||||||
|
IncludeLayers: config.ScanImageIncludeLayers,
|
||||||
|
FilePath: config.ProjectName,
|
||||||
}
|
}
|
||||||
dClientOptions := piperDocker.ClientOptions{ImageName: saveImageOptions.ContainerImage, RegistryURL: saveImageOptions.ContainerRegistryURL, LocalPath: "", IncludeLayers: saveImageOptions.IncludeLayers}
|
dClientOptions := piperDocker.ClientOptions{ImageName: saveImageOptions.ContainerImage, RegistryURL: saveImageOptions.ContainerRegistryURL, LocalPath: "", IncludeLayers: saveImageOptions.IncludeLayers}
|
||||||
dClient := &piperDocker.Client{}
|
dClient := &piperDocker.Client{}
|
||||||
dClient.SetOptions(dClientOptions)
|
dClient.SetOptions(dClientOptions)
|
||||||
if _, err := runContainerSaveImage(&saveImageOptions, &telemetry.CustomData{}, "./cache", "", dClient); err != nil {
|
if _, err := runContainerSaveImage(&saveImageOptions, &telemetry.CustomData{}, "./cache", "", dClient, utils); err != nil {
|
||||||
if strings.Contains(fmt.Sprint(err), "no image found") {
|
if strings.Contains(fmt.Sprint(err), "no image found") {
|
||||||
log.SetErrorCategory(log.ErrorConfiguration)
|
log.SetErrorCategory(log.ErrorConfiguration)
|
||||||
}
|
}
|
||||||
@@ -212,26 +213,6 @@ func runWhitesourceScan(config *ScanOptions, scan *ws.Scan, utils whitesourceUti
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func correctWhitesourceDockerConfigEnvVar(config *ScanOptions, utils whitesourceUtils) {
|
|
||||||
path := config.DockerConfigJSON
|
|
||||||
if len(path) > 0 {
|
|
||||||
log.Entry().Infof("Docker credentials configuration: %v", path)
|
|
||||||
if len(config.ScanImageRegistryURL) > 0 && len(config.ContainerRegistryUser) > 0 && len(config.ContainerRegistryPassword) > 0 {
|
|
||||||
var err error
|
|
||||||
path, err = piperDocker.CreateDockerConfigJSON(config.ScanImageRegistryURL, config.ContainerRegistryUser, config.ContainerRegistryPassword, "", config.DockerConfigJSON, utils)
|
|
||||||
if err != nil {
|
|
||||||
log.Entry().Warningf("failed to update Docker config.json: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
path, _ := utils.Abs(path)
|
|
||||||
// use parent directory
|
|
||||||
path = filepath.Dir(path)
|
|
||||||
os.Setenv("DOCKER_CONFIG", path)
|
|
||||||
} else {
|
|
||||||
log.Entry().Info("Docker credentials configuration: NONE")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkAndReportScanResults(config *ScanOptions, scan *ws.Scan, utils whitesourceUtils, sys whitesource, influx *whitesourceExecuteScanInflux) ([]piperutils.Path, error) {
|
func checkAndReportScanResults(config *ScanOptions, scan *ws.Scan, utils whitesourceUtils, sys whitesource, influx *whitesourceExecuteScanInflux) ([]piperutils.Path, error) {
|
||||||
reportPaths := []piperutils.Path{}
|
reportPaths := []piperutils.Path{}
|
||||||
if !config.Reporting && !config.SecurityVulnerabilities {
|
if !config.Reporting && !config.SecurityVulnerabilities {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -130,56 +129,6 @@ func TestRunWhitesourceExecuteScan(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCorrectWhitesourceDockerConfigEnvVar(t *testing.T) {
|
|
||||||
t.Run("with credentials", func(t *testing.T) {
|
|
||||||
// init
|
|
||||||
utilsMock := newWhitesourceUtilsMock()
|
|
||||||
utilsMock.CurrentDir = "/tmp/test"
|
|
||||||
|
|
||||||
dockerConfigFile := "myConfig/docker.json"
|
|
||||||
utilsMock.AddFile(dockerConfigFile, []byte("{}"))
|
|
||||||
|
|
||||||
resetValue := os.Getenv("DOCKER_CONFIG")
|
|
||||||
defer os.Setenv("DOCKER_CONFIG", resetValue)
|
|
||||||
|
|
||||||
// test
|
|
||||||
correctWhitesourceDockerConfigEnvVar(&ScanOptions{DockerConfigJSON: dockerConfigFile}, utilsMock)
|
|
||||||
// assert
|
|
||||||
absolutePath, _ := utilsMock.Abs(filepath.Dir(dockerConfigFile))
|
|
||||||
assert.Equal(t, absolutePath, os.Getenv("DOCKER_CONFIG"))
|
|
||||||
})
|
|
||||||
t.Run("with added credentials", func(t *testing.T) {
|
|
||||||
// init
|
|
||||||
utilsMock := newWhitesourceUtilsMock()
|
|
||||||
utilsMock.CurrentDir = "/tmp/test"
|
|
||||||
|
|
||||||
dockerConfigFile := "myConfig/docker.json"
|
|
||||||
utilsMock.AddFile(dockerConfigFile, []byte("{}"))
|
|
||||||
|
|
||||||
resetValue := os.Getenv("DOCKER_CONFIG")
|
|
||||||
defer os.Setenv("DOCKER_CONFIG", resetValue)
|
|
||||||
|
|
||||||
// test
|
|
||||||
correctWhitesourceDockerConfigEnvVar(&ScanOptions{DockerConfigJSON: dockerConfigFile, ScanImageRegistryURL: "https://test.registry", ContainerRegistryUser: "testuser", ContainerRegistryPassword: "testPassword"}, utilsMock)
|
|
||||||
// assert
|
|
||||||
absoluteDirPath, _ := utilsMock.Abs(filepath.Dir(dockerConfigFile))
|
|
||||||
absoluteFilePath, _ := utilsMock.Abs(dockerConfigFile)
|
|
||||||
assert.Equal(t, absoluteDirPath, os.Getenv("DOCKER_CONFIG"))
|
|
||||||
content, _ := utilsMock.FileRead(absoluteFilePath)
|
|
||||||
assert.Contains(t, string(content), "https://test.registry")
|
|
||||||
})
|
|
||||||
t.Run("without credentials", func(t *testing.T) {
|
|
||||||
// init
|
|
||||||
utilsMock := newWhitesourceUtilsMock()
|
|
||||||
resetValue := os.Getenv("DOCKER_CONFIG")
|
|
||||||
defer os.Setenv("DOCKER_CONFIG", resetValue)
|
|
||||||
// test
|
|
||||||
correctWhitesourceDockerConfigEnvVar(&ScanOptions{}, utilsMock)
|
|
||||||
// assert
|
|
||||||
assert.Equal(t, resetValue, os.Getenv("DOCKER_CONFIG"))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCheckAndReportScanResults(t *testing.T) {
|
func TestCheckAndReportScanResults(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
t.Run("no reports requested", func(t *testing.T) {
|
t.Run("no reports requested", func(t *testing.T) {
|
||||||
|
|||||||
@@ -7,6 +7,13 @@ metadata:
|
|||||||
It can be used no matter if a Docker daemon is available or not. It will also work inside a Kubernetes cluster without access to a daemon.
|
It can be used no matter if a Docker daemon is available or not. It will also work inside a Kubernetes cluster without access to a daemon.
|
||||||
spec:
|
spec:
|
||||||
inputs:
|
inputs:
|
||||||
|
secrets:
|
||||||
|
- name: dockerConfigJsonCredentialsId
|
||||||
|
description: Jenkins 'Secret file' credentials ID containing Docker config.json (with registry credential(s)). You can find more details about the Docker credentials in the [Docker documentation](https://docs.docker.com/engine/reference/commandline/login/).
|
||||||
|
type: jenkins
|
||||||
|
aliases:
|
||||||
|
- name: dockerCredentialsId
|
||||||
|
deprecated: true
|
||||||
params:
|
params:
|
||||||
- name: containerRegistryUrl
|
- name: containerRegistryUrl
|
||||||
aliases:
|
aliases:
|
||||||
@@ -37,6 +44,28 @@ spec:
|
|||||||
- PARAMETERS
|
- PARAMETERS
|
||||||
- STAGES
|
- STAGES
|
||||||
- STEPS
|
- STEPS
|
||||||
|
- name: containerRegistryPassword
|
||||||
|
description: "For `buildTool: docker`: Password for container registry access - typically provided by the CI/CD environment."
|
||||||
|
type: string
|
||||||
|
scope:
|
||||||
|
- PARAMETERS
|
||||||
|
- STAGES
|
||||||
|
- STEPS
|
||||||
|
secret: true
|
||||||
|
resourceRef:
|
||||||
|
- name: commonPipelineEnvironment
|
||||||
|
param: custom/repositoryPassword
|
||||||
|
- name: containerRegistryUser
|
||||||
|
description: "For `buildTool: docker`: Username for container registry access - typically provided by the CI/CD environment."
|
||||||
|
type: string
|
||||||
|
scope:
|
||||||
|
- PARAMETERS
|
||||||
|
- STAGES
|
||||||
|
- STEPS
|
||||||
|
secret: true
|
||||||
|
resourceRef:
|
||||||
|
- name: commonPipelineEnvironment
|
||||||
|
param: custom/repositoryUsername
|
||||||
- name: filePath
|
- name: filePath
|
||||||
type: string
|
type: string
|
||||||
description: The path to the file to which the image should be saved. Defaults to `containerImage.tar`
|
description: The path to the file to which the image should be saved. Defaults to `containerImage.tar`
|
||||||
@@ -51,3 +80,19 @@ spec:
|
|||||||
- PARAMETERS
|
- PARAMETERS
|
||||||
- STAGES
|
- STAGES
|
||||||
- STEPS
|
- STEPS
|
||||||
|
- name: dockerConfigJSON
|
||||||
|
type: string
|
||||||
|
description: Path to the file `.docker/config.json` - this is typically provided by your CI/CD system. You can find more details about the Docker credentials in the [Docker documentation](https://docs.docker.com/engine/reference/commandline/login/).
|
||||||
|
scope:
|
||||||
|
- PARAMETERS
|
||||||
|
- STAGES
|
||||||
|
- STEPS
|
||||||
|
secret: true
|
||||||
|
resourceRef:
|
||||||
|
- name: commonPipelineEnvironment
|
||||||
|
param: custom/dockerConfigJSON
|
||||||
|
- name: dockerConfigJsonCredentialsId
|
||||||
|
type: secret
|
||||||
|
- type: vaultSecretFile
|
||||||
|
name: dockerConfigFileVaultSecretName
|
||||||
|
default: docker-config
|
||||||
|
|||||||
Reference in New Issue
Block a user