Skip to content

Commit

Permalink
adding a new policy that allows scratch and root policy exceptions at…
Browse files Browse the repository at this point in the history
… the same time

Signed-off-by: Adam D. Cornett <[email protected]>
  • Loading branch information
acornett21 authored and bcrochet committed Jul 15, 2024
1 parent 80b05fd commit c9048da
Show file tree
Hide file tree
Showing 11 changed files with 84 additions and 19 deletions.
4 changes: 3 additions & 1 deletion cmd/preflight/cmd/list_checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ func printChecks(w io.Writer) {
fmt.Fprintln(w, formattedPolicyBlock("Container", engine.ContainerPolicy(context.TODO()), "invoked on container images"))
fmt.Fprintln(w, formattedPolicyBlock("Container Root Exception", engine.RootExceptionContainerPolicy(context.TODO()),
"automatically applied for container images if preflight determines a root exception flag has been added to your Red Hat Connect project"))
fmt.Fprintln(w, formattedPolicyBlock("Container Scratch Exception", engine.ScratchContainerPolicy(context.TODO()),
fmt.Fprintln(w, formattedPolicyBlock("Container Scratch (NonRoot) Exception", engine.ScratchNonRootContainerPolicy(context.TODO()),
"automatically applied for container checks if preflight determines a scratch exception flag has been added to your Red Hat Connect project"))
fmt.Fprintln(w, formattedPolicyBlock("Container Scratch (Root) Exception", engine.ScratchRootContainerPolicy(context.TODO()),
"automatically applied for container checks if preflight determines scratch and root exception flags have both been added to your Red Hat Connect project"))
}

// formattedPolicyBlock accepts information about the checklist
Expand Down
2 changes: 1 addition & 1 deletion cmd/preflight/cmd/list_checks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ var _ = Describe("list checks subcommand", func() {
})

It("should always contain the scratch exception policy", func() {
expected := formatList(engine.ScratchContainerPolicy(context.TODO()))
expected := formatList(engine.ScratchNonRootContainerPolicy(context.TODO()))
buf := strings.Builder{}
printChecks(&buf)

Expand Down
2 changes: 1 addition & 1 deletion container/check_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (c *containerCheck) Run(ctx context.Context) (certification.Results, error)
cfg := runtime.Config{
Image: c.image,
DockerConfig: c.dockerconfigjson,
Scratch: c.policy == policy.PolicyScratch,
Scratch: c.policy == policy.PolicyScratchNonRoot || c.policy == policy.PolicyScratchRoot,
Bundle: false,
Insecure: c.insecure,
Platform: c.platform,
Expand Down
23 changes: 18 additions & 5 deletions internal/engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -784,14 +784,21 @@ func InitializeContainerChecks(ctx context.Context, p policy.Policy, cfg Contain
cfg.CertificationProjectID,
&http.Client{Timeout: 60 * time.Second})),
}, nil
case policy.PolicyScratch:
case policy.PolicyScratchNonRoot:
return []check.Check{
&containerpol.HasLicenseCheck{},
containerpol.NewHasUniqueTagCheck(cfg.DockerConfig),
&containerpol.MaxLayersCheck{},
&containerpol.HasRequiredLabelsCheck{},
&containerpol.RunAsNonRootCheck{},
}, nil
case policy.PolicyScratchRoot:
return []check.Check{
&containerpol.HasLicenseCheck{},
containerpol.NewHasUniqueTagCheck(cfg.DockerConfig),
&containerpol.MaxLayersCheck{},
&containerpol.HasRequiredLabelsCheck{},
}, nil
}

return nil, fmt.Errorf("provided container policy %s is unknown", p)
Expand All @@ -812,7 +819,7 @@ func makeCheckList(checks []check.Check) []string {
func checkNamesFor(ctx context.Context, p policy.Policy) []string {
var c []check.Check
switch p {
case policy.PolicyContainer, policy.PolicyRoot, policy.PolicyScratch:
case policy.PolicyContainer, policy.PolicyRoot, policy.PolicyScratchNonRoot, policy.PolicyScratchRoot:
c, _ = InitializeContainerChecks(ctx, p, ContainerCheckConfig{})
case policy.PolicyOperator:
c, _ = InitializeOperatorChecks(ctx, p, OperatorCheckConfig{})
Expand All @@ -833,10 +840,16 @@ func ContainerPolicy(ctx context.Context) []string {
return checkNamesFor(ctx, policy.PolicyContainer)
}

// ScratchContainerPolicy returns the names of checks in the
// ScratchNonRootContainerPolicy returns the names of checks in the
// container policy with scratch exception.
func ScratchContainerPolicy(ctx context.Context) []string {
return checkNamesFor(ctx, policy.PolicyScratch)
func ScratchNonRootContainerPolicy(ctx context.Context) []string {
return checkNamesFor(ctx, policy.PolicyScratchNonRoot)
}

// ScratchRootContainerPolicy returns the names of checks in the
// container policy with scratch and root exception.
func ScratchRootContainerPolicy(ctx context.Context) []string {
return checkNamesFor(ctx, policy.PolicyScratchRoot)
}

// RootExceptionContainerPolicy returns the names of checks in the
Expand Down
14 changes: 12 additions & 2 deletions internal/engine/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,11 @@ var _ = Describe("Check Initialization", func() {
Expect(err).ToNot(HaveOccurred())
})
It("should properly return checks for the scratch policy", func() {
_, err := InitializeContainerChecks(context.TODO(), policy.PolicyScratch, ContainerCheckConfig{})
_, err := InitializeContainerChecks(context.TODO(), policy.PolicyScratchNonRoot, ContainerCheckConfig{})
Expect(err).ToNot(HaveOccurred())
})
It("should properly return checks for the scratch and root policy", func() {
_, err := InitializeContainerChecks(context.TODO(), policy.PolicyScratchRoot, ContainerCheckConfig{})
Expect(err).ToNot(HaveOccurred())
})
It("should properly return checks for the root policy", func() {
Expand Down Expand Up @@ -353,13 +357,19 @@ var _ = Describe("Check Name Queries", func() {
"FollowsRestrictedNetworkEnablementGuidelines",
"RequiredAnnotations",
}),
Entry("scratch container policy", ScratchContainerPolicy, []string{
Entry("scratch container policy", ScratchNonRootContainerPolicy, []string{
"HasLicense",
"HasUniqueTag",
"LayerCountAcceptable",
"HasRequiredLabel",
"RunAsNonRoot",
}),
Entry("scratch container policy", ScratchRootContainerPolicy, []string{
"HasLicense",
"HasUniqueTag",
"LayerCountAcceptable",
"HasRequiredLabel",
}),
Entry("root container policy", RootExceptionContainerPolicy, []string{
"HasLicense",
"HasUniqueTag",
Expand Down
11 changes: 11 additions & 0 deletions internal/lib/fakes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,17 @@ func gpFuncReturnRootException(ctx context.Context) (*pyxis.CertProject, error)
}, nil
}

// gpFuncReturnScratchRootException implements gpFunc and returns a root exception.
func gpFuncReturnScratchRootException(ctx context.Context) (*pyxis.CertProject, error) {
return &pyxis.CertProject{
Container: pyxis.Container{
DockerConfigJSON: "",
OsContentType: "Scratch Image",
Privileged: true,
},
}, nil
}

// gpFuncReturnNoException implements gpFunc and returns no exception indicators.
func gpFuncReturnNoException(ctx context.Context) (*pyxis.CertProject, error) {
return &pyxis.CertProject{
Expand Down
11 changes: 10 additions & 1 deletion internal/lib/lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,17 @@ func GetContainerPolicyExceptions(ctx context.Context, pc PyxisClient) (policy.P
return "", fmt.Errorf("could not retrieve project: %w", err)
}
logger.V(log.DBG).Info("certification project", "name", certProject.Name)

// if the partner has gotten a scratch exception from the business and os_content_type == "Scratch Image"
// and a partner sets `Host Level Access` in connect to `Privileged`, enable ScratchRootContainerPolicy checks
if certProject.ScratchProject() && certProject.Container.Privileged {
return policy.PolicyScratchRoot, nil
}

// if the partner has gotten a scratch exception from the business and os_content_type == "Scratch Image",
// enable ScratchNonRootContainerPolicy checks
if certProject.ScratchProject() {
return policy.PolicyScratch, nil
return policy.PolicyScratchNonRoot, nil
}

// if a partner sets `Host Level Access` in connect to `Privileged`, enable RootExceptionContainerPolicy checks
Expand Down
4 changes: 2 additions & 2 deletions internal/lib/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,12 +161,12 @@ func (s *ContainerCertificationSubmitter) Submit(ctx context.Context) error {
pol := policy.PolicyContainer

if certProject.ScratchProject() {
pol = policy.PolicyScratch
pol = policy.PolicyScratchNonRoot
}

// only read the rpm manifest file off of disk if the policy executed is not scratch
// scratch images do not have rpm manifests, the rpm-manifest.json file is not written to disk by the engine during execution
if pol != policy.PolicyScratch {
if pol != policy.PolicyScratchNonRoot {
rpmManifest, err := os.Open(path.Join(artifactWriter.Path(), check.DefaultRPMManifestFilename))
if err != nil {
return fmt.Errorf(
Expand Down
11 changes: 9 additions & 2 deletions internal/lib/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,14 @@ var _ = Describe("Policy Resolution", func() {
It("should return a scratch policy exception if the project has type flag in the API", func() {
fakePC.getProjectsFunc = gpFuncReturnScratchException
p, err := GetContainerPolicyExceptions(context.TODO(), fakePC)
Expect(p).To(Equal(policy.PolicyScratch))
Expect(p).To(Equal(policy.PolicyScratchNonRoot))
Expect(err).ToNot(HaveOccurred())
})

It("should return a scratch policy exception if the project has os_content_type flag in the API", func() {
fakePC.getProjectsFunc = gpFuncReturnScratchImageException
p, err := GetContainerPolicyExceptions(context.TODO(), fakePC)
Expect(p).To(Equal(policy.PolicyScratch))
Expect(p).To(Equal(policy.PolicyScratchNonRoot))
Expect(err).ToNot(HaveOccurred())
})

Expand All @@ -92,6 +92,13 @@ var _ = Describe("Policy Resolution", func() {
Expect(err).ToNot(HaveOccurred())
})

It("should return a scratch plus root policy exception if the project has the flag in the API", func() {
fakePC.getProjectsFunc = gpFuncReturnScratchRootException
p, err := GetContainerPolicyExceptions(context.TODO(), fakePC)
Expect(p).To(Equal(policy.PolicyScratchRoot))
Expect(err).ToNot(HaveOccurred())
})

It("should return a container policy exception if the project no exceptions in the API", func() {
fakePC.getProjectsFunc = gpFuncReturnNoException
p, err := GetContainerPolicyExceptions(context.TODO(), fakePC)
Expand Down
9 changes: 5 additions & 4 deletions internal/policy/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ package policy
type Policy = string

const (
PolicyOperator Policy = "operator"
PolicyContainer Policy = "container"
PolicyScratch Policy = "scratch"
PolicyRoot Policy = "root"
PolicyOperator Policy = "operator"
PolicyContainer Policy = "container"
PolicyScratchNonRoot Policy = "scratch-nonroot"
PolicyScratchRoot Policy = "scratch-root"
PolicyRoot Policy = "root"
)
12 changes: 12 additions & 0 deletions test/containerfiles/scratch-root-passes.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM scratch

COPY example-license.txt /licenses/

LABEL name="preflight test image scratch plus root container-policy" \
vendor="preflight test vendor" \
version="1" \
release="1" \
summary="testing the preflight tool" \
description="test the preflight tool"

USER root

0 comments on commit c9048da

Please sign in to comment.