Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slsa charts bypass #153

Merged
merged 6 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion pkg/path/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,41 +6,54 @@ const (

// RepositoryHelmIndexFile is the file on your Staging/Live branch that contains your Helm repository index
RepositoryHelmIndexFile = "index.yaml"

// RepositoryPackagesDir is a directory on your Staging branch that contains the files necessary to generate your package
RepositoryPackagesDir = "packages"

// RepositoryAssetsDir is a directory on your Staging/Live branch that contains chart archives for each version of your package
RepositoryAssetsDir = "assets"

// RepositoryChartsDir is a directory on your Staging/Live branch that contains unarchived charts for each version of your package
RepositoryChartsDir = "charts"

// PackageOptionsFile is the name of a file that contains information about how to prepare your package
// The expected structure of this file is one that can be marshalled into a PackageOptions struct
PackageOptionsFile = "package.yaml"

// PackageTemplatesDir is a directory containing templates used as additional chart options
PackageTemplatesDir = "templates"

// GeneratedChangesDir is a directory that contains GeneratedChanges
GeneratedChangesDir = "generated-changes"

// GeneratedChangesAdditionalChartDir is a directory that contains additionalCharts
GeneratedChangesAdditionalChartDir = "additional-charts"

// GeneratedChangesDependenciesDir is a directory that contains dependencies within GeneratedChangesDir
GeneratedChangesDependenciesDir = "dependencies"

// GeneratedChangesExcludeDir is a directory that contains excludes within GeneratedChangesDir
GeneratedChangesExcludeDir = "exclude"

// GeneratedChangesOverlayDir is a directory that contains overlays within GeneratedChangesDir
GeneratedChangesOverlayDir = "overlay"

// GeneratedChangesPatchDir is a directory that contains patches within GeneratedChangesDir
GeneratedChangesPatchDir = "patch"

// DependencyOptionsFile is a file that contains information about how to prepare your dependency
// The expected structure of this file is one that can be marshalled into a ChartOptions struct
DependencyOptionsFile = "dependency.yaml"

// ChartCRDDir represents the directory that we expect to contain CRDs within the chart
ChartCRDDir = "crds"

// ChartExtraFileDir represents the directory that contains non-YAML files
ChartExtraFileDir = "files"

// ChartCRDTgzFilename represents the filename of the crd's tgz file
ChartCRDTgzFilename = "crd-manifest.tgz"

// ChartValidateInstallCRDFile is the path to the file pushed to upstream that validates the existence of CRDs in the chart
ChartValidateInstallCRDFile = "templates/validate-install-crd.yaml"

Expand All @@ -50,12 +63,15 @@ const (
// RepositoryLogosDir is a directory on your Staging/Live branch that contains the files with the logos of each chart
RepositoryLogosDir = "assets/logos"

// RepositoryStAte file is a file to hold the current status of the released and developed assets versions
// RepositoryStateFile file is a file to hold the current status of the released and developed assets versions
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// RepositoryStateFile file is a file to hold the current status of the released and developed assets versions
// RepositoryStateFile is a file to hold the current status of the released and developed assets versions

RepositoryStateFile = "state.json"

// RepositoryReleaseYaml is the file on your Staging/Live branch that contains the release information
RepositoryReleaseYaml = "release.yaml"

// VersionRulesFile is the file that contains the version rules for the current branch on charts-build-scripts
VersionRulesFile = "version_rules.json"

// SlsaYamlFile is the file that contains the list of images already synced and signed for SLSA.
SlsaYamlFile = "slsa.yaml"
)
56 changes: 50 additions & 6 deletions pkg/regsync/generateconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@ import (
"sort"
"strings"

"github.com/rancher/charts-build-scripts/pkg/path"
"golang.org/x/exp/slices"
"gopkg.in/yaml.v2"
)

// ReadSlsaYamlFunc is a function type that reads the slsa.yaml file and returns a list of images.
type ReadSlsaYamlFunc func() ([]string, error)

// chartsToIgnoreTags and systemChartsToIgnoreTags defines the charts and system charts in which a specified
// image tag should be ignored.
var chartsToIgnoreTags = map[string]string{
Expand All @@ -26,18 +30,17 @@ var chartsToIgnoreTags = map[string]string{
func GenerateConfigFile() error {
imageTagMap := make(map[string][]string)

err := walkAssetsFolder(imageTagMap)
if err != nil {
if err := walkAssetsFolder(imageTagMap); err != nil {
return err
}

// Create the regsync config file
err = createRegSyncConfigFile(imageTagMap)
if err != nil {
// Remove the images that are already signed from the imageTagMap
if err := removeSlsaImages(imageTagMap, readSlsaYaml); err != nil {
return err
}

return nil
// Create the regsync config file
return createRegSyncConfigFile(imageTagMap)
}

// walkAssetsFolder walks over the assets folder, untars files, stores the values.yaml content
Expand Down Expand Up @@ -217,3 +220,44 @@ func walkMap(inputMap interface{}, callback func(map[interface{}]interface{})) {
}
}
}

// removeSlsaImages removes the images that are already signed from the imageTagMap.
func removeSlsaImages(imageTagMap map[string][]string, readSlsaYaml ReadSlsaYamlFunc) error {
// Get the list of images that should not be synced with the registry.
// These images are defined in the slsa.yaml file.
// We will remove these images from the imageTagMap.
slsaImgs, err := readSlsaYaml()
if err != nil {
return err
}

// The images will not be synced because they are already:
// - Signed
// - Synced with the registry
if slsaImgs != nil {
for _, img := range slsaImgs {
delete(imageTagMap, img)
}
}
return nil
}

func readSlsaYaml() ([]string, error) {
var slsaImgs []string

file, err := os.Open(path.SlsaYamlFile)
if err != nil {
return nil, nil // backward version compatibility

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be an error?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return nil, nil // backward version compatibility
return nil, err // backward version compatibility

}
defer file.Close()

decoder := yaml.NewDecoder(file)
if err := decoder.Decode(&slsaImgs); err != nil {
if err == io.EOF {
return slsaImgs, nil // Handle EOF error gracefully
}
return nil, err
}

return slsaImgs, nil
}
118 changes: 118 additions & 0 deletions pkg/regsync/generateconfig_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package regsync

import (
"testing"

"github.com/stretchr/testify/require"
)

func Test_SlsaYaml(t *testing.T) {

type input struct {
imageTagMap map[string][]string
readSlsaYamlMock func() ([]string, error)
}

type expected struct {
err error
imageTagMap map[string][]string
}

type test struct {
name string
input input
expected expected
}

tests := []test{
{
name: "#1",
input: input{
imageTagMap: map[string][]string{
"image1": {"tag1"},
"image2": {"tag2"},
"image3": {"tag3"},
},
readSlsaYamlMock: func() ([]string, error) {
return []string{"image1"}, nil
},
},
expected: expected{
err: nil,
imageTagMap: map[string][]string{
"image2": {"tag2"},
"image3": {"tag3"},
},
},
},
{
name: "#2",
input: input{
imageTagMap: map[string][]string{
"image1": {"tag1", "tag2"},
"image2": {"tag2"},
"image3": {"tag3", "tag4"},
},
readSlsaYamlMock: func() ([]string, error) {
return []string{"image1", "image2"}, nil
},
},
expected: expected{
err: nil,
imageTagMap: map[string][]string{
"image3": {"tag3", "tag4"},
},
},
},
{
name: "#3",
input: input{
imageTagMap: map[string][]string{
"image1": {"tag1", "tag2"},
},
readSlsaYamlMock: func() ([]string, error) {
return []string{"image1", "image2"}, nil
},
},
expected: expected{
err: nil,
imageTagMap: map[string][]string{},
},
},
{
name: "#4",
input: input{
imageTagMap: map[string][]string{},
readSlsaYamlMock: func() ([]string, error) {
return []string{"image1", "image2"}, nil
},
},
expected: expected{
err: nil,
imageTagMap: map[string][]string{},
},
},
{
name: "#5",
input: input{
imageTagMap: map[string][]string{},
readSlsaYamlMock: func() ([]string, error) {
return []string{}, nil
},
},
expected: expected{
err: nil,
imageTagMap: map[string][]string{},
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := removeSlsaImages(tt.input.imageTagMap, tt.input.readSlsaYamlMock)
require.NoError(t, err)
require.Equal(t, tt.expected.imageTagMap, tt.input.imageTagMap)

})
}
}
Loading