Skip to content

Commit

Permalink
add image-sha check
Browse files Browse the repository at this point in the history
  • Loading branch information
VedantMahabaleshwarkar committed May 2, 2023
1 parent 191de10 commit 95d0c1b
Show file tree
Hide file tree
Showing 9 changed files with 326 additions and 0 deletions.
16 changes: 16 additions & 0 deletions docs/generated/checks.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,22 @@ forbiddenServiceTypes:
```yaml
minReplicas: 3
```
## image-sha
**Enabled by default**: No
**Description**: Indicates if there are any image references that are not references by sha256 tags
**Remediation**: Reference all images using their sha256 tags.
**Template**: [image-sha](templates.md#latest-tag)
**Parameters**:
```yaml
AllowList:
- .*:[a-fA-F0-9]{64}$
```
## invalid-target-ports
**Enabled by default**: Yes
Expand Down
30 changes: 30 additions & 0 deletions docs/generated/templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,36 @@ KubeLinter supports the following templates:

## Latest Tag

**Key**: `image-sha`

**Description**: Flag applications running container images that do not satisfies "allowList" & "blockList" parameters criteria.

**Supported Objects**: DeploymentLike


**Parameters**:

```yaml
- arrayElemType: string
description: list of regular expressions specifying pattern(s) for container images
that will be blocked. */
name: blockList
negationAllowed: true
regexAllowed: true
required: false
type: array
- arrayElemType: string
description: list of regular expressions specifying pattern(s) for container images
that will be allowed.
name: allowList
negationAllowed: true
regexAllowed: true
required: false
type: array
```

## Latest Tag

**Key**: `latest-tag`

**Description**: Flag applications running container images that do not satisfies "allowList" & "blockList" parameters criteria.
Expand Down
17 changes: 17 additions & 0 deletions e2etests/bats-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,23 @@ get_value_from() {
[[ "${count}" == "2" ]]
}

@test "image-sha" {
tmp="tests/checks/image-sha.yml"
cmd="${KUBE_LINTER_BIN} lint --include image-sha --do-not-auto-add-defaults --format json ${tmp}"
run ${cmd}

print_info "${status}" "${output}" "${cmd}" "${tmp}"
[ "$status" -eq 1 ]

message1=$(get_value_from "${lines[0]}" '.Reports[0].Object.K8sObject.GroupVersionKind.Kind + ": " + .Reports[0].Diagnostic.Message')
message2=$(get_value_from "${lines[0]}" '.Reports[1].Object.K8sObject.GroupVersionKind.Kind + ": " + .Reports[1].Diagnostic.Message')
count=$(get_value_from "${lines[0]}" '.Reports | length')

[[ "${message1}" == "Deployment: The container \"app\" is using an invalid container image, \"app:latest\". Please reference the image using a SHA256 tag." ]]
[[ "${message2}" == "DeploymentConfig: The container \"app\" is using an invalid container image, \"app:latest\". Please reference the image using a SHA256 tag." ]]
[[ "${count}" == "2" ]]
}

@test "minimum-three-replicas" {
tmp="tests/checks/minimum-three-replicas.yml"
cmd="${KUBE_LINTER_BIN} lint --include minimum-three-replicas --do-not-auto-add-defaults --format json ${tmp}"
Expand Down
9 changes: 9 additions & 0 deletions pkg/builtinchecks/yamls/image-sha.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
name: "image-sha"
description: "Indicates if there are any image references that are not references by sha256 tags"
remediation: "Reference all images using their sha256 tags."
scope:
objectKinds:
- DeploymentLike
template: "image-sha"
params:
AllowList: [".*:[a-fA-F0-9]{64}$"]
1 change: 1 addition & 0 deletions pkg/templates/all/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
_ "golang.stackrox.io/kube-linter/pkg/templates/hostpid"
_ "golang.stackrox.io/kube-linter/pkg/templates/hpareplicas"
_ "golang.stackrox.io/kube-linter/pkg/templates/imagepullpolicy"
_ "golang.stackrox.io/kube-linter/pkg/templates/imageshatag"
_ "golang.stackrox.io/kube-linter/pkg/templates/latesttag"
_ "golang.stackrox.io/kube-linter/pkg/templates/livenessprobe"
_ "golang.stackrox.io/kube-linter/pkg/templates/memoryrequirements"
Expand Down
86 changes: 86 additions & 0 deletions pkg/templates/imageshatag/internal/params/gen-params.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions pkg/templates/imageshatag/internal/params/params.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package params

// Params represents the params accepted by this template.
type Params struct {

// list of regular expressions specifying pattern(s) for container images that will be blocked. */
BlockList []string

// list of regular expressions specifying pattern(s) for container images that will be allowed.
AllowList []string
}
77 changes: 77 additions & 0 deletions pkg/templates/imageshatag/template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package imageshatag

import (
"fmt"
"regexp"

"github.com/pkg/errors"
"golang.stackrox.io/kube-linter/pkg/check"
"golang.stackrox.io/kube-linter/pkg/config"
"golang.stackrox.io/kube-linter/pkg/diagnostic"
"golang.stackrox.io/kube-linter/pkg/objectkinds"
"golang.stackrox.io/kube-linter/pkg/templates"
"golang.stackrox.io/kube-linter/pkg/templates/imageshatag/internal/params"
"golang.stackrox.io/kube-linter/pkg/templates/util"
v1 "k8s.io/api/core/v1"
)

const (
templateKey = "image-sha"
)

func init() {
templates.Register(check.Template{
HumanName: "Latest Tag",
Key: templateKey,
Description: "Flag applications running container images that do not satisfies \"allowList\" & \"blockList\" parameters criteria.",
SupportedObjectKinds: config.ObjectKindsDesc{
ObjectKinds: []string{objectkinds.DeploymentLike},
},
Parameters: params.ParamDescs,
ParseAndValidateParams: params.ParseAndValidate,
Instantiate: params.WrapInstantiateFunc(func(p params.Params) (check.Func, error) {

blockedRegexes := make([]*regexp.Regexp, 0, len(p.BlockList))
for _, res := range p.BlockList {
rg, err := regexp.Compile(res)
if err != nil {
return nil, errors.Wrapf(err, "invalid regex %s", res)
}
blockedRegexes = append(blockedRegexes, rg)
}

allowedRegexes := make([]*regexp.Regexp, 0, len(p.AllowList))
for _, res := range p.AllowList {
rg, err := regexp.Compile(res)
if err != nil {
return nil, errors.Wrapf(err, "invalid regex %s", res)
}
allowedRegexes = append(allowedRegexes, rg)
}

if len(blockedRegexes) > 0 && len(allowedRegexes) > 0 {
err := fmt.Errorf("check has both \"allowList\" & \"blockList\" parameter's values set")
return nil, errors.Wrapf(err, "only one of the paramater lists can be used at a time")
}

return util.PerContainerCheck(func(container *v1.Container) (results []diagnostic.Diagnostic) {
if len(blockedRegexes) > 0 && isInList(blockedRegexes, container.Image) {
results = append(results, diagnostic.Diagnostic{Message: fmt.Sprintf("The container %q is using an invalid container image, %q. Please reference the image using a SHA256 tag.", container.Name, container.Image)})
} else if len(allowedRegexes) > 0 && !isInList(allowedRegexes, container.Image) {
results = append(results, diagnostic.Diagnostic{Message: fmt.Sprintf("The container %q is using an invalid container image, %q. Please reference the image using a SHA256 tag.", container.Name, container.Image)})
}
return results
}), nil
}),
})
}

// isInList returns true if a match found in the list for the given name
func isInList(regexlist []*regexp.Regexp, name string) bool {
for _, regex := range regexlist {
if regex.MatchString(name) {
return true
}
}
return false
}
79 changes: 79 additions & 0 deletions pkg/templates/imageshatag/template_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package imageshatag

import (
"testing"

"github.com/stretchr/testify/suite"

"golang.stackrox.io/kube-linter/pkg/diagnostic"
"golang.stackrox.io/kube-linter/pkg/lintcontext/mocks"
"golang.stackrox.io/kube-linter/pkg/templates"
"golang.stackrox.io/kube-linter/pkg/templates/imageshatag/internal/params"

v1 "k8s.io/api/core/v1"
)

var (
containerName = "test-container"
)

func TestContainerImage(t *testing.T) {
suite.Run(t, new(ContainerImageTestSuite))
}

type ContainerImageTestSuite struct {
templates.TemplateTestSuite

ctx *mocks.MockLintContext
}

func (s *ContainerImageTestSuite) SetupTest() {
s.Init(templateKey)
s.ctx = mocks.NewMockContext()
}

func (s *ContainerImageTestSuite) addDeploymentWithContainerImage(name, containerImage string) {
s.ctx.AddMockDeployment(s.T(), name)
s.ctx.AddContainerToDeployment(s.T(), name, v1.Container{Name: containerName, Image: containerImage})
}

func (s *ContainerImageTestSuite) TestImproperContainerImage() {
const (
depwithNotAllowedImageTag = "dep-with-not-allowed-image-tag"
)

s.addDeploymentWithContainerImage(depwithNotAllowedImageTag, "example.com/test:latest")

s.Validate(s.ctx, []templates.TestCase{
{
Param: params.Params{
AllowList: []string{".*:[a-fA-F0-9]{64}$"},
},
Diagnostics: map[string][]diagnostic.Diagnostic{
depwithNotAllowedImageTag: {
{Message: "The container \"test-container\" is using an invalid container image, \"example.com/test:latest\". Please reference the image using a SHA256 tag."},
},
},
ExpectInstantiationError: false,
},
})
}

func (s *ContainerImageTestSuite) TestAcceptableContainerImage() {
const (
depWithAcceptableImageTag = "dep-with-acceptable-container-image"
)

s.addDeploymentWithContainerImage(depWithAcceptableImageTag, "example.com/latest@sha256:75bf9b911b6481dcf29f7942240d1555adaa607eec7fc61bedb7f624f87c36d4")
s.Validate(s.ctx, []templates.TestCase{
{
Param: params.Params{
AllowList: []string{".*:[a-fA-F0-9]{64}$"},
},
Diagnostics: map[string][]diagnostic.Diagnostic{
depWithAcceptableImageTag: nil,
},
ExpectInstantiationError: false,
},
})
}

0 comments on commit 95d0c1b

Please sign in to comment.